@elasticias/ui 1.0.50 → 1.0.51
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.
- package/fesm2022/elasticias-ui.mjs +598 -91
- package/fesm2022/elasticias-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/styles/_patterns.scss +241 -9
- package/types/elasticias-ui.d.ts +219 -16
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Pipe, input, Component, output, booleanAttribute, Input, Directive, signal, computed, inject, EventEmitter, Injector, forwardRef, Output, ContentChild, HostBinding, ChangeDetectionStrategy, ElementRef, LOCALE_ID, ViewContainerRef, DestroyRef, ChangeDetectorRef, effect, HostListener, ViewChild, isDevMode, model, InjectionToken, afterNextRender, TemplateRef, ContentChildren, viewChild, untracked, contentChild, ViewEncapsulation, linkedSignal, ViewChildren, PLATFORM_ID } from '@angular/core';
|
|
2
|
+
import { Pipe, input, Component, output, booleanAttribute, Input, Directive, signal, computed, inject, EventEmitter, Injector, forwardRef, Output, ContentChild, HostBinding, ChangeDetectionStrategy, ElementRef, LOCALE_ID, ViewContainerRef, DestroyRef, ChangeDetectorRef, effect, HostListener, ViewChild, isDevMode, model, InjectionToken, afterNextRender, TemplateRef, ContentChildren, viewChild, untracked, contentChild, ViewEncapsulation, linkedSignal, ViewChildren, afterEveryRender, PLATFORM_ID } from '@angular/core';
|
|
3
3
|
import { NumberUtils, UuidUtils, PhoneUtils } from '@elasticias/utils';
|
|
4
4
|
import * as i1 from 'primeng/button';
|
|
5
5
|
import { ButtonModule } from 'primeng/button';
|
|
@@ -13,7 +13,10 @@ import * as i1$2 from 'primeng/select';
|
|
|
13
13
|
import { SelectModule } from 'primeng/select';
|
|
14
14
|
import * as i2 from 'primeng/multiselect';
|
|
15
15
|
import { MultiSelectModule } from 'primeng/multiselect';
|
|
16
|
-
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
16
|
+
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
|
17
|
+
import { merge, Subject, tap, switchMap, catchError, EMPTY } from 'rxjs';
|
|
18
|
+
import { ZIndexUtils } from 'primeng/utils';
|
|
19
|
+
import { EfViewportService, EfShortcutService, EF_PREFERENCE_STORE, formatShortcut, efTablePrefsKey, EfThemeConfigService, EF_BUILD_INFO, EfSessionIdleService, EF_SESSION, EfPermissionService, EfActiveModuleService, EF_SIDE_COLLAPSED_KEY, EfToastService } from '@elasticias/core';
|
|
17
20
|
import * as i1$4 from 'primeng/inputnumber';
|
|
18
21
|
import { InputNumberModule } from 'primeng/inputnumber';
|
|
19
22
|
import * as i1$5 from 'primeng/tooltip';
|
|
@@ -22,8 +25,6 @@ import * as i1$6 from 'primeng/dialog';
|
|
|
22
25
|
import { DialogModule, Dialog } from 'primeng/dialog';
|
|
23
26
|
import { SCREEN_REF_DATA_SERVICE, ScreenStateEnum } from '@elasticias/screens';
|
|
24
27
|
import { Permissions } from '@elasticias/types';
|
|
25
|
-
import { EfViewportService, EfShortcutService, EF_PREFERENCE_STORE, formatShortcut, efTablePrefsKey, EfThemeConfigService, EF_BUILD_INFO, EfSessionIdleService, EF_SESSION, EfPermissionService, EfActiveModuleService, EF_SIDE_COLLAPSED_KEY, EfToastService } from '@elasticias/core';
|
|
26
|
-
import { Subject, tap, switchMap, catchError, EMPTY } from 'rxjs';
|
|
27
28
|
import * as i1$7 from 'primeng/fieldset';
|
|
28
29
|
import { FieldsetModule } from 'primeng/fieldset';
|
|
29
30
|
import * as i2$2 from 'primeng/accordion';
|
|
@@ -556,6 +557,8 @@ class EfLabelComponent {
|
|
|
556
557
|
label;
|
|
557
558
|
/** Translation key — preferred. */
|
|
558
559
|
labelKey;
|
|
560
|
+
/** Interpolation params for `labelKey`. */
|
|
561
|
+
labelParams;
|
|
559
562
|
/** DOM id of the labelled input. Empty / unset → no `for` attr. */
|
|
560
563
|
for;
|
|
561
564
|
/** Caller-supplied extra class. The Comptoir `.ef-label` class is
|
|
@@ -576,11 +579,11 @@ class EfLabelComponent {
|
|
|
576
579
|
return parts.join(' ');
|
|
577
580
|
}
|
|
578
581
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfLabelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
579
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "21.2.11", type: EfLabelComponent, isStandalone: true, selector: "ef-label", inputs: { label: "label", labelKey: "labelKey", for: "for", styleClass: "styleClass", required: ["required", "required", booleanAttribute] }, ngImport: i0, template: `
|
|
582
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "21.2.11", type: EfLabelComponent, isStandalone: true, selector: "ef-label", inputs: { label: "label", labelKey: "labelKey", labelParams: "labelParams", for: "for", styleClass: "styleClass", required: ["required", "required", booleanAttribute] }, ngImport: i0, template: `
|
|
580
583
|
<label
|
|
581
584
|
[attr.for]="for || null"
|
|
582
585
|
[class]="resolvedClass"
|
|
583
|
-
>{{ labelKey ? (labelKey | translate) : label }}</label>
|
|
586
|
+
>{{ labelKey ? (labelKey | translate: labelParams) : label }}</label>
|
|
584
587
|
`, isInline: true, styles: [":host{display:contents}\n"], dependencies: [{ kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
585
588
|
}
|
|
586
589
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfLabelComponent, decorators: [{
|
|
@@ -589,12 +592,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
589
592
|
<label
|
|
590
593
|
[attr.for]="for || null"
|
|
591
594
|
[class]="resolvedClass"
|
|
592
|
-
>{{ labelKey ? (labelKey | translate) : label }}</label>
|
|
595
|
+
>{{ labelKey ? (labelKey | translate: labelParams) : label }}</label>
|
|
593
596
|
`, imports: [TranslateModule], styles: [":host{display:contents}\n"] }]
|
|
594
597
|
}], propDecorators: { label: [{
|
|
595
598
|
type: Input
|
|
596
599
|
}], labelKey: [{
|
|
597
600
|
type: Input
|
|
601
|
+
}], labelParams: [{
|
|
602
|
+
type: Input
|
|
598
603
|
}], for: [{
|
|
599
604
|
type: Input
|
|
600
605
|
}], styleClass: [{
|
|
@@ -758,9 +763,13 @@ class AbstractEfFormControl {
|
|
|
758
763
|
/** Translation key for the label — preferred (memory:
|
|
759
764
|
* feedback_i18n_keys says always use `*Key`). */
|
|
760
765
|
labelKey = input('', ...(ngDevMode ? [{ debugName: "labelKey" }] : /* istanbul ignore next */ []));
|
|
766
|
+
/** Interpolation params for `labelKey` (`{{name}}` in the translation). */
|
|
767
|
+
labelParams = input(undefined, ...(ngDevMode ? [{ debugName: "labelParams" }] : /* istanbul ignore next */ []));
|
|
761
768
|
/* ── Placeholder (i18n) ───────────────────────────────────── */
|
|
762
769
|
placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
763
770
|
placeholderKey = input('', ...(ngDevMode ? [{ debugName: "placeholderKey" }] : /* istanbul ignore next */ []));
|
|
771
|
+
/** Interpolation params for `placeholderKey` (`{{last4}}` and the like). */
|
|
772
|
+
placeholderParams = input(undefined, ...(ngDevMode ? [{ debugName: "placeholderParams" }] : /* istanbul ignore next */ []));
|
|
764
773
|
/* ── Hint (i18n) ──────────────────────────────────────────── */
|
|
765
774
|
/** Short help text shown under the control. Use `hintKey` for i18n. */
|
|
766
775
|
hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
|
|
@@ -770,6 +779,8 @@ class AbstractEfFormControl {
|
|
|
770
779
|
* Rendered by ef-input-text, ef-textarea, ef-inputnumber and ef-select;
|
|
771
780
|
* the other controls inherit the input but do not render it yet. */
|
|
772
781
|
hintKey = input('', ...(ngDevMode ? [{ debugName: "hintKey" }] : /* istanbul ignore next */ []));
|
|
782
|
+
/** Interpolation params for `hintKey`. */
|
|
783
|
+
hintParams = input(undefined, ...(ngDevMode ? [{ debugName: "hintParams" }] : /* istanbul ignore next */ []));
|
|
773
784
|
/* ── Screen-reader-only label (when no visible label) ─────── */
|
|
774
785
|
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
775
786
|
ariaLabelKey = input('', ...(ngDevMode ? [{ debugName: "ariaLabelKey" }] : /* istanbul ignore next */ []));
|
|
@@ -852,7 +863,7 @@ class AbstractEfFormControl {
|
|
|
852
863
|
/** Translated placeholder (or empty string). */
|
|
853
864
|
get effectivePlaceholder() {
|
|
854
865
|
if (this.placeholderKey()) {
|
|
855
|
-
return this.translateService.instant(this.placeholderKey());
|
|
866
|
+
return this.translateService.instant(this.placeholderKey(), this.placeholderParams());
|
|
856
867
|
}
|
|
857
868
|
return this.placeholder() || '';
|
|
858
869
|
}
|
|
@@ -876,7 +887,7 @@ class AbstractEfFormControl {
|
|
|
876
887
|
/** Translated hint (or empty string). */
|
|
877
888
|
get effectiveHint() {
|
|
878
889
|
if (this.hintKey()) {
|
|
879
|
-
return this.translateService.instant(this.hintKey());
|
|
890
|
+
return this.translateService.instant(this.hintKey(), this.hintParams());
|
|
880
891
|
}
|
|
881
892
|
return this.hint() || '';
|
|
882
893
|
}
|
|
@@ -906,12 +917,12 @@ class AbstractEfFormControl {
|
|
|
906
917
|
return `${this.effectiveId}-errors`;
|
|
907
918
|
}
|
|
908
919
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: AbstractEfFormControl, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
909
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.11", type: AbstractEfFormControl, isStandalone: true, selector: "ng-component", inputs: { inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, labelKey: { classPropertyName: "labelKey", publicName: "labelKey", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, placeholderKey: { classPropertyName: "placeholderKey", publicName: "placeholderKey", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, hintKey: { classPropertyName: "hintKey", publicName: "hintKey", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelKey: { classPropertyName: "ariaLabelKey", publicName: "ariaLabelKey", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, describedByExtra: { classPropertyName: "describedByExtra", publicName: "describedByExtra", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '', isInline: true });
|
|
920
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.11", type: AbstractEfFormControl, isStandalone: true, selector: "ng-component", inputs: { inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, labelKey: { classPropertyName: "labelKey", publicName: "labelKey", isSignal: true, isRequired: false, transformFunction: null }, labelParams: { classPropertyName: "labelParams", publicName: "labelParams", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, placeholderKey: { classPropertyName: "placeholderKey", publicName: "placeholderKey", isSignal: true, isRequired: false, transformFunction: null }, placeholderParams: { classPropertyName: "placeholderParams", publicName: "placeholderParams", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, hintKey: { classPropertyName: "hintKey", publicName: "hintKey", isSignal: true, isRequired: false, transformFunction: null }, hintParams: { classPropertyName: "hintParams", publicName: "hintParams", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelKey: { classPropertyName: "ariaLabelKey", publicName: "ariaLabelKey", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, describedByExtra: { classPropertyName: "describedByExtra", publicName: "describedByExtra", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '', isInline: true });
|
|
910
921
|
}
|
|
911
922
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: AbstractEfFormControl, decorators: [{
|
|
912
923
|
type: Component,
|
|
913
924
|
args: [{ template: '', standalone: true }]
|
|
914
|
-
}], propDecorators: { inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], labelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelKey", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], placeholderKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholderKey", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], hintKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintKey", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelKey", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClear", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], describedByExtra: [{ type: i0.Input, args: [{ isSignal: true, alias: "describedByExtra", required: false }] }] } });
|
|
925
|
+
}], propDecorators: { inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], labelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelKey", required: false }] }], labelParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelParams", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], placeholderKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholderKey", required: false }] }], placeholderParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholderParams", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], hintKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintKey", required: false }] }], hintParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintParams", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelKey", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClear", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], describedByExtra: [{ type: i0.Input, args: [{ isSignal: true, alias: "describedByExtra", required: false }] }] } });
|
|
915
926
|
|
|
916
927
|
/**
|
|
917
928
|
* Comptoir select — wraps PrimeNG `p-select` (single) or `p-multiselect`
|
|
@@ -1216,7 +1227,7 @@ class EfSelectComponent extends AbstractEfFormControl {
|
|
|
1216
1227
|
provide: NG_VALUE_ACCESSOR,
|
|
1217
1228
|
useExisting: forwardRef(() => EfSelectComponent),
|
|
1218
1229
|
multi: true,
|
|
1219
|
-
}], queries: [{ propertyName: "itemTemplate", first: true, predicate: ["item"], descendants: true }, { propertyName: "selectedItemTemplate", first: true, predicate: ["selectedItem"], descendants: true }, { propertyName: "groupTemplate", first: true, predicate: ["group"], descendants: true }, { propertyName: "headerTemplate", first: true, predicate: ["header"], descendants: true }, { propertyName: "footerTemplate", first: true, predicate: ["footer"], descendants: true }, { propertyName: "emptyTemplate", first: true, predicate: ["empty"], descendants: true }, { propertyName: "emptyFilterTemplate", first: true, predicate: ["emptyFilter"], descendants: true }, { propertyName: "filterTemplate", first: true, predicate: ["filter"], descendants: true }, { propertyName: "loaderTemplate", first: true, predicate: ["loader"], descendants: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n @if (variant === 'comptoir') {\n <!-- Comptoir variant \u2014 native <select> against the\n .ef-select-native pattern. No filter / virtualization /\n multi / templates; use the PrimeNG variant for those. -->\n <select\n class=\"ef-select-native\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [disabled]=\"isDisabled()\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n (change)=\"handleNativeSelectChange($any($event.target).value)\"\n >\n <option value=\"\" [selected]=\"value === null || value === undefined\">{{ placeholderText }}</option>\n @for (opt of filteredOptions; track $index) {\n <option\n [value]=\"optionValue ? opt[optionValue] : opt\"\n [selected]=\"(optionValue ? opt[optionValue] : opt) === value\"\n >\n {{ opt[optionLabel] }}\n </option>\n }\n </select>\n } @else if (multiple) {\n <!-- \u2500\u2500\u2500\u2500\u2500 PrimeNG multi-select branch \u2500\u2500\u2500\u2500\u2500 -->\n <p-multiselect\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (onChange)=\"handleSelectionChange($event)\"\n (onClear)=\"handleClear()\"\n (onFilter)=\"handleFilter($event)\"\n (onPanelShow)=\"handlePanelShow()\"\n (onPanelHide)=\"handlePanelHide()\"\n [options]=\"filteredOptions\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [optionDisabled]=\"optionDisabled\"\n [dataKey]=\"dataKey\"\n [group]=\"group\"\n [optionGroupLabel]=\"optionGroupLabel\"\n [optionGroupChildren]=\"optionGroupChildren\"\n [placeholder]=\"placeholderText\"\n [filter]=\"filter\"\n [filterPlaceHolder]=\"effectiveFilterPlaceholder\"\n [autofocusFilter]=\"autofocusFilter\"\n [autofocus]=\"autofocus\"\n [filterBy]=\"filterBy ?? optionLabel\"\n [showClear]=\"showClear()\"\n [disabled]=\"isDisabled()\"\n [appendTo]=\"appendTo\"\n [styleClass]=\"effectiveStyleClass\"\n [pt]=\"describedByPt\"\n [panelStyleClass]=\"effectivePanelStyleClass\"\n [scrollHeight]=\"scrollHeight\"\n [virtualScroll]=\"virtualScroll\"\n [virtualScrollItemSize]=\"virtualScrollItemSize\"\n [loading]=\"loading\"\n [emptyMessage]=\"effectiveEmptyMessage\"\n [emptyFilterMessage]=\"effectiveEmptyFilterMessage\"\n [display]=\"multiDisplay\"\n [showToggleAll]=\"showToggleAll\"\n [class.ng-invalid]=\"isInvalid\"\n [class.ng-touched]=\"ngControl?.touched\"\n fluid\n >\n @if (itemTemplate) {\n <ng-template #item let-item>\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\" />\n </ng-template>\n }\n @if (selectedItemTemplate) {\n <ng-template #selectedItems let-items>\n <ng-container *ngTemplateOutlet=\"selectedItemTemplate; context: { $implicit: items }\" />\n </ng-template>\n }\n @if (groupTemplate) {\n <ng-template #group let-group>\n <ng-container *ngTemplateOutlet=\"groupTemplate; context: { $implicit: group }\" />\n </ng-template>\n }\n @if (headerTemplate) {\n <ng-template #header>\n <ng-container *ngTemplateOutlet=\"headerTemplate\" />\n </ng-template>\n }\n @if (footerTemplate) {\n <ng-template #footer>\n <ng-container *ngTemplateOutlet=\"footerTemplate\" />\n </ng-template>\n }\n @if (emptyTemplate) {\n <ng-template #empty>\n <ng-container *ngTemplateOutlet=\"emptyTemplate\" />\n </ng-template>\n }\n @if (emptyFilterTemplate) {\n <ng-template #emptyfilter>\n <ng-container *ngTemplateOutlet=\"emptyFilterTemplate\" />\n </ng-template>\n }\n @if (filterTemplate) {\n <ng-template #filter let-options=\"options\">\n <ng-container *ngTemplateOutlet=\"filterTemplate; context: { $implicit: options }\" />\n </ng-template>\n }\n @if (loaderTemplate) {\n <ng-template #loader>\n <ng-container *ngTemplateOutlet=\"loaderTemplate\" />\n </ng-template>\n }\n <ng-template #clearicon>\n <span class=\"es-clear\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\">\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </span>\n </ng-template>\n <ng-template #dropdownicon>\n <svg class=\"ef-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n </ng-template>\n </p-multiselect>\n } @else {\n <!-- \u2500\u2500\u2500\u2500\u2500 PrimeNG single-select branch \u2500\u2500\u2500\u2500\u2500 -->\n <p-select\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (onChange)=\"handleSelectionChange($event)\"\n (onClear)=\"handleClear()\"\n (onFilter)=\"handleFilter($event)\"\n (onShow)=\"handlePanelShow()\"\n (onHide)=\"handlePanelHide()\"\n [options]=\"filteredOptions\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [optionDisabled]=\"optionDisabled\"\n [dataKey]=\"dataKey\"\n [group]=\"group\"\n [optionGroupLabel]=\"optionGroupLabel\"\n [optionGroupChildren]=\"optionGroupChildren\"\n [placeholder]=\"placeholderText\"\n [size]=\"size\"\n [filter]=\"filter\"\n [filterPlaceholder]=\"effectiveFilterPlaceholder\"\n [filterBy]=\"filterBy ?? optionLabel\"\n [autofocusFilter]=\"autofocusFilter\"\n [autofocus]=\"autofocus\"\n [showClear]=\"showClear()\"\n [editable]=\"editable\"\n [disabled]=\"isDisabled()\"\n [appendTo]=\"appendTo\"\n [styleClass]=\"effectiveStyleClass\"\n [pt]=\"describedByPt\"\n [panelStyleClass]=\"effectivePanelStyleClass\"\n [scrollHeight]=\"scrollHeight\"\n [virtualScroll]=\"virtualScroll\"\n [virtualScrollItemSize]=\"virtualScrollItemSize\"\n [loading]=\"loading\"\n [emptyMessage]=\"effectiveEmptyMessage\"\n [emptyFilterMessage]=\"effectiveEmptyFilterMessage\"\n [class.ng-invalid]=\"isInvalid\"\n [class.ng-touched]=\"ngControl?.touched\"\n fluid\n >\n @if (itemTemplate) {\n <ng-template #item let-item>\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\" />\n </ng-template>\n }\n @if (selectedItemTemplate) {\n <ng-template #selectedItem let-item>\n <ng-container *ngTemplateOutlet=\"selectedItemTemplate; context: { $implicit: item }\" />\n </ng-template>\n }\n @if (groupTemplate) {\n <ng-template #group let-group>\n <ng-container *ngTemplateOutlet=\"groupTemplate; context: { $implicit: group }\" />\n </ng-template>\n }\n @if (headerTemplate) {\n <ng-template #header>\n <ng-container *ngTemplateOutlet=\"headerTemplate\" />\n </ng-template>\n }\n @if (footerTemplate) {\n <ng-template #footer>\n <ng-container *ngTemplateOutlet=\"footerTemplate\" />\n </ng-template>\n }\n @if (emptyTemplate) {\n <ng-template #empty>\n <ng-container *ngTemplateOutlet=\"emptyTemplate\" />\n </ng-template>\n }\n @if (emptyFilterTemplate) {\n <ng-template #emptyfilter>\n <ng-container *ngTemplateOutlet=\"emptyFilterTemplate\" />\n </ng-template>\n }\n @if (filterTemplate) {\n <ng-template #filter let-options=\"options\">\n <ng-container *ngTemplateOutlet=\"filterTemplate; context: { $implicit: options }\" />\n </ng-template>\n }\n @if (loaderTemplate) {\n <ng-template #loader>\n <ng-container *ngTemplateOutlet=\"loaderTemplate\" />\n </ng-template>\n }\n <ng-template #clearicon>\n <span class=\"es-clear\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\">\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </span>\n </ng-template>\n <ng-template #dropdownicon>\n <svg class=\"ef-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n </ng-template>\n </p-select>\n }\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}\n"], dependencies: [{ kind: "ngmodule", type: SelectModule }, { kind: "component", type: i1$2.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i2.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "for", "styleClass", "required"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
1230
|
+
}], queries: [{ propertyName: "itemTemplate", first: true, predicate: ["item"], descendants: true }, { propertyName: "selectedItemTemplate", first: true, predicate: ["selectedItem"], descendants: true }, { propertyName: "groupTemplate", first: true, predicate: ["group"], descendants: true }, { propertyName: "headerTemplate", first: true, predicate: ["header"], descendants: true }, { propertyName: "footerTemplate", first: true, predicate: ["footer"], descendants: true }, { propertyName: "emptyTemplate", first: true, predicate: ["empty"], descendants: true }, { propertyName: "emptyFilterTemplate", first: true, predicate: ["emptyFilter"], descendants: true }, { propertyName: "filterTemplate", first: true, predicate: ["filter"], descendants: true }, { propertyName: "loaderTemplate", first: true, predicate: ["loader"], descendants: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n @if (variant === 'comptoir') {\n <!-- Comptoir variant \u2014 native <select> against the\n .ef-select-native pattern. No filter / virtualization /\n multi / templates; use the PrimeNG variant for those. -->\n <select\n class=\"ef-select-native\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [disabled]=\"isDisabled()\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n (change)=\"handleNativeSelectChange($any($event.target).value)\"\n >\n <option value=\"\" [selected]=\"value === null || value === undefined\">{{ placeholderText }}</option>\n @for (opt of filteredOptions; track $index) {\n <option\n [value]=\"optionValue ? opt[optionValue] : opt\"\n [selected]=\"(optionValue ? opt[optionValue] : opt) === value\"\n >\n {{ opt[optionLabel] }}\n </option>\n }\n </select>\n } @else if (multiple) {\n <!-- \u2500\u2500\u2500\u2500\u2500 PrimeNG multi-select branch \u2500\u2500\u2500\u2500\u2500 -->\n <p-multiselect\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (onChange)=\"handleSelectionChange($event)\"\n (onClear)=\"handleClear()\"\n (onFilter)=\"handleFilter($event)\"\n (onPanelShow)=\"handlePanelShow()\"\n (onPanelHide)=\"handlePanelHide()\"\n [options]=\"filteredOptions\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [optionDisabled]=\"optionDisabled\"\n [dataKey]=\"dataKey\"\n [group]=\"group\"\n [optionGroupLabel]=\"optionGroupLabel\"\n [optionGroupChildren]=\"optionGroupChildren\"\n [placeholder]=\"placeholderText\"\n [filter]=\"filter\"\n [filterPlaceHolder]=\"effectiveFilterPlaceholder\"\n [autofocusFilter]=\"autofocusFilter\"\n [autofocus]=\"autofocus\"\n [filterBy]=\"filterBy ?? optionLabel\"\n [showClear]=\"showClear()\"\n [disabled]=\"isDisabled()\"\n [appendTo]=\"appendTo\"\n [styleClass]=\"effectiveStyleClass\"\n [pt]=\"describedByPt\"\n [panelStyleClass]=\"effectivePanelStyleClass\"\n [scrollHeight]=\"scrollHeight\"\n [virtualScroll]=\"virtualScroll\"\n [virtualScrollItemSize]=\"virtualScrollItemSize\"\n [loading]=\"loading\"\n [emptyMessage]=\"effectiveEmptyMessage\"\n [emptyFilterMessage]=\"effectiveEmptyFilterMessage\"\n [display]=\"multiDisplay\"\n [showToggleAll]=\"showToggleAll\"\n [class.ng-invalid]=\"isInvalid\"\n [class.ng-touched]=\"ngControl?.touched\"\n fluid\n >\n @if (itemTemplate) {\n <ng-template #item let-item>\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\" />\n </ng-template>\n }\n @if (selectedItemTemplate) {\n <ng-template #selectedItems let-items>\n <ng-container *ngTemplateOutlet=\"selectedItemTemplate; context: { $implicit: items }\" />\n </ng-template>\n }\n @if (groupTemplate) {\n <ng-template #group let-group>\n <ng-container *ngTemplateOutlet=\"groupTemplate; context: { $implicit: group }\" />\n </ng-template>\n }\n @if (headerTemplate) {\n <ng-template #header>\n <ng-container *ngTemplateOutlet=\"headerTemplate\" />\n </ng-template>\n }\n @if (footerTemplate) {\n <ng-template #footer>\n <ng-container *ngTemplateOutlet=\"footerTemplate\" />\n </ng-template>\n }\n @if (emptyTemplate) {\n <ng-template #empty>\n <ng-container *ngTemplateOutlet=\"emptyTemplate\" />\n </ng-template>\n }\n @if (emptyFilterTemplate) {\n <ng-template #emptyfilter>\n <ng-container *ngTemplateOutlet=\"emptyFilterTemplate\" />\n </ng-template>\n }\n @if (filterTemplate) {\n <ng-template #filter let-options=\"options\">\n <ng-container *ngTemplateOutlet=\"filterTemplate; context: { $implicit: options }\" />\n </ng-template>\n }\n @if (loaderTemplate) {\n <ng-template #loader>\n <ng-container *ngTemplateOutlet=\"loaderTemplate\" />\n </ng-template>\n }\n <ng-template #clearicon>\n <span class=\"es-clear\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\">\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </span>\n </ng-template>\n <ng-template #dropdownicon>\n <svg class=\"ef-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n </ng-template>\n </p-multiselect>\n } @else {\n <!-- \u2500\u2500\u2500\u2500\u2500 PrimeNG single-select branch \u2500\u2500\u2500\u2500\u2500 -->\n <p-select\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (onChange)=\"handleSelectionChange($event)\"\n (onClear)=\"handleClear()\"\n (onFilter)=\"handleFilter($event)\"\n (onShow)=\"handlePanelShow()\"\n (onHide)=\"handlePanelHide()\"\n [options]=\"filteredOptions\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [optionDisabled]=\"optionDisabled\"\n [dataKey]=\"dataKey\"\n [group]=\"group\"\n [optionGroupLabel]=\"optionGroupLabel\"\n [optionGroupChildren]=\"optionGroupChildren\"\n [placeholder]=\"placeholderText\"\n [size]=\"size\"\n [filter]=\"filter\"\n [filterPlaceholder]=\"effectiveFilterPlaceholder\"\n [filterBy]=\"filterBy ?? optionLabel\"\n [autofocusFilter]=\"autofocusFilter\"\n [autofocus]=\"autofocus\"\n [showClear]=\"showClear()\"\n [editable]=\"editable\"\n [disabled]=\"isDisabled()\"\n [appendTo]=\"appendTo\"\n [styleClass]=\"effectiveStyleClass\"\n [pt]=\"describedByPt\"\n [panelStyleClass]=\"effectivePanelStyleClass\"\n [scrollHeight]=\"scrollHeight\"\n [virtualScroll]=\"virtualScroll\"\n [virtualScrollItemSize]=\"virtualScrollItemSize\"\n [loading]=\"loading\"\n [emptyMessage]=\"effectiveEmptyMessage\"\n [emptyFilterMessage]=\"effectiveEmptyFilterMessage\"\n [class.ng-invalid]=\"isInvalid\"\n [class.ng-touched]=\"ngControl?.touched\"\n fluid\n >\n @if (itemTemplate) {\n <ng-template #item let-item>\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\" />\n </ng-template>\n }\n @if (selectedItemTemplate) {\n <ng-template #selectedItem let-item>\n <ng-container *ngTemplateOutlet=\"selectedItemTemplate; context: { $implicit: item }\" />\n </ng-template>\n }\n @if (groupTemplate) {\n <ng-template #group let-group>\n <ng-container *ngTemplateOutlet=\"groupTemplate; context: { $implicit: group }\" />\n </ng-template>\n }\n @if (headerTemplate) {\n <ng-template #header>\n <ng-container *ngTemplateOutlet=\"headerTemplate\" />\n </ng-template>\n }\n @if (footerTemplate) {\n <ng-template #footer>\n <ng-container *ngTemplateOutlet=\"footerTemplate\" />\n </ng-template>\n }\n @if (emptyTemplate) {\n <ng-template #empty>\n <ng-container *ngTemplateOutlet=\"emptyTemplate\" />\n </ng-template>\n }\n @if (emptyFilterTemplate) {\n <ng-template #emptyfilter>\n <ng-container *ngTemplateOutlet=\"emptyFilterTemplate\" />\n </ng-template>\n }\n @if (filterTemplate) {\n <ng-template #filter let-options=\"options\">\n <ng-container *ngTemplateOutlet=\"filterTemplate; context: { $implicit: options }\" />\n </ng-template>\n }\n @if (loaderTemplate) {\n <ng-template #loader>\n <ng-container *ngTemplateOutlet=\"loaderTemplate\" />\n </ng-template>\n }\n <ng-template #clearicon>\n <span class=\"es-clear\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\">\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </span>\n </ng-template>\n <ng-template #dropdownicon>\n <svg class=\"ef-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n </ng-template>\n </p-select>\n }\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}\n"], dependencies: [{ kind: "ngmodule", type: SelectModule }, { kind: "component", type: i1$2.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i2.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "labelParams", "for", "styleClass", "required"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
1220
1231
|
}
|
|
1221
1232
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfSelectComponent, decorators: [{
|
|
1222
1233
|
type: Component,
|
|
@@ -1231,7 +1242,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
1231
1242
|
provide: NG_VALUE_ACCESSOR,
|
|
1232
1243
|
useExisting: forwardRef(() => EfSelectComponent),
|
|
1233
1244
|
multi: true,
|
|
1234
|
-
}], template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n @if (variant === 'comptoir') {\n <!-- Comptoir variant \u2014 native <select> against the\n .ef-select-native pattern. No filter / virtualization /\n multi / templates; use the PrimeNG variant for those. -->\n <select\n class=\"ef-select-native\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [disabled]=\"isDisabled()\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n (change)=\"handleNativeSelectChange($any($event.target).value)\"\n >\n <option value=\"\" [selected]=\"value === null || value === undefined\">{{ placeholderText }}</option>\n @for (opt of filteredOptions; track $index) {\n <option\n [value]=\"optionValue ? opt[optionValue] : opt\"\n [selected]=\"(optionValue ? opt[optionValue] : opt) === value\"\n >\n {{ opt[optionLabel] }}\n </option>\n }\n </select>\n } @else if (multiple) {\n <!-- \u2500\u2500\u2500\u2500\u2500 PrimeNG multi-select branch \u2500\u2500\u2500\u2500\u2500 -->\n <p-multiselect\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (onChange)=\"handleSelectionChange($event)\"\n (onClear)=\"handleClear()\"\n (onFilter)=\"handleFilter($event)\"\n (onPanelShow)=\"handlePanelShow()\"\n (onPanelHide)=\"handlePanelHide()\"\n [options]=\"filteredOptions\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [optionDisabled]=\"optionDisabled\"\n [dataKey]=\"dataKey\"\n [group]=\"group\"\n [optionGroupLabel]=\"optionGroupLabel\"\n [optionGroupChildren]=\"optionGroupChildren\"\n [placeholder]=\"placeholderText\"\n [filter]=\"filter\"\n [filterPlaceHolder]=\"effectiveFilterPlaceholder\"\n [autofocusFilter]=\"autofocusFilter\"\n [autofocus]=\"autofocus\"\n [filterBy]=\"filterBy ?? optionLabel\"\n [showClear]=\"showClear()\"\n [disabled]=\"isDisabled()\"\n [appendTo]=\"appendTo\"\n [styleClass]=\"effectiveStyleClass\"\n [pt]=\"describedByPt\"\n [panelStyleClass]=\"effectivePanelStyleClass\"\n [scrollHeight]=\"scrollHeight\"\n [virtualScroll]=\"virtualScroll\"\n [virtualScrollItemSize]=\"virtualScrollItemSize\"\n [loading]=\"loading\"\n [emptyMessage]=\"effectiveEmptyMessage\"\n [emptyFilterMessage]=\"effectiveEmptyFilterMessage\"\n [display]=\"multiDisplay\"\n [showToggleAll]=\"showToggleAll\"\n [class.ng-invalid]=\"isInvalid\"\n [class.ng-touched]=\"ngControl?.touched\"\n fluid\n >\n @if (itemTemplate) {\n <ng-template #item let-item>\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\" />\n </ng-template>\n }\n @if (selectedItemTemplate) {\n <ng-template #selectedItems let-items>\n <ng-container *ngTemplateOutlet=\"selectedItemTemplate; context: { $implicit: items }\" />\n </ng-template>\n }\n @if (groupTemplate) {\n <ng-template #group let-group>\n <ng-container *ngTemplateOutlet=\"groupTemplate; context: { $implicit: group }\" />\n </ng-template>\n }\n @if (headerTemplate) {\n <ng-template #header>\n <ng-container *ngTemplateOutlet=\"headerTemplate\" />\n </ng-template>\n }\n @if (footerTemplate) {\n <ng-template #footer>\n <ng-container *ngTemplateOutlet=\"footerTemplate\" />\n </ng-template>\n }\n @if (emptyTemplate) {\n <ng-template #empty>\n <ng-container *ngTemplateOutlet=\"emptyTemplate\" />\n </ng-template>\n }\n @if (emptyFilterTemplate) {\n <ng-template #emptyfilter>\n <ng-container *ngTemplateOutlet=\"emptyFilterTemplate\" />\n </ng-template>\n }\n @if (filterTemplate) {\n <ng-template #filter let-options=\"options\">\n <ng-container *ngTemplateOutlet=\"filterTemplate; context: { $implicit: options }\" />\n </ng-template>\n }\n @if (loaderTemplate) {\n <ng-template #loader>\n <ng-container *ngTemplateOutlet=\"loaderTemplate\" />\n </ng-template>\n }\n <ng-template #clearicon>\n <span class=\"es-clear\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\">\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </span>\n </ng-template>\n <ng-template #dropdownicon>\n <svg class=\"ef-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n </ng-template>\n </p-multiselect>\n } @else {\n <!-- \u2500\u2500\u2500\u2500\u2500 PrimeNG single-select branch \u2500\u2500\u2500\u2500\u2500 -->\n <p-select\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (onChange)=\"handleSelectionChange($event)\"\n (onClear)=\"handleClear()\"\n (onFilter)=\"handleFilter($event)\"\n (onShow)=\"handlePanelShow()\"\n (onHide)=\"handlePanelHide()\"\n [options]=\"filteredOptions\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [optionDisabled]=\"optionDisabled\"\n [dataKey]=\"dataKey\"\n [group]=\"group\"\n [optionGroupLabel]=\"optionGroupLabel\"\n [optionGroupChildren]=\"optionGroupChildren\"\n [placeholder]=\"placeholderText\"\n [size]=\"size\"\n [filter]=\"filter\"\n [filterPlaceholder]=\"effectiveFilterPlaceholder\"\n [filterBy]=\"filterBy ?? optionLabel\"\n [autofocusFilter]=\"autofocusFilter\"\n [autofocus]=\"autofocus\"\n [showClear]=\"showClear()\"\n [editable]=\"editable\"\n [disabled]=\"isDisabled()\"\n [appendTo]=\"appendTo\"\n [styleClass]=\"effectiveStyleClass\"\n [pt]=\"describedByPt\"\n [panelStyleClass]=\"effectivePanelStyleClass\"\n [scrollHeight]=\"scrollHeight\"\n [virtualScroll]=\"virtualScroll\"\n [virtualScrollItemSize]=\"virtualScrollItemSize\"\n [loading]=\"loading\"\n [emptyMessage]=\"effectiveEmptyMessage\"\n [emptyFilterMessage]=\"effectiveEmptyFilterMessage\"\n [class.ng-invalid]=\"isInvalid\"\n [class.ng-touched]=\"ngControl?.touched\"\n fluid\n >\n @if (itemTemplate) {\n <ng-template #item let-item>\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\" />\n </ng-template>\n }\n @if (selectedItemTemplate) {\n <ng-template #selectedItem let-item>\n <ng-container *ngTemplateOutlet=\"selectedItemTemplate; context: { $implicit: item }\" />\n </ng-template>\n }\n @if (groupTemplate) {\n <ng-template #group let-group>\n <ng-container *ngTemplateOutlet=\"groupTemplate; context: { $implicit: group }\" />\n </ng-template>\n }\n @if (headerTemplate) {\n <ng-template #header>\n <ng-container *ngTemplateOutlet=\"headerTemplate\" />\n </ng-template>\n }\n @if (footerTemplate) {\n <ng-template #footer>\n <ng-container *ngTemplateOutlet=\"footerTemplate\" />\n </ng-template>\n }\n @if (emptyTemplate) {\n <ng-template #empty>\n <ng-container *ngTemplateOutlet=\"emptyTemplate\" />\n </ng-template>\n }\n @if (emptyFilterTemplate) {\n <ng-template #emptyfilter>\n <ng-container *ngTemplateOutlet=\"emptyFilterTemplate\" />\n </ng-template>\n }\n @if (filterTemplate) {\n <ng-template #filter let-options=\"options\">\n <ng-container *ngTemplateOutlet=\"filterTemplate; context: { $implicit: options }\" />\n </ng-template>\n }\n @if (loaderTemplate) {\n <ng-template #loader>\n <ng-container *ngTemplateOutlet=\"loaderTemplate\" />\n </ng-template>\n }\n <ng-template #clearicon>\n <span class=\"es-clear\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\">\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </span>\n </ng-template>\n <ng-template #dropdownicon>\n <svg class=\"ef-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n </ng-template>\n </p-select>\n }\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}\n"] }]
|
|
1245
|
+
}], template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n @if (variant === 'comptoir') {\n <!-- Comptoir variant \u2014 native <select> against the\n .ef-select-native pattern. No filter / virtualization /\n multi / templates; use the PrimeNG variant for those. -->\n <select\n class=\"ef-select-native\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [disabled]=\"isDisabled()\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n (change)=\"handleNativeSelectChange($any($event.target).value)\"\n >\n <option value=\"\" [selected]=\"value === null || value === undefined\">{{ placeholderText }}</option>\n @for (opt of filteredOptions; track $index) {\n <option\n [value]=\"optionValue ? opt[optionValue] : opt\"\n [selected]=\"(optionValue ? opt[optionValue] : opt) === value\"\n >\n {{ opt[optionLabel] }}\n </option>\n }\n </select>\n } @else if (multiple) {\n <!-- \u2500\u2500\u2500\u2500\u2500 PrimeNG multi-select branch \u2500\u2500\u2500\u2500\u2500 -->\n <p-multiselect\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (onChange)=\"handleSelectionChange($event)\"\n (onClear)=\"handleClear()\"\n (onFilter)=\"handleFilter($event)\"\n (onPanelShow)=\"handlePanelShow()\"\n (onPanelHide)=\"handlePanelHide()\"\n [options]=\"filteredOptions\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [optionDisabled]=\"optionDisabled\"\n [dataKey]=\"dataKey\"\n [group]=\"group\"\n [optionGroupLabel]=\"optionGroupLabel\"\n [optionGroupChildren]=\"optionGroupChildren\"\n [placeholder]=\"placeholderText\"\n [filter]=\"filter\"\n [filterPlaceHolder]=\"effectiveFilterPlaceholder\"\n [autofocusFilter]=\"autofocusFilter\"\n [autofocus]=\"autofocus\"\n [filterBy]=\"filterBy ?? optionLabel\"\n [showClear]=\"showClear()\"\n [disabled]=\"isDisabled()\"\n [appendTo]=\"appendTo\"\n [styleClass]=\"effectiveStyleClass\"\n [pt]=\"describedByPt\"\n [panelStyleClass]=\"effectivePanelStyleClass\"\n [scrollHeight]=\"scrollHeight\"\n [virtualScroll]=\"virtualScroll\"\n [virtualScrollItemSize]=\"virtualScrollItemSize\"\n [loading]=\"loading\"\n [emptyMessage]=\"effectiveEmptyMessage\"\n [emptyFilterMessage]=\"effectiveEmptyFilterMessage\"\n [display]=\"multiDisplay\"\n [showToggleAll]=\"showToggleAll\"\n [class.ng-invalid]=\"isInvalid\"\n [class.ng-touched]=\"ngControl?.touched\"\n fluid\n >\n @if (itemTemplate) {\n <ng-template #item let-item>\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\" />\n </ng-template>\n }\n @if (selectedItemTemplate) {\n <ng-template #selectedItems let-items>\n <ng-container *ngTemplateOutlet=\"selectedItemTemplate; context: { $implicit: items }\" />\n </ng-template>\n }\n @if (groupTemplate) {\n <ng-template #group let-group>\n <ng-container *ngTemplateOutlet=\"groupTemplate; context: { $implicit: group }\" />\n </ng-template>\n }\n @if (headerTemplate) {\n <ng-template #header>\n <ng-container *ngTemplateOutlet=\"headerTemplate\" />\n </ng-template>\n }\n @if (footerTemplate) {\n <ng-template #footer>\n <ng-container *ngTemplateOutlet=\"footerTemplate\" />\n </ng-template>\n }\n @if (emptyTemplate) {\n <ng-template #empty>\n <ng-container *ngTemplateOutlet=\"emptyTemplate\" />\n </ng-template>\n }\n @if (emptyFilterTemplate) {\n <ng-template #emptyfilter>\n <ng-container *ngTemplateOutlet=\"emptyFilterTemplate\" />\n </ng-template>\n }\n @if (filterTemplate) {\n <ng-template #filter let-options=\"options\">\n <ng-container *ngTemplateOutlet=\"filterTemplate; context: { $implicit: options }\" />\n </ng-template>\n }\n @if (loaderTemplate) {\n <ng-template #loader>\n <ng-container *ngTemplateOutlet=\"loaderTemplate\" />\n </ng-template>\n }\n <ng-template #clearicon>\n <span class=\"es-clear\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\">\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </span>\n </ng-template>\n <ng-template #dropdownicon>\n <svg class=\"ef-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n </ng-template>\n </p-multiselect>\n } @else {\n <!-- \u2500\u2500\u2500\u2500\u2500 PrimeNG single-select branch \u2500\u2500\u2500\u2500\u2500 -->\n <p-select\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (onChange)=\"handleSelectionChange($event)\"\n (onClear)=\"handleClear()\"\n (onFilter)=\"handleFilter($event)\"\n (onShow)=\"handlePanelShow()\"\n (onHide)=\"handlePanelHide()\"\n [options]=\"filteredOptions\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [optionDisabled]=\"optionDisabled\"\n [dataKey]=\"dataKey\"\n [group]=\"group\"\n [optionGroupLabel]=\"optionGroupLabel\"\n [optionGroupChildren]=\"optionGroupChildren\"\n [placeholder]=\"placeholderText\"\n [size]=\"size\"\n [filter]=\"filter\"\n [filterPlaceholder]=\"effectiveFilterPlaceholder\"\n [filterBy]=\"filterBy ?? optionLabel\"\n [autofocusFilter]=\"autofocusFilter\"\n [autofocus]=\"autofocus\"\n [showClear]=\"showClear()\"\n [editable]=\"editable\"\n [disabled]=\"isDisabled()\"\n [appendTo]=\"appendTo\"\n [styleClass]=\"effectiveStyleClass\"\n [pt]=\"describedByPt\"\n [panelStyleClass]=\"effectivePanelStyleClass\"\n [scrollHeight]=\"scrollHeight\"\n [virtualScroll]=\"virtualScroll\"\n [virtualScrollItemSize]=\"virtualScrollItemSize\"\n [loading]=\"loading\"\n [emptyMessage]=\"effectiveEmptyMessage\"\n [emptyFilterMessage]=\"effectiveEmptyFilterMessage\"\n [class.ng-invalid]=\"isInvalid\"\n [class.ng-touched]=\"ngControl?.touched\"\n fluid\n >\n @if (itemTemplate) {\n <ng-template #item let-item>\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\" />\n </ng-template>\n }\n @if (selectedItemTemplate) {\n <ng-template #selectedItem let-item>\n <ng-container *ngTemplateOutlet=\"selectedItemTemplate; context: { $implicit: item }\" />\n </ng-template>\n }\n @if (groupTemplate) {\n <ng-template #group let-group>\n <ng-container *ngTemplateOutlet=\"groupTemplate; context: { $implicit: group }\" />\n </ng-template>\n }\n @if (headerTemplate) {\n <ng-template #header>\n <ng-container *ngTemplateOutlet=\"headerTemplate\" />\n </ng-template>\n }\n @if (footerTemplate) {\n <ng-template #footer>\n <ng-container *ngTemplateOutlet=\"footerTemplate\" />\n </ng-template>\n }\n @if (emptyTemplate) {\n <ng-template #empty>\n <ng-container *ngTemplateOutlet=\"emptyTemplate\" />\n </ng-template>\n }\n @if (emptyFilterTemplate) {\n <ng-template #emptyfilter>\n <ng-container *ngTemplateOutlet=\"emptyFilterTemplate\" />\n </ng-template>\n }\n @if (filterTemplate) {\n <ng-template #filter let-options=\"options\">\n <ng-container *ngTemplateOutlet=\"filterTemplate; context: { $implicit: options }\" />\n </ng-template>\n }\n @if (loaderTemplate) {\n <ng-template #loader>\n <ng-container *ngTemplateOutlet=\"loaderTemplate\" />\n </ng-template>\n }\n <ng-template #clearicon>\n <span class=\"es-clear\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\">\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </span>\n </ng-template>\n <ng-template #dropdownicon>\n <svg class=\"ef-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n </ng-template>\n </p-select>\n }\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}\n"] }]
|
|
1235
1246
|
}], propDecorators: { options: [{
|
|
1236
1247
|
type: Input
|
|
1237
1248
|
}], optionLabel: [{
|
|
@@ -1417,6 +1428,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
1417
1428
|
type: Output
|
|
1418
1429
|
}] } });
|
|
1419
1430
|
|
|
1431
|
+
/** Below this the panel is too short to use; it takes the space anyway. */
|
|
1432
|
+
const MIN_USABLE_HEIGHT = 160;
|
|
1433
|
+
function placeDpPanel(input) {
|
|
1434
|
+
const gap = input.gap ?? 8;
|
|
1435
|
+
const margin = input.margin ?? 8;
|
|
1436
|
+
const { anchor, panel, viewport, rtl } = input;
|
|
1437
|
+
// ── Block axis ──
|
|
1438
|
+
const roomBelow = viewport.height - anchor.bottom - gap - margin;
|
|
1439
|
+
const roomAbove = anchor.top - gap - margin;
|
|
1440
|
+
let side;
|
|
1441
|
+
if (panel.height <= roomBelow)
|
|
1442
|
+
side = 'below';
|
|
1443
|
+
else if (panel.height <= roomAbove)
|
|
1444
|
+
side = 'above';
|
|
1445
|
+
else
|
|
1446
|
+
side = roomAbove > roomBelow ? 'above' : 'below';
|
|
1447
|
+
const room = side === 'below' ? roomBelow : roomAbove;
|
|
1448
|
+
const maxHeight = Math.max(Math.floor(room), MIN_USABLE_HEIGHT);
|
|
1449
|
+
// ── Inline axis ──
|
|
1450
|
+
const width = Math.min(panel.width, viewport.width - 2 * margin);
|
|
1451
|
+
const startAligned = rtl ? anchor.right - width : anchor.left;
|
|
1452
|
+
const endAligned = rtl ? anchor.left : anchor.right - width;
|
|
1453
|
+
const fits = (left) => left >= margin && left + width <= viewport.width - margin;
|
|
1454
|
+
let left = fits(startAligned) ? startAligned : endAligned;
|
|
1455
|
+
left = Math.min(Math.max(left, margin), Math.max(margin, viewport.width - margin - width));
|
|
1456
|
+
return {
|
|
1457
|
+
side,
|
|
1458
|
+
top: side === 'below' ? Math.round(anchor.bottom + gap) : null,
|
|
1459
|
+
bottom: side === 'above' ? Math.round(viewport.height - anchor.top + gap) : null,
|
|
1460
|
+
left: Math.round(left),
|
|
1461
|
+
maxHeight,
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1420
1465
|
/**
|
|
1421
1466
|
* Comptoir period selector for smart-bars — a pill trigger ("Date — 30
|
|
1422
1467
|
* derniers jours v") that opens a popover with a preset list and an
|
|
@@ -1449,6 +1494,11 @@ class EfDatepickerAdvancedComponent extends AbstractEfFormControl {
|
|
|
1449
1494
|
vcr = inject(ViewContainerRef);
|
|
1450
1495
|
destroyRef = inject(DestroyRef);
|
|
1451
1496
|
cdr = inject(ChangeDetectorRef);
|
|
1497
|
+
viewport = inject(EfViewportService);
|
|
1498
|
+
/** The one picker whose panel is open on the page, if any. A click on
|
|
1499
|
+
* another picker's trigger never reaches the document (the trigger
|
|
1500
|
+
* stops it), so opening a picker closes this one explicitly. */
|
|
1501
|
+
static openPicker = null;
|
|
1452
1502
|
/**
|
|
1453
1503
|
* Self-injected form control (present only when the consumer binds
|
|
1454
1504
|
* `[(ngModel)]` / `formControlName` in field mode). We register as its
|
|
@@ -1465,7 +1515,19 @@ class EfDatepickerAdvancedComponent extends AbstractEfFormControl {
|
|
|
1465
1515
|
/** Active embedded view + its root node, while the panel is open. */
|
|
1466
1516
|
panelView = null;
|
|
1467
1517
|
panelRoot = null;
|
|
1468
|
-
|
|
1518
|
+
/** Every root node the panel template rendered (the sheet backdrop too). */
|
|
1519
|
+
portalNodes = [];
|
|
1520
|
+
repositionListener = null;
|
|
1521
|
+
resizeObserver = null;
|
|
1522
|
+
repositionFrame = 0;
|
|
1523
|
+
/** Phone width (EfViewportService, <= 767px): the panel opens as a
|
|
1524
|
+
* full-width bottom sheet with 48px targets instead of a popover. */
|
|
1525
|
+
sheet = computed(() => this.viewport.isMobile(), ...(ngDevMode ? [{ debugName: "sheet" }] : /* istanbul ignore next */ []));
|
|
1526
|
+
/** The dialog's accessible name: the field's own label in field mode
|
|
1527
|
+
* (the panel no longer repeats it as a visible title), else the
|
|
1528
|
+
* trigger label. */
|
|
1529
|
+
panelLabelKey = computed(() => this.mode() === 'field' && this.labelKey() ? this.labelKey() : this.triggerLabelKey(), ...(ngDevMode ? [{ debugName: "panelLabelKey" }] : /* istanbul ignore next */ []));
|
|
1530
|
+
panelLabel = computed(() => this.mode() === 'field' && !this.labelKey() && this.label() ? this.label() : this.triggerLabel(), ...(ngDevMode ? [{ debugName: "panelLabel" }] : /* istanbul ignore next */ []));
|
|
1469
1531
|
constructor() {
|
|
1470
1532
|
super();
|
|
1471
1533
|
// Register as our own NgControl's value accessor (field mode only).
|
|
@@ -1478,6 +1540,15 @@ class EfDatepickerAdvancedComponent extends AbstractEfFormControl {
|
|
|
1478
1540
|
else
|
|
1479
1541
|
this.detachPortal();
|
|
1480
1542
|
});
|
|
1543
|
+
// Crossing the phone breakpoint while open would leave a popover
|
|
1544
|
+
// styled as a sheet, or the reverse. Close rather than morph.
|
|
1545
|
+
let wasSheet = this.sheet();
|
|
1546
|
+
effect(() => {
|
|
1547
|
+
const isSheet = this.sheet();
|
|
1548
|
+
if (isSheet !== wasSheet && this.open())
|
|
1549
|
+
this.close();
|
|
1550
|
+
wasSheet = isSheet;
|
|
1551
|
+
});
|
|
1481
1552
|
// Last-line cleanup if the host is destroyed while the panel is open
|
|
1482
1553
|
// (route change, *ngIf collapse, …) — orphan DOM is the worst.
|
|
1483
1554
|
this.destroyRef.onDestroy(() => this.detachPortal());
|
|
@@ -1652,6 +1723,26 @@ class EfDatepickerAdvancedComponent extends AbstractEfFormControl {
|
|
|
1652
1723
|
/* ── Field mode: committed value (ControlValueAccessor) ──────────
|
|
1653
1724
|
The trigger in field mode renders THIS — the committed CVA value —
|
|
1654
1725
|
not the drafts (mid-selection) nor `resolvedRange()` (filter-only). */
|
|
1726
|
+
/** Bumps whenever the language or its translations change, so the
|
|
1727
|
+
* placeholder below re-reads the dictionary. */
|
|
1728
|
+
i18nTick = toSignal(merge(this.translate.onLangChange, this.translate.onTranslationChange), { initialValue: null });
|
|
1729
|
+
/**
|
|
1730
|
+
* Placeholder of the typed-date inputs. The parser takes ASCII digits
|
|
1731
|
+
* only, day first (`\d` and the keydown filter are [0-9/]), so the hint
|
|
1732
|
+
* spells that order in the reader's terms, in Latin letters: `JJ/MM/AAAA`
|
|
1733
|
+
* for French, `DD/MM/YYYY` otherwise, Arabic included. An Arabic-word
|
|
1734
|
+
* pattern (يوم/شهر/سنة) would lay out right to left, the reverse of the
|
|
1735
|
+
* digits the user then types. An app can override it per language with
|
|
1736
|
+
* the `date_input_placeholder` key.
|
|
1737
|
+
*/
|
|
1738
|
+
datePlaceholder = computed(() => {
|
|
1739
|
+
this.i18nTick();
|
|
1740
|
+
const key = 'date_input_placeholder';
|
|
1741
|
+
const translated = this.translate.instant(key);
|
|
1742
|
+
if (typeof translated === 'string' && translated && translated !== key)
|
|
1743
|
+
return translated;
|
|
1744
|
+
return String(this.locale).toLowerCase().startsWith('fr') ? 'JJ/MM/AAAA' : 'DD/MM/YYYY';
|
|
1745
|
+
}, ...(ngDevMode ? [{ debugName: "datePlaceholder" }] : /* istanbul ignore next */ []));
|
|
1655
1746
|
/** Committed value pushed in via `writeValue` / day-pick / Apply. */
|
|
1656
1747
|
fieldValue = signal(null, ...(ngDevMode ? [{ debugName: "fieldValue" }] : /* istanbul ignore next */ []));
|
|
1657
1748
|
/* isDisabled (the `[disabled]` input OR form-driven) and updateDisabledState
|
|
@@ -1810,7 +1901,7 @@ class EfDatepickerAdvancedComponent extends AbstractEfFormControl {
|
|
|
1810
1901
|
commitField(value) {
|
|
1811
1902
|
this.fieldValue.set(value);
|
|
1812
1903
|
this.onChange(this.toOutput(value));
|
|
1813
|
-
this.close();
|
|
1904
|
+
this.close(true);
|
|
1814
1905
|
}
|
|
1815
1906
|
/** Merge a day with a draft time. Returns start-of-day when `showTime`
|
|
1816
1907
|
* is off, so date-only commits stay time-free. */
|
|
@@ -1868,6 +1959,10 @@ class EfDatepickerAdvancedComponent extends AbstractEfFormControl {
|
|
|
1868
1959
|
return;
|
|
1869
1960
|
this.open.update(v => !v);
|
|
1870
1961
|
if (this.open()) {
|
|
1962
|
+
const previous = EfDatepickerAdvancedComponent.openPicker;
|
|
1963
|
+
if (previous && previous !== this)
|
|
1964
|
+
previous.close();
|
|
1965
|
+
EfDatepickerAdvancedComponent.openPicker = this;
|
|
1871
1966
|
this.calMode.set('days');
|
|
1872
1967
|
if (this.mode() === 'field') {
|
|
1873
1968
|
// No presets in field mode — open straight to the calendar.
|
|
@@ -1893,12 +1988,28 @@ class EfDatepickerAdvancedComponent extends AbstractEfFormControl {
|
|
|
1893
1988
|
this.closed.emit();
|
|
1894
1989
|
}
|
|
1895
1990
|
}
|
|
1896
|
-
|
|
1991
|
+
/**
|
|
1992
|
+
* Close the panel. `returnFocus` puts focus back on the trigger; Escape
|
|
1993
|
+
* and a commit pass it, so a keyboard user is not dropped on <body>
|
|
1994
|
+
* when the panel's DOM goes. An outside click or another picker opening
|
|
1995
|
+
* does not, since the user has just put focus somewhere else.
|
|
1996
|
+
*/
|
|
1997
|
+
close(returnFocus = false) {
|
|
1897
1998
|
if (!this.open())
|
|
1898
1999
|
return;
|
|
1899
2000
|
this.open.set(false);
|
|
2001
|
+
if (EfDatepickerAdvancedComponent.openPicker === this)
|
|
2002
|
+
EfDatepickerAdvancedComponent.openPicker = null;
|
|
1900
2003
|
this.onTouched();
|
|
1901
2004
|
this.closed.emit();
|
|
2005
|
+
if (returnFocus)
|
|
2006
|
+
this.focusTrigger();
|
|
2007
|
+
}
|
|
2008
|
+
/** The trigger sits behind an `@if` (field box or pill), so find it now. */
|
|
2009
|
+
focusTrigger() {
|
|
2010
|
+
this.host.nativeElement
|
|
2011
|
+
.querySelector('.dp__field-trigger, .dp__trigger')
|
|
2012
|
+
?.focus();
|
|
1902
2013
|
}
|
|
1903
2014
|
onDocumentClick(event) {
|
|
1904
2015
|
if (!this.open())
|
|
@@ -1914,7 +2025,36 @@ class EfDatepickerAdvancedComponent extends AbstractEfFormControl {
|
|
|
1914
2025
|
return;
|
|
1915
2026
|
this.close();
|
|
1916
2027
|
}
|
|
2028
|
+
/** The phone sheet is aria-modal, so Tab wraps inside it instead of
|
|
2029
|
+
* reaching the page behind the backdrop. */
|
|
2030
|
+
onTab(event) {
|
|
2031
|
+
if (event.key !== 'Tab')
|
|
2032
|
+
return;
|
|
2033
|
+
const root = this.panelRoot;
|
|
2034
|
+
if (!this.open() || !this.sheet() || !root)
|
|
2035
|
+
return;
|
|
2036
|
+
const items = Array.from(root.querySelectorAll('button, input, select, textarea, [href], [tabindex]')).filter(el => !el.hasAttribute('disabled') && el.tabIndex >= 0 && !el.closest('.dp__view:not(.active)'));
|
|
2037
|
+
if (!items.length)
|
|
2038
|
+
return;
|
|
2039
|
+
const first = items[0];
|
|
2040
|
+
const last = items[items.length - 1];
|
|
2041
|
+
const active = document.activeElement;
|
|
2042
|
+
if (!root.contains(active)) {
|
|
2043
|
+
event.preventDefault();
|
|
2044
|
+
(event.shiftKey ? last : first).focus();
|
|
2045
|
+
}
|
|
2046
|
+
else if (event.shiftKey && active === first) {
|
|
2047
|
+
event.preventDefault();
|
|
2048
|
+
last.focus();
|
|
2049
|
+
}
|
|
2050
|
+
else if (!event.shiftKey && active === last) {
|
|
2051
|
+
event.preventDefault();
|
|
2052
|
+
first.focus();
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
1917
2055
|
onEscape() {
|
|
2056
|
+
if (!this.open())
|
|
2057
|
+
return;
|
|
1918
2058
|
// Drill back out one level at a time: year/month picker → day grid
|
|
1919
2059
|
// → presets → closed.
|
|
1920
2060
|
if (this.view() === 'custom' && this.calMode() !== 'days') {
|
|
@@ -1926,7 +2066,7 @@ class EfDatepickerAdvancedComponent extends AbstractEfFormControl {
|
|
|
1926
2066
|
this.backToPresets();
|
|
1927
2067
|
return;
|
|
1928
2068
|
}
|
|
1929
|
-
this.close();
|
|
2069
|
+
this.close(true);
|
|
1930
2070
|
}
|
|
1931
2071
|
/* ── Preset actions ─────────────────────────────────────────── */
|
|
1932
2072
|
selectPreset(preset) {
|
|
@@ -1936,7 +2076,7 @@ class EfDatepickerAdvancedComponent extends AbstractEfFormControl {
|
|
|
1936
2076
|
}
|
|
1937
2077
|
const range = this.computePresetRange(preset.key);
|
|
1938
2078
|
this.rangeChange.emit(range);
|
|
1939
|
-
this.close();
|
|
2079
|
+
this.close(true);
|
|
1940
2080
|
}
|
|
1941
2081
|
isActivePreset(key) {
|
|
1942
2082
|
return (this.activePreset() ?? 'last_30_days') === key;
|
|
@@ -2049,64 +2189,141 @@ class EfDatepickerAdvancedComponent extends AbstractEfFormControl {
|
|
|
2049
2189
|
// Re-sync to canonical (zero-padded) regardless of how it was typed.
|
|
2050
2190
|
el.value = which === 'start' ? this.fromText() : this.toText();
|
|
2051
2191
|
}
|
|
2052
|
-
/* ── Portal (panel rendered as a child of <body>) ─────────────
|
|
2192
|
+
/* ── Portal (panel rendered as a child of <body>) ─────────────
|
|
2193
|
+
The panel is appended to <body>, so no ancestor's overflow (a card,
|
|
2194
|
+
a table scroller, the shell's content area) can clip it, and it is
|
|
2195
|
+
fixed to the viewport, so it can never extend the page. PrimeNG's
|
|
2196
|
+
ZIndexUtils gives it the next overlay layer: a picker opened inside
|
|
2197
|
+
a dialog (1101 and up) paints above that dialog. The CDK overlay
|
|
2198
|
+
container sits at a flat 1000, below every PrimeNG dialog, and the
|
|
2199
|
+
CDK is not a dependency of this library. */
|
|
2053
2200
|
attachPortal() {
|
|
2054
2201
|
if (this.panelView || !this.panelTpl)
|
|
2055
2202
|
return;
|
|
2056
2203
|
this.panelView = this.vcr.createEmbeddedView(this.panelTpl);
|
|
2057
2204
|
this.panelView.detectChanges();
|
|
2058
|
-
|
|
2205
|
+
this.portalNodes = this.panelView.rootNodes.filter((n) => n instanceof HTMLElement);
|
|
2206
|
+
const root = this.portalNodes.find(n => n.classList.contains('dp__panel'));
|
|
2059
2207
|
if (!root)
|
|
2060
2208
|
return;
|
|
2061
2209
|
this.panelRoot = root;
|
|
2062
|
-
|
|
2210
|
+
// The panel leaves the host's subtree, so it would lose a `dir` set
|
|
2211
|
+
// on an ancestor below <html>; carry the direction across.
|
|
2212
|
+
root.setAttribute('dir', this.isRtl() ? 'rtl' : 'ltr');
|
|
2213
|
+
for (const node of this.portalNodes) {
|
|
2214
|
+
document.body.appendChild(node);
|
|
2215
|
+
ZIndexUtils.set('overlay', node, 1000);
|
|
2216
|
+
}
|
|
2217
|
+
// The sheet is pinned to the bottom edge by CSS; only the popover
|
|
2218
|
+
// follows its trigger. On a phone the on-screen keyboard fires
|
|
2219
|
+
// resize and scroll, which must not move or close the sheet.
|
|
2220
|
+
if (this.sheet()) {
|
|
2221
|
+
this.focusIntoSheet(root);
|
|
2222
|
+
return;
|
|
2223
|
+
}
|
|
2063
2224
|
this.positionPanel();
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2225
|
+
this.repositionListener = (event) => {
|
|
2226
|
+
// A scroll inside the panel itself does not move the trigger.
|
|
2227
|
+
if (event.type === 'scroll' && event.target instanceof Node && root.contains(event.target))
|
|
2228
|
+
return;
|
|
2229
|
+
this.schedulePosition();
|
|
2230
|
+
};
|
|
2231
|
+
document.addEventListener('scroll', this.repositionListener, { capture: true, passive: true });
|
|
2232
|
+
window.addEventListener('resize', this.repositionListener, { passive: true });
|
|
2233
|
+
// The content changes height (day grid ↔ month grid, a time row):
|
|
2234
|
+
// re-decide the side so the taller view still fits.
|
|
2235
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
2236
|
+
this.resizeObserver = new ResizeObserver(() => this.schedulePosition());
|
|
2237
|
+
this.resizeObserver.observe(root);
|
|
2238
|
+
}
|
|
2068
2239
|
}
|
|
2069
2240
|
detachPortal() {
|
|
2070
|
-
if (this.
|
|
2071
|
-
|
|
2072
|
-
this.
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2241
|
+
if (this.repositionListener) {
|
|
2242
|
+
document.removeEventListener('scroll', this.repositionListener, { capture: true });
|
|
2243
|
+
window.removeEventListener('resize', this.repositionListener);
|
|
2244
|
+
this.repositionListener = null;
|
|
2245
|
+
}
|
|
2246
|
+
this.resizeObserver?.disconnect();
|
|
2247
|
+
this.resizeObserver = null;
|
|
2248
|
+
if (this.repositionFrame) {
|
|
2249
|
+
cancelAnimationFrame(this.repositionFrame);
|
|
2250
|
+
this.repositionFrame = 0;
|
|
2251
|
+
}
|
|
2252
|
+
for (const node of this.portalNodes) {
|
|
2253
|
+
ZIndexUtils.clear(node);
|
|
2254
|
+
node.parentNode?.removeChild(node);
|
|
2255
|
+
}
|
|
2256
|
+
this.portalNodes = [];
|
|
2077
2257
|
this.panelRoot = null;
|
|
2078
2258
|
if (this.panelView) {
|
|
2079
2259
|
this.panelView.destroy();
|
|
2080
2260
|
this.panelView = null;
|
|
2081
2261
|
}
|
|
2262
|
+
if (EfDatepickerAdvancedComponent.openPicker === this)
|
|
2263
|
+
EfDatepickerAdvancedComponent.openPicker = null;
|
|
2264
|
+
}
|
|
2265
|
+
/** The sheet is modal and sits at the end of <body>, far from the
|
|
2266
|
+
* trigger in tab order, so focus moves into it: the picked day, else
|
|
2267
|
+
* today, else the first day of the month. A day, not the date input,
|
|
2268
|
+
* which would raise the on-screen keyboard over the calendar. */
|
|
2269
|
+
focusIntoSheet(root) {
|
|
2270
|
+
const day = root.querySelector('.dp__view.active .dp__cal-day.range-start') ??
|
|
2271
|
+
root.querySelector('.dp__view.active .dp__cal-day.today') ??
|
|
2272
|
+
root.querySelector('.dp__view.active .dp__cal-day:not(.out)') ??
|
|
2273
|
+
root.querySelector('.dp__view.active button');
|
|
2274
|
+
day?.focus();
|
|
2275
|
+
}
|
|
2276
|
+
schedulePosition() {
|
|
2277
|
+
if (this.repositionFrame)
|
|
2278
|
+
return;
|
|
2279
|
+
this.repositionFrame = requestAnimationFrame(() => {
|
|
2280
|
+
this.repositionFrame = 0;
|
|
2281
|
+
this.positionPanel();
|
|
2282
|
+
});
|
|
2082
2283
|
}
|
|
2083
|
-
/**
|
|
2084
|
-
*
|
|
2085
|
-
*
|
|
2086
|
-
*
|
|
2087
|
-
* panel is clipped for triggers that sit near the right of the page. */
|
|
2284
|
+
/** Place the popover against the trigger (see `placeDpPanel`). Sizes
|
|
2285
|
+
* come from offsetWidth / scrollHeight, not getBoundingClientRect:
|
|
2286
|
+
* the entry animation scales the panel, and scrollHeight still tells
|
|
2287
|
+
* the natural height once a previous placement capped it. */
|
|
2088
2288
|
positionPanel() {
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
const
|
|
2093
|
-
const
|
|
2094
|
-
const
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
left = rect.right - panelWidth;
|
|
2101
|
-
}
|
|
2102
|
-
// Never let it spill past the left edge either.
|
|
2103
|
-
left = Math.max(margin, left);
|
|
2104
|
-
Object.assign(this.panelRoot.style, {
|
|
2105
|
-
position: 'absolute',
|
|
2106
|
-
top: `${window.scrollY + rect.bottom + 8}px`,
|
|
2107
|
-
left: `${window.scrollX + left}px`,
|
|
2108
|
-
insetInlineEnd: 'auto',
|
|
2289
|
+
const panel = this.panelRoot;
|
|
2290
|
+
if (!panel || this.sheet())
|
|
2291
|
+
return;
|
|
2292
|
+
const anchor = this.triggerRect();
|
|
2293
|
+
const vw = document.documentElement.clientWidth || window.innerWidth;
|
|
2294
|
+
const vh = document.documentElement.clientHeight || window.innerHeight;
|
|
2295
|
+
const place = placeDpPanel({
|
|
2296
|
+
anchor,
|
|
2297
|
+
panel: { width: panel.offsetWidth || 340, height: panel.scrollHeight || 0 },
|
|
2298
|
+
viewport: { width: vw, height: vh },
|
|
2299
|
+
rtl: this.isRtl(),
|
|
2109
2300
|
});
|
|
2301
|
+
const px = (n) => (n == null ? 'auto' : `${n}px`);
|
|
2302
|
+
// All four physical sides, so no logical inset from a stylesheet
|
|
2303
|
+
// can pull the panel across in a right-to-left page.
|
|
2304
|
+
Object.assign(panel.style, {
|
|
2305
|
+
position: 'fixed',
|
|
2306
|
+
top: px(place.top),
|
|
2307
|
+
bottom: px(place.bottom),
|
|
2308
|
+
left: px(place.left),
|
|
2309
|
+
right: 'auto',
|
|
2310
|
+
maxHeight: px(place.maxHeight),
|
|
2311
|
+
});
|
|
2312
|
+
panel.dataset['side'] = place.side;
|
|
2313
|
+
}
|
|
2314
|
+
/** The trigger button's box, not the wrapper's: the wrapper also holds
|
|
2315
|
+
* the label above and the error text below. */
|
|
2316
|
+
triggerRect() {
|
|
2317
|
+
const trigger = this.host.nativeElement.querySelector('.dp__field-trigger, .dp__trigger');
|
|
2318
|
+
return (trigger ?? this.anchorEl.nativeElement).getBoundingClientRect();
|
|
2319
|
+
}
|
|
2320
|
+
/** The nearest `dir` wins (the app sets it on <html>); computed style covers a page without one. */
|
|
2321
|
+
isRtl() {
|
|
2322
|
+
const el = this.host.nativeElement;
|
|
2323
|
+
const dir = el.closest('[dir]')?.getAttribute('dir');
|
|
2324
|
+
if (dir)
|
|
2325
|
+
return dir.toLowerCase() === 'rtl';
|
|
2326
|
+
return getComputedStyle(el).direction === 'rtl';
|
|
2110
2327
|
}
|
|
2111
2328
|
/** Click handling: 1st click sets start, 2nd click sets end (or swap).
|
|
2112
2329
|
* Field single-date mode short-circuits — one click commits & closes. */
|
|
@@ -2156,7 +2373,7 @@ class EfDatepickerAdvancedComponent extends AbstractEfFormControl {
|
|
|
2156
2373
|
return;
|
|
2157
2374
|
}
|
|
2158
2375
|
this.rangeChange.emit(this.buildRange('custom', s, e));
|
|
2159
|
-
this.close();
|
|
2376
|
+
this.close(true);
|
|
2160
2377
|
}
|
|
2161
2378
|
/** Field single-date mode: commit a typed `JJ/MM/AAAA` value & close.
|
|
2162
2379
|
* With `showTime`, only update the draft — Apply commits date + time. */
|
|
@@ -2380,11 +2597,11 @@ class EfDatepickerAdvancedComponent extends AbstractEfFormControl {
|
|
|
2380
2597
|
trackByDay = (_, day) => day.date.getTime();
|
|
2381
2598
|
trackByDow = (_, label) => label;
|
|
2382
2599
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfDatepickerAdvancedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2383
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfDatepickerAdvancedComponent, isStandalone: true, selector: "ef-datepicker-advanced", inputs: { triggerLabelKey: { classPropertyName: "triggerLabelKey", publicName: "triggerLabelKey", isSignal: true, isRequired: false, transformFunction: null }, triggerLabel: { classPropertyName: "triggerLabel", publicName: "triggerLabel", isSignal: true, isRequired: false, transformFunction: null }, presets: { classPropertyName: "presets", publicName: "presets", isSignal: true, isRequired: false, transformFunction: null }, activePreset: { classPropertyName: "activePreset", publicName: "activePreset", isSignal: true, isRequired: false, transformFunction: null }, start: { classPropertyName: "start", publicName: "start", isSignal: true, isRequired: false, transformFunction: null }, end: { classPropertyName: "end", publicName: "end", isSignal: true, isRequired: false, transformFunction: null }, allowCustomRange: { classPropertyName: "allowCustomRange", publicName: "allowCustomRange", isSignal: true, isRequired: false, transformFunction: null }, allowAllTime: { classPropertyName: "allowAllTime", publicName: "allowAllTime", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, showTime: { classPropertyName: "showTime", publicName: "showTime", isSignal: true, isRequired: false, transformFunction: null }, valueFormat: { classPropertyName: "valueFormat", publicName: "valueFormat", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rangeChange: "rangeChange", opened: "opened", closed: "closed" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown.escape": "onEscape()" }, properties: { "class.dp-host--field": "mode() === 'field'" } }, viewQueries: [{ propertyName: "anchorEl", first: true, predicate: ["anchorEl"], descendants: true, static: true }, { propertyName: "panelTpl", first: true, predicate: ["panelTpl"], descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"dp\" [class.dp--field]=\"mode() === 'field'\" #anchorEl>\n @if (mode() === 'field' && variant() === 'field') {\n <!-- Field mode \u2014 labelled field-box trigger (form control) -->\n @if (labelKey() || label()) {\n <ef-label\n [labelKey]=\"labelKey()\"\n [label]=\"label()\"\n [for]=\"effectiveId\"\n [required]=\"required()\"\n />\n }\n <div class=\"ef-field-control\">\n <button\n type=\"button\"\n class=\"dp__field-trigger\"\n [id]=\"effectiveId\"\n [class.open]=\"open()\"\n [class.is-invalid]=\"isInvalid\"\n [class.is-placeholder]=\"!hasFieldValue()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"(showRequired || serverErrors) ? errorsId : null\"\n [attr.aria-label]=\"(labelKey() || label()) ? null : (ariaLabelKey() ? (ariaLabelKey() | translate) : (ariaLabel() || null))\"\n (click)=\"toggle($event)\"\n >\n <i class=\"ico pi pi-calendar\" aria-hidden=\"true\"></i>\n <span class=\"val\">\n @if (hasFieldValue()) {\n <bdi dir=\"ltr\">{{ fieldDisplay() }}</bdi>\n } @else {\n {{ placeholderKey() ? (placeholderKey() | translate) : placeholder() }}\n }\n </span>\n <svg class=\"caret ef-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"6 9 12 15 18 9\" /></svg>\n </button>\n @if (canClear) {\n <!-- Overlaid sibling (not nested in the trigger <button>) so it\n clears instead of toggling the calendar. -->\n <ef-clear-button\n class=\"dp__field-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n </div>\n } @else {\n <!-- Pill trigger \u2014 filter mode, or field mode with variant=\"pill\" -->\n <button\n type=\"button\"\n class=\"dp__trigger\"\n [class.open]=\"open()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-label]=\"mode() === 'field' && !(labelKey() || label()) ? (ariaLabelKey() ? (ariaLabelKey() | translate) : (ariaLabel() || null)) : null\"\n (click)=\"toggle($event)\"\n >\n <i class=\"ico pi pi-calendar\" aria-hidden=\"true\"></i>\n @if (mode() === 'field') {\n <span class=\"val\">\n @if (hasFieldValue()) {\n <bdi dir=\"ltr\">{{ fieldDisplay() }}</bdi>\n } @else {\n {{ placeholderKey() ? (placeholderKey() | translate) : placeholder() }}\n }\n </span>\n } @else {\n <span class=\"lbl\">\n {{ triggerLabelKey() ? (triggerLabelKey() | translate) : triggerLabel() }}\n </span>\n <span class=\"val\">\n @if (resolvedRange().labelKey; as key) {\n {{ key | translate }}\n } @else {\n {{ resolvedRange().label }}\n }\n </span>\n }\n <svg class=\"caret ef-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"6 9 12 15 18 9\" /></svg>\n </button>\n }\n</div>\n\n<!--\n Popover panel \u2014 instantiated via ViewContainerRef + appended to\n <body> when `open()` flips on. Anchored absolutely to the trigger\n via positionPanel(); escapes ancestor overflow / stacking contexts.\n-->\n<ng-template #panelTpl>\n <div\n class=\"dp__panel open\"\n [class.wide]=\"view() === 'custom'\"\n role=\"dialog\"\n [attr.aria-label]=\"triggerLabelKey() | translate\"\n >\n <!-- \u2500\u2500 View 1 \u2014 preset list \u2500\u2500 -->\n <div class=\"dp__view\" [class.active]=\"view() === 'presets'\">\n @for (preset of effectivePresets(); track trackByPreset($index, preset)) {\n <button\n type=\"button\"\n class=\"dp__preset\"\n [class.active]=\"isActivePreset(preset.key)\"\n (click)=\"selectPreset(preset)\"\n >\n {{ preset.labelKey ? (preset.labelKey | translate) : preset.label }}\n <span class=\"hint\">{{ presetHint(preset) }}</span>\n </button>\n }\n\n @if (allowCustomRange()) {\n <div class=\"dp__divider\" role=\"separator\"></div>\n <button\n type=\"button\"\n class=\"dp__preset muted\"\n (click)=\"openCustomView()\"\n >\n <span style=\"display:inline-flex;align-items:center;gap:8px\">\n <i class=\"ico-lead pi pi-calendar\" aria-hidden=\"true\"></i>\n {{ 'date_preset_custom' | translate }}\n </span>\n <span class=\"hint\">\u21E7</span>\n </button>\n }\n </div>\n\n <!-- \u2500\u2500 View 2 \u2014 custom range with calendar \u2500\u2500 -->\n <div class=\"dp__view\" [class.active]=\"view() === 'custom'\">\n <div class=\"dp__custom-head\">\n @if (mode() !== 'field') {\n <button\n type=\"button\"\n class=\"dp__back\"\n [attr.aria-label]=\"'common_back' | translate\"\n (click)=\"backToPresets()\"\n >\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n } @else {\n <span></span>\n }\n <div class=\"dp__custom-title\">\n <span class=\"pulse-dot\"></span>\n {{ (mode() === 'field' && selectionMode() === 'single' ? 'date_select_date' : 'date_custom_range') | translate }}\n </div>\n <span></span>\n </div>\n\n <div class=\"dp__cal\">\n <div class=\"dp__cal-head\">\n <button\n type=\"button\"\n class=\"dp__cal-nav\"\n [attr.aria-label]=\"'date_prev_month' | translate\"\n (click)=\"headerPrev()\"\n >\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n <div class=\"dp__cal-title\">\n @switch (calMode()) {\n @case ('years') {\n <span class=\"dp__cal-pick is-static\">{{ yearWindowLabel() }}</span>\n }\n @case ('months') {\n <button type=\"button\" class=\"dp__cal-pick\" (click)=\"openYearPicker()\">\n {{ yearLabel() }}\n </button>\n }\n @default {\n <button type=\"button\" class=\"dp__cal-pick\" (click)=\"openMonthPicker()\">\n {{ monthLabel() }}\n </button>\n <button type=\"button\" class=\"dp__cal-pick\" (click)=\"openYearPicker()\">\n {{ yearLabel() }}\n </button>\n }\n }\n </div>\n <button\n type=\"button\"\n class=\"dp__cal-nav\"\n [attr.aria-label]=\"'date_next_month' | translate\"\n (click)=\"headerNext()\"\n >\n <i class=\"pi pi-chevron-right\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n @switch (calMode()) {\n @case ('months') {\n <div class=\"dp__cal-cells\">\n @for (cell of monthCells(); track cell.month) {\n <button\n type=\"button\"\n class=\"dp__cal-cell\"\n [class.active]=\"cell.active\"\n [class.today]=\"cell.today\"\n (click)=\"selectMonth(cell.month)\"\n >\n {{ cell.label }}\n </button>\n }\n </div>\n }\n @case ('years') {\n <div class=\"dp__cal-cells\">\n @for (cell of yearCells(); track cell.year) {\n <button\n type=\"button\"\n class=\"dp__cal-cell\"\n [class.active]=\"cell.active\"\n [class.today]=\"cell.today\"\n (click)=\"selectYear(cell.year)\"\n >\n {{ cell.year }}\n </button>\n }\n </div>\n }\n @default {\n <div class=\"dp__cal-dow-row\">\n @for (label of dowLabels(); track trackByDow($index, label)) {\n <div class=\"dp__cal-dow\">{{ label }}</div>\n }\n </div>\n\n <div class=\"dp__cal-grid\">\n @for (day of calendarGrid(); track trackByDay($index, day)) {\n <button\n type=\"button\"\n class=\"dp__cal-day\"\n [class.out]=\"day.out\"\n [class.today]=\"day.today\"\n [class.in-range]=\"day.inRange\"\n [class.range-start]=\"day.rangeStart\"\n [class.range-end]=\"day.rangeEnd\"\n [attr.aria-label]=\"day.date | date:'fullDate'\"\n (click)=\"selectDay(day.date)\"\n >\n <span class=\"day-num\">{{ day.day }}</span>\n </button>\n }\n </div>\n }\n }\n </div>\n\n @if (mode() === 'field' && selectionMode() === 'single') {\n <!-- Single-date field: date (+ time when enabled). Without time,\n picking a day commits & closes; with time, Apply commits. -->\n <div class=\"dp__inputs\" [class.dp__inputs--single]=\"!showTime()\">\n <div class=\"dp__field\">\n <label [attr.for]=\"effectiveId + '-single'\">{{ 'date_select_date' | translate }}</label>\n <input\n #singleInput\n [id]=\"effectiveId + '-single'\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"10\"\n [value]=\"fromText()\"\n placeholder=\"JJ/MM/AAAA\"\n (keydown)=\"onDateKeydown($event, singleInput)\"\n (change)=\"commitSingleInput(singleInput)\"\n />\n </div>\n @if (showTime()) {\n <div class=\"dp__field\">\n <label [attr.for]=\"effectiveId + '-single-time'\">{{ 'date_time' | translate }}</label>\n <input\n #singleTime\n [id]=\"effectiveId + '-single-time'\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"5\"\n [value]=\"fromTimeText()\"\n placeholder=\"HH:mm\"\n (keydown)=\"onTimeKeydown($event, singleTime)\"\n (change)=\"commitTimeInput('start', singleTime)\"\n />\n </div>\n }\n </div>\n } @else {\n <div class=\"dp__inputs\">\n <div class=\"dp__field\">\n <label [attr.for]=\"effectiveId + '-from'\">{{ 'date_from' | translate }}</label>\n <input\n #fromInput\n [id]=\"effectiveId + '-from'\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"10\"\n [value]=\"fromText()\"\n placeholder=\"JJ/MM/AAAA\"\n (keydown)=\"onDateKeydown($event, fromInput)\"\n (change)=\"commitDateInput('start', fromInput)\"\n />\n @if (mode() === 'field' && showTime()) {\n <input\n #fromTime\n [attr.aria-label]=\"'date_time' | translate\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"5\"\n [value]=\"fromTimeText()\"\n placeholder=\"HH:mm\"\n (keydown)=\"onTimeKeydown($event, fromTime)\"\n (change)=\"commitTimeInput('start', fromTime)\"\n />\n }\n </div>\n <div class=\"dp__field\">\n <label [attr.for]=\"effectiveId + '-to'\">{{ 'date_to' | translate }}</label>\n <input\n #toInput\n [id]=\"effectiveId + '-to'\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"10\"\n [value]=\"toText()\"\n placeholder=\"JJ/MM/AAAA\"\n (keydown)=\"onDateKeydown($event, toInput)\"\n (change)=\"commitDateInput('end', toInput)\"\n />\n @if (mode() === 'field' && showTime()) {\n <input\n #toTime\n [attr.aria-label]=\"'date_time' | translate\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"5\"\n [value]=\"toTimeText()\"\n placeholder=\"HH:mm\"\n (keydown)=\"onTimeKeydown($event, toTime)\"\n (change)=\"commitTimeInput('end', toTime)\"\n />\n }\n </div>\n </div>\n }\n\n <!-- Footer: hidden only for single field WITHOUT time (commit on pick). -->\n @if (!(mode() === 'field' && selectionMode() === 'single' && !showTime())) {\n <div class=\"dp__actions\">\n @if (mode() === 'field' && selectionMode() === 'single') {\n <span></span>\n } @else {\n <span class=\"duration\">\n <span class=\"num\">{{ draftDurationDays() }}</span>\n {{ 'date_days' | translate }}\n </span>\n }\n <div class=\"right\">\n @if (mode() !== 'field') {\n <button\n type=\"button\"\n class=\"btn btn-ghost btn-sm\"\n (click)=\"backToPresets()\"\n >\n {{ 'common_back' | translate }}\n </button>\n }\n <button\n type=\"button\"\n class=\"btn btn-primary btn-sm\"\n [disabled]=\"!draftStart()\"\n (click)=\"applyCustomRange()\"\n >\n {{ 'common_apply' | translate }}\n </button>\n </div>\n </div>\n }\n </div>\n </div>\n</ng-template>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:inline-block}:host(.dp-host--field){display:block}.hint{unicode-bidi:plaintext}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "for", "styleClass", "required"] }, { kind: "component", type: EfClearButtonComponent, selector: "ef-clear-button", inputs: ["ariaLabel", "disabled"], outputs: ["clear"] }, { kind: "pipe", type: i1$3.DatePipe, name: "date" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2600
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfDatepickerAdvancedComponent, isStandalone: true, selector: "ef-datepicker-advanced", inputs: { triggerLabelKey: { classPropertyName: "triggerLabelKey", publicName: "triggerLabelKey", isSignal: true, isRequired: false, transformFunction: null }, triggerLabel: { classPropertyName: "triggerLabel", publicName: "triggerLabel", isSignal: true, isRequired: false, transformFunction: null }, presets: { classPropertyName: "presets", publicName: "presets", isSignal: true, isRequired: false, transformFunction: null }, activePreset: { classPropertyName: "activePreset", publicName: "activePreset", isSignal: true, isRequired: false, transformFunction: null }, start: { classPropertyName: "start", publicName: "start", isSignal: true, isRequired: false, transformFunction: null }, end: { classPropertyName: "end", publicName: "end", isSignal: true, isRequired: false, transformFunction: null }, allowCustomRange: { classPropertyName: "allowCustomRange", publicName: "allowCustomRange", isSignal: true, isRequired: false, transformFunction: null }, allowAllTime: { classPropertyName: "allowAllTime", publicName: "allowAllTime", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, showTime: { classPropertyName: "showTime", publicName: "showTime", isSignal: true, isRequired: false, transformFunction: null }, valueFormat: { classPropertyName: "valueFormat", publicName: "valueFormat", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rangeChange: "rangeChange", opened: "opened", closed: "closed" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown": "onTab($event)", "document:keydown.escape": "onEscape()" }, properties: { "class.dp-host--field": "mode() === 'field'" } }, viewQueries: [{ propertyName: "anchorEl", first: true, predicate: ["anchorEl"], descendants: true, static: true }, { propertyName: "panelTpl", first: true, predicate: ["panelTpl"], descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"dp\" [class.dp--field]=\"mode() === 'field'\" #anchorEl>\n @if (mode() === 'field' && variant() === 'field') {\n <!-- Field mode \u2014 labelled field-box trigger (form control) -->\n @if (labelKey() || label()) {\n <ef-label\n [labelKey]=\"labelKey()\"\n [label]=\"label()\"\n [for]=\"effectiveId\"\n [required]=\"required()\"\n />\n }\n <div class=\"ef-field-control\">\n <button\n type=\"button\"\n class=\"dp__field-trigger\"\n [id]=\"effectiveId\"\n [class.open]=\"open()\"\n [class.is-invalid]=\"isInvalid\"\n [class.is-placeholder]=\"!hasFieldValue()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"(showRequired || serverErrors) ? errorsId : null\"\n [attr.aria-label]=\"(labelKey() || label()) ? null : (ariaLabelKey() ? (ariaLabelKey() | translate) : (ariaLabel() || null))\"\n (click)=\"toggle($event)\"\n >\n <i class=\"ico pi pi-calendar\" aria-hidden=\"true\"></i>\n <span class=\"val\">\n @if (hasFieldValue()) {\n <bdi dir=\"ltr\">{{ fieldDisplay() }}</bdi>\n } @else {\n {{ placeholderKey() ? (placeholderKey() | translate) : placeholder() }}\n }\n </span>\n <svg class=\"caret ef-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"6 9 12 15 18 9\" /></svg>\n </button>\n @if (canClear) {\n <!-- Overlaid sibling (not nested in the trigger <button>) so it\n clears instead of toggling the calendar. -->\n <ef-clear-button\n class=\"dp__field-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n </div>\n } @else {\n <!-- Pill trigger \u2014 filter mode, or field mode with variant=\"pill\" -->\n <button\n type=\"button\"\n class=\"dp__trigger\"\n [class.open]=\"open()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-label]=\"mode() === 'field' && !(labelKey() || label()) ? (ariaLabelKey() ? (ariaLabelKey() | translate) : (ariaLabel() || null)) : null\"\n (click)=\"toggle($event)\"\n >\n <i class=\"ico pi pi-calendar\" aria-hidden=\"true\"></i>\n @if (mode() === 'field') {\n <span class=\"val\">\n @if (hasFieldValue()) {\n <bdi dir=\"ltr\">{{ fieldDisplay() }}</bdi>\n } @else {\n {{ placeholderKey() ? (placeholderKey() | translate) : placeholder() }}\n }\n </span>\n } @else {\n <span class=\"lbl\">\n {{ triggerLabelKey() ? (triggerLabelKey() | translate) : triggerLabel() }}\n </span>\n <span class=\"val\">\n @if (resolvedRange().labelKey; as key) {\n {{ key | translate }}\n } @else {\n {{ resolvedRange().label }}\n }\n </span>\n }\n <svg class=\"caret ef-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"6 9 12 15 18 9\" /></svg>\n </button>\n }\n</div>\n\n<!--\n Popover panel \u2014 instantiated via ViewContainerRef + appended to\n <body> when `open()` flips on. Fixed to the viewport and placed\n against the trigger by positionPanel() (flips above, keeps inside the\n viewport); on a phone it is a bottom sheet over a backdrop instead.\n-->\n<ng-template #panelTpl>\n @if (sheet()) {\n <div class=\"dp__backdrop\" aria-hidden=\"true\" (click)=\"close(true)\"></div>\n }\n <div\n class=\"dp__panel open\"\n [class.wide]=\"view() === 'custom'\"\n [class.dp__panel--sheet]=\"sheet()\"\n role=\"dialog\"\n [attr.aria-modal]=\"sheet() ? 'true' : null\"\n [attr.aria-label]=\"panelLabelKey() ? (panelLabelKey() | translate) : panelLabel()\"\n >\n <!-- \u2500\u2500 View 1 \u2014 preset list \u2500\u2500 -->\n <div class=\"dp__view\" [class.active]=\"view() === 'presets'\">\n @for (preset of effectivePresets(); track trackByPreset($index, preset)) {\n <button\n type=\"button\"\n class=\"dp__preset\"\n [class.active]=\"isActivePreset(preset.key)\"\n (click)=\"selectPreset(preset)\"\n >\n {{ preset.labelKey ? (preset.labelKey | translate) : preset.label }}\n <span class=\"hint\">{{ presetHint(preset) }}</span>\n </button>\n }\n\n @if (allowCustomRange()) {\n <div class=\"dp__divider\" role=\"separator\"></div>\n <button\n type=\"button\"\n class=\"dp__preset muted\"\n (click)=\"openCustomView()\"\n >\n <span style=\"display:inline-flex;align-items:center;gap:8px\">\n <i class=\"ico-lead pi pi-calendar\" aria-hidden=\"true\"></i>\n {{ 'date_preset_custom' | translate }}\n </span>\n <span class=\"hint\">\u21E7</span>\n </button>\n }\n </div>\n\n <!-- \u2500\u2500 View 2 \u2014 custom range with calendar \u2500\u2500 -->\n <div class=\"dp__view\" [class.active]=\"view() === 'custom'\">\n <!-- A single-date field drops this title: the field's own label\n names the dialog (aria-label above), and the date input below\n carries the same words, so \"S\u00E9lectionner une date\" read twice. -->\n @if (!(mode() === 'field' && selectionMode() === 'single')) {\n <div class=\"dp__custom-head\">\n @if (mode() !== 'field') {\n <button\n type=\"button\"\n class=\"dp__back\"\n [attr.aria-label]=\"'common_back' | translate\"\n (click)=\"backToPresets()\"\n >\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n } @else {\n <span></span>\n }\n <div class=\"dp__custom-title\">\n <span class=\"pulse-dot\"></span>\n {{ 'date_custom_range' | translate }}\n </div>\n <span></span>\n </div>\n }\n\n <div class=\"dp__cal\">\n <div class=\"dp__cal-head\">\n <button\n type=\"button\"\n class=\"dp__cal-nav\"\n [attr.aria-label]=\"'date_prev_month' | translate\"\n (click)=\"headerPrev()\"\n >\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n <div class=\"dp__cal-title\">\n @switch (calMode()) {\n @case ('years') {\n <span class=\"dp__cal-pick is-static\">{{ yearWindowLabel() }}</span>\n }\n @case ('months') {\n <button type=\"button\" class=\"dp__cal-pick\" (click)=\"openYearPicker()\">\n {{ yearLabel() }}\n </button>\n }\n @default {\n <button type=\"button\" class=\"dp__cal-pick\" (click)=\"openMonthPicker()\">\n {{ monthLabel() }}\n </button>\n <button type=\"button\" class=\"dp__cal-pick\" (click)=\"openYearPicker()\">\n {{ yearLabel() }}\n </button>\n }\n }\n </div>\n <button\n type=\"button\"\n class=\"dp__cal-nav\"\n [attr.aria-label]=\"'date_next_month' | translate\"\n (click)=\"headerNext()\"\n >\n <i class=\"pi pi-chevron-right\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n @switch (calMode()) {\n @case ('months') {\n <div class=\"dp__cal-cells\">\n @for (cell of monthCells(); track cell.month) {\n <button\n type=\"button\"\n class=\"dp__cal-cell\"\n [class.active]=\"cell.active\"\n [class.today]=\"cell.today\"\n (click)=\"selectMonth(cell.month)\"\n >\n {{ cell.label }}\n </button>\n }\n </div>\n }\n @case ('years') {\n <div class=\"dp__cal-cells\">\n @for (cell of yearCells(); track cell.year) {\n <button\n type=\"button\"\n class=\"dp__cal-cell\"\n [class.active]=\"cell.active\"\n [class.today]=\"cell.today\"\n (click)=\"selectYear(cell.year)\"\n >\n {{ cell.year }}\n </button>\n }\n </div>\n }\n @default {\n <div class=\"dp__cal-dow-row\">\n @for (label of dowLabels(); track trackByDow($index, label)) {\n <div class=\"dp__cal-dow\">{{ label }}</div>\n }\n </div>\n\n <div class=\"dp__cal-grid\">\n @for (day of calendarGrid(); track trackByDay($index, day)) {\n <button\n type=\"button\"\n class=\"dp__cal-day\"\n [class.out]=\"day.out\"\n [class.today]=\"day.today\"\n [class.in-range]=\"day.inRange\"\n [class.range-start]=\"day.rangeStart\"\n [class.range-end]=\"day.rangeEnd\"\n [attr.aria-label]=\"day.date | date:'fullDate'\"\n (click)=\"selectDay(day.date)\"\n >\n <span class=\"day-num\">{{ day.day }}</span>\n </button>\n }\n </div>\n }\n }\n </div>\n\n @if (mode() === 'field' && selectionMode() === 'single') {\n <!-- Single-date field: date (+ time when enabled). Without time,\n picking a day commits & closes; with time, Apply commits. -->\n <div class=\"dp__inputs\" [class.dp__inputs--single]=\"!showTime()\">\n <div class=\"dp__field\">\n <label [attr.for]=\"effectiveId + '-single'\">{{ 'date_select_date' | translate }}</label>\n <input\n #singleInput\n [id]=\"effectiveId + '-single'\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"10\"\n [value]=\"fromText()\"\n [placeholder]=\"datePlaceholder()\"\n (keydown)=\"onDateKeydown($event, singleInput)\"\n (change)=\"commitSingleInput(singleInput)\"\n />\n </div>\n @if (showTime()) {\n <div class=\"dp__field\">\n <label [attr.for]=\"effectiveId + '-single-time'\">{{ 'date_time' | translate }}</label>\n <input\n #singleTime\n [id]=\"effectiveId + '-single-time'\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"5\"\n [value]=\"fromTimeText()\"\n placeholder=\"HH:mm\"\n (keydown)=\"onTimeKeydown($event, singleTime)\"\n (change)=\"commitTimeInput('start', singleTime)\"\n />\n </div>\n }\n </div>\n } @else {\n <div class=\"dp__inputs\">\n <div class=\"dp__field\">\n <label [attr.for]=\"effectiveId + '-from'\">{{ 'date_from' | translate }}</label>\n <input\n #fromInput\n [id]=\"effectiveId + '-from'\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"10\"\n [value]=\"fromText()\"\n [placeholder]=\"datePlaceholder()\"\n (keydown)=\"onDateKeydown($event, fromInput)\"\n (change)=\"commitDateInput('start', fromInput)\"\n />\n @if (mode() === 'field' && showTime()) {\n <input\n #fromTime\n [attr.aria-label]=\"'date_time' | translate\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"5\"\n [value]=\"fromTimeText()\"\n placeholder=\"HH:mm\"\n (keydown)=\"onTimeKeydown($event, fromTime)\"\n (change)=\"commitTimeInput('start', fromTime)\"\n />\n }\n </div>\n <div class=\"dp__field\">\n <label [attr.for]=\"effectiveId + '-to'\">{{ 'date_to' | translate }}</label>\n <input\n #toInput\n [id]=\"effectiveId + '-to'\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"10\"\n [value]=\"toText()\"\n [placeholder]=\"datePlaceholder()\"\n (keydown)=\"onDateKeydown($event, toInput)\"\n (change)=\"commitDateInput('end', toInput)\"\n />\n @if (mode() === 'field' && showTime()) {\n <input\n #toTime\n [attr.aria-label]=\"'date_time' | translate\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"5\"\n [value]=\"toTimeText()\"\n placeholder=\"HH:mm\"\n (keydown)=\"onTimeKeydown($event, toTime)\"\n (change)=\"commitTimeInput('end', toTime)\"\n />\n }\n </div>\n </div>\n }\n\n <!-- Footer: hidden only for single field WITHOUT time (commit on pick). -->\n @if (!(mode() === 'field' && selectionMode() === 'single' && !showTime())) {\n <div class=\"dp__actions\">\n @if (mode() === 'field' && selectionMode() === 'single') {\n <span></span>\n } @else {\n <span class=\"duration\">\n <span class=\"num\">{{ draftDurationDays() }}</span>\n {{ 'date_days' | translate }}\n </span>\n }\n <div class=\"right\">\n @if (mode() !== 'field') {\n <button\n type=\"button\"\n class=\"btn btn-ghost btn-sm\"\n (click)=\"backToPresets()\"\n >\n {{ 'common_back' | translate }}\n </button>\n }\n <button\n type=\"button\"\n class=\"btn btn-primary btn-sm\"\n [disabled]=\"!draftStart()\"\n (click)=\"applyCustomRange()\"\n >\n {{ 'common_apply' | translate }}\n </button>\n </div>\n </div>\n }\n </div>\n </div>\n</ng-template>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:inline-block}:host(.dp-host--field){display:block}.hint{unicode-bidi:plaintext}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "labelParams", "for", "styleClass", "required"] }, { kind: "component", type: EfClearButtonComponent, selector: "ef-clear-button", inputs: ["ariaLabel", "disabled"], outputs: ["clear"] }, { kind: "pipe", type: i1$3.DatePipe, name: "date" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2384
2601
|
}
|
|
2385
2602
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfDatepickerAdvancedComponent, decorators: [{
|
|
2386
2603
|
type: Component,
|
|
2387
|
-
args: [{ selector: 'ef-datepicker-advanced', standalone: true, imports: [CommonModule, TranslateModule, EfLabelComponent, EfClearButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class.dp-host--field]': "mode() === 'field'" }, template: "<div class=\"dp\" [class.dp--field]=\"mode() === 'field'\" #anchorEl>\n @if (mode() === 'field' && variant() === 'field') {\n <!-- Field mode \u2014 labelled field-box trigger (form control) -->\n @if (labelKey() || label()) {\n <ef-label\n [labelKey]=\"labelKey()\"\n [label]=\"label()\"\n [for]=\"effectiveId\"\n [required]=\"required()\"\n />\n }\n <div class=\"ef-field-control\">\n <button\n type=\"button\"\n class=\"dp__field-trigger\"\n [id]=\"effectiveId\"\n [class.open]=\"open()\"\n [class.is-invalid]=\"isInvalid\"\n [class.is-placeholder]=\"!hasFieldValue()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"(showRequired || serverErrors) ? errorsId : null\"\n [attr.aria-label]=\"(labelKey() || label()) ? null : (ariaLabelKey() ? (ariaLabelKey() | translate) : (ariaLabel() || null))\"\n (click)=\"toggle($event)\"\n >\n <i class=\"ico pi pi-calendar\" aria-hidden=\"true\"></i>\n <span class=\"val\">\n @if (hasFieldValue()) {\n <bdi dir=\"ltr\">{{ fieldDisplay() }}</bdi>\n } @else {\n {{ placeholderKey() ? (placeholderKey() | translate) : placeholder() }}\n }\n </span>\n <svg class=\"caret ef-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"6 9 12 15 18 9\" /></svg>\n </button>\n @if (canClear) {\n <!-- Overlaid sibling (not nested in the trigger <button>) so it\n clears instead of toggling the calendar. -->\n <ef-clear-button\n class=\"dp__field-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n </div>\n } @else {\n <!-- Pill trigger \u2014 filter mode, or field mode with variant=\"pill\" -->\n <button\n type=\"button\"\n class=\"dp__trigger\"\n [class.open]=\"open()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-label]=\"mode() === 'field' && !(labelKey() || label()) ? (ariaLabelKey() ? (ariaLabelKey() | translate) : (ariaLabel() || null)) : null\"\n (click)=\"toggle($event)\"\n >\n <i class=\"ico pi pi-calendar\" aria-hidden=\"true\"></i>\n @if (mode() === 'field') {\n <span class=\"val\">\n @if (hasFieldValue()) {\n <bdi dir=\"ltr\">{{ fieldDisplay() }}</bdi>\n } @else {\n {{ placeholderKey() ? (placeholderKey() | translate) : placeholder() }}\n }\n </span>\n } @else {\n <span class=\"lbl\">\n {{ triggerLabelKey() ? (triggerLabelKey() | translate) : triggerLabel() }}\n </span>\n <span class=\"val\">\n @if (resolvedRange().labelKey; as key) {\n {{ key | translate }}\n } @else {\n {{ resolvedRange().label }}\n }\n </span>\n }\n <svg class=\"caret ef-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"6 9 12 15 18 9\" /></svg>\n </button>\n }\n</div>\n\n<!--\n Popover panel \u2014 instantiated via ViewContainerRef + appended to\n <body> when `open()` flips on. Anchored absolutely to the trigger\n via positionPanel(); escapes ancestor overflow / stacking contexts.\n-->\n<ng-template #panelTpl>\n <div\n class=\"dp__panel open\"\n [class.wide]=\"view() === 'custom'\"\n role=\"dialog\"\n [attr.aria-label]=\"triggerLabelKey() | translate\"\n >\n <!-- \u2500\u2500 View 1 \u2014 preset list \u2500\u2500 -->\n <div class=\"dp__view\" [class.active]=\"view() === 'presets'\">\n @for (preset of effectivePresets(); track trackByPreset($index, preset)) {\n <button\n type=\"button\"\n class=\"dp__preset\"\n [class.active]=\"isActivePreset(preset.key)\"\n (click)=\"selectPreset(preset)\"\n >\n {{ preset.labelKey ? (preset.labelKey | translate) : preset.label }}\n <span class=\"hint\">{{ presetHint(preset) }}</span>\n </button>\n }\n\n @if (allowCustomRange()) {\n <div class=\"dp__divider\" role=\"separator\"></div>\n <button\n type=\"button\"\n class=\"dp__preset muted\"\n (click)=\"openCustomView()\"\n >\n <span style=\"display:inline-flex;align-items:center;gap:8px\">\n <i class=\"ico-lead pi pi-calendar\" aria-hidden=\"true\"></i>\n {{ 'date_preset_custom' | translate }}\n </span>\n <span class=\"hint\">\u21E7</span>\n </button>\n }\n </div>\n\n <!-- \u2500\u2500 View 2 \u2014 custom range with calendar \u2500\u2500 -->\n <div class=\"dp__view\" [class.active]=\"view() === 'custom'\">\n <div class=\"dp__custom-head\">\n @if (mode() !== 'field') {\n <button\n type=\"button\"\n class=\"dp__back\"\n [attr.aria-label]=\"'common_back' | translate\"\n (click)=\"backToPresets()\"\n >\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n } @else {\n <span></span>\n }\n <div class=\"dp__custom-title\">\n <span class=\"pulse-dot\"></span>\n {{ (mode() === 'field' && selectionMode() === 'single' ? 'date_select_date' : 'date_custom_range') | translate }}\n </div>\n <span></span>\n </div>\n\n <div class=\"dp__cal\">\n <div class=\"dp__cal-head\">\n <button\n type=\"button\"\n class=\"dp__cal-nav\"\n [attr.aria-label]=\"'date_prev_month' | translate\"\n (click)=\"headerPrev()\"\n >\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n <div class=\"dp__cal-title\">\n @switch (calMode()) {\n @case ('years') {\n <span class=\"dp__cal-pick is-static\">{{ yearWindowLabel() }}</span>\n }\n @case ('months') {\n <button type=\"button\" class=\"dp__cal-pick\" (click)=\"openYearPicker()\">\n {{ yearLabel() }}\n </button>\n }\n @default {\n <button type=\"button\" class=\"dp__cal-pick\" (click)=\"openMonthPicker()\">\n {{ monthLabel() }}\n </button>\n <button type=\"button\" class=\"dp__cal-pick\" (click)=\"openYearPicker()\">\n {{ yearLabel() }}\n </button>\n }\n }\n </div>\n <button\n type=\"button\"\n class=\"dp__cal-nav\"\n [attr.aria-label]=\"'date_next_month' | translate\"\n (click)=\"headerNext()\"\n >\n <i class=\"pi pi-chevron-right\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n @switch (calMode()) {\n @case ('months') {\n <div class=\"dp__cal-cells\">\n @for (cell of monthCells(); track cell.month) {\n <button\n type=\"button\"\n class=\"dp__cal-cell\"\n [class.active]=\"cell.active\"\n [class.today]=\"cell.today\"\n (click)=\"selectMonth(cell.month)\"\n >\n {{ cell.label }}\n </button>\n }\n </div>\n }\n @case ('years') {\n <div class=\"dp__cal-cells\">\n @for (cell of yearCells(); track cell.year) {\n <button\n type=\"button\"\n class=\"dp__cal-cell\"\n [class.active]=\"cell.active\"\n [class.today]=\"cell.today\"\n (click)=\"selectYear(cell.year)\"\n >\n {{ cell.year }}\n </button>\n }\n </div>\n }\n @default {\n <div class=\"dp__cal-dow-row\">\n @for (label of dowLabels(); track trackByDow($index, label)) {\n <div class=\"dp__cal-dow\">{{ label }}</div>\n }\n </div>\n\n <div class=\"dp__cal-grid\">\n @for (day of calendarGrid(); track trackByDay($index, day)) {\n <button\n type=\"button\"\n class=\"dp__cal-day\"\n [class.out]=\"day.out\"\n [class.today]=\"day.today\"\n [class.in-range]=\"day.inRange\"\n [class.range-start]=\"day.rangeStart\"\n [class.range-end]=\"day.rangeEnd\"\n [attr.aria-label]=\"day.date | date:'fullDate'\"\n (click)=\"selectDay(day.date)\"\n >\n <span class=\"day-num\">{{ day.day }}</span>\n </button>\n }\n </div>\n }\n }\n </div>\n\n @if (mode() === 'field' && selectionMode() === 'single') {\n <!-- Single-date field: date (+ time when enabled). Without time,\n picking a day commits & closes; with time, Apply commits. -->\n <div class=\"dp__inputs\" [class.dp__inputs--single]=\"!showTime()\">\n <div class=\"dp__field\">\n <label [attr.for]=\"effectiveId + '-single'\">{{ 'date_select_date' | translate }}</label>\n <input\n #singleInput\n [id]=\"effectiveId + '-single'\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"10\"\n [value]=\"fromText()\"\n placeholder=\"JJ/MM/AAAA\"\n (keydown)=\"onDateKeydown($event, singleInput)\"\n (change)=\"commitSingleInput(singleInput)\"\n />\n </div>\n @if (showTime()) {\n <div class=\"dp__field\">\n <label [attr.for]=\"effectiveId + '-single-time'\">{{ 'date_time' | translate }}</label>\n <input\n #singleTime\n [id]=\"effectiveId + '-single-time'\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"5\"\n [value]=\"fromTimeText()\"\n placeholder=\"HH:mm\"\n (keydown)=\"onTimeKeydown($event, singleTime)\"\n (change)=\"commitTimeInput('start', singleTime)\"\n />\n </div>\n }\n </div>\n } @else {\n <div class=\"dp__inputs\">\n <div class=\"dp__field\">\n <label [attr.for]=\"effectiveId + '-from'\">{{ 'date_from' | translate }}</label>\n <input\n #fromInput\n [id]=\"effectiveId + '-from'\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"10\"\n [value]=\"fromText()\"\n placeholder=\"JJ/MM/AAAA\"\n (keydown)=\"onDateKeydown($event, fromInput)\"\n (change)=\"commitDateInput('start', fromInput)\"\n />\n @if (mode() === 'field' && showTime()) {\n <input\n #fromTime\n [attr.aria-label]=\"'date_time' | translate\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"5\"\n [value]=\"fromTimeText()\"\n placeholder=\"HH:mm\"\n (keydown)=\"onTimeKeydown($event, fromTime)\"\n (change)=\"commitTimeInput('start', fromTime)\"\n />\n }\n </div>\n <div class=\"dp__field\">\n <label [attr.for]=\"effectiveId + '-to'\">{{ 'date_to' | translate }}</label>\n <input\n #toInput\n [id]=\"effectiveId + '-to'\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"10\"\n [value]=\"toText()\"\n placeholder=\"JJ/MM/AAAA\"\n (keydown)=\"onDateKeydown($event, toInput)\"\n (change)=\"commitDateInput('end', toInput)\"\n />\n @if (mode() === 'field' && showTime()) {\n <input\n #toTime\n [attr.aria-label]=\"'date_time' | translate\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"5\"\n [value]=\"toTimeText()\"\n placeholder=\"HH:mm\"\n (keydown)=\"onTimeKeydown($event, toTime)\"\n (change)=\"commitTimeInput('end', toTime)\"\n />\n }\n </div>\n </div>\n }\n\n <!-- Footer: hidden only for single field WITHOUT time (commit on pick). -->\n @if (!(mode() === 'field' && selectionMode() === 'single' && !showTime())) {\n <div class=\"dp__actions\">\n @if (mode() === 'field' && selectionMode() === 'single') {\n <span></span>\n } @else {\n <span class=\"duration\">\n <span class=\"num\">{{ draftDurationDays() }}</span>\n {{ 'date_days' | translate }}\n </span>\n }\n <div class=\"right\">\n @if (mode() !== 'field') {\n <button\n type=\"button\"\n class=\"btn btn-ghost btn-sm\"\n (click)=\"backToPresets()\"\n >\n {{ 'common_back' | translate }}\n </button>\n }\n <button\n type=\"button\"\n class=\"btn btn-primary btn-sm\"\n [disabled]=\"!draftStart()\"\n (click)=\"applyCustomRange()\"\n >\n {{ 'common_apply' | translate }}\n </button>\n </div>\n </div>\n }\n </div>\n </div>\n</ng-template>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:inline-block}:host(.dp-host--field){display:block}.hint{unicode-bidi:plaintext}\n"] }]
|
|
2604
|
+
args: [{ selector: 'ef-datepicker-advanced', standalone: true, imports: [CommonModule, TranslateModule, EfLabelComponent, EfClearButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class.dp-host--field]': "mode() === 'field'" }, template: "<div class=\"dp\" [class.dp--field]=\"mode() === 'field'\" #anchorEl>\n @if (mode() === 'field' && variant() === 'field') {\n <!-- Field mode \u2014 labelled field-box trigger (form control) -->\n @if (labelKey() || label()) {\n <ef-label\n [labelKey]=\"labelKey()\"\n [label]=\"label()\"\n [for]=\"effectiveId\"\n [required]=\"required()\"\n />\n }\n <div class=\"ef-field-control\">\n <button\n type=\"button\"\n class=\"dp__field-trigger\"\n [id]=\"effectiveId\"\n [class.open]=\"open()\"\n [class.is-invalid]=\"isInvalid\"\n [class.is-placeholder]=\"!hasFieldValue()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"(showRequired || serverErrors) ? errorsId : null\"\n [attr.aria-label]=\"(labelKey() || label()) ? null : (ariaLabelKey() ? (ariaLabelKey() | translate) : (ariaLabel() || null))\"\n (click)=\"toggle($event)\"\n >\n <i class=\"ico pi pi-calendar\" aria-hidden=\"true\"></i>\n <span class=\"val\">\n @if (hasFieldValue()) {\n <bdi dir=\"ltr\">{{ fieldDisplay() }}</bdi>\n } @else {\n {{ placeholderKey() ? (placeholderKey() | translate) : placeholder() }}\n }\n </span>\n <svg class=\"caret ef-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"6 9 12 15 18 9\" /></svg>\n </button>\n @if (canClear) {\n <!-- Overlaid sibling (not nested in the trigger <button>) so it\n clears instead of toggling the calendar. -->\n <ef-clear-button\n class=\"dp__field-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n </div>\n } @else {\n <!-- Pill trigger \u2014 filter mode, or field mode with variant=\"pill\" -->\n <button\n type=\"button\"\n class=\"dp__trigger\"\n [class.open]=\"open()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-label]=\"mode() === 'field' && !(labelKey() || label()) ? (ariaLabelKey() ? (ariaLabelKey() | translate) : (ariaLabel() || null)) : null\"\n (click)=\"toggle($event)\"\n >\n <i class=\"ico pi pi-calendar\" aria-hidden=\"true\"></i>\n @if (mode() === 'field') {\n <span class=\"val\">\n @if (hasFieldValue()) {\n <bdi dir=\"ltr\">{{ fieldDisplay() }}</bdi>\n } @else {\n {{ placeholderKey() ? (placeholderKey() | translate) : placeholder() }}\n }\n </span>\n } @else {\n <span class=\"lbl\">\n {{ triggerLabelKey() ? (triggerLabelKey() | translate) : triggerLabel() }}\n </span>\n <span class=\"val\">\n @if (resolvedRange().labelKey; as key) {\n {{ key | translate }}\n } @else {\n {{ resolvedRange().label }}\n }\n </span>\n }\n <svg class=\"caret ef-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"6 9 12 15 18 9\" /></svg>\n </button>\n }\n</div>\n\n<!--\n Popover panel \u2014 instantiated via ViewContainerRef + appended to\n <body> when `open()` flips on. Fixed to the viewport and placed\n against the trigger by positionPanel() (flips above, keeps inside the\n viewport); on a phone it is a bottom sheet over a backdrop instead.\n-->\n<ng-template #panelTpl>\n @if (sheet()) {\n <div class=\"dp__backdrop\" aria-hidden=\"true\" (click)=\"close(true)\"></div>\n }\n <div\n class=\"dp__panel open\"\n [class.wide]=\"view() === 'custom'\"\n [class.dp__panel--sheet]=\"sheet()\"\n role=\"dialog\"\n [attr.aria-modal]=\"sheet() ? 'true' : null\"\n [attr.aria-label]=\"panelLabelKey() ? (panelLabelKey() | translate) : panelLabel()\"\n >\n <!-- \u2500\u2500 View 1 \u2014 preset list \u2500\u2500 -->\n <div class=\"dp__view\" [class.active]=\"view() === 'presets'\">\n @for (preset of effectivePresets(); track trackByPreset($index, preset)) {\n <button\n type=\"button\"\n class=\"dp__preset\"\n [class.active]=\"isActivePreset(preset.key)\"\n (click)=\"selectPreset(preset)\"\n >\n {{ preset.labelKey ? (preset.labelKey | translate) : preset.label }}\n <span class=\"hint\">{{ presetHint(preset) }}</span>\n </button>\n }\n\n @if (allowCustomRange()) {\n <div class=\"dp__divider\" role=\"separator\"></div>\n <button\n type=\"button\"\n class=\"dp__preset muted\"\n (click)=\"openCustomView()\"\n >\n <span style=\"display:inline-flex;align-items:center;gap:8px\">\n <i class=\"ico-lead pi pi-calendar\" aria-hidden=\"true\"></i>\n {{ 'date_preset_custom' | translate }}\n </span>\n <span class=\"hint\">\u21E7</span>\n </button>\n }\n </div>\n\n <!-- \u2500\u2500 View 2 \u2014 custom range with calendar \u2500\u2500 -->\n <div class=\"dp__view\" [class.active]=\"view() === 'custom'\">\n <!-- A single-date field drops this title: the field's own label\n names the dialog (aria-label above), and the date input below\n carries the same words, so \"S\u00E9lectionner une date\" read twice. -->\n @if (!(mode() === 'field' && selectionMode() === 'single')) {\n <div class=\"dp__custom-head\">\n @if (mode() !== 'field') {\n <button\n type=\"button\"\n class=\"dp__back\"\n [attr.aria-label]=\"'common_back' | translate\"\n (click)=\"backToPresets()\"\n >\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n } @else {\n <span></span>\n }\n <div class=\"dp__custom-title\">\n <span class=\"pulse-dot\"></span>\n {{ 'date_custom_range' | translate }}\n </div>\n <span></span>\n </div>\n }\n\n <div class=\"dp__cal\">\n <div class=\"dp__cal-head\">\n <button\n type=\"button\"\n class=\"dp__cal-nav\"\n [attr.aria-label]=\"'date_prev_month' | translate\"\n (click)=\"headerPrev()\"\n >\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n <div class=\"dp__cal-title\">\n @switch (calMode()) {\n @case ('years') {\n <span class=\"dp__cal-pick is-static\">{{ yearWindowLabel() }}</span>\n }\n @case ('months') {\n <button type=\"button\" class=\"dp__cal-pick\" (click)=\"openYearPicker()\">\n {{ yearLabel() }}\n </button>\n }\n @default {\n <button type=\"button\" class=\"dp__cal-pick\" (click)=\"openMonthPicker()\">\n {{ monthLabel() }}\n </button>\n <button type=\"button\" class=\"dp__cal-pick\" (click)=\"openYearPicker()\">\n {{ yearLabel() }}\n </button>\n }\n }\n </div>\n <button\n type=\"button\"\n class=\"dp__cal-nav\"\n [attr.aria-label]=\"'date_next_month' | translate\"\n (click)=\"headerNext()\"\n >\n <i class=\"pi pi-chevron-right\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n @switch (calMode()) {\n @case ('months') {\n <div class=\"dp__cal-cells\">\n @for (cell of monthCells(); track cell.month) {\n <button\n type=\"button\"\n class=\"dp__cal-cell\"\n [class.active]=\"cell.active\"\n [class.today]=\"cell.today\"\n (click)=\"selectMonth(cell.month)\"\n >\n {{ cell.label }}\n </button>\n }\n </div>\n }\n @case ('years') {\n <div class=\"dp__cal-cells\">\n @for (cell of yearCells(); track cell.year) {\n <button\n type=\"button\"\n class=\"dp__cal-cell\"\n [class.active]=\"cell.active\"\n [class.today]=\"cell.today\"\n (click)=\"selectYear(cell.year)\"\n >\n {{ cell.year }}\n </button>\n }\n </div>\n }\n @default {\n <div class=\"dp__cal-dow-row\">\n @for (label of dowLabels(); track trackByDow($index, label)) {\n <div class=\"dp__cal-dow\">{{ label }}</div>\n }\n </div>\n\n <div class=\"dp__cal-grid\">\n @for (day of calendarGrid(); track trackByDay($index, day)) {\n <button\n type=\"button\"\n class=\"dp__cal-day\"\n [class.out]=\"day.out\"\n [class.today]=\"day.today\"\n [class.in-range]=\"day.inRange\"\n [class.range-start]=\"day.rangeStart\"\n [class.range-end]=\"day.rangeEnd\"\n [attr.aria-label]=\"day.date | date:'fullDate'\"\n (click)=\"selectDay(day.date)\"\n >\n <span class=\"day-num\">{{ day.day }}</span>\n </button>\n }\n </div>\n }\n }\n </div>\n\n @if (mode() === 'field' && selectionMode() === 'single') {\n <!-- Single-date field: date (+ time when enabled). Without time,\n picking a day commits & closes; with time, Apply commits. -->\n <div class=\"dp__inputs\" [class.dp__inputs--single]=\"!showTime()\">\n <div class=\"dp__field\">\n <label [attr.for]=\"effectiveId + '-single'\">{{ 'date_select_date' | translate }}</label>\n <input\n #singleInput\n [id]=\"effectiveId + '-single'\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"10\"\n [value]=\"fromText()\"\n [placeholder]=\"datePlaceholder()\"\n (keydown)=\"onDateKeydown($event, singleInput)\"\n (change)=\"commitSingleInput(singleInput)\"\n />\n </div>\n @if (showTime()) {\n <div class=\"dp__field\">\n <label [attr.for]=\"effectiveId + '-single-time'\">{{ 'date_time' | translate }}</label>\n <input\n #singleTime\n [id]=\"effectiveId + '-single-time'\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"5\"\n [value]=\"fromTimeText()\"\n placeholder=\"HH:mm\"\n (keydown)=\"onTimeKeydown($event, singleTime)\"\n (change)=\"commitTimeInput('start', singleTime)\"\n />\n </div>\n }\n </div>\n } @else {\n <div class=\"dp__inputs\">\n <div class=\"dp__field\">\n <label [attr.for]=\"effectiveId + '-from'\">{{ 'date_from' | translate }}</label>\n <input\n #fromInput\n [id]=\"effectiveId + '-from'\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"10\"\n [value]=\"fromText()\"\n [placeholder]=\"datePlaceholder()\"\n (keydown)=\"onDateKeydown($event, fromInput)\"\n (change)=\"commitDateInput('start', fromInput)\"\n />\n @if (mode() === 'field' && showTime()) {\n <input\n #fromTime\n [attr.aria-label]=\"'date_time' | translate\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"5\"\n [value]=\"fromTimeText()\"\n placeholder=\"HH:mm\"\n (keydown)=\"onTimeKeydown($event, fromTime)\"\n (change)=\"commitTimeInput('start', fromTime)\"\n />\n }\n </div>\n <div class=\"dp__field\">\n <label [attr.for]=\"effectiveId + '-to'\">{{ 'date_to' | translate }}</label>\n <input\n #toInput\n [id]=\"effectiveId + '-to'\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"10\"\n [value]=\"toText()\"\n [placeholder]=\"datePlaceholder()\"\n (keydown)=\"onDateKeydown($event, toInput)\"\n (change)=\"commitDateInput('end', toInput)\"\n />\n @if (mode() === 'field' && showTime()) {\n <input\n #toTime\n [attr.aria-label]=\"'date_time' | translate\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"5\"\n [value]=\"toTimeText()\"\n placeholder=\"HH:mm\"\n (keydown)=\"onTimeKeydown($event, toTime)\"\n (change)=\"commitTimeInput('end', toTime)\"\n />\n }\n </div>\n </div>\n }\n\n <!-- Footer: hidden only for single field WITHOUT time (commit on pick). -->\n @if (!(mode() === 'field' && selectionMode() === 'single' && !showTime())) {\n <div class=\"dp__actions\">\n @if (mode() === 'field' && selectionMode() === 'single') {\n <span></span>\n } @else {\n <span class=\"duration\">\n <span class=\"num\">{{ draftDurationDays() }}</span>\n {{ 'date_days' | translate }}\n </span>\n }\n <div class=\"right\">\n @if (mode() !== 'field') {\n <button\n type=\"button\"\n class=\"btn btn-ghost btn-sm\"\n (click)=\"backToPresets()\"\n >\n {{ 'common_back' | translate }}\n </button>\n }\n <button\n type=\"button\"\n class=\"btn btn-primary btn-sm\"\n [disabled]=\"!draftStart()\"\n (click)=\"applyCustomRange()\"\n >\n {{ 'common_apply' | translate }}\n </button>\n </div>\n </div>\n }\n </div>\n </div>\n</ng-template>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:inline-block}:host(.dp-host--field){display:block}.hint{unicode-bidi:plaintext}\n"] }]
|
|
2388
2605
|
}], ctorParameters: () => [], propDecorators: { anchorEl: [{
|
|
2389
2606
|
type: ViewChild,
|
|
2390
2607
|
args: ['anchorEl', { static: true }]
|
|
@@ -2394,6 +2611,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
2394
2611
|
}], triggerLabelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "triggerLabelKey", required: false }] }], triggerLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "triggerLabel", required: false }] }], presets: [{ type: i0.Input, args: [{ isSignal: true, alias: "presets", required: false }] }], activePreset: [{ type: i0.Input, args: [{ isSignal: true, alias: "activePreset", required: false }] }], start: [{ type: i0.Input, args: [{ isSignal: true, alias: "start", required: false }] }], end: [{ type: i0.Input, args: [{ isSignal: true, alias: "end", required: false }] }], allowCustomRange: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowCustomRange", required: false }] }], allowAllTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowAllTime", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], showTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTime", required: false }] }], valueFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueFormat", required: false }] }], rangeChange: [{ type: i0.Output, args: ["rangeChange"] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }], onDocumentClick: [{
|
|
2395
2612
|
type: HostListener,
|
|
2396
2613
|
args: ['document:click', ['$event']]
|
|
2614
|
+
}], onTab: [{
|
|
2615
|
+
type: HostListener,
|
|
2616
|
+
args: ['document:keydown', ['$event']]
|
|
2397
2617
|
}], onEscape: [{
|
|
2398
2618
|
type: HostListener,
|
|
2399
2619
|
args: ['document:keydown.escape']
|
|
@@ -2832,11 +3052,11 @@ class EfInputNumberComponent extends AbstractEfFormControl {
|
|
|
2832
3052
|
this.handleClear();
|
|
2833
3053
|
}
|
|
2834
3054
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfInputNumberComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2835
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfInputNumberComponent, isStandalone: true, selector: "ef-inputnumber", inputs: { value: "value", invalid: "invalid", format: "format", showButtons: ["showButtons", "showButtons", booleanAttribute], buttonLayout: "buttonLayout", incrementButtonClass: "incrementButtonClass", decrementButtonClass: "decrementButtonClass", incrementButtonIcon: "incrementButtonIcon", decrementButtonIcon: "decrementButtonIcon", prefix: "prefix", suffix: "suffix", currency: "currency", currencyDisplay: "currencyDisplay", locale: "locale", localeMatcher: "localeMatcher", mode: "mode", useGrouping: "useGrouping", minFractionDigits: "minFractionDigits", maxFractionDigits: "maxFractionDigits", min: "min", max: "max", step: "step", allowEmpty: "allowEmpty", inputSize: "inputSize", size: "size", maxlength: "maxlength", minlength: "minlength", pattern: "pattern", tabindex: "tabindex", title: "title", ariaLabelledBy: "ariaLabelledBy", inputStyle: "inputStyle", inputStyleClass: "inputStyleClass", style: "style", styleClass: "styleClass", primeNgVariant: "primeNgVariant", autofocus: ["autofocus", "autofocus", booleanAttribute], autocomplete: "autocomplete", fluid: "fluid", inline: ["inline", "inline", booleanAttribute], variant: "variant", pt: "pt", ptOptions: "ptOptions", dt: "dt", unstyled: "unstyled" }, outputs: { inputEvent: "inputEvent", valueChangeEvent: "valueChangeEvent", focusEvent: "focusEvent", blurEvent: "blurEvent", keyDownEvent: "keyDownEvent", clearEvent: "clearEvent" }, host: { properties: { "class.ef-inline": "this.isInline" } }, viewQueries: [{ propertyName: "inputNumber", first: true, predicate: ["inputNumber"], descendants: true }], usesInheritance: true, ngImport: i0, template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n @if (variant === 'comptoir') {\n @if (prefix || suffix || showButtons || currencyAddon) {\n <!-- Comptoir variant \u2014 native text input (inputmode=decimal, accepts\n \",\" or \".\" as decimal separator) wrapped in .ef-input-group when\n there's a prefix / suffix / stepper. min/max are enforced by\n blur-time clamping in the component (text inputs have no native\n range semantics). -->\n <div class=\"ef-input-group\" [class.is-invalid]=\"isInvalid\">\n @if (showButtons) {\n <button\n type=\"button\"\n class=\"ef-input-addon is-stepper\"\n [disabled]=\"isDisabled() || (min !== undefined && (value ?? 0) <= min)\"\n (click)=\"step$(-1)\"\n [attr.aria-label]=\"('ef_input_decrement' | translate) || 'Decrease'\"\n >\n <i class=\"pi pi-minus\" aria-hidden=\"true\"></i>\n </button>\n }\n @if (prefix) {\n <span class=\"ef-input-addon is-prefix\" aria-hidden=\"true\">{{ prefix }}</span>\n }\n @if (currencyAddon?.side === 'prefix') {\n <span class=\"ef-input-addon is-prefix is-currency\" [id]=\"currencyId\">{{ currencyAddon?.symbol }}</span>\n }\n <input\n type=\"text\"\n inputmode=\"decimal\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [value]=\"displayText\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? 'off'\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors, currencyAddon ? currencyId : null)\"\n (input)=\"handleNativeInput($any($event.target).value)\"\n (focus)=\"handleFocus($event)\"\n (blur)=\"handleBlur($event)\"\n (keydown)=\"handleKeyDown($event)\"\n />\n @if (canClear) {\n <ef-clear-button\n class=\"ef-group-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n @if (suffix) {\n <span class=\"ef-input-addon is-suffix\" aria-hidden=\"true\">{{ suffix }}</span>\n }\n @if (currencyAddon?.side === 'suffix') {\n <span class=\"ef-input-addon is-suffix is-currency\" [id]=\"currencyId\">{{ currencyAddon?.symbol }}</span>\n }\n @if (showButtons) {\n <button\n type=\"button\"\n class=\"ef-input-addon is-stepper\"\n [disabled]=\"isDisabled() || (max !== undefined && (value ?? 0) >= max)\"\n (click)=\"step$(1)\"\n [attr.aria-label]=\"('ef_input_increment' | translate) || 'Increase'\"\n >\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n } @else {\n <!-- Comptoir variant \u2014 bare native text input (inputmode=decimal,\n accepts \",\" or \".\" as decimal separator; optional inline clear).\n min/max are enforced by blur-time clamping in the component. -->\n <div class=\"ef-input-wrap\">\n <input\n type=\"text\"\n inputmode=\"decimal\"\n class=\"ef-input\"\n [class.has-clear]=\"canClear\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [value]=\"displayText\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? 'off'\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n (input)=\"handleNativeInput($any($event.target).value)\"\n (focus)=\"handleFocus($event)\"\n (blur)=\"handleBlur($event)\"\n (keydown)=\"handleKeyDown($event)\"\n />\n @if (canClear) {\n <ef-clear-button\n class=\"ef-input-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n </div>\n }\n } @else {\n <!-- PrimeNG variant (default) \u2014 full locale-aware formatting. -->\n <p-inputnumber\n #inputNumber\n [(ngModel)]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n [invalid]=\"isInvalid\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n [format]=\"format\"\n [showButtons]=\"showButtons\"\n [buttonLayout]=\"buttonLayout\"\n [incrementButtonClass]=\"incrementButtonClass\"\n [decrementButtonClass]=\"decrementButtonClass\"\n [incrementButtonIcon]=\"incrementButtonIcon\"\n [decrementButtonIcon]=\"decrementButtonIcon\"\n [prefix]=\"prefix\"\n [suffix]=\"suffix\"\n [currency]=\"currency\"\n [currencyDisplay]=\"currencyDisplay\"\n [locale]=\"locale\"\n [localeMatcher]=\"localeMatcher\"\n [mode]=\"mode\"\n [useGrouping]=\"useGrouping\"\n [minFractionDigits]=\"minFractionDigits\"\n [maxFractionDigits]=\"maxFractionDigits\"\n [min]=\"min\"\n [max]=\"max\"\n [step]=\"step\"\n [allowEmpty]=\"allowEmpty\"\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [placeholder]=\"effectivePlaceholder\"\n [inputSize]=\"inputSize\"\n [size]=\"size\"\n [maxlength]=\"maxlength ?? null\"\n [minlength]=\"minlength ?? null\"\n [pattern]=\"$any(pattern)\"\n [tabindex]=\"tabindex\"\n [title]=\"title\"\n [ariaLabel]=\"effectiveAriaLabel ?? undefined\"\n [ariaLabelledBy]=\"ariaLabelledBy\"\n [ariaDescribedBy]=\"describedBy(showRequired || !!serverErrors) ?? undefined\"\n [ariaRequired]=\"required()\"\n [inputStyle]=\"inputStyle\"\n [inputStyleClass]=\"inputStyleClass\"\n [style]=\"style\"\n [styleClass]=\"styleClass\"\n [showClear]=\"showClear()\"\n [variant]=\"primeNgVariant\"\n [autofocus]=\"autofocus\"\n [autocomplete]=\"autocomplete\"\n [fluid]=\"fluid\"\n [pt]=\"pt\"\n [ptOptions]=\"ptOptions\"\n [dt]=\"dt\"\n [unstyled]=\"unstyled\"\n (onInput)=\"handleInput($event)\"\n (onFocus)=\"handleFocus($event)\"\n (onBlur)=\"handleBlur($event)\"\n (onKeyDown)=\"handleKeyDown($event)\"\n (onClear)=\"handleClear()\"\n />\n }\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block;width:100%}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}:host(.ef-inputnumber-small) ::ng-deep .p-inputnumber-input{font-size:.875rem;padding:.375rem .5rem}:host(.ef-inputnumber-large) ::ng-deep .p-inputnumber-input{font-size:1.125rem;padding:.75rem 1rem}:host(.ng-invalid.ng-touched) ::ng-deep .p-inputnumber-input,:host(.ng-invalid.ng-dirty) ::ng-deep .p-inputnumber-input{border-color:var(--red-500, #ef4444)}:host(.ng-invalid.ng-touched) ::ng-deep .p-inputnumber-input:focus,:host(.ng-invalid.ng-dirty) ::ng-deep .p-inputnumber-input:focus{box-shadow:0 0 0 .2rem #ef444440}\n"], dependencies: [{ kind: "ngmodule", type: InputNumberModule }, { kind: "component", type: i1$4.InputNumber, selector: "p-inputNumber, p-inputnumber, p-input-number", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "placeholder", "tabindex", "title", "ariaLabelledBy", "ariaDescribedBy", "ariaLabel", "ariaRequired", "autocomplete", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$1.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i2$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2$1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "for", "styleClass", "required"] }, { kind: "component", type: EfClearButtonComponent, selector: "ef-clear-button", inputs: ["ariaLabel", "disabled"], outputs: ["clear"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
3055
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfInputNumberComponent, isStandalone: true, selector: "ef-inputnumber", inputs: { value: "value", invalid: "invalid", format: "format", showButtons: ["showButtons", "showButtons", booleanAttribute], buttonLayout: "buttonLayout", incrementButtonClass: "incrementButtonClass", decrementButtonClass: "decrementButtonClass", incrementButtonIcon: "incrementButtonIcon", decrementButtonIcon: "decrementButtonIcon", prefix: "prefix", suffix: "suffix", currency: "currency", currencyDisplay: "currencyDisplay", locale: "locale", localeMatcher: "localeMatcher", mode: "mode", useGrouping: "useGrouping", minFractionDigits: "minFractionDigits", maxFractionDigits: "maxFractionDigits", min: "min", max: "max", step: "step", allowEmpty: "allowEmpty", inputSize: "inputSize", size: "size", maxlength: "maxlength", minlength: "minlength", pattern: "pattern", tabindex: "tabindex", title: "title", ariaLabelledBy: "ariaLabelledBy", inputStyle: "inputStyle", inputStyleClass: "inputStyleClass", style: "style", styleClass: "styleClass", primeNgVariant: "primeNgVariant", autofocus: ["autofocus", "autofocus", booleanAttribute], autocomplete: "autocomplete", fluid: "fluid", inline: ["inline", "inline", booleanAttribute], variant: "variant", pt: "pt", ptOptions: "ptOptions", dt: "dt", unstyled: "unstyled" }, outputs: { inputEvent: "inputEvent", valueChangeEvent: "valueChangeEvent", focusEvent: "focusEvent", blurEvent: "blurEvent", keyDownEvent: "keyDownEvent", clearEvent: "clearEvent" }, host: { properties: { "class.ef-inline": "this.isInline" } }, viewQueries: [{ propertyName: "inputNumber", first: true, predicate: ["inputNumber"], descendants: true }], usesInheritance: true, ngImport: i0, template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n @if (variant === 'comptoir') {\n @if (prefix || suffix || showButtons || currencyAddon) {\n <!-- Comptoir variant \u2014 native text input (inputmode=decimal, accepts\n \",\" or \".\" as decimal separator) wrapped in .ef-input-group when\n there's a prefix / suffix / stepper. min/max are enforced by\n blur-time clamping in the component (text inputs have no native\n range semantics). -->\n <div class=\"ef-input-group\" [class.is-invalid]=\"isInvalid\">\n @if (showButtons) {\n <button\n type=\"button\"\n class=\"ef-input-addon is-stepper\"\n [disabled]=\"isDisabled() || (min !== undefined && (value ?? 0) <= min)\"\n (click)=\"step$(-1)\"\n [attr.aria-label]=\"('ef_input_decrement' | translate) || 'Decrease'\"\n >\n <i class=\"pi pi-minus\" aria-hidden=\"true\"></i>\n </button>\n }\n @if (prefix) {\n <span class=\"ef-input-addon is-prefix\" aria-hidden=\"true\">{{ prefix }}</span>\n }\n @if (currencyAddon?.side === 'prefix') {\n <span class=\"ef-input-addon is-prefix is-currency\" [id]=\"currencyId\">{{ currencyAddon?.symbol }}</span>\n }\n <input\n type=\"text\"\n inputmode=\"decimal\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [value]=\"displayText\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? 'off'\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors, currencyAddon ? currencyId : null)\"\n (input)=\"handleNativeInput($any($event.target).value)\"\n (focus)=\"handleFocus($event)\"\n (blur)=\"handleBlur($event)\"\n (keydown)=\"handleKeyDown($event)\"\n />\n @if (canClear) {\n <ef-clear-button\n class=\"ef-group-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n @if (suffix) {\n <span class=\"ef-input-addon is-suffix\" aria-hidden=\"true\">{{ suffix }}</span>\n }\n @if (currencyAddon?.side === 'suffix') {\n <span class=\"ef-input-addon is-suffix is-currency\" [id]=\"currencyId\">{{ currencyAddon?.symbol }}</span>\n }\n @if (showButtons) {\n <button\n type=\"button\"\n class=\"ef-input-addon is-stepper\"\n [disabled]=\"isDisabled() || (max !== undefined && (value ?? 0) >= max)\"\n (click)=\"step$(1)\"\n [attr.aria-label]=\"('ef_input_increment' | translate) || 'Increase'\"\n >\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n } @else {\n <!-- Comptoir variant \u2014 bare native text input (inputmode=decimal,\n accepts \",\" or \".\" as decimal separator; optional inline clear).\n min/max are enforced by blur-time clamping in the component. -->\n <div class=\"ef-input-wrap\">\n <input\n type=\"text\"\n inputmode=\"decimal\"\n class=\"ef-input\"\n [class.has-clear]=\"canClear\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [value]=\"displayText\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? 'off'\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n (input)=\"handleNativeInput($any($event.target).value)\"\n (focus)=\"handleFocus($event)\"\n (blur)=\"handleBlur($event)\"\n (keydown)=\"handleKeyDown($event)\"\n />\n @if (canClear) {\n <ef-clear-button\n class=\"ef-input-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n </div>\n }\n } @else {\n <!-- PrimeNG variant (default) \u2014 full locale-aware formatting. -->\n <p-inputnumber\n #inputNumber\n [(ngModel)]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n [invalid]=\"isInvalid\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n [format]=\"format\"\n [showButtons]=\"showButtons\"\n [buttonLayout]=\"buttonLayout\"\n [incrementButtonClass]=\"incrementButtonClass\"\n [decrementButtonClass]=\"decrementButtonClass\"\n [incrementButtonIcon]=\"incrementButtonIcon\"\n [decrementButtonIcon]=\"decrementButtonIcon\"\n [prefix]=\"prefix\"\n [suffix]=\"suffix\"\n [currency]=\"currency\"\n [currencyDisplay]=\"currencyDisplay\"\n [locale]=\"locale\"\n [localeMatcher]=\"localeMatcher\"\n [mode]=\"mode\"\n [useGrouping]=\"useGrouping\"\n [minFractionDigits]=\"minFractionDigits\"\n [maxFractionDigits]=\"maxFractionDigits\"\n [min]=\"min\"\n [max]=\"max\"\n [step]=\"step\"\n [allowEmpty]=\"allowEmpty\"\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [placeholder]=\"effectivePlaceholder\"\n [inputSize]=\"inputSize\"\n [size]=\"size\"\n [maxlength]=\"maxlength ?? null\"\n [minlength]=\"minlength ?? null\"\n [pattern]=\"$any(pattern)\"\n [tabindex]=\"tabindex\"\n [title]=\"title\"\n [ariaLabel]=\"effectiveAriaLabel ?? undefined\"\n [ariaLabelledBy]=\"ariaLabelledBy\"\n [ariaDescribedBy]=\"describedBy(showRequired || !!serverErrors) ?? undefined\"\n [ariaRequired]=\"required()\"\n [inputStyle]=\"inputStyle\"\n [inputStyleClass]=\"inputStyleClass\"\n [style]=\"style\"\n [styleClass]=\"styleClass\"\n [showClear]=\"showClear()\"\n [variant]=\"primeNgVariant\"\n [autofocus]=\"autofocus\"\n [autocomplete]=\"autocomplete\"\n [fluid]=\"fluid\"\n [pt]=\"pt\"\n [ptOptions]=\"ptOptions\"\n [dt]=\"dt\"\n [unstyled]=\"unstyled\"\n (onInput)=\"handleInput($event)\"\n (onFocus)=\"handleFocus($event)\"\n (onBlur)=\"handleBlur($event)\"\n (onKeyDown)=\"handleKeyDown($event)\"\n (onClear)=\"handleClear()\"\n />\n }\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block;width:100%}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}:host(.ef-inputnumber-small) ::ng-deep .p-inputnumber-input{font-size:.875rem;padding:.375rem .5rem}:host(.ef-inputnumber-large) ::ng-deep .p-inputnumber-input{font-size:1.125rem;padding:.75rem 1rem}:host(.ng-invalid.ng-touched) ::ng-deep .p-inputnumber-input,:host(.ng-invalid.ng-dirty) ::ng-deep .p-inputnumber-input{border-color:var(--red-500, #ef4444)}:host(.ng-invalid.ng-touched) ::ng-deep .p-inputnumber-input:focus,:host(.ng-invalid.ng-dirty) ::ng-deep .p-inputnumber-input:focus{box-shadow:0 0 0 .2rem #ef444440}\n"], dependencies: [{ kind: "ngmodule", type: InputNumberModule }, { kind: "component", type: i1$4.InputNumber, selector: "p-inputNumber, p-inputnumber, p-input-number", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "placeholder", "tabindex", "title", "ariaLabelledBy", "ariaDescribedBy", "ariaLabel", "ariaRequired", "autocomplete", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$1.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i2$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2$1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "labelParams", "for", "styleClass", "required"] }, { kind: "component", type: EfClearButtonComponent, selector: "ef-clear-button", inputs: ["ariaLabel", "disabled"], outputs: ["clear"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
2836
3056
|
}
|
|
2837
3057
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfInputNumberComponent, decorators: [{
|
|
2838
3058
|
type: Component,
|
|
2839
|
-
args: [{ selector: 'ef-inputnumber', standalone: true, imports: [InputNumberModule, FormsModule, TranslateModule, EfLabelComponent, EfClearButtonComponent], template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n @if (variant === 'comptoir') {\n @if (prefix || suffix || showButtons || currencyAddon) {\n <!-- Comptoir variant \u2014 native text input (inputmode=decimal, accepts\n \",\" or \".\" as decimal separator) wrapped in .ef-input-group when\n there's a prefix / suffix / stepper. min/max are enforced by\n blur-time clamping in the component (text inputs have no native\n range semantics). -->\n <div class=\"ef-input-group\" [class.is-invalid]=\"isInvalid\">\n @if (showButtons) {\n <button\n type=\"button\"\n class=\"ef-input-addon is-stepper\"\n [disabled]=\"isDisabled() || (min !== undefined && (value ?? 0) <= min)\"\n (click)=\"step$(-1)\"\n [attr.aria-label]=\"('ef_input_decrement' | translate) || 'Decrease'\"\n >\n <i class=\"pi pi-minus\" aria-hidden=\"true\"></i>\n </button>\n }\n @if (prefix) {\n <span class=\"ef-input-addon is-prefix\" aria-hidden=\"true\">{{ prefix }}</span>\n }\n @if (currencyAddon?.side === 'prefix') {\n <span class=\"ef-input-addon is-prefix is-currency\" [id]=\"currencyId\">{{ currencyAddon?.symbol }}</span>\n }\n <input\n type=\"text\"\n inputmode=\"decimal\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [value]=\"displayText\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? 'off'\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors, currencyAddon ? currencyId : null)\"\n (input)=\"handleNativeInput($any($event.target).value)\"\n (focus)=\"handleFocus($event)\"\n (blur)=\"handleBlur($event)\"\n (keydown)=\"handleKeyDown($event)\"\n />\n @if (canClear) {\n <ef-clear-button\n class=\"ef-group-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n @if (suffix) {\n <span class=\"ef-input-addon is-suffix\" aria-hidden=\"true\">{{ suffix }}</span>\n }\n @if (currencyAddon?.side === 'suffix') {\n <span class=\"ef-input-addon is-suffix is-currency\" [id]=\"currencyId\">{{ currencyAddon?.symbol }}</span>\n }\n @if (showButtons) {\n <button\n type=\"button\"\n class=\"ef-input-addon is-stepper\"\n [disabled]=\"isDisabled() || (max !== undefined && (value ?? 0) >= max)\"\n (click)=\"step$(1)\"\n [attr.aria-label]=\"('ef_input_increment' | translate) || 'Increase'\"\n >\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n } @else {\n <!-- Comptoir variant \u2014 bare native text input (inputmode=decimal,\n accepts \",\" or \".\" as decimal separator; optional inline clear).\n min/max are enforced by blur-time clamping in the component. -->\n <div class=\"ef-input-wrap\">\n <input\n type=\"text\"\n inputmode=\"decimal\"\n class=\"ef-input\"\n [class.has-clear]=\"canClear\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [value]=\"displayText\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? 'off'\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n (input)=\"handleNativeInput($any($event.target).value)\"\n (focus)=\"handleFocus($event)\"\n (blur)=\"handleBlur($event)\"\n (keydown)=\"handleKeyDown($event)\"\n />\n @if (canClear) {\n <ef-clear-button\n class=\"ef-input-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n </div>\n }\n } @else {\n <!-- PrimeNG variant (default) \u2014 full locale-aware formatting. -->\n <p-inputnumber\n #inputNumber\n [(ngModel)]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n [invalid]=\"isInvalid\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n [format]=\"format\"\n [showButtons]=\"showButtons\"\n [buttonLayout]=\"buttonLayout\"\n [incrementButtonClass]=\"incrementButtonClass\"\n [decrementButtonClass]=\"decrementButtonClass\"\n [incrementButtonIcon]=\"incrementButtonIcon\"\n [decrementButtonIcon]=\"decrementButtonIcon\"\n [prefix]=\"prefix\"\n [suffix]=\"suffix\"\n [currency]=\"currency\"\n [currencyDisplay]=\"currencyDisplay\"\n [locale]=\"locale\"\n [localeMatcher]=\"localeMatcher\"\n [mode]=\"mode\"\n [useGrouping]=\"useGrouping\"\n [minFractionDigits]=\"minFractionDigits\"\n [maxFractionDigits]=\"maxFractionDigits\"\n [min]=\"min\"\n [max]=\"max\"\n [step]=\"step\"\n [allowEmpty]=\"allowEmpty\"\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [placeholder]=\"effectivePlaceholder\"\n [inputSize]=\"inputSize\"\n [size]=\"size\"\n [maxlength]=\"maxlength ?? null\"\n [minlength]=\"minlength ?? null\"\n [pattern]=\"$any(pattern)\"\n [tabindex]=\"tabindex\"\n [title]=\"title\"\n [ariaLabel]=\"effectiveAriaLabel ?? undefined\"\n [ariaLabelledBy]=\"ariaLabelledBy\"\n [ariaDescribedBy]=\"describedBy(showRequired || !!serverErrors) ?? undefined\"\n [ariaRequired]=\"required()\"\n [inputStyle]=\"inputStyle\"\n [inputStyleClass]=\"inputStyleClass\"\n [style]=\"style\"\n [styleClass]=\"styleClass\"\n [showClear]=\"showClear()\"\n [variant]=\"primeNgVariant\"\n [autofocus]=\"autofocus\"\n [autocomplete]=\"autocomplete\"\n [fluid]=\"fluid\"\n [pt]=\"pt\"\n [ptOptions]=\"ptOptions\"\n [dt]=\"dt\"\n [unstyled]=\"unstyled\"\n (onInput)=\"handleInput($event)\"\n (onFocus)=\"handleFocus($event)\"\n (onBlur)=\"handleBlur($event)\"\n (onKeyDown)=\"handleKeyDown($event)\"\n (onClear)=\"handleClear()\"\n />\n }\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block;width:100%}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}:host(.ef-inputnumber-small) ::ng-deep .p-inputnumber-input{font-size:.875rem;padding:.375rem .5rem}:host(.ef-inputnumber-large) ::ng-deep .p-inputnumber-input{font-size:1.125rem;padding:.75rem 1rem}:host(.ng-invalid.ng-touched) ::ng-deep .p-inputnumber-input,:host(.ng-invalid.ng-dirty) ::ng-deep .p-inputnumber-input{border-color:var(--red-500, #ef4444)}:host(.ng-invalid.ng-touched) ::ng-deep .p-inputnumber-input:focus,:host(.ng-invalid.ng-dirty) ::ng-deep .p-inputnumber-input:focus{box-shadow:0 0 0 .2rem #ef444440}\n"] }]
|
|
3059
|
+
args: [{ selector: 'ef-inputnumber', standalone: true, imports: [InputNumberModule, FormsModule, TranslateModule, EfLabelComponent, EfClearButtonComponent], template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n @if (variant === 'comptoir') {\n @if (prefix || suffix || showButtons || currencyAddon) {\n <!-- Comptoir variant \u2014 native text input (inputmode=decimal, accepts\n \",\" or \".\" as decimal separator) wrapped in .ef-input-group when\n there's a prefix / suffix / stepper. min/max are enforced by\n blur-time clamping in the component (text inputs have no native\n range semantics). -->\n <div class=\"ef-input-group\" [class.is-invalid]=\"isInvalid\">\n @if (showButtons) {\n <button\n type=\"button\"\n class=\"ef-input-addon is-stepper\"\n [disabled]=\"isDisabled() || (min !== undefined && (value ?? 0) <= min)\"\n (click)=\"step$(-1)\"\n [attr.aria-label]=\"('ef_input_decrement' | translate) || 'Decrease'\"\n >\n <i class=\"pi pi-minus\" aria-hidden=\"true\"></i>\n </button>\n }\n @if (prefix) {\n <span class=\"ef-input-addon is-prefix\" aria-hidden=\"true\">{{ prefix }}</span>\n }\n @if (currencyAddon?.side === 'prefix') {\n <span class=\"ef-input-addon is-prefix is-currency\" [id]=\"currencyId\">{{ currencyAddon?.symbol }}</span>\n }\n <input\n type=\"text\"\n inputmode=\"decimal\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [value]=\"displayText\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? 'off'\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors, currencyAddon ? currencyId : null)\"\n (input)=\"handleNativeInput($any($event.target).value)\"\n (focus)=\"handleFocus($event)\"\n (blur)=\"handleBlur($event)\"\n (keydown)=\"handleKeyDown($event)\"\n />\n @if (canClear) {\n <ef-clear-button\n class=\"ef-group-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n @if (suffix) {\n <span class=\"ef-input-addon is-suffix\" aria-hidden=\"true\">{{ suffix }}</span>\n }\n @if (currencyAddon?.side === 'suffix') {\n <span class=\"ef-input-addon is-suffix is-currency\" [id]=\"currencyId\">{{ currencyAddon?.symbol }}</span>\n }\n @if (showButtons) {\n <button\n type=\"button\"\n class=\"ef-input-addon is-stepper\"\n [disabled]=\"isDisabled() || (max !== undefined && (value ?? 0) >= max)\"\n (click)=\"step$(1)\"\n [attr.aria-label]=\"('ef_input_increment' | translate) || 'Increase'\"\n >\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n } @else {\n <!-- Comptoir variant \u2014 bare native text input (inputmode=decimal,\n accepts \",\" or \".\" as decimal separator; optional inline clear).\n min/max are enforced by blur-time clamping in the component. -->\n <div class=\"ef-input-wrap\">\n <input\n type=\"text\"\n inputmode=\"decimal\"\n class=\"ef-input\"\n [class.has-clear]=\"canClear\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [value]=\"displayText\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? 'off'\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n (input)=\"handleNativeInput($any($event.target).value)\"\n (focus)=\"handleFocus($event)\"\n (blur)=\"handleBlur($event)\"\n (keydown)=\"handleKeyDown($event)\"\n />\n @if (canClear) {\n <ef-clear-button\n class=\"ef-input-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n </div>\n }\n } @else {\n <!-- PrimeNG variant (default) \u2014 full locale-aware formatting. -->\n <p-inputnumber\n #inputNumber\n [(ngModel)]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n [invalid]=\"isInvalid\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n [format]=\"format\"\n [showButtons]=\"showButtons\"\n [buttonLayout]=\"buttonLayout\"\n [incrementButtonClass]=\"incrementButtonClass\"\n [decrementButtonClass]=\"decrementButtonClass\"\n [incrementButtonIcon]=\"incrementButtonIcon\"\n [decrementButtonIcon]=\"decrementButtonIcon\"\n [prefix]=\"prefix\"\n [suffix]=\"suffix\"\n [currency]=\"currency\"\n [currencyDisplay]=\"currencyDisplay\"\n [locale]=\"locale\"\n [localeMatcher]=\"localeMatcher\"\n [mode]=\"mode\"\n [useGrouping]=\"useGrouping\"\n [minFractionDigits]=\"minFractionDigits\"\n [maxFractionDigits]=\"maxFractionDigits\"\n [min]=\"min\"\n [max]=\"max\"\n [step]=\"step\"\n [allowEmpty]=\"allowEmpty\"\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [placeholder]=\"effectivePlaceholder\"\n [inputSize]=\"inputSize\"\n [size]=\"size\"\n [maxlength]=\"maxlength ?? null\"\n [minlength]=\"minlength ?? null\"\n [pattern]=\"$any(pattern)\"\n [tabindex]=\"tabindex\"\n [title]=\"title\"\n [ariaLabel]=\"effectiveAriaLabel ?? undefined\"\n [ariaLabelledBy]=\"ariaLabelledBy\"\n [ariaDescribedBy]=\"describedBy(showRequired || !!serverErrors) ?? undefined\"\n [ariaRequired]=\"required()\"\n [inputStyle]=\"inputStyle\"\n [inputStyleClass]=\"inputStyleClass\"\n [style]=\"style\"\n [styleClass]=\"styleClass\"\n [showClear]=\"showClear()\"\n [variant]=\"primeNgVariant\"\n [autofocus]=\"autofocus\"\n [autocomplete]=\"autocomplete\"\n [fluid]=\"fluid\"\n [pt]=\"pt\"\n [ptOptions]=\"ptOptions\"\n [dt]=\"dt\"\n [unstyled]=\"unstyled\"\n (onInput)=\"handleInput($event)\"\n (onFocus)=\"handleFocus($event)\"\n (onBlur)=\"handleBlur($event)\"\n (onKeyDown)=\"handleKeyDown($event)\"\n (onClear)=\"handleClear()\"\n />\n }\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block;width:100%}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}:host(.ef-inputnumber-small) ::ng-deep .p-inputnumber-input{font-size:.875rem;padding:.375rem .5rem}:host(.ef-inputnumber-large) ::ng-deep .p-inputnumber-input{font-size:1.125rem;padding:.75rem 1rem}:host(.ng-invalid.ng-touched) ::ng-deep .p-inputnumber-input,:host(.ng-invalid.ng-dirty) ::ng-deep .p-inputnumber-input{border-color:var(--red-500, #ef4444)}:host(.ng-invalid.ng-touched) ::ng-deep .p-inputnumber-input:focus,:host(.ng-invalid.ng-dirty) ::ng-deep .p-inputnumber-input:focus{box-shadow:0 0 0 .2rem #ef444440}\n"] }]
|
|
2840
3060
|
}], ctorParameters: () => [], propDecorators: { inputNumber: [{
|
|
2841
3061
|
type: ViewChild,
|
|
2842
3062
|
args: ['inputNumber', { static: false }]
|
|
@@ -3967,6 +4187,19 @@ const DEFAULT_META = { cls: 'op-default' };
|
|
|
3967
4187
|
* ```html
|
|
3968
4188
|
* <ef-change-history [entries]="auditEntries()" />
|
|
3969
4189
|
* ```
|
|
4190
|
+
*
|
|
4191
|
+
* Values are shown raw by default (`Received → Confirmed`). To show a field's
|
|
4192
|
+
* values translated, pass `valueLists`: a map of field path to the
|
|
4193
|
+
* `{ code, label }` items of a reference list. Each old/new value that matches
|
|
4194
|
+
* an item `code` (case-insensitive) is shown as its `label` (translated when
|
|
4195
|
+
* the label is an i18n key); anything unmatched stays raw. The reserved key
|
|
4196
|
+
* `status` maps the `fromStatus` / `toStatus` of status-change entries.
|
|
4197
|
+
* On a V2 detail screen, `AbstractDetailScreenV2.auditValueLists()` builds this
|
|
4198
|
+
* map from `ScreenConfig.AUDIT_VALUE_LISTS`.
|
|
4199
|
+
*
|
|
4200
|
+
* ```html
|
|
4201
|
+
* <ef-change-history [entries]="auditEntries()" [valueLists]="auditValueLists()" />
|
|
4202
|
+
* ```
|
|
3970
4203
|
*/
|
|
3971
4204
|
class EfChangeHistoryComponent {
|
|
3972
4205
|
translate = inject(TranslateService);
|
|
@@ -3982,6 +4215,14 @@ class EfChangeHistoryComponent {
|
|
|
3982
4215
|
collapsible = input(true, ...(ngDevMode ? [{ debugName: "collapsible" }] : /* istanbul ignore next */ []));
|
|
3983
4216
|
/** Initial collapsed state — expanded by default. */
|
|
3984
4217
|
startCollapsed = input(false, ...(ngDevMode ? [{ debugName: "startCollapsed" }] : /* istanbul ignore next */ []));
|
|
4218
|
+
/** Optional field path to reference items, used to show a field's raw
|
|
4219
|
+
* old/new values as translated labels. Unmapped fields and unmatched
|
|
4220
|
+
* values stay raw. Key `status` covers `fromStatus` / `toStatus`. */
|
|
4221
|
+
valueLists = input(null, ...(ngDevMode ? [{ debugName: "valueLists" }] : /* istanbul ignore next */ []));
|
|
4222
|
+
/** Optional formatter for an entry's free-text note. Return the text to show,
|
|
4223
|
+
* or null to keep the raw note. Rendered as plain text (never HTML). A
|
|
4224
|
+
* screen uses it to translate coded notes such as `order_email_resent:order_confirmed`. */
|
|
4225
|
+
noteFormatter = input(null, ...(ngDevMode ? [{ debugName: "noteFormatter" }] : /* istanbul ignore next */ []));
|
|
3985
4226
|
/** "Show more" dialog visibility. */
|
|
3986
4227
|
dialogVisible = signal(false, ...(ngDevMode ? [{ debugName: "dialogVisible" }] : /* istanbul ignore next */ []));
|
|
3987
4228
|
/** First `previewLimit` rows shown inline in the card. */
|
|
@@ -4020,6 +4261,30 @@ class EfChangeHistoryComponent {
|
|
|
4020
4261
|
const key = `audit_field_${field.replace(/\./g, '_')}`;
|
|
4021
4262
|
return this.resolveLabel(key, field);
|
|
4022
4263
|
}
|
|
4264
|
+
/** The note as shown: the formatter's text, else the raw note. */
|
|
4265
|
+
noteText(note) {
|
|
4266
|
+
if (!note)
|
|
4267
|
+
return '';
|
|
4268
|
+
const fn = this.noteFormatter();
|
|
4269
|
+
if (!fn)
|
|
4270
|
+
return note;
|
|
4271
|
+
return fn(note) ?? note;
|
|
4272
|
+
}
|
|
4273
|
+
/** Display value for a field's raw value: the matching list item's label
|
|
4274
|
+
* (translated when it is an i18n key), else the raw value. */
|
|
4275
|
+
valueLabel(field, value) {
|
|
4276
|
+
if (value == null || value === '')
|
|
4277
|
+
return '';
|
|
4278
|
+
const items = field ? this.valueLists()?.[field] : undefined;
|
|
4279
|
+
if (!items?.length)
|
|
4280
|
+
return value;
|
|
4281
|
+
const needle = String(value).toLowerCase();
|
|
4282
|
+
const hit = items.find((i) => String(i.code).toLowerCase() === needle);
|
|
4283
|
+
if (!hit?.label)
|
|
4284
|
+
return value;
|
|
4285
|
+
const translated = this.translate.instant(hit.label);
|
|
4286
|
+
return translated || hit.label;
|
|
4287
|
+
}
|
|
4023
4288
|
/** Returns the translation when present, otherwise a humanized fallback of
|
|
4024
4289
|
* the source token (ngx-translate echoes the key on a miss). */
|
|
4025
4290
|
resolveLabel(key, source) {
|
|
@@ -4047,7 +4312,7 @@ class EfChangeHistoryComponent {
|
|
|
4047
4312
|
return `${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${d.getFullYear()} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
4048
4313
|
}
|
|
4049
4314
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfChangeHistoryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4050
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfChangeHistoryComponent, isStandalone: true, selector: "ef-change-history", inputs: { entries: { classPropertyName: "entries", publicName: "entries", isSignal: true, isRequired: false, transformFunction: null }, titleKey: { classPropertyName: "titleKey", publicName: "titleKey", isSignal: true, isRequired: false, transformFunction: null }, emptyKey: { classPropertyName: "emptyKey", publicName: "emptyKey", isSignal: true, isRequired: false, transformFunction: null }, limit: { classPropertyName: "limit", publicName: "limit", isSignal: true, isRequired: false, transformFunction: null }, previewLimit: { classPropertyName: "previewLimit", publicName: "previewLimit", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, startCollapsed: { classPropertyName: "startCollapsed", publicName: "startCollapsed", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ef-card\n [titleKey]=\"titleKey()\"\n [collapsible]=\"collapsible()\"\n [collapsed]=\"startCollapsed()\"\n>\n <ng-container head-extra>\n @if (hasMore()) {\n <ef-button\n variant=\"comptoir\"\n severity=\"ghost\"\n size=\"small\"\n icon=\"pi pi-window-maximize\"\n tooltipKey=\"audit_view_all\"\n (clickEvent)=\"openDialog($event)\"\n />\n }\n </ng-container>\n\n <ng-container\n *ngTemplateOutlet=\"timeline; context: { $implicit: previewRows(), compact: true }\"\n ></ng-container>\n\n @if (hasMore()) {\n <button type=\"button\" class=\"ch-more\" (click)=\"openDialog()\">\n {{ 'audit_view_all' | translate }} ({{ rows().length }})\n </button>\n }\n</ef-card>\n\n<!-- \"Show more\" dialog \u2014 full timeline, roomy + scrollable -->\n<ef-dialog\n [visible]=\"dialogVisible()\"\n (visibleChange)=\"dialogVisible.set($event)\"\n [headerKey]=\"titleKey()\"\n [defaultActions]=\"false\"\n size=\"lg\"\n [modal]=\"true\"\n position=\"center\"\n>\n <ng-container\n *ngTemplateOutlet=\"timeline; context: { $implicit: rows(), compact: false }\"\n ></ng-container>\n</ef-dialog>\n\n<ng-template #timeline let-items let-compact=\"compact\">\n @if (items.length === 0) {\n <p class=\"text-mute small\">{{ emptyKey() | translate }}</p>\n } @else {\n <div class=\"timeline\" [class.timeline--compact]=\"compact\">\n @for (row of items; track $index) {\n <div class=\"item\">\n <span class=\"dot\" [class]=\"row.meta.cls\" aria-hidden=\"true\"></span>\n <div class=\"body\">\n <div class=\"label\">{{ row.operationLabel }}</div>\n <div class=\"meta\">\n @if (row.entry.userName) {\n <span class=\"strong\">{{ row.entry.userName }}</span> \u00B7\n }\n {{ formatWhen(row.entry.occurredAt) }}\n </div>\n\n @if (row.entry.operation === 'status_changed' && (row.entry.fromStatus || row.entry.toStatus)) {\n <div class=\"ch-status small\">\n <span class=\"text-mute\">{{ row.entry.fromStatus || '\u2014' }}</span>\n <i class=\"pi pi-arrow-right\"></i>\n <span class=\"strong\">{{ row.entry.toStatus || '\u2014' }}</span>\n </div>\n }\n\n @if (row.entry.changes?.length) {\n <ul class=\"ch-changes small\">\n @for (c of row.entry.changes; track c.field) {\n <li>\n <span class=\"ch-field\">{{ fieldLabel(c.field) }}</span>\n <span class=\"text-mute\">{{ c.oldValue || '\u2014' }}</span>\n <i class=\"pi pi-arrow-right\"></i>\n <span class=\"strong\">{{ c.newValue || '\u2014' }}</span>\n </li>\n }\n </ul>\n }\n\n @if (row.entry.note) {\n <div class=\"ch-note small\">{{ row.entry.note }}</div>\n }\n </div>\n </div>\n }\n </div>\n }\n</ng-template>\n", styles: [":host{display:block}.dot.op-created{background:var(--st-delivered-fg, #16a34a);border-color:var(--st-delivered-fg, #16a34a)}.dot.op-updated{background:#2563eb;border-color:#2563eb}.dot.op-status{background:#f59e0b;border-color:#f59e0b}.dot.op-deleted{background:#dc2626;border-color:#dc2626}.dot.op-note{background:#64748b;border-color:#64748b}.dot.op-default{background:var(--ink-300, #94a3b8);border-color:var(--ink-300, #94a3b8)}.ch-status{display:flex;align-items:center;gap:6px;margin-top:4px}.item .body{min-width:0}.ch-changes{list-style:none;padding:0;margin:4px 0 0}.ch-changes li{display:flex;align-items:center;gap:6px;padding:1px 0;min-width:0}.ch-field{font-weight:600;min-width:120px}.timeline--compact .ch-changes .ch-field{flex:0 0 auto}.timeline--compact .ch-changes .text-mute,.timeline--compact .ch-changes .strong{flex:1 1 0;min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.timeline:not(.timeline--compact) .ch-changes li{flex-wrap:wrap}.timeline:not(.timeline--compact) .ch-changes .text-mute,.timeline:not(.timeline--compact) .ch-changes .strong{overflow-wrap:anywhere}.ch-changes .pi,.ch-status .pi{font-size:10px;opacity:.6}.ch-note{margin-top:4px;padding-left:8px;border-left:2px solid var(--rule-soft, #e2e8f0);color:var(--ink-600, #475569)}.ch-more{margin-top:8px;border:0;background:transparent;cursor:pointer;padding:2px 0;font-size:12px;font-weight:600;color:var(--module, #2563eb)}.ch-more:hover{text-decoration:underline}:host-context([dir=rtl]) .ch-changes .pi-arrow-right,:host-context([dir=rtl]) .ch-status .pi-arrow-right{transform:scaleX(-1)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfCardComponent, selector: "ef-card", inputs: ["titleKey", "title", "metaKey", "meta", "tone", "forceHead", "hasFoot", "collapsible", "overflowVisible", "loading", "skeleton", "skeletonRows", "skeletonHeight", "collapsed"], outputs: ["collapsedChange"] }, { kind: "component", type: EfDialogComponent, selector: "ef-dialog", inputs: ["header", "headerKey", "subtitle", "subtitleKey", "visible", "modal", "closable", "draggable", "resizable", "dismissableMask", "closeOnEscape", "severity", "icon", "size", "style", "styleClass", "position", "appendTo", "variant", "actions", "defaultActions", "saveLabelKey", "cancelLabelKey", "saveLabel", "cancelLabel", "saveIcon", "cancelIcon", "saveDisabled", "saveStyleClass", "cancelStyleClass", "saveSeverity", "cancelSeverity"], outputs: ["visibleChange", "showEvent", "hideEvent", "saveAction", "cancelAction", "actionClick"] }, { kind: "component", type: EfButtonComponent, selector: "ef-button", inputs: ["label", "labelKey", "icon", "ariaLabelKey", "variant", "severity", "size", "disabled", "rounded", "outlined", "raised", "text", "loading", "styleClass", "type", "badge", "badgeSeverity", "tooltip", "tooltipKey", "tooltipPosition", "autofocus"], outputs: ["clickEvent"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4315
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfChangeHistoryComponent, isStandalone: true, selector: "ef-change-history", inputs: { entries: { classPropertyName: "entries", publicName: "entries", isSignal: true, isRequired: false, transformFunction: null }, titleKey: { classPropertyName: "titleKey", publicName: "titleKey", isSignal: true, isRequired: false, transformFunction: null }, emptyKey: { classPropertyName: "emptyKey", publicName: "emptyKey", isSignal: true, isRequired: false, transformFunction: null }, limit: { classPropertyName: "limit", publicName: "limit", isSignal: true, isRequired: false, transformFunction: null }, previewLimit: { classPropertyName: "previewLimit", publicName: "previewLimit", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, startCollapsed: { classPropertyName: "startCollapsed", publicName: "startCollapsed", isSignal: true, isRequired: false, transformFunction: null }, valueLists: { classPropertyName: "valueLists", publicName: "valueLists", isSignal: true, isRequired: false, transformFunction: null }, noteFormatter: { classPropertyName: "noteFormatter", publicName: "noteFormatter", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ef-card\n [titleKey]=\"titleKey()\"\n [collapsible]=\"collapsible()\"\n [collapsed]=\"startCollapsed()\"\n>\n <ng-container head-extra>\n @if (hasMore()) {\n <ef-button\n variant=\"comptoir\"\n severity=\"ghost\"\n size=\"small\"\n icon=\"pi pi-window-maximize\"\n tooltipKey=\"audit_view_all\"\n (clickEvent)=\"openDialog($event)\"\n />\n }\n </ng-container>\n\n <ng-container\n *ngTemplateOutlet=\"timeline; context: { $implicit: previewRows(), compact: true }\"\n ></ng-container>\n\n @if (hasMore()) {\n <button type=\"button\" class=\"ch-more\" (click)=\"openDialog()\">\n {{ 'audit_view_all' | translate }} ({{ rows().length }})\n </button>\n }\n</ef-card>\n\n<!-- \"Show more\" dialog \u2014 full timeline, roomy + scrollable -->\n<ef-dialog\n [visible]=\"dialogVisible()\"\n (visibleChange)=\"dialogVisible.set($event)\"\n [headerKey]=\"titleKey()\"\n [defaultActions]=\"false\"\n size=\"lg\"\n [modal]=\"true\"\n position=\"center\"\n>\n <ng-container\n *ngTemplateOutlet=\"timeline; context: { $implicit: rows(), compact: false }\"\n ></ng-container>\n</ef-dialog>\n\n<ng-template #timeline let-items let-compact=\"compact\">\n @if (items.length === 0) {\n <p class=\"text-mute small\">{{ emptyKey() | translate }}</p>\n } @else {\n <div class=\"timeline\" [class.timeline--compact]=\"compact\">\n @for (row of items; track $index) {\n <div class=\"item\">\n <span class=\"dot\" [class]=\"row.meta.cls\" aria-hidden=\"true\"></span>\n <div class=\"body\">\n <div class=\"label\">{{ row.operationLabel }}</div>\n <div class=\"meta\">\n @if (row.entry.userName) {\n <span class=\"strong\">{{ row.entry.userName }}</span> \u00B7\n }\n {{ formatWhen(row.entry.occurredAt) }}\n </div>\n\n @if (row.entry.operation === 'status_changed' && (row.entry.fromStatus || row.entry.toStatus)) {\n <div class=\"ch-status small\">\n <span class=\"text-mute\" dir=\"auto\">{{ valueLabel('status', row.entry.fromStatus) || '\u2014' }}</span>\n <i class=\"pi pi-arrow-right\"></i>\n <span class=\"strong\" dir=\"auto\">{{ valueLabel('status', row.entry.toStatus) || '\u2014' }}</span>\n </div>\n }\n\n @if (row.entry.changes?.length) {\n <ul class=\"ch-changes small\">\n @for (c of row.entry.changes; track c.field) {\n <li>\n <span class=\"ch-field\">{{ fieldLabel(c.field) }}</span>\n <span class=\"text-mute\" dir=\"auto\">{{ valueLabel(c.field, c.oldValue) || '\u2014' }}</span>\n <i class=\"pi pi-arrow-right\"></i>\n <span class=\"strong\" dir=\"auto\">{{ valueLabel(c.field, c.newValue) || '\u2014' }}</span>\n </li>\n }\n </ul>\n }\n\n @if (row.entry.note) {\n <div class=\"ch-note small\">{{ noteText(row.entry.note) }}</div>\n }\n </div>\n </div>\n }\n </div>\n }\n</ng-template>\n", styles: [":host{display:block}.dot.op-created{background:var(--st-delivered-fg, #16a34a);border-color:var(--st-delivered-fg, #16a34a)}.dot.op-updated{background:#2563eb;border-color:#2563eb}.dot.op-status{background:#f59e0b;border-color:#f59e0b}.dot.op-deleted{background:#dc2626;border-color:#dc2626}.dot.op-note{background:#64748b;border-color:#64748b}.dot.op-default{background:var(--ink-300, #94a3b8);border-color:var(--ink-300, #94a3b8)}.ch-status{display:flex;align-items:center;gap:6px;margin-top:4px}.item .body{min-width:0}.ch-changes{list-style:none;padding:0;margin:4px 0 0}.ch-changes li{display:flex;align-items:center;gap:6px;padding:1px 0;min-width:0}.ch-field{font-weight:600;min-width:120px}.timeline--compact .ch-changes .ch-field{flex:0 0 auto}.timeline--compact .ch-changes .text-mute,.timeline--compact .ch-changes .strong{flex:1 1 0;min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.timeline:not(.timeline--compact) .ch-changes li{flex-wrap:wrap}.timeline:not(.timeline--compact) .ch-changes .text-mute,.timeline:not(.timeline--compact) .ch-changes .strong{overflow-wrap:anywhere}.ch-changes .pi,.ch-status .pi{font-size:10px;opacity:.6}.ch-note{margin-top:4px;padding-left:8px;border-left:2px solid var(--rule-soft, #e2e8f0);color:var(--ink-600, #475569)}.ch-more{margin-top:8px;border:0;background:transparent;cursor:pointer;padding:2px 0;font-size:12px;font-weight:600;color:var(--module, #2563eb)}.ch-more:hover{text-decoration:underline}.ch-changes .text-mute,.ch-changes .strong,.ch-status .text-mute,.ch-status .strong{text-align:start}:host-context([dir=rtl]) .ch-changes .pi-arrow-right,:host-context([dir=rtl]) .ch-status .pi-arrow-right{transform:scaleX(-1)}@media(max-width:767px){.timeline--compact .ch-changes li{flex-wrap:wrap}.timeline--compact .ch-changes .text-mute,.timeline--compact .ch-changes .strong{flex:1 1 auto;overflow:visible;white-space:normal;text-overflow:clip;overflow-wrap:anywhere}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfCardComponent, selector: "ef-card", inputs: ["titleKey", "title", "metaKey", "meta", "tone", "forceHead", "hasFoot", "collapsible", "overflowVisible", "loading", "skeleton", "skeletonRows", "skeletonHeight", "collapsed"], outputs: ["collapsedChange"] }, { kind: "component", type: EfDialogComponent, selector: "ef-dialog", inputs: ["header", "headerKey", "subtitle", "subtitleKey", "visible", "modal", "closable", "draggable", "resizable", "dismissableMask", "closeOnEscape", "severity", "icon", "size", "style", "styleClass", "position", "appendTo", "variant", "actions", "defaultActions", "saveLabelKey", "cancelLabelKey", "saveLabel", "cancelLabel", "saveIcon", "cancelIcon", "saveDisabled", "saveStyleClass", "cancelStyleClass", "saveSeverity", "cancelSeverity"], outputs: ["visibleChange", "showEvent", "hideEvent", "saveAction", "cancelAction", "actionClick"] }, { kind: "component", type: EfButtonComponent, selector: "ef-button", inputs: ["label", "labelKey", "icon", "ariaLabelKey", "variant", "severity", "size", "disabled", "rounded", "outlined", "raised", "text", "loading", "styleClass", "type", "badge", "badgeSeverity", "tooltip", "tooltipKey", "tooltipPosition", "autofocus"], outputs: ["clickEvent"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4051
4316
|
}
|
|
4052
4317
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfChangeHistoryComponent, decorators: [{
|
|
4053
4318
|
type: Component,
|
|
@@ -4057,8 +4322,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
4057
4322
|
EfCardComponent,
|
|
4058
4323
|
EfDialogComponent,
|
|
4059
4324
|
EfButtonComponent,
|
|
4060
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ef-card\n [titleKey]=\"titleKey()\"\n [collapsible]=\"collapsible()\"\n [collapsed]=\"startCollapsed()\"\n>\n <ng-container head-extra>\n @if (hasMore()) {\n <ef-button\n variant=\"comptoir\"\n severity=\"ghost\"\n size=\"small\"\n icon=\"pi pi-window-maximize\"\n tooltipKey=\"audit_view_all\"\n (clickEvent)=\"openDialog($event)\"\n />\n }\n </ng-container>\n\n <ng-container\n *ngTemplateOutlet=\"timeline; context: { $implicit: previewRows(), compact: true }\"\n ></ng-container>\n\n @if (hasMore()) {\n <button type=\"button\" class=\"ch-more\" (click)=\"openDialog()\">\n {{ 'audit_view_all' | translate }} ({{ rows().length }})\n </button>\n }\n</ef-card>\n\n<!-- \"Show more\" dialog \u2014 full timeline, roomy + scrollable -->\n<ef-dialog\n [visible]=\"dialogVisible()\"\n (visibleChange)=\"dialogVisible.set($event)\"\n [headerKey]=\"titleKey()\"\n [defaultActions]=\"false\"\n size=\"lg\"\n [modal]=\"true\"\n position=\"center\"\n>\n <ng-container\n *ngTemplateOutlet=\"timeline; context: { $implicit: rows(), compact: false }\"\n ></ng-container>\n</ef-dialog>\n\n<ng-template #timeline let-items let-compact=\"compact\">\n @if (items.length === 0) {\n <p class=\"text-mute small\">{{ emptyKey() | translate }}</p>\n } @else {\n <div class=\"timeline\" [class.timeline--compact]=\"compact\">\n @for (row of items; track $index) {\n <div class=\"item\">\n <span class=\"dot\" [class]=\"row.meta.cls\" aria-hidden=\"true\"></span>\n <div class=\"body\">\n <div class=\"label\">{{ row.operationLabel }}</div>\n <div class=\"meta\">\n @if (row.entry.userName) {\n <span class=\"strong\">{{ row.entry.userName }}</span> \u00B7\n }\n {{ formatWhen(row.entry.occurredAt) }}\n </div>\n\n @if (row.entry.operation === 'status_changed' && (row.entry.fromStatus || row.entry.toStatus)) {\n <div class=\"ch-status small\">\n <span class=\"text-mute\">{{ row.entry.fromStatus || '\u2014' }}</span>\n <i class=\"pi pi-arrow-right\"></i>\n <span class=\"strong\">{{ row.entry.toStatus || '\u2014' }}</span>\n </div>\n }\n\n @if (row.entry.changes?.length) {\n <ul class=\"ch-changes small\">\n @for (c of row.entry.changes; track c.field) {\n <li>\n <span class=\"ch-field\">{{ fieldLabel(c.field) }}</span>\n <span class=\"text-mute\">{{ c.oldValue || '\u2014' }}</span>\n <i class=\"pi pi-arrow-right\"></i>\n <span class=\"strong\">{{ c.newValue || '\u2014' }}</span>\n </li>\n }\n </ul>\n }\n\n @if (row.entry.note) {\n <div class=\"ch-note small\">{{ row.entry.note }}</div>\n }\n </div>\n </div>\n }\n </div>\n }\n</ng-template>\n", styles: [":host{display:block}.dot.op-created{background:var(--st-delivered-fg, #16a34a);border-color:var(--st-delivered-fg, #16a34a)}.dot.op-updated{background:#2563eb;border-color:#2563eb}.dot.op-status{background:#f59e0b;border-color:#f59e0b}.dot.op-deleted{background:#dc2626;border-color:#dc2626}.dot.op-note{background:#64748b;border-color:#64748b}.dot.op-default{background:var(--ink-300, #94a3b8);border-color:var(--ink-300, #94a3b8)}.ch-status{display:flex;align-items:center;gap:6px;margin-top:4px}.item .body{min-width:0}.ch-changes{list-style:none;padding:0;margin:4px 0 0}.ch-changes li{display:flex;align-items:center;gap:6px;padding:1px 0;min-width:0}.ch-field{font-weight:600;min-width:120px}.timeline--compact .ch-changes .ch-field{flex:0 0 auto}.timeline--compact .ch-changes .text-mute,.timeline--compact .ch-changes .strong{flex:1 1 0;min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.timeline:not(.timeline--compact) .ch-changes li{flex-wrap:wrap}.timeline:not(.timeline--compact) .ch-changes .text-mute,.timeline:not(.timeline--compact) .ch-changes .strong{overflow-wrap:anywhere}.ch-changes .pi,.ch-status .pi{font-size:10px;opacity:.6}.ch-note{margin-top:4px;padding-left:8px;border-left:2px solid var(--rule-soft, #e2e8f0);color:var(--ink-600, #475569)}.ch-more{margin-top:8px;border:0;background:transparent;cursor:pointer;padding:2px 0;font-size:12px;font-weight:600;color:var(--module, #2563eb)}.ch-more:hover{text-decoration:underline}:host-context([dir=rtl]) .ch-changes .pi-arrow-right,:host-context([dir=rtl]) .ch-status .pi-arrow-right{transform:scaleX(-1)}\n"] }]
|
|
4061
|
-
}], propDecorators: { entries: [{ type: i0.Input, args: [{ isSignal: true, alias: "entries", required: false }] }], titleKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleKey", required: false }] }], emptyKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyKey", required: false }] }], limit: [{ type: i0.Input, args: [{ isSignal: true, alias: "limit", required: false }] }], previewLimit: [{ type: i0.Input, args: [{ isSignal: true, alias: "previewLimit", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], startCollapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "startCollapsed", required: false }] }] } });
|
|
4325
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ef-card\n [titleKey]=\"titleKey()\"\n [collapsible]=\"collapsible()\"\n [collapsed]=\"startCollapsed()\"\n>\n <ng-container head-extra>\n @if (hasMore()) {\n <ef-button\n variant=\"comptoir\"\n severity=\"ghost\"\n size=\"small\"\n icon=\"pi pi-window-maximize\"\n tooltipKey=\"audit_view_all\"\n (clickEvent)=\"openDialog($event)\"\n />\n }\n </ng-container>\n\n <ng-container\n *ngTemplateOutlet=\"timeline; context: { $implicit: previewRows(), compact: true }\"\n ></ng-container>\n\n @if (hasMore()) {\n <button type=\"button\" class=\"ch-more\" (click)=\"openDialog()\">\n {{ 'audit_view_all' | translate }} ({{ rows().length }})\n </button>\n }\n</ef-card>\n\n<!-- \"Show more\" dialog \u2014 full timeline, roomy + scrollable -->\n<ef-dialog\n [visible]=\"dialogVisible()\"\n (visibleChange)=\"dialogVisible.set($event)\"\n [headerKey]=\"titleKey()\"\n [defaultActions]=\"false\"\n size=\"lg\"\n [modal]=\"true\"\n position=\"center\"\n>\n <ng-container\n *ngTemplateOutlet=\"timeline; context: { $implicit: rows(), compact: false }\"\n ></ng-container>\n</ef-dialog>\n\n<ng-template #timeline let-items let-compact=\"compact\">\n @if (items.length === 0) {\n <p class=\"text-mute small\">{{ emptyKey() | translate }}</p>\n } @else {\n <div class=\"timeline\" [class.timeline--compact]=\"compact\">\n @for (row of items; track $index) {\n <div class=\"item\">\n <span class=\"dot\" [class]=\"row.meta.cls\" aria-hidden=\"true\"></span>\n <div class=\"body\">\n <div class=\"label\">{{ row.operationLabel }}</div>\n <div class=\"meta\">\n @if (row.entry.userName) {\n <span class=\"strong\">{{ row.entry.userName }}</span> \u00B7\n }\n {{ formatWhen(row.entry.occurredAt) }}\n </div>\n\n @if (row.entry.operation === 'status_changed' && (row.entry.fromStatus || row.entry.toStatus)) {\n <div class=\"ch-status small\">\n <span class=\"text-mute\" dir=\"auto\">{{ valueLabel('status', row.entry.fromStatus) || '\u2014' }}</span>\n <i class=\"pi pi-arrow-right\"></i>\n <span class=\"strong\" dir=\"auto\">{{ valueLabel('status', row.entry.toStatus) || '\u2014' }}</span>\n </div>\n }\n\n @if (row.entry.changes?.length) {\n <ul class=\"ch-changes small\">\n @for (c of row.entry.changes; track c.field) {\n <li>\n <span class=\"ch-field\">{{ fieldLabel(c.field) }}</span>\n <span class=\"text-mute\" dir=\"auto\">{{ valueLabel(c.field, c.oldValue) || '\u2014' }}</span>\n <i class=\"pi pi-arrow-right\"></i>\n <span class=\"strong\" dir=\"auto\">{{ valueLabel(c.field, c.newValue) || '\u2014' }}</span>\n </li>\n }\n </ul>\n }\n\n @if (row.entry.note) {\n <div class=\"ch-note small\">{{ noteText(row.entry.note) }}</div>\n }\n </div>\n </div>\n }\n </div>\n }\n</ng-template>\n", styles: [":host{display:block}.dot.op-created{background:var(--st-delivered-fg, #16a34a);border-color:var(--st-delivered-fg, #16a34a)}.dot.op-updated{background:#2563eb;border-color:#2563eb}.dot.op-status{background:#f59e0b;border-color:#f59e0b}.dot.op-deleted{background:#dc2626;border-color:#dc2626}.dot.op-note{background:#64748b;border-color:#64748b}.dot.op-default{background:var(--ink-300, #94a3b8);border-color:var(--ink-300, #94a3b8)}.ch-status{display:flex;align-items:center;gap:6px;margin-top:4px}.item .body{min-width:0}.ch-changes{list-style:none;padding:0;margin:4px 0 0}.ch-changes li{display:flex;align-items:center;gap:6px;padding:1px 0;min-width:0}.ch-field{font-weight:600;min-width:120px}.timeline--compact .ch-changes .ch-field{flex:0 0 auto}.timeline--compact .ch-changes .text-mute,.timeline--compact .ch-changes .strong{flex:1 1 0;min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.timeline:not(.timeline--compact) .ch-changes li{flex-wrap:wrap}.timeline:not(.timeline--compact) .ch-changes .text-mute,.timeline:not(.timeline--compact) .ch-changes .strong{overflow-wrap:anywhere}.ch-changes .pi,.ch-status .pi{font-size:10px;opacity:.6}.ch-note{margin-top:4px;padding-left:8px;border-left:2px solid var(--rule-soft, #e2e8f0);color:var(--ink-600, #475569)}.ch-more{margin-top:8px;border:0;background:transparent;cursor:pointer;padding:2px 0;font-size:12px;font-weight:600;color:var(--module, #2563eb)}.ch-more:hover{text-decoration:underline}.ch-changes .text-mute,.ch-changes .strong,.ch-status .text-mute,.ch-status .strong{text-align:start}:host-context([dir=rtl]) .ch-changes .pi-arrow-right,:host-context([dir=rtl]) .ch-status .pi-arrow-right{transform:scaleX(-1)}@media(max-width:767px){.timeline--compact .ch-changes li{flex-wrap:wrap}.timeline--compact .ch-changes .text-mute,.timeline--compact .ch-changes .strong{flex:1 1 auto;overflow:visible;white-space:normal;text-overflow:clip;overflow-wrap:anywhere}}\n"] }]
|
|
4326
|
+
}], propDecorators: { entries: [{ type: i0.Input, args: [{ isSignal: true, alias: "entries", required: false }] }], titleKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleKey", required: false }] }], emptyKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyKey", required: false }] }], limit: [{ type: i0.Input, args: [{ isSignal: true, alias: "limit", required: false }] }], previewLimit: [{ type: i0.Input, args: [{ isSignal: true, alias: "previewLimit", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], startCollapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "startCollapsed", required: false }] }], valueLists: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueLists", required: false }] }], noteFormatter: [{ type: i0.Input, args: [{ isSignal: true, alias: "noteFormatter", required: false }] }] } });
|
|
4062
4327
|
|
|
4063
4328
|
/**
|
|
4064
4329
|
* Keyboard-navigable product search. Filters products by display name,
|
|
@@ -5683,6 +5948,19 @@ class EfDataCardComponent {
|
|
|
5683
5948
|
return undefined;
|
|
5684
5949
|
return path.split('.').reduce((obj, key) => (obj == null ? obj : obj[key]), row);
|
|
5685
5950
|
}
|
|
5951
|
+
/**
|
|
5952
|
+
* Whether a cell has anything to show. The mobile secondary line joins its
|
|
5953
|
+
* parts with a "·" drawn before every part but the first, so an empty part
|
|
5954
|
+
* left a dangling separator; skipping it keeps separators between present
|
|
5955
|
+
* values only. A column with a custom body template is always present (the
|
|
5956
|
+
* template decides what to render); a boolean always renders a glyph.
|
|
5957
|
+
*/
|
|
5958
|
+
hasCellContent(row, col) {
|
|
5959
|
+
if (this.bodyTemplate(col.id) || col.type === 'boolean')
|
|
5960
|
+
return true;
|
|
5961
|
+
const v = this.cellValue(row, col);
|
|
5962
|
+
return v !== null && v !== undefined && !(typeof v === 'string' && v.trim() === '');
|
|
5963
|
+
}
|
|
5686
5964
|
headerAlignClass(col) {
|
|
5687
5965
|
if (col.align === 'end')
|
|
5688
5966
|
return 'num';
|
|
@@ -6086,7 +6364,7 @@ class EfDataCardComponent {
|
|
|
6086
6364
|
};
|
|
6087
6365
|
trackByColumn = (_, col) => col.id;
|
|
6088
6366
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfDataCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6089
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfDataCardComponent, isStandalone: true, selector: "ef-data-card", inputs: { mobileLayout: { classPropertyName: "mobileLayout", publicName: "mobileLayout", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, trackByField: { classPropertyName: "trackByField", publicName: "trackByField", isSignal: true, isRequired: false, transformFunction: null }, pageNumber: { classPropertyName: "pageNumber", publicName: "pageNumber", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, totalCount: { classPropertyName: "totalCount", publicName: "totalCount", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, hidePager: { classPropertyName: "hidePager", publicName: "hidePager", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, errorMsg: { classPropertyName: "errorMsg", publicName: "errorMsg", isSignal: true, isRequired: false, transformFunction: null }, loadingKey: { classPropertyName: "loadingKey", publicName: "loadingKey", isSignal: true, isRequired: false, transformFunction: null }, rowDoubleClickable: { classPropertyName: "rowDoubleClickable", publicName: "rowDoubleClickable", isSignal: true, isRequired: false, transformFunction: null }, rowPressed: { classPropertyName: "rowPressed", publicName: "rowPressed", isSignal: true, isRequired: false, transformFunction: null }, rowDisabled: { classPropertyName: "rowDisabled", publicName: "rowDisabled", isSignal: true, isRequired: false, transformFunction: null }, hasActionsColumn: { classPropertyName: "hasActionsColumn", publicName: "hasActionsColumn", isSignal: true, isRequired: false, transformFunction: null }, rowActionsContext: { classPropertyName: "rowActionsContext", publicName: "rowActionsContext", isSignal: true, isRequired: false, transformFunction: null }, showViewAction: { classPropertyName: "showViewAction", publicName: "showViewAction", isSignal: true, isRequired: false, transformFunction: null }, showEditAction: { classPropertyName: "showEditAction", publicName: "showEditAction", isSignal: true, isRequired: false, transformFunction: null }, showDuplicateAction: { classPropertyName: "showDuplicateAction", publicName: "showDuplicateAction", isSignal: true, isRequired: false, transformFunction: null }, showDeleteAction: { classPropertyName: "showDeleteAction", publicName: "showDeleteAction", isSignal: true, isRequired: false, transformFunction: null }, rowActionState: { classPropertyName: "rowActionState", publicName: "rowActionState", isSignal: true, isRequired: false, transformFunction: null }, showDensityControl: { classPropertyName: "showDensityControl", publicName: "showDensityControl", isSignal: true, isRequired: false, transformFunction: null }, showColumnPicker: { classPropertyName: "showColumnPicker", publicName: "showColumnPicker", isSignal: true, isRequired: false, transformFunction: null }, showExportControl: { classPropertyName: "showExportControl", publicName: "showExportControl", isSignal: true, isRequired: false, transformFunction: null }, exporting: { classPropertyName: "exporting", publicName: "exporting", isSignal: true, isRequired: false, transformFunction: null }, screenContext: { classPropertyName: "screenContext", publicName: "screenContext", isSignal: true, isRequired: false, transformFunction: null }, searchTerm: { classPropertyName: "searchTerm", publicName: "searchTerm", isSignal: true, isRequired: false, transformFunction: null }, tableKey: { classPropertyName: "tableKey", publicName: "tableKey", isSignal: true, isRequired: false, transformFunction: null }, defaultDensity: { classPropertyName: "defaultDensity", publicName: "defaultDensity", isSignal: true, isRequired: false, transformFunction: null }, reorderable: { classPropertyName: "reorderable", publicName: "reorderable", isSignal: true, isRequired: false, transformFunction: null }, fixedLayout: { classPropertyName: "fixedLayout", publicName: "fixedLayout", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageChange: "pageChange", pageSizeChange: "pageSizeChange", sortChange: "sortChange", rowDoubleClick: "rowDoubleClick", rowAction: "rowAction", exportRequest: "exportRequest", clearSearch: "clearSearch", retry: "retry", rowReorder: "rowReorder" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown.escape": "onEscape()" } }, queries: [{ propertyName: "bodyTemplates", predicate: EfColumnTemplateDirective }, { propertyName: "headerTemplates", predicate: EfColumnHeaderTemplateDirective }], ngImport: i0, template: "<!-- One cell renderer, shared by the table, the mobile row and the mobile\n detail. A new column type is added here once, not three times. -->\n<ng-template #cellTpl let-row let-col=\"col\" let-index=\"index\">\n @if (bodyTemplate(col.id); as bodyTpl) {\n <ng-container *ngTemplateOutlet=\"bodyTpl; context: { $implicit: row, col: col, index: index }\"></ng-container>\n } @else {\n @switch (col.type) {\n @case ('number') {\n {{ $any(cellValue(row, col)) | number:numberFormat(col):col.locale }}\n }\n @case ('money') {\n {{ $any(cellValue(row, col)) | currency:(col.currencyCode || 'EUR'):(col.currencyDisplay || 'symbol'):numberFormat(col):col.locale }}\n }\n @case ('date') {\n {{ $any(cellValue(row, col)) | date:(col.dateFormat || 'dd/MM/yyyy'):undefined:col.locale }}\n }\n @case ('datetime') {\n {{ $any(cellValue(row, col)) | date:(col.dateFormat || 'dd/MM/yyyy HH:mm'):undefined:col.locale }}\n }\n @case ('boolean') {\n @if (cellValue(row, col)) {\n <span class=\"bool yes\"><span class=\"dot\"></span> \u2713</span>\n } @else {\n <span class=\"bool no\"><span class=\"dot\"></span> \u2014</span>\n }\n }\n @case ('mono') {\n <span class=\"ref\">{{ cellValue(row, col) }}</span>\n }\n @case ('chip') {\n <span [class]=\"'chip ' + (col.chipPrefix || 'chip-') + cellValue(row, col)\">\n \u25CF {{ cellValue(row, col) }}\n </span>\n }\n @case ('status') {\n <ef-status-chip\n [referenceKey]=\"col.referenceKey || ''\"\n [code]=\"$any(cellValue(row, col))\"\n />\n }\n @case ('reference') {\n {{ resolveReference(cellValue(row, col), col) }}\n }\n @default {\n {{ cellValue(row, col) }}\n }\n }\n }\n</ng-template>\n\n<!-- .has-open-menu unclips the shell for the Densit\u00E9 / Colonnes menus,\n which hang below .tbl-head. Row menus are fixed to the viewport\n (ef-row-actions) and need no lift; counting them also left the shell\n unclipped for good when a row went while its menu was open. -->\n<div class=\"tbl-wrap\" [class.has-open-menu]=\"openTool() !== null\" [attr.data-density]=\"density()\">\n <!-- \u2500\u2500 HEAD ROW \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"tbl-head\">\n <div class=\"count small\">\n @if (loading()) {\n <span>{{ loadingKey() | translate }}</span>\n } @else if (errorMsg()) {\n <!-- A refresh that failed on top of rows already on screen keeps the\n rows, so this chip is the only sign anything went wrong \u2014 and the\n rows it sits above may now be stale. It has to say so, and say it\n in the product's words: `errorMsg` is `err.message` from the HTTP\n layer, and \"500 Internal Server Error\" tells an operator nothing\n they can act on.\n\n The raw string stays on `data-error`, where devtools can read it\n and no reader ever meets it. `title` was the obvious place and is\n the wrong one: it renders the exception in a tooltip, which is\n precisely what this change exists to stop, and it is unreachable\n on touch besides.\n\n role=\"status\", not \"alert\": polite is right for a signal beside\n rows that are still usable, and when the body is showing the\n error state it already carries the alert \u2014 one failure must not\n interrupt a screen-reader twice. -->\n <span\n role=\"status\"\n [attr.data-error]=\"errorMsg()\"\n style=\"color: var(--st-cancelled-fg); font-weight: 600;\"\n >\u26A0 {{ 'common_error_refresh_failed' | translate }}</span\n >\n } @else {\n <ng-content select=\"[tbl-head-info]\"></ng-content>\n }\n </div>\n <div class=\"tbl-tools\">\n <ng-content select=\"[tbl-head-actions]\"></ng-content>\n\n @if (canExport()) {\n <div class=\"tbl-tool\">\n <button\n class=\"btn btn-ghost btn-sm\"\n type=\"button\"\n [disabled]=\"exporting()\"\n [attr.aria-busy]=\"exporting()\"\n (click)=\"requestExport()\"\n >\n <i\n class=\"pi\"\n [class.pi-download]=\"!exporting()\"\n [class.pi-spinner]=\"exporting()\"\n [class.is-spinning]=\"exporting()\"\n aria-hidden=\"true\"\n ></i>\n {{ (exporting() ? 'common_export_running' : 'common_export') | translate }}\n </button>\n </div>\n }\n\n @if (showDensityControl()) {\n <div class=\"tbl-tool\">\n <button\n class=\"btn btn-ghost btn-sm\"\n type=\"button\"\n [class.open]=\"openTool() === 'density'\"\n [attr.aria-expanded]=\"openTool() === 'density'\"\n aria-haspopup=\"menu\"\n (click)=\"toggleTool('density')\"\n >\n <i class=\"pi pi-bars\" aria-hidden=\"true\"></i>\n {{ 'common_density' | translate }}\n </button>\n @if (openTool() === 'density') {\n <div class=\"tbl-tool__menu\" role=\"menu\">\n @for (opt of densityOptions; track opt.value) {\n <button\n type=\"button\"\n role=\"menuitemradio\"\n [attr.aria-checked]=\"density() === opt.value\"\n [class.is-active]=\"density() === opt.value\"\n (click)=\"setDensity(opt.value)\"\n >\n <i class=\"pi\" [ngClass]=\"opt.icon\" aria-hidden=\"true\"></i>\n <span class=\"tbl-tool__label\">{{ opt.labelKey | translate }}</span>\n @if (density() === opt.value) {\n <i class=\"pi pi-check tbl-tool__check\" aria-hidden=\"true\"></i>\n }\n </button>\n }\n </div>\n }\n </div>\n }\n\n @if (showColumnPicker()) {\n <div class=\"tbl-tool\">\n <button\n class=\"btn btn-ghost btn-sm\"\n type=\"button\"\n [class.open]=\"openTool() === 'columns'\"\n [attr.aria-expanded]=\"openTool() === 'columns'\"\n aria-haspopup=\"menu\"\n (click)=\"toggleTool('columns')\"\n >\n <i class=\"pi pi-th-large\" aria-hidden=\"true\"></i>\n {{ 'common_columns' | translate }}\n </button>\n @if (openTool() === 'columns') {\n <div class=\"tbl-tool__menu tbl-tool__menu--wide\" role=\"menu\">\n @for (col of hideableColumns(); track col.id) {\n <button\n type=\"button\"\n role=\"menuitemcheckbox\"\n [attr.aria-checked]=\"!isColumnHidden(col.id)\"\n [class.is-active]=\"!isColumnHidden(col.id)\"\n (click)=\"toggleColumn(col.id)\"\n >\n <i class=\"pi\" [class.pi-check]=\"!isColumnHidden(col.id)\" aria-hidden=\"true\"></i>\n <span class=\"tbl-tool__label\">{{\n col.headerKey ? (col.headerKey | translate) : (col.header || col.id)\n }}</span>\n </button>\n }\n <div class=\"tbl-tool__sep\"></div>\n <button type=\"button\" role=\"menuitem\" (click)=\"resetColumns()\">\n <i class=\"pi pi-refresh\" aria-hidden=\"true\"></i>\n <span class=\"tbl-tool__label\">{{ 'common_columns_reset' | translate }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 TABLE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (isMobile()) {\n <!-- \u2500\u2500 MOBILE LIST \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n A six-column table on a 390px screen either overflows or\n squeezes every column to nothing, so the rows become a list.\n Same columns, same cell renderers; each column just declares\n (or is given) a role. Both shapes expand to the same detail \u2014\n they differ only in how much they show collapsed. -->\n <ul\n class=\"mlist\"\n [attr.data-layout]=\"effectiveMobileLayout()\"\n [attr.aria-busy]=\"loading() ? 'true' : null\"\n >\n @for (row of rows(); track trackByRow($index, row); let i = $index) {\n <li class=\"mrow\" [class.is-open]=\"isExpanded(row, i)\" [class.is-disabled]=\"isRowDisabled(row)\">\n <div\n class=\"mrow__head\"\n [class.is-tappable]=\"headIsControl()\"\n [attr.role]=\"headIsControl() ? 'button' : null\"\n [attr.tabindex]=\"headIsControl() ? 0 : null\"\n [attr.aria-pressed]=\"pressedAttr(row)\"\n [attr.aria-disabled]=\"isRowDisabled(row) ? 'true' : null\"\n [attr.aria-expanded]=\"!rowNavigable() && hasDetail() ? isExpanded(row, i) : null\"\n (click)=\"onRowHeadActivate(row, i, $event)\"\n (keydown.enter)=\"onRowHeadActivate(row, i, $event)\"\n (keydown.space)=\"onRowHeadActivate(row, i, $event)\"\n >\n <div class=\"mrow__lead\">\n @if (primaryColumn(); as pc) {\n <span class=\"mrow__primary\" efOverflowTooltip efTooltip efTooltipPosition=\"top\">\n <ng-container *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: pc, index: i }\"></ng-container>\n </span>\n }\n @if (effectiveMobileLayout() === 'row' && secondaryColumns().length) {\n <span class=\"mrow__secondary\">\n @for (sc of secondaryColumns(); track sc.id) {\n <span class=\"mrow__sec-part\">\n <ng-container *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: sc, index: i }\"></ng-container>\n </span>\n }\n </span>\n }\n </div>\n\n <div class=\"mrow__trail\">\n @if (statusColumn(); as st) {\n <ng-container *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: st, index: i }\"></ng-container>\n }\n @if (hasActionsColumn()) {\n <span class=\"mrow__actions\">\n <ef-row-actions [items]=\"defaultRowActions(row)\" [context]=\"rowActionsContext()\" />\n </span>\n }\n @if (hasDetail() && effectiveMobileLayout() === 'row') {\n @if (rowNavigable()) {\n <!-- The head opens the record, so expansion needs a control\n of its own. Same glyph, promoted from decoration to a\n button that carries the state it was already drawing. -->\n <button\n type=\"button\"\n class=\"mrow__disclosure\"\n [attr.aria-expanded]=\"isExpanded(row, i)\"\n [attr.aria-label]=\"(isExpanded(row, i) ? 'common_view_less' : 'common_view_more') | translate\"\n (click)=\"onDisclosureToggle(row, i, $event)\"\n >\n <i\n class=\"pi pi-chevron-down mrow__chevron\"\n [class.is-open]=\"isExpanded(row, i)\"\n aria-hidden=\"true\"\n ></i>\n </button>\n } @else {\n <!-- Says the row opens, and which way it is now. Decorative:\n the whole head is the control, and it carries the state. -->\n <i\n class=\"pi pi-chevron-down mrow__chevron\"\n [class.is-open]=\"isExpanded(row, i)\"\n aria-hidden=\"true\"\n ></i>\n }\n }\n </div>\n </div>\n\n @if (visibleDetail(row, i).length) {\n <dl class=\"mrow__detail\">\n @for (col of visibleDetail(row, i); track col.id) {\n <div class=\"mrow__field\">\n <dt>{{ $any(col).headerKey ? ($any(col).headerKey | translate) : $any(col).header }}</dt>\n <dd [class]=\"cellAlignClass($any(col))\">\n <ng-container *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: col, index: i }\"></ng-container>\n </dd>\n </div>\n }\n </dl>\n }\n\n @if (hasMoreThanPreview() && effectiveMobileLayout() === 'card') {\n <button class=\"mrow__more\" type=\"button\" (click)=\"toggleExpanded(row, i)\">\n <span>{{ (isExpanded(row, i) ? 'common_view_less' : 'common_view_more') | translate }}</span>\n <i class=\"pi pi-chevron-down mrow__chevron\" [class.is-open]=\"isExpanded(row, i)\" aria-hidden=\"true\"></i>\n </button>\n }\n </li>\n } @empty {\n <!-- No rows has three meanings and they must not share a screen:\n still loading is a skeleton, the request failed is the error\n state, finished-and-empty is the empty state. Saying \"nothing\n here yet\" to someone whose data is still in flight \u2014 or whose\n request just failed \u2014 is a lie the whole time it shows. -->\n @if (showSkeleton()) {\n @for (line of skeletonLines(); track line) {\n <li class=\"mrow mrow--skeleton\">\n <ef-skeleton variant=\"text\" [rows]=\"2\" />\n </li>\n }\n } @else if (showError()) {\n <li class=\"mrow mrow--empty\">\n <ng-container *ngTemplateOutlet=\"errorStateTpl\"></ng-container>\n </li>\n } @else {\n <li class=\"mrow mrow--empty\">\n <ng-content select=\"[empty-state-mobile]\">\n <ng-container *ngTemplateOutlet=\"defaultEmptyTpl\"></ng-container>\n </ng-content>\n </li>\n }\n }\n </ul>\n } @else {\n <!-- The table scrolls sideways inside its own box. Between 768px and\n the table's natural width (about 900-1100px) the columns past the\n edge were simply clipped by .tbl-wrap, with no way to reach them.\n .tbl-wrap stays the clipping shell, so the head and the pager do\n not scroll with the rows. Row menus are fixed to the viewport\n (ef-row-actions), so this scroller never clips them. -->\n <div class=\"tbl-scroll\">\n <table class=\"tbl\" [class.tbl--fixed]=\"fixedLayout()\">\n <thead>\n <tr>\n @if (reorderable()) {\n <th class=\"tbl-drag-h\" aria-hidden=\"true\"></th>\n }\n @for (col of effectiveColumns(); track trackByColumn($index, col)) {\n <!-- A sortable header is a real <button> inside the <th>, not a\n click handler on the cell. The cell carries aria-sort so the\n current order is announced; the arrow stays decorative.\n Sorting was previously mouse-only and silent to assistive\n technology, on every list screen in the product. -->\n <th\n [class]=\"headerAlignClass(col)\"\n [class.is-sortable]=\"col.sortable\"\n [style.width]=\"col.width || null\"\n [attr.data-col]=\"col.id\"\n [attr.aria-sort]=\"ariaSort(col)\"\n >\n @if (col.sortable) {\n <button type=\"button\" class=\"tbl-sort\" (click)=\"onHeaderClick(col)\">\n <ng-container *ngTemplateOutlet=\"headerLabelTpl; context: { col: col }\"></ng-container>\n @if (sortIndicator(col)) {\n <span aria-hidden=\"true\" class=\"tbl-sort__arrow\">{{ sortIndicator(col) }}</span>\n }\n </button>\n } @else {\n <ng-container *ngTemplateOutlet=\"headerLabelTpl; context: { col: col }\"></ng-container>\n }\n </th>\n }\n </tr>\n </thead>\n\n <tbody [attr.aria-busy]=\"loading() ? 'true' : null\">\n @for (row of rows(); track trackByRow($index, row); let i = $index) {\n <tr\n [class.is-row-clickable]=\"rowDoubleClickable()\"\n [class.is-dragging]=\"dragFrom() === i\"\n [class.is-drop-target]=\"dragFrom() !== null && dragFrom() !== i && dragTo() === i\"\n [attr.tabindex]=\"rowDoubleClickable() ? 0 : null\"\n [attr.role]=\"rowDoubleClickable() ? 'button' : null\"\n [attr.aria-pressed]=\"pressedAttr(row)\"\n [attr.aria-disabled]=\"isRowDisabled(row) ? 'true' : null\"\n [class.is-disabled]=\"isRowDisabled(row)\"\n (click)=\"onRowClick(row, $event)\"\n (keydown.enter)=\"onRowKeydown(row, $event)\"\n (keydown.space)=\"onRowSpace(row, $event)\"\n >\n @if (reorderable()) {\n <td class=\"tbl-drag\">\n <!-- The row is draggable only by this handle, so a drag never\n starts from a cell the operator meant to click or select.\n Arrow keys move the row for anyone not using a pointer. -->\n <button\n type=\"button\"\n class=\"tbl-drag__grip\"\n [attr.aria-label]=\"'common_reorder_row' | translate\"\n (pointerdown)=\"onDragPointerDown(i, $event)\"\n (pointermove)=\"onDragPointerMove($event)\"\n (pointerup)=\"onDragPointerUp()\"\n (pointercancel)=\"onDragPointerUp()\"\n (keydown)=\"onDragKeydown(i, $event)\"\n >\n <i class=\"pi pi-ellipsis-v\" aria-hidden=\"true\"></i>\n </button>\n </td>\n }\n @for (col of effectiveColumns(); track trackByColumn($index, col); let colIdx = $index) {\n <td efOverflowTooltip efTooltip efTooltipPosition=\"top\" [class]=\"cellAlignClass(col)\" [attr.data-col]=\"col.id\">\n @if (col.id === 'actions' && hasActionsColumn()) {\n <!-- Auto row-actions cell: View / Edit / Duplicate / Delete,\n gated by [show*Action] flags + ScreenContext perms. -->\n <ef-row-actions\n [items]=\"defaultRowActions(row)\"\n [context]=\"rowActionsContext()\"\n />\n } @else {\n <ng-container\n *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: col, index: i }\"\n ></ng-container>\n }\n </td>\n }\n </tr>\n } @empty {\n <!-- Placeholder rows keep the real columns, so the table that\n arrives lands where the shimmer already was instead of\n reflowing the page under the reader's eyes. -->\n @if (showSkeleton()) {\n @for (line of skeletonLines(); track line) {\n <tr>\n @if (reorderable()) {\n <td class=\"tbl-drag\"></td>\n }\n @for (col of effectiveColumns(); track trackByColumn($index, col)) {\n <td [class]=\"cellAlignClass(col)\" [attr.data-col]=\"col.id\">\n <ef-skeleton variant=\"text\" [rows]=\"1\" />\n </td>\n }\n </tr>\n }\n } @else if (showError()) {\n <!-- The failure replaces the body, so it spans the full row \u2014\n crammed into column one it reads as a value, not a state. -->\n <tr>\n <td [attr.colspan]=\"effectiveColumns().length + (reorderable() ? 1 : 0)\">\n <ng-container *ngTemplateOutlet=\"errorStateTpl\"></ng-container>\n </td>\n </tr>\n } @else {\n <tr>\n <td [attr.colspan]=\"effectiveColumns().length + (reorderable() ? 1 : 0)\">\n <ng-content select=\"[empty-state]\">\n <ng-container *ngTemplateOutlet=\"defaultEmptyTpl\"></ng-container>\n </ng-content>\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n }\n\n <!-- \u2500\u2500 PAGER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <!-- Paging controls for an empty result set are chrome with nothing to\n page, so they stay out of the way until there is something to move\n through. -->\n @if (!hidePager() && rows().length > 0) {\n <ef-pager\n [pageNumber]=\"pageNumber()\"\n [pageSize]=\"pageSize()\"\n [totalCount]=\"totalCount()\"\n [pageSizeOptions]=\"pageSizeOptions()\"\n (pageChange)=\"pageChange.emit($event)\"\n (pageSizeChange)=\"pageSizeChange.emit($event)\"\n />\n }\n</div>\n\n<!-- \u2500\u2500 DEFAULT EMPTY STATE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Rendered when a screen projects nothing into [empty-state] /\n [empty-state-mobile]. It words itself from `searchTerm`, because\n \"nothing matched what you typed\" and \"there is nothing here yet\"\n are different situations needing different ways out. -->\n<ng-template #defaultEmptyTpl>\n <div class=\"tbl-empty\">\n <i\n class=\"pi tbl-empty__icon\"\n [class.pi-search]=\"!!searchTerm()\"\n [class.pi-inbox]=\"!searchTerm()\"\n aria-hidden=\"true\"\n ></i>\n\n @if (searchTerm()) {\n <p class=\"tbl-empty__title\">\n {{ 'common_empty_no_results' | translate: { term: searchTerm() } }}\n </p>\n <p class=\"tbl-empty__hint\">{{ 'common_empty_no_results_hint' | translate }}</p>\n <button class=\"btn btn-sm tbl-empty__action\" type=\"button\" (click)=\"clearSearch.emit()\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n {{ 'common_empty_clear_filters' | translate }}\n </button>\n } @else {\n <p class=\"tbl-empty__title\">{{ 'common_empty_none_yet' | translate }}</p>\n <p class=\"tbl-empty__hint\">{{ 'common_empty_none_yet_hint' | translate }}</p>\n }\n </div>\n</ng-template>\n\n<!-- \u2500\u2500 ERROR STATE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n A request that failed and came back with nothing is not an empty\n collection, and must never borrow its words: \"nothing here yet\" is\n a confident false statement about the operator's data, and they may\n act on it. So this replaces the empty state outright, projected\n [empty-state] included \u2014 a screen's own \"create your first client\"\n copy is just as wrong here as the default.\n\n It says nothing about *what* failed. `errorMsg` is `err.message`\n from the HTTP layer, and a status line or an exception string tells\n an operator nothing they can act on; it survives on the head chip's\n `data-error` attribute, for whoever is reading the DOM. This carries\n the two things that help: their data is intact, and the way to try\n again.\n\n role=\"alert\" goes on this div, never on the <tr> / <td> / <li> that\n hosts it \u2014 a role on a row or cell takes the table's semantics down\n with it. It fires on insertion, which is exactly when @if adds it. -->\n<ng-template #errorStateTpl>\n <div class=\"tbl-empty tbl-empty--error\" role=\"alert\">\n <i class=\"pi pi-exclamation-triangle tbl-empty__icon\" aria-hidden=\"true\"></i>\n <p class=\"tbl-empty__title\">{{ 'common_error_load_failed' | translate }}</p>\n <p class=\"tbl-empty__hint\">{{ 'common_error_load_failed_hint' | translate }}</p>\n <button class=\"btn btn-sm tbl-empty__action\" type=\"button\" (click)=\"retry.emit()\">\n <i class=\"pi pi-refresh\" aria-hidden=\"true\"></i>\n {{ 'common_retry' | translate }}\n </button>\n </div>\n</ng-template>\n\n<!-- Header label, shared by the sortable and plain header cells so the\n projection rules stay in one place. -->\n<ng-template #headerLabelTpl let-col=\"col\">\n @if (headerTemplate(col.id); as headerTpl) {\n <ng-container *ngTemplateOutlet=\"headerTpl; context: { $implicit: col, col: col }\"></ng-container>\n } @else if (col.headerKey) {\n <span>{{ col.headerKey | translate }}</span>\n } @else if (col.header) {\n <span>{{ col.header }}</span>\n }\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:block;min-width:0}.tbl thead th.is-sortable{cursor:pointer;-webkit-user-select:none;user-select:none;transition:color var(--t-fast, .14s) var(--ease-out, ease-out)}.tbl thead th.is-sortable:hover{color:var(--text)}.tbl tbody tr.is-row-clickable{cursor:pointer}.tbl-tools{display:flex;gap:6px;align-items:center}.tbl-tool{position:relative}.tbl-tool>.btn.open{color:var(--text);background:var(--paper)}.tbl-tool__menu{position:absolute;top:calc(100% + 8px);inset-inline-end:0;z-index:40;min-width:212px;max-height:340px;overflow-y:auto;padding:6px;display:grid;gap:1px;background:var(--paper-alt);border:1px solid var(--rule);border-radius:var(--r-xl);box-shadow:var(--shadow-3);animation:row-actions-menu-in var(--t-base) var(--ease-spring)}.tbl-tool__menu--wide{min-width:248px}.tbl-tool__menu button{appearance:none;background:transparent;border:0;width:100%;text-align:start;font:inherit;font-size:13.5px;font-weight:600;color:var(--text);display:flex;align-items:center;gap:12px;padding:9px 12px;border-radius:var(--r-md);cursor:pointer;transition:background-color var(--t-fast) var(--ease-out)}.tbl-tool__menu button:hover,.tbl-tool__menu button:focus-visible{background:var(--paper);outline:0}.tbl-tool__menu button>.pi{width:16px;flex:none;font-size:14px;color:var(--muted)}.tbl-tool__menu button.is-active>.pi{color:var(--module, var(--text))}.tbl-tool__label{flex:1 1 auto;min-width:0}.tbl-tool__check{color:var(--module, var(--text))}.tbl-tool__sep{margin:5px 2px;border-top:1px solid var(--rule)}.tbl-wrap[data-density=compact] .tbl tbody td{--tbl-cell-pad-block: 4px;padding-top:var(--tbl-cell-pad-block);padding-bottom:var(--tbl-cell-pad-block)}.tbl-wrap[data-density=comfortable] .tbl tbody td{--tbl-cell-pad-block: 16px;padding-top:var(--tbl-cell-pad-block);padding-bottom:var(--tbl-cell-pad-block)}.mlist{list-style:none;margin:0;padding:0;display:grid}.mrow{border-top:1px solid var(--rule)}.mrow:first-child{border-top:0}.mrow__head{display:flex;align-items:flex-start;gap:var(--s-3);padding:12px var(--s-4);min-height:var(--hit-touch)}.mrow__head.is-tappable{cursor:pointer}.mrow__head.is-tappable:active{background:var(--paper)}.mrow__lead{flex:1;min-width:0;display:grid;gap:2px}.mrow__primary{font-weight:700;font-size:14px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mrow__secondary{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 6px;font-size:12.5px;color:var(--text-mute);min-width:0}.mrow__secondary .mrow__sec-part+.mrow__sec-part:before{content:\"\\b7\";margin-inline-end:6px;color:var(--text-soft)}.mrow__trail{display:flex;align-items:center;justify-content:flex-end;gap:var(--s-2);flex-shrink:0;min-height:20px}.mrow__actions{display:inline-flex}.mrow__detail{margin:0;padding:0 var(--s-4) 12px;display:grid;gap:1px}.mrow__field{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-3);padding:7px 0;border-top:1px solid var(--rule-soft)}.mrow__field dt{font-size:11.5px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--text-mute);flex-shrink:0}.mrow__field dd{margin:0;font-size:13.5px;color:var(--text);text-align:end;min-width:0;overflow-wrap:anywhere}.mrow__more{appearance:none;width:100%;background:transparent;border:0;border-top:1px solid var(--rule-soft);padding:10px;font:inherit;font-size:13px;font-weight:600;color:var(--tenant-500);cursor:pointer}.mlist[data-layout=card]{gap:var(--s-3);padding:var(--s-3) var(--s-3) 0}.mlist[data-layout=card] .mrow{border:1px solid var(--rule);border-radius:var(--r-lg);background:var(--paper-alt);overflow:hidden}.mlist[data-layout=card] .mrow__head{border-bottom:1px solid var(--rule)}.mrow__chevron{font-size:11px;color:var(--text-soft);transition:transform var(--t-fast) var(--ease-out);flex-shrink:0}.mrow__chevron.is-open{transform:rotate(180deg);color:var(--text-mute)}.mrow__head.is-tappable:focus-visible{outline:2px solid var(--tenant-400);outline-offset:-2px}.mrow__more{display:flex;align-items:center;justify-content:center;gap:6px}.tbl-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s-2);padding:var(--s-9) var(--s-4);text-align:center}.tbl-empty__icon{font-size:28px;line-height:1;color:var(--ink-400);margin-block-end:var(--s-1)}.tbl-empty__title{margin:0;font-size:15px;font-weight:600;color:var(--text);max-inline-size:46ch;overflow-wrap:break-word}.tbl-empty__hint{margin:0;font-size:13px;color:var(--ink-600);max-inline-size:52ch;text-wrap:pretty}.tbl-empty__action{margin-block-start:var(--s-3);min-block-size:var(--hit-base)}.tbl-empty--error .tbl-empty__icon{color:var(--st-cancelled-fg)}.mrow--empty{padding:0}.mrow--skeleton{padding:12px var(--s-4)}@media(max-width:767px){.tbl-empty{padding:var(--s-7) var(--s-4)}.tbl-empty__action{min-block-size:var(--hit-touch)}}.pi.is-spinning{animation:ef-export-spin .9s linear infinite}@keyframes ef-export-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.pi.is-spinning{animation:none}}.tbl th.tbl-drag-h,.tbl td.tbl-drag{width:28px;padding-inline:4px 0;text-align:center}.tbl td.tbl-drag{cursor:default}.tbl .tbl-drag__grip{display:inline-flex;align-items:center;justify-content:center;width:22px;height:26px;padding:0;border:0;border-radius:6px;background:transparent;color:var(--text-soft);cursor:grab;touch-action:none;transition:background var(--t-fast, .14s) var(--ease-out, ease-out),color var(--t-fast, .14s) var(--ease-out, ease-out)}.tbl .tbl-drag__grip:hover{background:var(--paper);color:var(--text)}.tbl .tbl-drag__grip:focus-visible{outline:2px solid var(--tenant, var(--rule));outline-offset:1px}.tbl .tbl-drag__grip:active{cursor:grabbing}.tbl .tbl-drag__grip i{font-size:12px}.tbl tbody tr.is-dragging>td{background:var(--paper);opacity:.55}.tbl tbody tr.is-drop-target>td{box-shadow:inset 0 2px 0 var(--tenant, var(--rule))}.tbl.tbl--fixed{table-layout:fixed}.mrow__disclosure{appearance:none;background:transparent;border:0;padding:0;margin:0;display:inline-grid;place-items:center;color:var(--text-mute);cursor:pointer;position:relative}.mrow__disclosure:after{content:\"\";position:absolute;top:50%;left:50%;width:var(--hit-touch);height:var(--hit-touch);transform:translate(-50%,-50%)}.mrow__disclosure:focus-visible{outline:2px solid color-mix(in srgb,var(--ink-active) 40%,transparent);outline-offset:2px;border-radius:var(--r-pill)}@media(pointer:coarse){.mrow__actions+.mrow__disclosure{margin-inline-start:calc((var(--hit-touch) - 11px) / 2 - var(--s-2) + 1px)}}.tbl tbody tr.is-disabled{cursor:not-allowed}.tbl tbody tr.is-disabled td,.mrow.is-disabled .mrow__head{opacity:.5}.mrow.is-disabled .mrow__head{cursor:not-allowed}.tbl-head:has(>.count:empty):has(>.tbl-tools:empty){display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfPagerComponent, selector: "ef-pager", inputs: ["pageNumber", "pageSize", "totalCount", "pageSizeOptions", "linesPerPageLabelKey", "ofLabelKey", "prevLabelKey", "nextLabelKey", "firstLabelKey", "lastLabelKey", "hideSize"], outputs: ["pageChange", "pageSizeChange"] }, { kind: "component", type: EfStatusChipComponent, selector: "ef-status-chip", inputs: ["referenceKey", "code", "color", "label", "labelKey", "hideDot", "showCode"] }, { kind: "component", type: EfSkeletonComponent, selector: "ef-skeleton", inputs: ["variant", "rows", "height"] }, { kind: "component", type: EfRowActionsComponent, selector: "ef-row-actions", inputs: ["items", "context", "triggerIcon", "triggerAriaKey", "disabled", "menuLeft"], outputs: ["opened", "closed"] }, { kind: "directive", type: EfOverflowTooltipDirective, selector: "[efOverflowTooltip]" }, { kind: "directive", type: EfTooltipDirective, selector: "[efTooltip]" }, { kind: "pipe", type: i1$3.DecimalPipe, name: "number" }, { kind: "pipe", type: i1$3.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i1$3.DatePipe, name: "date" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6367
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfDataCardComponent, isStandalone: true, selector: "ef-data-card", inputs: { mobileLayout: { classPropertyName: "mobileLayout", publicName: "mobileLayout", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, trackByField: { classPropertyName: "trackByField", publicName: "trackByField", isSignal: true, isRequired: false, transformFunction: null }, pageNumber: { classPropertyName: "pageNumber", publicName: "pageNumber", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, totalCount: { classPropertyName: "totalCount", publicName: "totalCount", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, hidePager: { classPropertyName: "hidePager", publicName: "hidePager", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, errorMsg: { classPropertyName: "errorMsg", publicName: "errorMsg", isSignal: true, isRequired: false, transformFunction: null }, loadingKey: { classPropertyName: "loadingKey", publicName: "loadingKey", isSignal: true, isRequired: false, transformFunction: null }, rowDoubleClickable: { classPropertyName: "rowDoubleClickable", publicName: "rowDoubleClickable", isSignal: true, isRequired: false, transformFunction: null }, rowPressed: { classPropertyName: "rowPressed", publicName: "rowPressed", isSignal: true, isRequired: false, transformFunction: null }, rowDisabled: { classPropertyName: "rowDisabled", publicName: "rowDisabled", isSignal: true, isRequired: false, transformFunction: null }, hasActionsColumn: { classPropertyName: "hasActionsColumn", publicName: "hasActionsColumn", isSignal: true, isRequired: false, transformFunction: null }, rowActionsContext: { classPropertyName: "rowActionsContext", publicName: "rowActionsContext", isSignal: true, isRequired: false, transformFunction: null }, showViewAction: { classPropertyName: "showViewAction", publicName: "showViewAction", isSignal: true, isRequired: false, transformFunction: null }, showEditAction: { classPropertyName: "showEditAction", publicName: "showEditAction", isSignal: true, isRequired: false, transformFunction: null }, showDuplicateAction: { classPropertyName: "showDuplicateAction", publicName: "showDuplicateAction", isSignal: true, isRequired: false, transformFunction: null }, showDeleteAction: { classPropertyName: "showDeleteAction", publicName: "showDeleteAction", isSignal: true, isRequired: false, transformFunction: null }, rowActionState: { classPropertyName: "rowActionState", publicName: "rowActionState", isSignal: true, isRequired: false, transformFunction: null }, showDensityControl: { classPropertyName: "showDensityControl", publicName: "showDensityControl", isSignal: true, isRequired: false, transformFunction: null }, showColumnPicker: { classPropertyName: "showColumnPicker", publicName: "showColumnPicker", isSignal: true, isRequired: false, transformFunction: null }, showExportControl: { classPropertyName: "showExportControl", publicName: "showExportControl", isSignal: true, isRequired: false, transformFunction: null }, exporting: { classPropertyName: "exporting", publicName: "exporting", isSignal: true, isRequired: false, transformFunction: null }, screenContext: { classPropertyName: "screenContext", publicName: "screenContext", isSignal: true, isRequired: false, transformFunction: null }, searchTerm: { classPropertyName: "searchTerm", publicName: "searchTerm", isSignal: true, isRequired: false, transformFunction: null }, tableKey: { classPropertyName: "tableKey", publicName: "tableKey", isSignal: true, isRequired: false, transformFunction: null }, defaultDensity: { classPropertyName: "defaultDensity", publicName: "defaultDensity", isSignal: true, isRequired: false, transformFunction: null }, reorderable: { classPropertyName: "reorderable", publicName: "reorderable", isSignal: true, isRequired: false, transformFunction: null }, fixedLayout: { classPropertyName: "fixedLayout", publicName: "fixedLayout", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageChange: "pageChange", pageSizeChange: "pageSizeChange", sortChange: "sortChange", rowDoubleClick: "rowDoubleClick", rowAction: "rowAction", exportRequest: "exportRequest", clearSearch: "clearSearch", retry: "retry", rowReorder: "rowReorder" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown.escape": "onEscape()" } }, queries: [{ propertyName: "bodyTemplates", predicate: EfColumnTemplateDirective }, { propertyName: "headerTemplates", predicate: EfColumnHeaderTemplateDirective }], ngImport: i0, template: "<!-- One cell renderer, shared by the table, the mobile row and the mobile\n detail. A new column type is added here once, not three times. -->\n<ng-template #cellTpl let-row let-col=\"col\" let-index=\"index\">\n @if (bodyTemplate(col.id); as bodyTpl) {\n <ng-container *ngTemplateOutlet=\"bodyTpl; context: { $implicit: row, col: col, index: index }\"></ng-container>\n } @else {\n @switch (col.type) {\n @case ('number') {\n {{ $any(cellValue(row, col)) | number:numberFormat(col):col.locale }}\n }\n @case ('money') {\n {{ $any(cellValue(row, col)) | currency:(col.currencyCode || 'EUR'):(col.currencyDisplay || 'symbol'):numberFormat(col):col.locale }}\n }\n @case ('date') {\n {{ $any(cellValue(row, col)) | date:(col.dateFormat || 'dd/MM/yyyy'):undefined:col.locale }}\n }\n @case ('datetime') {\n {{ $any(cellValue(row, col)) | date:(col.dateFormat || 'dd/MM/yyyy HH:mm'):undefined:col.locale }}\n }\n @case ('boolean') {\n @if (cellValue(row, col)) {\n <span class=\"bool yes\"><span class=\"dot\"></span> \u2713</span>\n } @else {\n <span class=\"bool no\"><span class=\"dot\"></span> \u2014</span>\n }\n }\n @case ('mono') {\n <span class=\"ref\">{{ cellValue(row, col) }}</span>\n }\n @case ('chip') {\n <span [class]=\"'chip ' + (col.chipPrefix || 'chip-') + cellValue(row, col)\">\n \u25CF {{ cellValue(row, col) }}\n </span>\n }\n @case ('status') {\n <ef-status-chip\n [referenceKey]=\"col.referenceKey || ''\"\n [code]=\"$any(cellValue(row, col))\"\n />\n }\n @case ('reference') {\n {{ resolveReference(cellValue(row, col), col) }}\n }\n @default {\n {{ cellValue(row, col) }}\n }\n }\n }\n</ng-template>\n\n<!-- .has-open-menu unclips the shell for the Densit\u00E9 / Colonnes menus,\n which hang below .tbl-head. Row menus are fixed to the viewport\n (ef-row-actions) and need no lift; counting them also left the shell\n unclipped for good when a row went while its menu was open. -->\n<div class=\"tbl-wrap\" [class.has-open-menu]=\"openTool() !== null\" [attr.data-density]=\"density()\">\n <!-- \u2500\u2500 HEAD ROW \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"tbl-head\">\n <div class=\"count small\">\n @if (loading()) {\n <span>{{ loadingKey() | translate }}</span>\n } @else if (errorMsg()) {\n <!-- A refresh that failed on top of rows already on screen keeps the\n rows, so this chip is the only sign anything went wrong \u2014 and the\n rows it sits above may now be stale. It has to say so, and say it\n in the product's words: `errorMsg` is `err.message` from the HTTP\n layer, and \"500 Internal Server Error\" tells an operator nothing\n they can act on.\n\n The raw string stays on `data-error`, where devtools can read it\n and no reader ever meets it. `title` was the obvious place and is\n the wrong one: it renders the exception in a tooltip, which is\n precisely what this change exists to stop, and it is unreachable\n on touch besides.\n\n role=\"status\", not \"alert\": polite is right for a signal beside\n rows that are still usable, and when the body is showing the\n error state it already carries the alert \u2014 one failure must not\n interrupt a screen-reader twice. -->\n <span\n role=\"status\"\n [attr.data-error]=\"errorMsg()\"\n style=\"color: var(--st-cancelled-fg); font-weight: 600;\"\n >\u26A0 {{ 'common_error_refresh_failed' | translate }}</span\n >\n } @else {\n <ng-content select=\"[tbl-head-info]\"></ng-content>\n }\n </div>\n <div class=\"tbl-tools\">\n <ng-content select=\"[tbl-head-actions]\"></ng-content>\n\n @if (canExport()) {\n <div class=\"tbl-tool\">\n <button\n class=\"btn btn-ghost btn-sm\"\n type=\"button\"\n [disabled]=\"exporting()\"\n [attr.aria-busy]=\"exporting()\"\n (click)=\"requestExport()\"\n >\n <i\n class=\"pi\"\n [class.pi-download]=\"!exporting()\"\n [class.pi-spinner]=\"exporting()\"\n [class.is-spinning]=\"exporting()\"\n aria-hidden=\"true\"\n ></i>\n {{ (exporting() ? 'common_export_running' : 'common_export') | translate }}\n </button>\n </div>\n }\n\n @if (showDensityControl()) {\n <div class=\"tbl-tool\">\n <button\n class=\"btn btn-ghost btn-sm\"\n type=\"button\"\n [class.open]=\"openTool() === 'density'\"\n [attr.aria-expanded]=\"openTool() === 'density'\"\n aria-haspopup=\"menu\"\n (click)=\"toggleTool('density')\"\n >\n <i class=\"pi pi-bars\" aria-hidden=\"true\"></i>\n {{ 'common_density' | translate }}\n </button>\n @if (openTool() === 'density') {\n <div class=\"tbl-tool__menu\" role=\"menu\">\n @for (opt of densityOptions; track opt.value) {\n <button\n type=\"button\"\n role=\"menuitemradio\"\n [attr.aria-checked]=\"density() === opt.value\"\n [class.is-active]=\"density() === opt.value\"\n (click)=\"setDensity(opt.value)\"\n >\n <i class=\"pi\" [ngClass]=\"opt.icon\" aria-hidden=\"true\"></i>\n <span class=\"tbl-tool__label\">{{ opt.labelKey | translate }}</span>\n @if (density() === opt.value) {\n <i class=\"pi pi-check tbl-tool__check\" aria-hidden=\"true\"></i>\n }\n </button>\n }\n </div>\n }\n </div>\n }\n\n @if (showColumnPicker()) {\n <div class=\"tbl-tool\">\n <button\n class=\"btn btn-ghost btn-sm\"\n type=\"button\"\n [class.open]=\"openTool() === 'columns'\"\n [attr.aria-expanded]=\"openTool() === 'columns'\"\n aria-haspopup=\"menu\"\n (click)=\"toggleTool('columns')\"\n >\n <i class=\"pi pi-th-large\" aria-hidden=\"true\"></i>\n {{ 'common_columns' | translate }}\n </button>\n @if (openTool() === 'columns') {\n <div class=\"tbl-tool__menu tbl-tool__menu--wide\" role=\"menu\">\n @for (col of hideableColumns(); track col.id) {\n <button\n type=\"button\"\n role=\"menuitemcheckbox\"\n [attr.aria-checked]=\"!isColumnHidden(col.id)\"\n [class.is-active]=\"!isColumnHidden(col.id)\"\n (click)=\"toggleColumn(col.id)\"\n >\n <i class=\"pi\" [class.pi-check]=\"!isColumnHidden(col.id)\" aria-hidden=\"true\"></i>\n <span class=\"tbl-tool__label\">{{\n col.headerKey ? (col.headerKey | translate) : (col.header || col.id)\n }}</span>\n </button>\n }\n <div class=\"tbl-tool__sep\"></div>\n <button type=\"button\" role=\"menuitem\" (click)=\"resetColumns()\">\n <i class=\"pi pi-refresh\" aria-hidden=\"true\"></i>\n <span class=\"tbl-tool__label\">{{ 'common_columns_reset' | translate }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 TABLE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (isMobile()) {\n <!-- \u2500\u2500 MOBILE LIST \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n A six-column table on a 390px screen either overflows or\n squeezes every column to nothing, so the rows become a list.\n Same columns, same cell renderers; each column just declares\n (or is given) a role. Both shapes expand to the same detail \u2014\n they differ only in how much they show collapsed. -->\n <ul\n class=\"mlist\"\n [attr.data-layout]=\"effectiveMobileLayout()\"\n [attr.aria-busy]=\"loading() ? 'true' : null\"\n >\n @for (row of rows(); track trackByRow($index, row); let i = $index) {\n <li class=\"mrow\" [class.is-open]=\"isExpanded(row, i)\" [class.is-disabled]=\"isRowDisabled(row)\">\n <div\n class=\"mrow__head\"\n [class.is-tappable]=\"headIsControl()\"\n [attr.role]=\"headIsControl() ? 'button' : null\"\n [attr.tabindex]=\"headIsControl() ? 0 : null\"\n [attr.aria-pressed]=\"pressedAttr(row)\"\n [attr.aria-disabled]=\"isRowDisabled(row) ? 'true' : null\"\n [attr.aria-expanded]=\"!rowNavigable() && hasDetail() ? isExpanded(row, i) : null\"\n (click)=\"onRowHeadActivate(row, i, $event)\"\n (keydown.enter)=\"onRowHeadActivate(row, i, $event)\"\n (keydown.space)=\"onRowHeadActivate(row, i, $event)\"\n >\n <div class=\"mrow__lead\">\n @if (primaryColumn(); as pc) {\n <span class=\"mrow__primary\" efOverflowTooltip efTooltip efTooltipPosition=\"top\">\n <ng-container *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: pc, index: i }\"></ng-container>\n </span>\n }\n @if (effectiveMobileLayout() === 'row' && secondaryColumns().length) {\n <span class=\"mrow__secondary\">\n @for (sc of secondaryColumns(); track sc.id) {\n @if (hasCellContent(row, sc)) {\n <span class=\"mrow__sec-part\">\n <ng-container *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: sc, index: i }\"></ng-container>\n </span>\n }\n }\n </span>\n }\n </div>\n\n <div class=\"mrow__trail\">\n @if (statusColumn(); as st) {\n <ng-container *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: st, index: i }\"></ng-container>\n }\n @if (hasActionsColumn()) {\n <span class=\"mrow__actions\">\n <ef-row-actions [items]=\"defaultRowActions(row)\" [context]=\"rowActionsContext()\" />\n </span>\n }\n @if (hasDetail() && effectiveMobileLayout() === 'row') {\n @if (rowNavigable()) {\n <!-- The head opens the record, so expansion needs a control\n of its own. Same glyph, promoted from decoration to a\n button that carries the state it was already drawing. -->\n <button\n type=\"button\"\n class=\"mrow__disclosure\"\n [attr.aria-expanded]=\"isExpanded(row, i)\"\n [attr.aria-label]=\"(isExpanded(row, i) ? 'common_view_less' : 'common_view_more') | translate\"\n (click)=\"onDisclosureToggle(row, i, $event)\"\n >\n <i\n class=\"pi pi-chevron-down mrow__chevron\"\n [class.is-open]=\"isExpanded(row, i)\"\n aria-hidden=\"true\"\n ></i>\n </button>\n } @else {\n <!-- Says the row opens, and which way it is now. Decorative:\n the whole head is the control, and it carries the state. -->\n <i\n class=\"pi pi-chevron-down mrow__chevron\"\n [class.is-open]=\"isExpanded(row, i)\"\n aria-hidden=\"true\"\n ></i>\n }\n }\n </div>\n </div>\n\n @if (visibleDetail(row, i).length) {\n <dl class=\"mrow__detail\">\n @for (col of visibleDetail(row, i); track col.id) {\n <div class=\"mrow__field\">\n <dt>{{ $any(col).headerKey ? ($any(col).headerKey | translate) : $any(col).header }}</dt>\n <dd [class]=\"cellAlignClass($any(col))\">\n <ng-container *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: col, index: i }\"></ng-container>\n </dd>\n </div>\n }\n </dl>\n }\n\n @if (hasMoreThanPreview() && effectiveMobileLayout() === 'card') {\n <button class=\"mrow__more\" type=\"button\" (click)=\"toggleExpanded(row, i)\">\n <span>{{ (isExpanded(row, i) ? 'common_view_less' : 'common_view_more') | translate }}</span>\n <i class=\"pi pi-chevron-down mrow__chevron\" [class.is-open]=\"isExpanded(row, i)\" aria-hidden=\"true\"></i>\n </button>\n }\n </li>\n } @empty {\n <!-- No rows has three meanings and they must not share a screen:\n still loading is a skeleton, the request failed is the error\n state, finished-and-empty is the empty state. Saying \"nothing\n here yet\" to someone whose data is still in flight \u2014 or whose\n request just failed \u2014 is a lie the whole time it shows. -->\n @if (showSkeleton()) {\n @for (line of skeletonLines(); track line) {\n <li class=\"mrow mrow--skeleton\">\n <ef-skeleton variant=\"text\" [rows]=\"2\" />\n </li>\n }\n } @else if (showError()) {\n <li class=\"mrow mrow--empty\">\n <ng-container *ngTemplateOutlet=\"errorStateTpl\"></ng-container>\n </li>\n } @else {\n <li class=\"mrow mrow--empty\">\n <ng-content select=\"[empty-state-mobile]\">\n <ng-container *ngTemplateOutlet=\"defaultEmptyTpl\"></ng-container>\n </ng-content>\n </li>\n }\n }\n </ul>\n } @else {\n <!-- The table scrolls sideways inside its own box. Between 768px and\n the table's natural width (about 900-1100px) the columns past the\n edge were simply clipped by .tbl-wrap, with no way to reach them.\n .tbl-wrap stays the clipping shell, so the head and the pager do\n not scroll with the rows. Row menus are fixed to the viewport\n (ef-row-actions), so this scroller never clips them. -->\n <div class=\"tbl-scroll\">\n <table class=\"tbl\" [class.tbl--fixed]=\"fixedLayout()\">\n <thead>\n <tr>\n @if (reorderable()) {\n <th class=\"tbl-drag-h\" aria-hidden=\"true\"></th>\n }\n @for (col of effectiveColumns(); track trackByColumn($index, col)) {\n <!-- A sortable header is a real <button> inside the <th>, not a\n click handler on the cell. The cell carries aria-sort so the\n current order is announced; the arrow stays decorative.\n Sorting was previously mouse-only and silent to assistive\n technology, on every list screen in the product. -->\n <th\n [class]=\"headerAlignClass(col)\"\n [class.is-sortable]=\"col.sortable\"\n [style.width]=\"col.width || null\"\n [attr.data-col]=\"col.id\"\n [attr.aria-sort]=\"ariaSort(col)\"\n >\n @if (col.sortable) {\n <button type=\"button\" class=\"tbl-sort\" (click)=\"onHeaderClick(col)\">\n <ng-container *ngTemplateOutlet=\"headerLabelTpl; context: { col: col }\"></ng-container>\n @if (sortIndicator(col)) {\n <span aria-hidden=\"true\" class=\"tbl-sort__arrow\">{{ sortIndicator(col) }}</span>\n }\n </button>\n } @else {\n <ng-container *ngTemplateOutlet=\"headerLabelTpl; context: { col: col }\"></ng-container>\n }\n </th>\n }\n </tr>\n </thead>\n\n <tbody [attr.aria-busy]=\"loading() ? 'true' : null\">\n @for (row of rows(); track trackByRow($index, row); let i = $index) {\n <tr\n [class.is-row-clickable]=\"rowDoubleClickable()\"\n [class.is-dragging]=\"dragFrom() === i\"\n [class.is-drop-target]=\"dragFrom() !== null && dragFrom() !== i && dragTo() === i\"\n [attr.tabindex]=\"rowDoubleClickable() ? 0 : null\"\n [attr.role]=\"rowDoubleClickable() ? 'button' : null\"\n [attr.aria-pressed]=\"pressedAttr(row)\"\n [attr.aria-disabled]=\"isRowDisabled(row) ? 'true' : null\"\n [class.is-disabled]=\"isRowDisabled(row)\"\n (click)=\"onRowClick(row, $event)\"\n (keydown.enter)=\"onRowKeydown(row, $event)\"\n (keydown.space)=\"onRowSpace(row, $event)\"\n >\n @if (reorderable()) {\n <td class=\"tbl-drag\">\n <!-- The row is draggable only by this handle, so a drag never\n starts from a cell the operator meant to click or select.\n Arrow keys move the row for anyone not using a pointer. -->\n <button\n type=\"button\"\n class=\"tbl-drag__grip\"\n [attr.aria-label]=\"'common_reorder_row' | translate\"\n (pointerdown)=\"onDragPointerDown(i, $event)\"\n (pointermove)=\"onDragPointerMove($event)\"\n (pointerup)=\"onDragPointerUp()\"\n (pointercancel)=\"onDragPointerUp()\"\n (keydown)=\"onDragKeydown(i, $event)\"\n >\n <i class=\"pi pi-ellipsis-v\" aria-hidden=\"true\"></i>\n </button>\n </td>\n }\n @for (col of effectiveColumns(); track trackByColumn($index, col); let colIdx = $index) {\n <td efOverflowTooltip efTooltip efTooltipPosition=\"top\" [class]=\"cellAlignClass(col)\" [attr.data-col]=\"col.id\">\n @if (col.id === 'actions' && hasActionsColumn()) {\n <!-- Auto row-actions cell: View / Edit / Duplicate / Delete,\n gated by [show*Action] flags + ScreenContext perms. -->\n <ef-row-actions\n [items]=\"defaultRowActions(row)\"\n [context]=\"rowActionsContext()\"\n />\n } @else {\n <ng-container\n *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: col, index: i }\"\n ></ng-container>\n }\n </td>\n }\n </tr>\n } @empty {\n <!-- Placeholder rows keep the real columns, so the table that\n arrives lands where the shimmer already was instead of\n reflowing the page under the reader's eyes. -->\n @if (showSkeleton()) {\n @for (line of skeletonLines(); track line) {\n <tr>\n @if (reorderable()) {\n <td class=\"tbl-drag\"></td>\n }\n @for (col of effectiveColumns(); track trackByColumn($index, col)) {\n <td [class]=\"cellAlignClass(col)\" [attr.data-col]=\"col.id\">\n <ef-skeleton variant=\"text\" [rows]=\"1\" />\n </td>\n }\n </tr>\n }\n } @else if (showError()) {\n <!-- The failure replaces the body, so it spans the full row \u2014\n crammed into column one it reads as a value, not a state. -->\n <tr>\n <td [attr.colspan]=\"effectiveColumns().length + (reorderable() ? 1 : 0)\">\n <ng-container *ngTemplateOutlet=\"errorStateTpl\"></ng-container>\n </td>\n </tr>\n } @else {\n <tr>\n <td [attr.colspan]=\"effectiveColumns().length + (reorderable() ? 1 : 0)\">\n <ng-content select=\"[empty-state]\">\n <ng-container *ngTemplateOutlet=\"defaultEmptyTpl\"></ng-container>\n </ng-content>\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n }\n\n <!-- \u2500\u2500 PAGER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <!-- Paging controls for an empty result set are chrome with nothing to\n page, so they stay out of the way until there is something to move\n through. -->\n @if (!hidePager() && rows().length > 0) {\n <ef-pager\n [pageNumber]=\"pageNumber()\"\n [pageSize]=\"pageSize()\"\n [totalCount]=\"totalCount()\"\n [pageSizeOptions]=\"pageSizeOptions()\"\n (pageChange)=\"pageChange.emit($event)\"\n (pageSizeChange)=\"pageSizeChange.emit($event)\"\n />\n }\n</div>\n\n<!-- \u2500\u2500 DEFAULT EMPTY STATE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Rendered when a screen projects nothing into [empty-state] /\n [empty-state-mobile]. It words itself from `searchTerm`, because\n \"nothing matched what you typed\" and \"there is nothing here yet\"\n are different situations needing different ways out. -->\n<ng-template #defaultEmptyTpl>\n <div class=\"tbl-empty\">\n <i\n class=\"pi tbl-empty__icon\"\n [class.pi-search]=\"!!searchTerm()\"\n [class.pi-inbox]=\"!searchTerm()\"\n aria-hidden=\"true\"\n ></i>\n\n @if (searchTerm()) {\n <p class=\"tbl-empty__title\">\n {{ 'common_empty_no_results' | translate: { term: searchTerm() } }}\n </p>\n <p class=\"tbl-empty__hint\">{{ 'common_empty_no_results_hint' | translate }}</p>\n <button class=\"btn btn-sm tbl-empty__action\" type=\"button\" (click)=\"clearSearch.emit()\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n {{ 'common_empty_clear_filters' | translate }}\n </button>\n } @else {\n <p class=\"tbl-empty__title\">{{ 'common_empty_none_yet' | translate }}</p>\n <p class=\"tbl-empty__hint\">{{ 'common_empty_none_yet_hint' | translate }}</p>\n }\n </div>\n</ng-template>\n\n<!-- \u2500\u2500 ERROR STATE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n A request that failed and came back with nothing is not an empty\n collection, and must never borrow its words: \"nothing here yet\" is\n a confident false statement about the operator's data, and they may\n act on it. So this replaces the empty state outright, projected\n [empty-state] included \u2014 a screen's own \"create your first client\"\n copy is just as wrong here as the default.\n\n It says nothing about *what* failed. `errorMsg` is `err.message`\n from the HTTP layer, and a status line or an exception string tells\n an operator nothing they can act on; it survives on the head chip's\n `data-error` attribute, for whoever is reading the DOM. This carries\n the two things that help: their data is intact, and the way to try\n again.\n\n role=\"alert\" goes on this div, never on the <tr> / <td> / <li> that\n hosts it \u2014 a role on a row or cell takes the table's semantics down\n with it. It fires on insertion, which is exactly when @if adds it. -->\n<ng-template #errorStateTpl>\n <div class=\"tbl-empty tbl-empty--error\" role=\"alert\">\n <i class=\"pi pi-exclamation-triangle tbl-empty__icon\" aria-hidden=\"true\"></i>\n <p class=\"tbl-empty__title\">{{ 'common_error_load_failed' | translate }}</p>\n <p class=\"tbl-empty__hint\">{{ 'common_error_load_failed_hint' | translate }}</p>\n <button class=\"btn btn-sm tbl-empty__action\" type=\"button\" (click)=\"retry.emit()\">\n <i class=\"pi pi-refresh\" aria-hidden=\"true\"></i>\n {{ 'common_retry' | translate }}\n </button>\n </div>\n</ng-template>\n\n<!-- Header label, shared by the sortable and plain header cells so the\n projection rules stay in one place. -->\n<ng-template #headerLabelTpl let-col=\"col\">\n @if (headerTemplate(col.id); as headerTpl) {\n <ng-container *ngTemplateOutlet=\"headerTpl; context: { $implicit: col, col: col }\"></ng-container>\n } @else if (col.headerKey) {\n <span>{{ col.headerKey | translate }}</span>\n } @else if (col.header) {\n <span>{{ col.header }}</span>\n }\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:block;min-width:0}.tbl thead th.is-sortable{cursor:pointer;-webkit-user-select:none;user-select:none;transition:color var(--t-fast, .14s) var(--ease-out, ease-out)}.tbl thead th.is-sortable:hover{color:var(--text)}.tbl tbody tr.is-row-clickable{cursor:pointer}.tbl-tools{display:flex;gap:6px;align-items:center}.tbl-tool{position:relative}.tbl-tool>.btn.open{color:var(--text);background:var(--paper)}.tbl-tool__menu{position:absolute;top:calc(100% + 8px);inset-inline-end:0;z-index:40;min-width:212px;max-height:340px;overflow-y:auto;padding:6px;display:grid;gap:1px;background:var(--paper-alt);border:1px solid var(--rule);border-radius:var(--r-xl);box-shadow:var(--shadow-3);animation:row-actions-menu-in var(--t-base) var(--ease-spring)}.tbl-tool__menu--wide{min-width:248px}.tbl-tool__menu button{appearance:none;background:transparent;border:0;width:100%;text-align:start;font:inherit;font-size:13.5px;font-weight:600;color:var(--text);display:flex;align-items:center;gap:12px;padding:9px 12px;border-radius:var(--r-md);cursor:pointer;transition:background-color var(--t-fast) var(--ease-out)}.tbl-tool__menu button:hover,.tbl-tool__menu button:focus-visible{background:var(--paper);outline:0}.tbl-tool__menu button>.pi{width:16px;flex:none;font-size:14px;color:var(--muted)}.tbl-tool__menu button.is-active>.pi{color:var(--module, var(--text))}.tbl-tool__label{flex:1 1 auto;min-width:0}.tbl-tool__check{color:var(--module, var(--text))}.tbl-tool__sep{margin:5px 2px;border-top:1px solid var(--rule)}.tbl-wrap[data-density=compact] .tbl tbody td{--tbl-cell-pad-block: 4px;padding-top:var(--tbl-cell-pad-block);padding-bottom:var(--tbl-cell-pad-block)}.tbl-wrap[data-density=comfortable] .tbl tbody td{--tbl-cell-pad-block: 16px;padding-top:var(--tbl-cell-pad-block);padding-bottom:var(--tbl-cell-pad-block)}.mlist{list-style:none;margin:0;padding:0;display:grid}.mrow{border-top:1px solid var(--rule)}.mrow:first-child{border-top:0}.mrow__head{display:flex;align-items:flex-start;gap:var(--s-3);padding:12px var(--s-4);min-height:var(--hit-touch)}.mrow__head.is-tappable{cursor:pointer}.mrow__head.is-tappable:active{background:var(--paper)}.mrow__lead{flex:1;min-width:0;display:grid;gap:2px}.mrow__primary{font-weight:700;font-size:14px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mrow__secondary{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 6px;font-size:12.5px;color:var(--text-mute);min-width:0}.mrow__secondary .mrow__sec-part+.mrow__sec-part:before{content:\"\\b7\";margin-inline-end:6px;color:var(--text-soft)}.mrow__trail{display:flex;align-items:center;justify-content:flex-end;gap:var(--s-2);flex-shrink:0;min-height:20px}.mrow__actions{display:inline-flex}.mrow__detail{margin:0;padding:0 var(--s-4) 12px;display:grid;gap:1px}.mrow__field{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-3);padding:7px 0;border-top:1px solid var(--rule-soft)}.mrow__field dt{font-size:11.5px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--text-mute);flex-shrink:0}.mrow__field dd{margin:0;font-size:13.5px;color:var(--text);text-align:end;min-width:0;overflow-wrap:anywhere}.mrow__more{appearance:none;width:100%;background:transparent;border:0;border-top:1px solid var(--rule-soft);padding:10px;font:inherit;font-size:13px;font-weight:600;color:var(--tenant-500);cursor:pointer}.mlist[data-layout=card]{gap:var(--s-3);padding:var(--s-3) var(--s-3) 0}.mlist[data-layout=card] .mrow{border:1px solid var(--rule);border-radius:var(--r-lg);background:var(--paper-alt);overflow:hidden}.mlist[data-layout=card] .mrow__head{border-bottom:1px solid var(--rule)}.mrow__chevron{font-size:11px;color:var(--text-soft);transition:transform var(--t-fast) var(--ease-out);flex-shrink:0}.mrow__chevron.is-open{transform:rotate(180deg);color:var(--text-mute)}.mrow__head.is-tappable:focus-visible{outline:2px solid var(--tenant-400);outline-offset:-2px}.mrow__more{display:flex;align-items:center;justify-content:center;gap:6px}.tbl-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s-2);padding:var(--s-9) var(--s-4);text-align:center}.tbl-empty__icon{font-size:28px;line-height:1;color:var(--ink-400);margin-block-end:var(--s-1)}.tbl-empty__title{margin:0;font-size:15px;font-weight:600;color:var(--text);max-inline-size:46ch;overflow-wrap:break-word}.tbl-empty__hint{margin:0;font-size:13px;color:var(--ink-600);max-inline-size:52ch;text-wrap:pretty}.tbl-empty__action{margin-block-start:var(--s-3);min-block-size:var(--hit-base)}.tbl-empty--error .tbl-empty__icon{color:var(--st-cancelled-fg)}.mrow--empty{padding:0}.mrow--skeleton{padding:12px var(--s-4)}@media(max-width:767px){.tbl-empty{padding:var(--s-7) var(--s-4)}.tbl-empty__action{min-block-size:var(--hit-touch)}}.pi.is-spinning{animation:ef-export-spin .9s linear infinite}@keyframes ef-export-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.pi.is-spinning{animation:none}}.tbl th.tbl-drag-h,.tbl td.tbl-drag{width:28px;padding-inline:4px 0;text-align:center}.tbl td.tbl-drag{cursor:default}.tbl .tbl-drag__grip{display:inline-flex;align-items:center;justify-content:center;width:22px;height:26px;padding:0;border:0;border-radius:6px;background:transparent;color:var(--text-soft);cursor:grab;touch-action:none;transition:background var(--t-fast, .14s) var(--ease-out, ease-out),color var(--t-fast, .14s) var(--ease-out, ease-out)}.tbl .tbl-drag__grip:hover{background:var(--paper);color:var(--text)}.tbl .tbl-drag__grip:focus-visible{outline:2px solid var(--tenant, var(--rule));outline-offset:1px}.tbl .tbl-drag__grip:active{cursor:grabbing}.tbl .tbl-drag__grip i{font-size:12px}.tbl tbody tr.is-dragging>td{background:var(--paper);opacity:.55}.tbl tbody tr.is-drop-target>td{box-shadow:inset 0 2px 0 var(--tenant, var(--rule))}.tbl.tbl--fixed{table-layout:fixed}.mrow__disclosure{appearance:none;background:transparent;border:0;padding:0;margin:0;display:inline-grid;place-items:center;color:var(--text-mute);cursor:pointer;position:relative}.mrow__disclosure:after{content:\"\";position:absolute;top:50%;left:50%;width:var(--hit-touch);height:var(--hit-touch);transform:translate(-50%,-50%)}.mrow__disclosure:focus-visible{outline:2px solid color-mix(in srgb,var(--ink-active) 40%,transparent);outline-offset:2px;border-radius:var(--r-pill)}@media(pointer:coarse){.mrow__actions+.mrow__disclosure{margin-inline-start:calc((var(--hit-touch) - 11px) / 2 - var(--s-2) + 1px)}}.tbl tbody tr.is-disabled{cursor:not-allowed}.tbl tbody tr.is-disabled td,.mrow.is-disabled .mrow__head{opacity:.5}.mrow.is-disabled .mrow__head{cursor:not-allowed}.tbl-head:has(>.count:empty):has(>.tbl-tools:empty){display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfPagerComponent, selector: "ef-pager", inputs: ["pageNumber", "pageSize", "totalCount", "pageSizeOptions", "linesPerPageLabelKey", "ofLabelKey", "prevLabelKey", "nextLabelKey", "firstLabelKey", "lastLabelKey", "hideSize"], outputs: ["pageChange", "pageSizeChange"] }, { kind: "component", type: EfStatusChipComponent, selector: "ef-status-chip", inputs: ["referenceKey", "code", "color", "label", "labelKey", "hideDot", "showCode"] }, { kind: "component", type: EfSkeletonComponent, selector: "ef-skeleton", inputs: ["variant", "rows", "height"] }, { kind: "component", type: EfRowActionsComponent, selector: "ef-row-actions", inputs: ["items", "context", "triggerIcon", "triggerAriaKey", "disabled", "menuLeft"], outputs: ["opened", "closed"] }, { kind: "directive", type: EfOverflowTooltipDirective, selector: "[efOverflowTooltip]" }, { kind: "directive", type: EfTooltipDirective, selector: "[efTooltip]" }, { kind: "pipe", type: i1$3.DecimalPipe, name: "number" }, { kind: "pipe", type: i1$3.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i1$3.DatePipe, name: "date" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6090
6368
|
}
|
|
6091
6369
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfDataCardComponent, decorators: [{
|
|
6092
6370
|
type: Component,
|
|
@@ -6099,7 +6377,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
6099
6377
|
EfRowActionsComponent,
|
|
6100
6378
|
EfOverflowTooltipDirective,
|
|
6101
6379
|
EfTooltipDirective,
|
|
6102
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- One cell renderer, shared by the table, the mobile row and the mobile\n detail. A new column type is added here once, not three times. -->\n<ng-template #cellTpl let-row let-col=\"col\" let-index=\"index\">\n @if (bodyTemplate(col.id); as bodyTpl) {\n <ng-container *ngTemplateOutlet=\"bodyTpl; context: { $implicit: row, col: col, index: index }\"></ng-container>\n } @else {\n @switch (col.type) {\n @case ('number') {\n {{ $any(cellValue(row, col)) | number:numberFormat(col):col.locale }}\n }\n @case ('money') {\n {{ $any(cellValue(row, col)) | currency:(col.currencyCode || 'EUR'):(col.currencyDisplay || 'symbol'):numberFormat(col):col.locale }}\n }\n @case ('date') {\n {{ $any(cellValue(row, col)) | date:(col.dateFormat || 'dd/MM/yyyy'):undefined:col.locale }}\n }\n @case ('datetime') {\n {{ $any(cellValue(row, col)) | date:(col.dateFormat || 'dd/MM/yyyy HH:mm'):undefined:col.locale }}\n }\n @case ('boolean') {\n @if (cellValue(row, col)) {\n <span class=\"bool yes\"><span class=\"dot\"></span> \u2713</span>\n } @else {\n <span class=\"bool no\"><span class=\"dot\"></span> \u2014</span>\n }\n }\n @case ('mono') {\n <span class=\"ref\">{{ cellValue(row, col) }}</span>\n }\n @case ('chip') {\n <span [class]=\"'chip ' + (col.chipPrefix || 'chip-') + cellValue(row, col)\">\n \u25CF {{ cellValue(row, col) }}\n </span>\n }\n @case ('status') {\n <ef-status-chip\n [referenceKey]=\"col.referenceKey || ''\"\n [code]=\"$any(cellValue(row, col))\"\n />\n }\n @case ('reference') {\n {{ resolveReference(cellValue(row, col), col) }}\n }\n @default {\n {{ cellValue(row, col) }}\n }\n }\n }\n</ng-template>\n\n<!-- .has-open-menu unclips the shell for the Densit\u00E9 / Colonnes menus,\n which hang below .tbl-head. Row menus are fixed to the viewport\n (ef-row-actions) and need no lift; counting them also left the shell\n unclipped for good when a row went while its menu was open. -->\n<div class=\"tbl-wrap\" [class.has-open-menu]=\"openTool() !== null\" [attr.data-density]=\"density()\">\n <!-- \u2500\u2500 HEAD ROW \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"tbl-head\">\n <div class=\"count small\">\n @if (loading()) {\n <span>{{ loadingKey() | translate }}</span>\n } @else if (errorMsg()) {\n <!-- A refresh that failed on top of rows already on screen keeps the\n rows, so this chip is the only sign anything went wrong \u2014 and the\n rows it sits above may now be stale. It has to say so, and say it\n in the product's words: `errorMsg` is `err.message` from the HTTP\n layer, and \"500 Internal Server Error\" tells an operator nothing\n they can act on.\n\n The raw string stays on `data-error`, where devtools can read it\n and no reader ever meets it. `title` was the obvious place and is\n the wrong one: it renders the exception in a tooltip, which is\n precisely what this change exists to stop, and it is unreachable\n on touch besides.\n\n role=\"status\", not \"alert\": polite is right for a signal beside\n rows that are still usable, and when the body is showing the\n error state it already carries the alert \u2014 one failure must not\n interrupt a screen-reader twice. -->\n <span\n role=\"status\"\n [attr.data-error]=\"errorMsg()\"\n style=\"color: var(--st-cancelled-fg); font-weight: 600;\"\n >\u26A0 {{ 'common_error_refresh_failed' | translate }}</span\n >\n } @else {\n <ng-content select=\"[tbl-head-info]\"></ng-content>\n }\n </div>\n <div class=\"tbl-tools\">\n <ng-content select=\"[tbl-head-actions]\"></ng-content>\n\n @if (canExport()) {\n <div class=\"tbl-tool\">\n <button\n class=\"btn btn-ghost btn-sm\"\n type=\"button\"\n [disabled]=\"exporting()\"\n [attr.aria-busy]=\"exporting()\"\n (click)=\"requestExport()\"\n >\n <i\n class=\"pi\"\n [class.pi-download]=\"!exporting()\"\n [class.pi-spinner]=\"exporting()\"\n [class.is-spinning]=\"exporting()\"\n aria-hidden=\"true\"\n ></i>\n {{ (exporting() ? 'common_export_running' : 'common_export') | translate }}\n </button>\n </div>\n }\n\n @if (showDensityControl()) {\n <div class=\"tbl-tool\">\n <button\n class=\"btn btn-ghost btn-sm\"\n type=\"button\"\n [class.open]=\"openTool() === 'density'\"\n [attr.aria-expanded]=\"openTool() === 'density'\"\n aria-haspopup=\"menu\"\n (click)=\"toggleTool('density')\"\n >\n <i class=\"pi pi-bars\" aria-hidden=\"true\"></i>\n {{ 'common_density' | translate }}\n </button>\n @if (openTool() === 'density') {\n <div class=\"tbl-tool__menu\" role=\"menu\">\n @for (opt of densityOptions; track opt.value) {\n <button\n type=\"button\"\n role=\"menuitemradio\"\n [attr.aria-checked]=\"density() === opt.value\"\n [class.is-active]=\"density() === opt.value\"\n (click)=\"setDensity(opt.value)\"\n >\n <i class=\"pi\" [ngClass]=\"opt.icon\" aria-hidden=\"true\"></i>\n <span class=\"tbl-tool__label\">{{ opt.labelKey | translate }}</span>\n @if (density() === opt.value) {\n <i class=\"pi pi-check tbl-tool__check\" aria-hidden=\"true\"></i>\n }\n </button>\n }\n </div>\n }\n </div>\n }\n\n @if (showColumnPicker()) {\n <div class=\"tbl-tool\">\n <button\n class=\"btn btn-ghost btn-sm\"\n type=\"button\"\n [class.open]=\"openTool() === 'columns'\"\n [attr.aria-expanded]=\"openTool() === 'columns'\"\n aria-haspopup=\"menu\"\n (click)=\"toggleTool('columns')\"\n >\n <i class=\"pi pi-th-large\" aria-hidden=\"true\"></i>\n {{ 'common_columns' | translate }}\n </button>\n @if (openTool() === 'columns') {\n <div class=\"tbl-tool__menu tbl-tool__menu--wide\" role=\"menu\">\n @for (col of hideableColumns(); track col.id) {\n <button\n type=\"button\"\n role=\"menuitemcheckbox\"\n [attr.aria-checked]=\"!isColumnHidden(col.id)\"\n [class.is-active]=\"!isColumnHidden(col.id)\"\n (click)=\"toggleColumn(col.id)\"\n >\n <i class=\"pi\" [class.pi-check]=\"!isColumnHidden(col.id)\" aria-hidden=\"true\"></i>\n <span class=\"tbl-tool__label\">{{\n col.headerKey ? (col.headerKey | translate) : (col.header || col.id)\n }}</span>\n </button>\n }\n <div class=\"tbl-tool__sep\"></div>\n <button type=\"button\" role=\"menuitem\" (click)=\"resetColumns()\">\n <i class=\"pi pi-refresh\" aria-hidden=\"true\"></i>\n <span class=\"tbl-tool__label\">{{ 'common_columns_reset' | translate }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 TABLE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (isMobile()) {\n <!-- \u2500\u2500 MOBILE LIST \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n A six-column table on a 390px screen either overflows or\n squeezes every column to nothing, so the rows become a list.\n Same columns, same cell renderers; each column just declares\n (or is given) a role. Both shapes expand to the same detail \u2014\n they differ only in how much they show collapsed. -->\n <ul\n class=\"mlist\"\n [attr.data-layout]=\"effectiveMobileLayout()\"\n [attr.aria-busy]=\"loading() ? 'true' : null\"\n >\n @for (row of rows(); track trackByRow($index, row); let i = $index) {\n <li class=\"mrow\" [class.is-open]=\"isExpanded(row, i)\" [class.is-disabled]=\"isRowDisabled(row)\">\n <div\n class=\"mrow__head\"\n [class.is-tappable]=\"headIsControl()\"\n [attr.role]=\"headIsControl() ? 'button' : null\"\n [attr.tabindex]=\"headIsControl() ? 0 : null\"\n [attr.aria-pressed]=\"pressedAttr(row)\"\n [attr.aria-disabled]=\"isRowDisabled(row) ? 'true' : null\"\n [attr.aria-expanded]=\"!rowNavigable() && hasDetail() ? isExpanded(row, i) : null\"\n (click)=\"onRowHeadActivate(row, i, $event)\"\n (keydown.enter)=\"onRowHeadActivate(row, i, $event)\"\n (keydown.space)=\"onRowHeadActivate(row, i, $event)\"\n >\n <div class=\"mrow__lead\">\n @if (primaryColumn(); as pc) {\n <span class=\"mrow__primary\" efOverflowTooltip efTooltip efTooltipPosition=\"top\">\n <ng-container *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: pc, index: i }\"></ng-container>\n </span>\n }\n @if (effectiveMobileLayout() === 'row' && secondaryColumns().length) {\n <span class=\"mrow__secondary\">\n @for (sc of secondaryColumns(); track sc.id) {\n <span class=\"mrow__sec-part\">\n <ng-container *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: sc, index: i }\"></ng-container>\n </span>\n }\n </span>\n }\n </div>\n\n <div class=\"mrow__trail\">\n @if (statusColumn(); as st) {\n <ng-container *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: st, index: i }\"></ng-container>\n }\n @if (hasActionsColumn()) {\n <span class=\"mrow__actions\">\n <ef-row-actions [items]=\"defaultRowActions(row)\" [context]=\"rowActionsContext()\" />\n </span>\n }\n @if (hasDetail() && effectiveMobileLayout() === 'row') {\n @if (rowNavigable()) {\n <!-- The head opens the record, so expansion needs a control\n of its own. Same glyph, promoted from decoration to a\n button that carries the state it was already drawing. -->\n <button\n type=\"button\"\n class=\"mrow__disclosure\"\n [attr.aria-expanded]=\"isExpanded(row, i)\"\n [attr.aria-label]=\"(isExpanded(row, i) ? 'common_view_less' : 'common_view_more') | translate\"\n (click)=\"onDisclosureToggle(row, i, $event)\"\n >\n <i\n class=\"pi pi-chevron-down mrow__chevron\"\n [class.is-open]=\"isExpanded(row, i)\"\n aria-hidden=\"true\"\n ></i>\n </button>\n } @else {\n <!-- Says the row opens, and which way it is now. Decorative:\n the whole head is the control, and it carries the state. -->\n <i\n class=\"pi pi-chevron-down mrow__chevron\"\n [class.is-open]=\"isExpanded(row, i)\"\n aria-hidden=\"true\"\n ></i>\n }\n }\n </div>\n </div>\n\n @if (visibleDetail(row, i).length) {\n <dl class=\"mrow__detail\">\n @for (col of visibleDetail(row, i); track col.id) {\n <div class=\"mrow__field\">\n <dt>{{ $any(col).headerKey ? ($any(col).headerKey | translate) : $any(col).header }}</dt>\n <dd [class]=\"cellAlignClass($any(col))\">\n <ng-container *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: col, index: i }\"></ng-container>\n </dd>\n </div>\n }\n </dl>\n }\n\n @if (hasMoreThanPreview() && effectiveMobileLayout() === 'card') {\n <button class=\"mrow__more\" type=\"button\" (click)=\"toggleExpanded(row, i)\">\n <span>{{ (isExpanded(row, i) ? 'common_view_less' : 'common_view_more') | translate }}</span>\n <i class=\"pi pi-chevron-down mrow__chevron\" [class.is-open]=\"isExpanded(row, i)\" aria-hidden=\"true\"></i>\n </button>\n }\n </li>\n } @empty {\n <!-- No rows has three meanings and they must not share a screen:\n still loading is a skeleton, the request failed is the error\n state, finished-and-empty is the empty state. Saying \"nothing\n here yet\" to someone whose data is still in flight \u2014 or whose\n request just failed \u2014 is a lie the whole time it shows. -->\n @if (showSkeleton()) {\n @for (line of skeletonLines(); track line) {\n <li class=\"mrow mrow--skeleton\">\n <ef-skeleton variant=\"text\" [rows]=\"2\" />\n </li>\n }\n } @else if (showError()) {\n <li class=\"mrow mrow--empty\">\n <ng-container *ngTemplateOutlet=\"errorStateTpl\"></ng-container>\n </li>\n } @else {\n <li class=\"mrow mrow--empty\">\n <ng-content select=\"[empty-state-mobile]\">\n <ng-container *ngTemplateOutlet=\"defaultEmptyTpl\"></ng-container>\n </ng-content>\n </li>\n }\n }\n </ul>\n } @else {\n <!-- The table scrolls sideways inside its own box. Between 768px and\n the table's natural width (about 900-1100px) the columns past the\n edge were simply clipped by .tbl-wrap, with no way to reach them.\n .tbl-wrap stays the clipping shell, so the head and the pager do\n not scroll with the rows. Row menus are fixed to the viewport\n (ef-row-actions), so this scroller never clips them. -->\n <div class=\"tbl-scroll\">\n <table class=\"tbl\" [class.tbl--fixed]=\"fixedLayout()\">\n <thead>\n <tr>\n @if (reorderable()) {\n <th class=\"tbl-drag-h\" aria-hidden=\"true\"></th>\n }\n @for (col of effectiveColumns(); track trackByColumn($index, col)) {\n <!-- A sortable header is a real <button> inside the <th>, not a\n click handler on the cell. The cell carries aria-sort so the\n current order is announced; the arrow stays decorative.\n Sorting was previously mouse-only and silent to assistive\n technology, on every list screen in the product. -->\n <th\n [class]=\"headerAlignClass(col)\"\n [class.is-sortable]=\"col.sortable\"\n [style.width]=\"col.width || null\"\n [attr.data-col]=\"col.id\"\n [attr.aria-sort]=\"ariaSort(col)\"\n >\n @if (col.sortable) {\n <button type=\"button\" class=\"tbl-sort\" (click)=\"onHeaderClick(col)\">\n <ng-container *ngTemplateOutlet=\"headerLabelTpl; context: { col: col }\"></ng-container>\n @if (sortIndicator(col)) {\n <span aria-hidden=\"true\" class=\"tbl-sort__arrow\">{{ sortIndicator(col) }}</span>\n }\n </button>\n } @else {\n <ng-container *ngTemplateOutlet=\"headerLabelTpl; context: { col: col }\"></ng-container>\n }\n </th>\n }\n </tr>\n </thead>\n\n <tbody [attr.aria-busy]=\"loading() ? 'true' : null\">\n @for (row of rows(); track trackByRow($index, row); let i = $index) {\n <tr\n [class.is-row-clickable]=\"rowDoubleClickable()\"\n [class.is-dragging]=\"dragFrom() === i\"\n [class.is-drop-target]=\"dragFrom() !== null && dragFrom() !== i && dragTo() === i\"\n [attr.tabindex]=\"rowDoubleClickable() ? 0 : null\"\n [attr.role]=\"rowDoubleClickable() ? 'button' : null\"\n [attr.aria-pressed]=\"pressedAttr(row)\"\n [attr.aria-disabled]=\"isRowDisabled(row) ? 'true' : null\"\n [class.is-disabled]=\"isRowDisabled(row)\"\n (click)=\"onRowClick(row, $event)\"\n (keydown.enter)=\"onRowKeydown(row, $event)\"\n (keydown.space)=\"onRowSpace(row, $event)\"\n >\n @if (reorderable()) {\n <td class=\"tbl-drag\">\n <!-- The row is draggable only by this handle, so a drag never\n starts from a cell the operator meant to click or select.\n Arrow keys move the row for anyone not using a pointer. -->\n <button\n type=\"button\"\n class=\"tbl-drag__grip\"\n [attr.aria-label]=\"'common_reorder_row' | translate\"\n (pointerdown)=\"onDragPointerDown(i, $event)\"\n (pointermove)=\"onDragPointerMove($event)\"\n (pointerup)=\"onDragPointerUp()\"\n (pointercancel)=\"onDragPointerUp()\"\n (keydown)=\"onDragKeydown(i, $event)\"\n >\n <i class=\"pi pi-ellipsis-v\" aria-hidden=\"true\"></i>\n </button>\n </td>\n }\n @for (col of effectiveColumns(); track trackByColumn($index, col); let colIdx = $index) {\n <td efOverflowTooltip efTooltip efTooltipPosition=\"top\" [class]=\"cellAlignClass(col)\" [attr.data-col]=\"col.id\">\n @if (col.id === 'actions' && hasActionsColumn()) {\n <!-- Auto row-actions cell: View / Edit / Duplicate / Delete,\n gated by [show*Action] flags + ScreenContext perms. -->\n <ef-row-actions\n [items]=\"defaultRowActions(row)\"\n [context]=\"rowActionsContext()\"\n />\n } @else {\n <ng-container\n *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: col, index: i }\"\n ></ng-container>\n }\n </td>\n }\n </tr>\n } @empty {\n <!-- Placeholder rows keep the real columns, so the table that\n arrives lands where the shimmer already was instead of\n reflowing the page under the reader's eyes. -->\n @if (showSkeleton()) {\n @for (line of skeletonLines(); track line) {\n <tr>\n @if (reorderable()) {\n <td class=\"tbl-drag\"></td>\n }\n @for (col of effectiveColumns(); track trackByColumn($index, col)) {\n <td [class]=\"cellAlignClass(col)\" [attr.data-col]=\"col.id\">\n <ef-skeleton variant=\"text\" [rows]=\"1\" />\n </td>\n }\n </tr>\n }\n } @else if (showError()) {\n <!-- The failure replaces the body, so it spans the full row \u2014\n crammed into column one it reads as a value, not a state. -->\n <tr>\n <td [attr.colspan]=\"effectiveColumns().length + (reorderable() ? 1 : 0)\">\n <ng-container *ngTemplateOutlet=\"errorStateTpl\"></ng-container>\n </td>\n </tr>\n } @else {\n <tr>\n <td [attr.colspan]=\"effectiveColumns().length + (reorderable() ? 1 : 0)\">\n <ng-content select=\"[empty-state]\">\n <ng-container *ngTemplateOutlet=\"defaultEmptyTpl\"></ng-container>\n </ng-content>\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n }\n\n <!-- \u2500\u2500 PAGER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <!-- Paging controls for an empty result set are chrome with nothing to\n page, so they stay out of the way until there is something to move\n through. -->\n @if (!hidePager() && rows().length > 0) {\n <ef-pager\n [pageNumber]=\"pageNumber()\"\n [pageSize]=\"pageSize()\"\n [totalCount]=\"totalCount()\"\n [pageSizeOptions]=\"pageSizeOptions()\"\n (pageChange)=\"pageChange.emit($event)\"\n (pageSizeChange)=\"pageSizeChange.emit($event)\"\n />\n }\n</div>\n\n<!-- \u2500\u2500 DEFAULT EMPTY STATE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Rendered when a screen projects nothing into [empty-state] /\n [empty-state-mobile]. It words itself from `searchTerm`, because\n \"nothing matched what you typed\" and \"there is nothing here yet\"\n are different situations needing different ways out. -->\n<ng-template #defaultEmptyTpl>\n <div class=\"tbl-empty\">\n <i\n class=\"pi tbl-empty__icon\"\n [class.pi-search]=\"!!searchTerm()\"\n [class.pi-inbox]=\"!searchTerm()\"\n aria-hidden=\"true\"\n ></i>\n\n @if (searchTerm()) {\n <p class=\"tbl-empty__title\">\n {{ 'common_empty_no_results' | translate: { term: searchTerm() } }}\n </p>\n <p class=\"tbl-empty__hint\">{{ 'common_empty_no_results_hint' | translate }}</p>\n <button class=\"btn btn-sm tbl-empty__action\" type=\"button\" (click)=\"clearSearch.emit()\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n {{ 'common_empty_clear_filters' | translate }}\n </button>\n } @else {\n <p class=\"tbl-empty__title\">{{ 'common_empty_none_yet' | translate }}</p>\n <p class=\"tbl-empty__hint\">{{ 'common_empty_none_yet_hint' | translate }}</p>\n }\n </div>\n</ng-template>\n\n<!-- \u2500\u2500 ERROR STATE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n A request that failed and came back with nothing is not an empty\n collection, and must never borrow its words: \"nothing here yet\" is\n a confident false statement about the operator's data, and they may\n act on it. So this replaces the empty state outright, projected\n [empty-state] included \u2014 a screen's own \"create your first client\"\n copy is just as wrong here as the default.\n\n It says nothing about *what* failed. `errorMsg` is `err.message`\n from the HTTP layer, and a status line or an exception string tells\n an operator nothing they can act on; it survives on the head chip's\n `data-error` attribute, for whoever is reading the DOM. This carries\n the two things that help: their data is intact, and the way to try\n again.\n\n role=\"alert\" goes on this div, never on the <tr> / <td> / <li> that\n hosts it \u2014 a role on a row or cell takes the table's semantics down\n with it. It fires on insertion, which is exactly when @if adds it. -->\n<ng-template #errorStateTpl>\n <div class=\"tbl-empty tbl-empty--error\" role=\"alert\">\n <i class=\"pi pi-exclamation-triangle tbl-empty__icon\" aria-hidden=\"true\"></i>\n <p class=\"tbl-empty__title\">{{ 'common_error_load_failed' | translate }}</p>\n <p class=\"tbl-empty__hint\">{{ 'common_error_load_failed_hint' | translate }}</p>\n <button class=\"btn btn-sm tbl-empty__action\" type=\"button\" (click)=\"retry.emit()\">\n <i class=\"pi pi-refresh\" aria-hidden=\"true\"></i>\n {{ 'common_retry' | translate }}\n </button>\n </div>\n</ng-template>\n\n<!-- Header label, shared by the sortable and plain header cells so the\n projection rules stay in one place. -->\n<ng-template #headerLabelTpl let-col=\"col\">\n @if (headerTemplate(col.id); as headerTpl) {\n <ng-container *ngTemplateOutlet=\"headerTpl; context: { $implicit: col, col: col }\"></ng-container>\n } @else if (col.headerKey) {\n <span>{{ col.headerKey | translate }}</span>\n } @else if (col.header) {\n <span>{{ col.header }}</span>\n }\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:block;min-width:0}.tbl thead th.is-sortable{cursor:pointer;-webkit-user-select:none;user-select:none;transition:color var(--t-fast, .14s) var(--ease-out, ease-out)}.tbl thead th.is-sortable:hover{color:var(--text)}.tbl tbody tr.is-row-clickable{cursor:pointer}.tbl-tools{display:flex;gap:6px;align-items:center}.tbl-tool{position:relative}.tbl-tool>.btn.open{color:var(--text);background:var(--paper)}.tbl-tool__menu{position:absolute;top:calc(100% + 8px);inset-inline-end:0;z-index:40;min-width:212px;max-height:340px;overflow-y:auto;padding:6px;display:grid;gap:1px;background:var(--paper-alt);border:1px solid var(--rule);border-radius:var(--r-xl);box-shadow:var(--shadow-3);animation:row-actions-menu-in var(--t-base) var(--ease-spring)}.tbl-tool__menu--wide{min-width:248px}.tbl-tool__menu button{appearance:none;background:transparent;border:0;width:100%;text-align:start;font:inherit;font-size:13.5px;font-weight:600;color:var(--text);display:flex;align-items:center;gap:12px;padding:9px 12px;border-radius:var(--r-md);cursor:pointer;transition:background-color var(--t-fast) var(--ease-out)}.tbl-tool__menu button:hover,.tbl-tool__menu button:focus-visible{background:var(--paper);outline:0}.tbl-tool__menu button>.pi{width:16px;flex:none;font-size:14px;color:var(--muted)}.tbl-tool__menu button.is-active>.pi{color:var(--module, var(--text))}.tbl-tool__label{flex:1 1 auto;min-width:0}.tbl-tool__check{color:var(--module, var(--text))}.tbl-tool__sep{margin:5px 2px;border-top:1px solid var(--rule)}.tbl-wrap[data-density=compact] .tbl tbody td{--tbl-cell-pad-block: 4px;padding-top:var(--tbl-cell-pad-block);padding-bottom:var(--tbl-cell-pad-block)}.tbl-wrap[data-density=comfortable] .tbl tbody td{--tbl-cell-pad-block: 16px;padding-top:var(--tbl-cell-pad-block);padding-bottom:var(--tbl-cell-pad-block)}.mlist{list-style:none;margin:0;padding:0;display:grid}.mrow{border-top:1px solid var(--rule)}.mrow:first-child{border-top:0}.mrow__head{display:flex;align-items:flex-start;gap:var(--s-3);padding:12px var(--s-4);min-height:var(--hit-touch)}.mrow__head.is-tappable{cursor:pointer}.mrow__head.is-tappable:active{background:var(--paper)}.mrow__lead{flex:1;min-width:0;display:grid;gap:2px}.mrow__primary{font-weight:700;font-size:14px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mrow__secondary{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 6px;font-size:12.5px;color:var(--text-mute);min-width:0}.mrow__secondary .mrow__sec-part+.mrow__sec-part:before{content:\"\\b7\";margin-inline-end:6px;color:var(--text-soft)}.mrow__trail{display:flex;align-items:center;justify-content:flex-end;gap:var(--s-2);flex-shrink:0;min-height:20px}.mrow__actions{display:inline-flex}.mrow__detail{margin:0;padding:0 var(--s-4) 12px;display:grid;gap:1px}.mrow__field{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-3);padding:7px 0;border-top:1px solid var(--rule-soft)}.mrow__field dt{font-size:11.5px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--text-mute);flex-shrink:0}.mrow__field dd{margin:0;font-size:13.5px;color:var(--text);text-align:end;min-width:0;overflow-wrap:anywhere}.mrow__more{appearance:none;width:100%;background:transparent;border:0;border-top:1px solid var(--rule-soft);padding:10px;font:inherit;font-size:13px;font-weight:600;color:var(--tenant-500);cursor:pointer}.mlist[data-layout=card]{gap:var(--s-3);padding:var(--s-3) var(--s-3) 0}.mlist[data-layout=card] .mrow{border:1px solid var(--rule);border-radius:var(--r-lg);background:var(--paper-alt);overflow:hidden}.mlist[data-layout=card] .mrow__head{border-bottom:1px solid var(--rule)}.mrow__chevron{font-size:11px;color:var(--text-soft);transition:transform var(--t-fast) var(--ease-out);flex-shrink:0}.mrow__chevron.is-open{transform:rotate(180deg);color:var(--text-mute)}.mrow__head.is-tappable:focus-visible{outline:2px solid var(--tenant-400);outline-offset:-2px}.mrow__more{display:flex;align-items:center;justify-content:center;gap:6px}.tbl-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s-2);padding:var(--s-9) var(--s-4);text-align:center}.tbl-empty__icon{font-size:28px;line-height:1;color:var(--ink-400);margin-block-end:var(--s-1)}.tbl-empty__title{margin:0;font-size:15px;font-weight:600;color:var(--text);max-inline-size:46ch;overflow-wrap:break-word}.tbl-empty__hint{margin:0;font-size:13px;color:var(--ink-600);max-inline-size:52ch;text-wrap:pretty}.tbl-empty__action{margin-block-start:var(--s-3);min-block-size:var(--hit-base)}.tbl-empty--error .tbl-empty__icon{color:var(--st-cancelled-fg)}.mrow--empty{padding:0}.mrow--skeleton{padding:12px var(--s-4)}@media(max-width:767px){.tbl-empty{padding:var(--s-7) var(--s-4)}.tbl-empty__action{min-block-size:var(--hit-touch)}}.pi.is-spinning{animation:ef-export-spin .9s linear infinite}@keyframes ef-export-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.pi.is-spinning{animation:none}}.tbl th.tbl-drag-h,.tbl td.tbl-drag{width:28px;padding-inline:4px 0;text-align:center}.tbl td.tbl-drag{cursor:default}.tbl .tbl-drag__grip{display:inline-flex;align-items:center;justify-content:center;width:22px;height:26px;padding:0;border:0;border-radius:6px;background:transparent;color:var(--text-soft);cursor:grab;touch-action:none;transition:background var(--t-fast, .14s) var(--ease-out, ease-out),color var(--t-fast, .14s) var(--ease-out, ease-out)}.tbl .tbl-drag__grip:hover{background:var(--paper);color:var(--text)}.tbl .tbl-drag__grip:focus-visible{outline:2px solid var(--tenant, var(--rule));outline-offset:1px}.tbl .tbl-drag__grip:active{cursor:grabbing}.tbl .tbl-drag__grip i{font-size:12px}.tbl tbody tr.is-dragging>td{background:var(--paper);opacity:.55}.tbl tbody tr.is-drop-target>td{box-shadow:inset 0 2px 0 var(--tenant, var(--rule))}.tbl.tbl--fixed{table-layout:fixed}.mrow__disclosure{appearance:none;background:transparent;border:0;padding:0;margin:0;display:inline-grid;place-items:center;color:var(--text-mute);cursor:pointer;position:relative}.mrow__disclosure:after{content:\"\";position:absolute;top:50%;left:50%;width:var(--hit-touch);height:var(--hit-touch);transform:translate(-50%,-50%)}.mrow__disclosure:focus-visible{outline:2px solid color-mix(in srgb,var(--ink-active) 40%,transparent);outline-offset:2px;border-radius:var(--r-pill)}@media(pointer:coarse){.mrow__actions+.mrow__disclosure{margin-inline-start:calc((var(--hit-touch) - 11px) / 2 - var(--s-2) + 1px)}}.tbl tbody tr.is-disabled{cursor:not-allowed}.tbl tbody tr.is-disabled td,.mrow.is-disabled .mrow__head{opacity:.5}.mrow.is-disabled .mrow__head{cursor:not-allowed}.tbl-head:has(>.count:empty):has(>.tbl-tools:empty){display:none}\n"] }]
|
|
6380
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- One cell renderer, shared by the table, the mobile row and the mobile\n detail. A new column type is added here once, not three times. -->\n<ng-template #cellTpl let-row let-col=\"col\" let-index=\"index\">\n @if (bodyTemplate(col.id); as bodyTpl) {\n <ng-container *ngTemplateOutlet=\"bodyTpl; context: { $implicit: row, col: col, index: index }\"></ng-container>\n } @else {\n @switch (col.type) {\n @case ('number') {\n {{ $any(cellValue(row, col)) | number:numberFormat(col):col.locale }}\n }\n @case ('money') {\n {{ $any(cellValue(row, col)) | currency:(col.currencyCode || 'EUR'):(col.currencyDisplay || 'symbol'):numberFormat(col):col.locale }}\n }\n @case ('date') {\n {{ $any(cellValue(row, col)) | date:(col.dateFormat || 'dd/MM/yyyy'):undefined:col.locale }}\n }\n @case ('datetime') {\n {{ $any(cellValue(row, col)) | date:(col.dateFormat || 'dd/MM/yyyy HH:mm'):undefined:col.locale }}\n }\n @case ('boolean') {\n @if (cellValue(row, col)) {\n <span class=\"bool yes\"><span class=\"dot\"></span> \u2713</span>\n } @else {\n <span class=\"bool no\"><span class=\"dot\"></span> \u2014</span>\n }\n }\n @case ('mono') {\n <span class=\"ref\">{{ cellValue(row, col) }}</span>\n }\n @case ('chip') {\n <span [class]=\"'chip ' + (col.chipPrefix || 'chip-') + cellValue(row, col)\">\n \u25CF {{ cellValue(row, col) }}\n </span>\n }\n @case ('status') {\n <ef-status-chip\n [referenceKey]=\"col.referenceKey || ''\"\n [code]=\"$any(cellValue(row, col))\"\n />\n }\n @case ('reference') {\n {{ resolveReference(cellValue(row, col), col) }}\n }\n @default {\n {{ cellValue(row, col) }}\n }\n }\n }\n</ng-template>\n\n<!-- .has-open-menu unclips the shell for the Densit\u00E9 / Colonnes menus,\n which hang below .tbl-head. Row menus are fixed to the viewport\n (ef-row-actions) and need no lift; counting them also left the shell\n unclipped for good when a row went while its menu was open. -->\n<div class=\"tbl-wrap\" [class.has-open-menu]=\"openTool() !== null\" [attr.data-density]=\"density()\">\n <!-- \u2500\u2500 HEAD ROW \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"tbl-head\">\n <div class=\"count small\">\n @if (loading()) {\n <span>{{ loadingKey() | translate }}</span>\n } @else if (errorMsg()) {\n <!-- A refresh that failed on top of rows already on screen keeps the\n rows, so this chip is the only sign anything went wrong \u2014 and the\n rows it sits above may now be stale. It has to say so, and say it\n in the product's words: `errorMsg` is `err.message` from the HTTP\n layer, and \"500 Internal Server Error\" tells an operator nothing\n they can act on.\n\n The raw string stays on `data-error`, where devtools can read it\n and no reader ever meets it. `title` was the obvious place and is\n the wrong one: it renders the exception in a tooltip, which is\n precisely what this change exists to stop, and it is unreachable\n on touch besides.\n\n role=\"status\", not \"alert\": polite is right for a signal beside\n rows that are still usable, and when the body is showing the\n error state it already carries the alert \u2014 one failure must not\n interrupt a screen-reader twice. -->\n <span\n role=\"status\"\n [attr.data-error]=\"errorMsg()\"\n style=\"color: var(--st-cancelled-fg); font-weight: 600;\"\n >\u26A0 {{ 'common_error_refresh_failed' | translate }}</span\n >\n } @else {\n <ng-content select=\"[tbl-head-info]\"></ng-content>\n }\n </div>\n <div class=\"tbl-tools\">\n <ng-content select=\"[tbl-head-actions]\"></ng-content>\n\n @if (canExport()) {\n <div class=\"tbl-tool\">\n <button\n class=\"btn btn-ghost btn-sm\"\n type=\"button\"\n [disabled]=\"exporting()\"\n [attr.aria-busy]=\"exporting()\"\n (click)=\"requestExport()\"\n >\n <i\n class=\"pi\"\n [class.pi-download]=\"!exporting()\"\n [class.pi-spinner]=\"exporting()\"\n [class.is-spinning]=\"exporting()\"\n aria-hidden=\"true\"\n ></i>\n {{ (exporting() ? 'common_export_running' : 'common_export') | translate }}\n </button>\n </div>\n }\n\n @if (showDensityControl()) {\n <div class=\"tbl-tool\">\n <button\n class=\"btn btn-ghost btn-sm\"\n type=\"button\"\n [class.open]=\"openTool() === 'density'\"\n [attr.aria-expanded]=\"openTool() === 'density'\"\n aria-haspopup=\"menu\"\n (click)=\"toggleTool('density')\"\n >\n <i class=\"pi pi-bars\" aria-hidden=\"true\"></i>\n {{ 'common_density' | translate }}\n </button>\n @if (openTool() === 'density') {\n <div class=\"tbl-tool__menu\" role=\"menu\">\n @for (opt of densityOptions; track opt.value) {\n <button\n type=\"button\"\n role=\"menuitemradio\"\n [attr.aria-checked]=\"density() === opt.value\"\n [class.is-active]=\"density() === opt.value\"\n (click)=\"setDensity(opt.value)\"\n >\n <i class=\"pi\" [ngClass]=\"opt.icon\" aria-hidden=\"true\"></i>\n <span class=\"tbl-tool__label\">{{ opt.labelKey | translate }}</span>\n @if (density() === opt.value) {\n <i class=\"pi pi-check tbl-tool__check\" aria-hidden=\"true\"></i>\n }\n </button>\n }\n </div>\n }\n </div>\n }\n\n @if (showColumnPicker()) {\n <div class=\"tbl-tool\">\n <button\n class=\"btn btn-ghost btn-sm\"\n type=\"button\"\n [class.open]=\"openTool() === 'columns'\"\n [attr.aria-expanded]=\"openTool() === 'columns'\"\n aria-haspopup=\"menu\"\n (click)=\"toggleTool('columns')\"\n >\n <i class=\"pi pi-th-large\" aria-hidden=\"true\"></i>\n {{ 'common_columns' | translate }}\n </button>\n @if (openTool() === 'columns') {\n <div class=\"tbl-tool__menu tbl-tool__menu--wide\" role=\"menu\">\n @for (col of hideableColumns(); track col.id) {\n <button\n type=\"button\"\n role=\"menuitemcheckbox\"\n [attr.aria-checked]=\"!isColumnHidden(col.id)\"\n [class.is-active]=\"!isColumnHidden(col.id)\"\n (click)=\"toggleColumn(col.id)\"\n >\n <i class=\"pi\" [class.pi-check]=\"!isColumnHidden(col.id)\" aria-hidden=\"true\"></i>\n <span class=\"tbl-tool__label\">{{\n col.headerKey ? (col.headerKey | translate) : (col.header || col.id)\n }}</span>\n </button>\n }\n <div class=\"tbl-tool__sep\"></div>\n <button type=\"button\" role=\"menuitem\" (click)=\"resetColumns()\">\n <i class=\"pi pi-refresh\" aria-hidden=\"true\"></i>\n <span class=\"tbl-tool__label\">{{ 'common_columns_reset' | translate }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 TABLE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (isMobile()) {\n <!-- \u2500\u2500 MOBILE LIST \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n A six-column table on a 390px screen either overflows or\n squeezes every column to nothing, so the rows become a list.\n Same columns, same cell renderers; each column just declares\n (or is given) a role. Both shapes expand to the same detail \u2014\n they differ only in how much they show collapsed. -->\n <ul\n class=\"mlist\"\n [attr.data-layout]=\"effectiveMobileLayout()\"\n [attr.aria-busy]=\"loading() ? 'true' : null\"\n >\n @for (row of rows(); track trackByRow($index, row); let i = $index) {\n <li class=\"mrow\" [class.is-open]=\"isExpanded(row, i)\" [class.is-disabled]=\"isRowDisabled(row)\">\n <div\n class=\"mrow__head\"\n [class.is-tappable]=\"headIsControl()\"\n [attr.role]=\"headIsControl() ? 'button' : null\"\n [attr.tabindex]=\"headIsControl() ? 0 : null\"\n [attr.aria-pressed]=\"pressedAttr(row)\"\n [attr.aria-disabled]=\"isRowDisabled(row) ? 'true' : null\"\n [attr.aria-expanded]=\"!rowNavigable() && hasDetail() ? isExpanded(row, i) : null\"\n (click)=\"onRowHeadActivate(row, i, $event)\"\n (keydown.enter)=\"onRowHeadActivate(row, i, $event)\"\n (keydown.space)=\"onRowHeadActivate(row, i, $event)\"\n >\n <div class=\"mrow__lead\">\n @if (primaryColumn(); as pc) {\n <span class=\"mrow__primary\" efOverflowTooltip efTooltip efTooltipPosition=\"top\">\n <ng-container *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: pc, index: i }\"></ng-container>\n </span>\n }\n @if (effectiveMobileLayout() === 'row' && secondaryColumns().length) {\n <span class=\"mrow__secondary\">\n @for (sc of secondaryColumns(); track sc.id) {\n @if (hasCellContent(row, sc)) {\n <span class=\"mrow__sec-part\">\n <ng-container *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: sc, index: i }\"></ng-container>\n </span>\n }\n }\n </span>\n }\n </div>\n\n <div class=\"mrow__trail\">\n @if (statusColumn(); as st) {\n <ng-container *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: st, index: i }\"></ng-container>\n }\n @if (hasActionsColumn()) {\n <span class=\"mrow__actions\">\n <ef-row-actions [items]=\"defaultRowActions(row)\" [context]=\"rowActionsContext()\" />\n </span>\n }\n @if (hasDetail() && effectiveMobileLayout() === 'row') {\n @if (rowNavigable()) {\n <!-- The head opens the record, so expansion needs a control\n of its own. Same glyph, promoted from decoration to a\n button that carries the state it was already drawing. -->\n <button\n type=\"button\"\n class=\"mrow__disclosure\"\n [attr.aria-expanded]=\"isExpanded(row, i)\"\n [attr.aria-label]=\"(isExpanded(row, i) ? 'common_view_less' : 'common_view_more') | translate\"\n (click)=\"onDisclosureToggle(row, i, $event)\"\n >\n <i\n class=\"pi pi-chevron-down mrow__chevron\"\n [class.is-open]=\"isExpanded(row, i)\"\n aria-hidden=\"true\"\n ></i>\n </button>\n } @else {\n <!-- Says the row opens, and which way it is now. Decorative:\n the whole head is the control, and it carries the state. -->\n <i\n class=\"pi pi-chevron-down mrow__chevron\"\n [class.is-open]=\"isExpanded(row, i)\"\n aria-hidden=\"true\"\n ></i>\n }\n }\n </div>\n </div>\n\n @if (visibleDetail(row, i).length) {\n <dl class=\"mrow__detail\">\n @for (col of visibleDetail(row, i); track col.id) {\n <div class=\"mrow__field\">\n <dt>{{ $any(col).headerKey ? ($any(col).headerKey | translate) : $any(col).header }}</dt>\n <dd [class]=\"cellAlignClass($any(col))\">\n <ng-container *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: col, index: i }\"></ng-container>\n </dd>\n </div>\n }\n </dl>\n }\n\n @if (hasMoreThanPreview() && effectiveMobileLayout() === 'card') {\n <button class=\"mrow__more\" type=\"button\" (click)=\"toggleExpanded(row, i)\">\n <span>{{ (isExpanded(row, i) ? 'common_view_less' : 'common_view_more') | translate }}</span>\n <i class=\"pi pi-chevron-down mrow__chevron\" [class.is-open]=\"isExpanded(row, i)\" aria-hidden=\"true\"></i>\n </button>\n }\n </li>\n } @empty {\n <!-- No rows has three meanings and they must not share a screen:\n still loading is a skeleton, the request failed is the error\n state, finished-and-empty is the empty state. Saying \"nothing\n here yet\" to someone whose data is still in flight \u2014 or whose\n request just failed \u2014 is a lie the whole time it shows. -->\n @if (showSkeleton()) {\n @for (line of skeletonLines(); track line) {\n <li class=\"mrow mrow--skeleton\">\n <ef-skeleton variant=\"text\" [rows]=\"2\" />\n </li>\n }\n } @else if (showError()) {\n <li class=\"mrow mrow--empty\">\n <ng-container *ngTemplateOutlet=\"errorStateTpl\"></ng-container>\n </li>\n } @else {\n <li class=\"mrow mrow--empty\">\n <ng-content select=\"[empty-state-mobile]\">\n <ng-container *ngTemplateOutlet=\"defaultEmptyTpl\"></ng-container>\n </ng-content>\n </li>\n }\n }\n </ul>\n } @else {\n <!-- The table scrolls sideways inside its own box. Between 768px and\n the table's natural width (about 900-1100px) the columns past the\n edge were simply clipped by .tbl-wrap, with no way to reach them.\n .tbl-wrap stays the clipping shell, so the head and the pager do\n not scroll with the rows. Row menus are fixed to the viewport\n (ef-row-actions), so this scroller never clips them. -->\n <div class=\"tbl-scroll\">\n <table class=\"tbl\" [class.tbl--fixed]=\"fixedLayout()\">\n <thead>\n <tr>\n @if (reorderable()) {\n <th class=\"tbl-drag-h\" aria-hidden=\"true\"></th>\n }\n @for (col of effectiveColumns(); track trackByColumn($index, col)) {\n <!-- A sortable header is a real <button> inside the <th>, not a\n click handler on the cell. The cell carries aria-sort so the\n current order is announced; the arrow stays decorative.\n Sorting was previously mouse-only and silent to assistive\n technology, on every list screen in the product. -->\n <th\n [class]=\"headerAlignClass(col)\"\n [class.is-sortable]=\"col.sortable\"\n [style.width]=\"col.width || null\"\n [attr.data-col]=\"col.id\"\n [attr.aria-sort]=\"ariaSort(col)\"\n >\n @if (col.sortable) {\n <button type=\"button\" class=\"tbl-sort\" (click)=\"onHeaderClick(col)\">\n <ng-container *ngTemplateOutlet=\"headerLabelTpl; context: { col: col }\"></ng-container>\n @if (sortIndicator(col)) {\n <span aria-hidden=\"true\" class=\"tbl-sort__arrow\">{{ sortIndicator(col) }}</span>\n }\n </button>\n } @else {\n <ng-container *ngTemplateOutlet=\"headerLabelTpl; context: { col: col }\"></ng-container>\n }\n </th>\n }\n </tr>\n </thead>\n\n <tbody [attr.aria-busy]=\"loading() ? 'true' : null\">\n @for (row of rows(); track trackByRow($index, row); let i = $index) {\n <tr\n [class.is-row-clickable]=\"rowDoubleClickable()\"\n [class.is-dragging]=\"dragFrom() === i\"\n [class.is-drop-target]=\"dragFrom() !== null && dragFrom() !== i && dragTo() === i\"\n [attr.tabindex]=\"rowDoubleClickable() ? 0 : null\"\n [attr.role]=\"rowDoubleClickable() ? 'button' : null\"\n [attr.aria-pressed]=\"pressedAttr(row)\"\n [attr.aria-disabled]=\"isRowDisabled(row) ? 'true' : null\"\n [class.is-disabled]=\"isRowDisabled(row)\"\n (click)=\"onRowClick(row, $event)\"\n (keydown.enter)=\"onRowKeydown(row, $event)\"\n (keydown.space)=\"onRowSpace(row, $event)\"\n >\n @if (reorderable()) {\n <td class=\"tbl-drag\">\n <!-- The row is draggable only by this handle, so a drag never\n starts from a cell the operator meant to click or select.\n Arrow keys move the row for anyone not using a pointer. -->\n <button\n type=\"button\"\n class=\"tbl-drag__grip\"\n [attr.aria-label]=\"'common_reorder_row' | translate\"\n (pointerdown)=\"onDragPointerDown(i, $event)\"\n (pointermove)=\"onDragPointerMove($event)\"\n (pointerup)=\"onDragPointerUp()\"\n (pointercancel)=\"onDragPointerUp()\"\n (keydown)=\"onDragKeydown(i, $event)\"\n >\n <i class=\"pi pi-ellipsis-v\" aria-hidden=\"true\"></i>\n </button>\n </td>\n }\n @for (col of effectiveColumns(); track trackByColumn($index, col); let colIdx = $index) {\n <td efOverflowTooltip efTooltip efTooltipPosition=\"top\" [class]=\"cellAlignClass(col)\" [attr.data-col]=\"col.id\">\n @if (col.id === 'actions' && hasActionsColumn()) {\n <!-- Auto row-actions cell: View / Edit / Duplicate / Delete,\n gated by [show*Action] flags + ScreenContext perms. -->\n <ef-row-actions\n [items]=\"defaultRowActions(row)\"\n [context]=\"rowActionsContext()\"\n />\n } @else {\n <ng-container\n *ngTemplateOutlet=\"cellTpl; context: { $implicit: row, col: col, index: i }\"\n ></ng-container>\n }\n </td>\n }\n </tr>\n } @empty {\n <!-- Placeholder rows keep the real columns, so the table that\n arrives lands where the shimmer already was instead of\n reflowing the page under the reader's eyes. -->\n @if (showSkeleton()) {\n @for (line of skeletonLines(); track line) {\n <tr>\n @if (reorderable()) {\n <td class=\"tbl-drag\"></td>\n }\n @for (col of effectiveColumns(); track trackByColumn($index, col)) {\n <td [class]=\"cellAlignClass(col)\" [attr.data-col]=\"col.id\">\n <ef-skeleton variant=\"text\" [rows]=\"1\" />\n </td>\n }\n </tr>\n }\n } @else if (showError()) {\n <!-- The failure replaces the body, so it spans the full row \u2014\n crammed into column one it reads as a value, not a state. -->\n <tr>\n <td [attr.colspan]=\"effectiveColumns().length + (reorderable() ? 1 : 0)\">\n <ng-container *ngTemplateOutlet=\"errorStateTpl\"></ng-container>\n </td>\n </tr>\n } @else {\n <tr>\n <td [attr.colspan]=\"effectiveColumns().length + (reorderable() ? 1 : 0)\">\n <ng-content select=\"[empty-state]\">\n <ng-container *ngTemplateOutlet=\"defaultEmptyTpl\"></ng-container>\n </ng-content>\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n }\n\n <!-- \u2500\u2500 PAGER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <!-- Paging controls for an empty result set are chrome with nothing to\n page, so they stay out of the way until there is something to move\n through. -->\n @if (!hidePager() && rows().length > 0) {\n <ef-pager\n [pageNumber]=\"pageNumber()\"\n [pageSize]=\"pageSize()\"\n [totalCount]=\"totalCount()\"\n [pageSizeOptions]=\"pageSizeOptions()\"\n (pageChange)=\"pageChange.emit($event)\"\n (pageSizeChange)=\"pageSizeChange.emit($event)\"\n />\n }\n</div>\n\n<!-- \u2500\u2500 DEFAULT EMPTY STATE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Rendered when a screen projects nothing into [empty-state] /\n [empty-state-mobile]. It words itself from `searchTerm`, because\n \"nothing matched what you typed\" and \"there is nothing here yet\"\n are different situations needing different ways out. -->\n<ng-template #defaultEmptyTpl>\n <div class=\"tbl-empty\">\n <i\n class=\"pi tbl-empty__icon\"\n [class.pi-search]=\"!!searchTerm()\"\n [class.pi-inbox]=\"!searchTerm()\"\n aria-hidden=\"true\"\n ></i>\n\n @if (searchTerm()) {\n <p class=\"tbl-empty__title\">\n {{ 'common_empty_no_results' | translate: { term: searchTerm() } }}\n </p>\n <p class=\"tbl-empty__hint\">{{ 'common_empty_no_results_hint' | translate }}</p>\n <button class=\"btn btn-sm tbl-empty__action\" type=\"button\" (click)=\"clearSearch.emit()\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n {{ 'common_empty_clear_filters' | translate }}\n </button>\n } @else {\n <p class=\"tbl-empty__title\">{{ 'common_empty_none_yet' | translate }}</p>\n <p class=\"tbl-empty__hint\">{{ 'common_empty_none_yet_hint' | translate }}</p>\n }\n </div>\n</ng-template>\n\n<!-- \u2500\u2500 ERROR STATE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n A request that failed and came back with nothing is not an empty\n collection, and must never borrow its words: \"nothing here yet\" is\n a confident false statement about the operator's data, and they may\n act on it. So this replaces the empty state outright, projected\n [empty-state] included \u2014 a screen's own \"create your first client\"\n copy is just as wrong here as the default.\n\n It says nothing about *what* failed. `errorMsg` is `err.message`\n from the HTTP layer, and a status line or an exception string tells\n an operator nothing they can act on; it survives on the head chip's\n `data-error` attribute, for whoever is reading the DOM. This carries\n the two things that help: their data is intact, and the way to try\n again.\n\n role=\"alert\" goes on this div, never on the <tr> / <td> / <li> that\n hosts it \u2014 a role on a row or cell takes the table's semantics down\n with it. It fires on insertion, which is exactly when @if adds it. -->\n<ng-template #errorStateTpl>\n <div class=\"tbl-empty tbl-empty--error\" role=\"alert\">\n <i class=\"pi pi-exclamation-triangle tbl-empty__icon\" aria-hidden=\"true\"></i>\n <p class=\"tbl-empty__title\">{{ 'common_error_load_failed' | translate }}</p>\n <p class=\"tbl-empty__hint\">{{ 'common_error_load_failed_hint' | translate }}</p>\n <button class=\"btn btn-sm tbl-empty__action\" type=\"button\" (click)=\"retry.emit()\">\n <i class=\"pi pi-refresh\" aria-hidden=\"true\"></i>\n {{ 'common_retry' | translate }}\n </button>\n </div>\n</ng-template>\n\n<!-- Header label, shared by the sortable and plain header cells so the\n projection rules stay in one place. -->\n<ng-template #headerLabelTpl let-col=\"col\">\n @if (headerTemplate(col.id); as headerTpl) {\n <ng-container *ngTemplateOutlet=\"headerTpl; context: { $implicit: col, col: col }\"></ng-container>\n } @else if (col.headerKey) {\n <span>{{ col.headerKey | translate }}</span>\n } @else if (col.header) {\n <span>{{ col.header }}</span>\n }\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:block;min-width:0}.tbl thead th.is-sortable{cursor:pointer;-webkit-user-select:none;user-select:none;transition:color var(--t-fast, .14s) var(--ease-out, ease-out)}.tbl thead th.is-sortable:hover{color:var(--text)}.tbl tbody tr.is-row-clickable{cursor:pointer}.tbl-tools{display:flex;gap:6px;align-items:center}.tbl-tool{position:relative}.tbl-tool>.btn.open{color:var(--text);background:var(--paper)}.tbl-tool__menu{position:absolute;top:calc(100% + 8px);inset-inline-end:0;z-index:40;min-width:212px;max-height:340px;overflow-y:auto;padding:6px;display:grid;gap:1px;background:var(--paper-alt);border:1px solid var(--rule);border-radius:var(--r-xl);box-shadow:var(--shadow-3);animation:row-actions-menu-in var(--t-base) var(--ease-spring)}.tbl-tool__menu--wide{min-width:248px}.tbl-tool__menu button{appearance:none;background:transparent;border:0;width:100%;text-align:start;font:inherit;font-size:13.5px;font-weight:600;color:var(--text);display:flex;align-items:center;gap:12px;padding:9px 12px;border-radius:var(--r-md);cursor:pointer;transition:background-color var(--t-fast) var(--ease-out)}.tbl-tool__menu button:hover,.tbl-tool__menu button:focus-visible{background:var(--paper);outline:0}.tbl-tool__menu button>.pi{width:16px;flex:none;font-size:14px;color:var(--muted)}.tbl-tool__menu button.is-active>.pi{color:var(--module, var(--text))}.tbl-tool__label{flex:1 1 auto;min-width:0}.tbl-tool__check{color:var(--module, var(--text))}.tbl-tool__sep{margin:5px 2px;border-top:1px solid var(--rule)}.tbl-wrap[data-density=compact] .tbl tbody td{--tbl-cell-pad-block: 4px;padding-top:var(--tbl-cell-pad-block);padding-bottom:var(--tbl-cell-pad-block)}.tbl-wrap[data-density=comfortable] .tbl tbody td{--tbl-cell-pad-block: 16px;padding-top:var(--tbl-cell-pad-block);padding-bottom:var(--tbl-cell-pad-block)}.mlist{list-style:none;margin:0;padding:0;display:grid}.mrow{border-top:1px solid var(--rule)}.mrow:first-child{border-top:0}.mrow__head{display:flex;align-items:flex-start;gap:var(--s-3);padding:12px var(--s-4);min-height:var(--hit-touch)}.mrow__head.is-tappable{cursor:pointer}.mrow__head.is-tappable:active{background:var(--paper)}.mrow__lead{flex:1;min-width:0;display:grid;gap:2px}.mrow__primary{font-weight:700;font-size:14px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mrow__secondary{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 6px;font-size:12.5px;color:var(--text-mute);min-width:0}.mrow__secondary .mrow__sec-part+.mrow__sec-part:before{content:\"\\b7\";margin-inline-end:6px;color:var(--text-soft)}.mrow__trail{display:flex;align-items:center;justify-content:flex-end;gap:var(--s-2);flex-shrink:0;min-height:20px}.mrow__actions{display:inline-flex}.mrow__detail{margin:0;padding:0 var(--s-4) 12px;display:grid;gap:1px}.mrow__field{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-3);padding:7px 0;border-top:1px solid var(--rule-soft)}.mrow__field dt{font-size:11.5px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--text-mute);flex-shrink:0}.mrow__field dd{margin:0;font-size:13.5px;color:var(--text);text-align:end;min-width:0;overflow-wrap:anywhere}.mrow__more{appearance:none;width:100%;background:transparent;border:0;border-top:1px solid var(--rule-soft);padding:10px;font:inherit;font-size:13px;font-weight:600;color:var(--tenant-500);cursor:pointer}.mlist[data-layout=card]{gap:var(--s-3);padding:var(--s-3) var(--s-3) 0}.mlist[data-layout=card] .mrow{border:1px solid var(--rule);border-radius:var(--r-lg);background:var(--paper-alt);overflow:hidden}.mlist[data-layout=card] .mrow__head{border-bottom:1px solid var(--rule)}.mrow__chevron{font-size:11px;color:var(--text-soft);transition:transform var(--t-fast) var(--ease-out);flex-shrink:0}.mrow__chevron.is-open{transform:rotate(180deg);color:var(--text-mute)}.mrow__head.is-tappable:focus-visible{outline:2px solid var(--tenant-400);outline-offset:-2px}.mrow__more{display:flex;align-items:center;justify-content:center;gap:6px}.tbl-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s-2);padding:var(--s-9) var(--s-4);text-align:center}.tbl-empty__icon{font-size:28px;line-height:1;color:var(--ink-400);margin-block-end:var(--s-1)}.tbl-empty__title{margin:0;font-size:15px;font-weight:600;color:var(--text);max-inline-size:46ch;overflow-wrap:break-word}.tbl-empty__hint{margin:0;font-size:13px;color:var(--ink-600);max-inline-size:52ch;text-wrap:pretty}.tbl-empty__action{margin-block-start:var(--s-3);min-block-size:var(--hit-base)}.tbl-empty--error .tbl-empty__icon{color:var(--st-cancelled-fg)}.mrow--empty{padding:0}.mrow--skeleton{padding:12px var(--s-4)}@media(max-width:767px){.tbl-empty{padding:var(--s-7) var(--s-4)}.tbl-empty__action{min-block-size:var(--hit-touch)}}.pi.is-spinning{animation:ef-export-spin .9s linear infinite}@keyframes ef-export-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.pi.is-spinning{animation:none}}.tbl th.tbl-drag-h,.tbl td.tbl-drag{width:28px;padding-inline:4px 0;text-align:center}.tbl td.tbl-drag{cursor:default}.tbl .tbl-drag__grip{display:inline-flex;align-items:center;justify-content:center;width:22px;height:26px;padding:0;border:0;border-radius:6px;background:transparent;color:var(--text-soft);cursor:grab;touch-action:none;transition:background var(--t-fast, .14s) var(--ease-out, ease-out),color var(--t-fast, .14s) var(--ease-out, ease-out)}.tbl .tbl-drag__grip:hover{background:var(--paper);color:var(--text)}.tbl .tbl-drag__grip:focus-visible{outline:2px solid var(--tenant, var(--rule));outline-offset:1px}.tbl .tbl-drag__grip:active{cursor:grabbing}.tbl .tbl-drag__grip i{font-size:12px}.tbl tbody tr.is-dragging>td{background:var(--paper);opacity:.55}.tbl tbody tr.is-drop-target>td{box-shadow:inset 0 2px 0 var(--tenant, var(--rule))}.tbl.tbl--fixed{table-layout:fixed}.mrow__disclosure{appearance:none;background:transparent;border:0;padding:0;margin:0;display:inline-grid;place-items:center;color:var(--text-mute);cursor:pointer;position:relative}.mrow__disclosure:after{content:\"\";position:absolute;top:50%;left:50%;width:var(--hit-touch);height:var(--hit-touch);transform:translate(-50%,-50%)}.mrow__disclosure:focus-visible{outline:2px solid color-mix(in srgb,var(--ink-active) 40%,transparent);outline-offset:2px;border-radius:var(--r-pill)}@media(pointer:coarse){.mrow__actions+.mrow__disclosure{margin-inline-start:calc((var(--hit-touch) - 11px) / 2 - var(--s-2) + 1px)}}.tbl tbody tr.is-disabled{cursor:not-allowed}.tbl tbody tr.is-disabled td,.mrow.is-disabled .mrow__head{opacity:.5}.mrow.is-disabled .mrow__head{cursor:not-allowed}.tbl-head:has(>.count:empty):has(>.tbl-tools:empty){display:none}\n"] }]
|
|
6103
6381
|
}], propDecorators: { mobileLayout: [{ type: i0.Input, args: [{ isSignal: true, alias: "mobileLayout", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], trackByField: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackByField", required: false }] }], pageNumber: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageNumber", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], totalCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalCount", required: false }] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], hidePager: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidePager", required: false }] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], errorMsg: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMsg", required: false }] }], loadingKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingKey", required: false }] }], rowDoubleClickable: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowDoubleClickable", required: false }] }], rowPressed: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowPressed", required: false }] }], rowDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowDisabled", required: false }] }], hasActionsColumn: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasActionsColumn", required: false }] }], rowActionsContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActionsContext", required: false }] }], showViewAction: [{ type: i0.Input, args: [{ isSignal: true, alias: "showViewAction", required: false }] }], showEditAction: [{ type: i0.Input, args: [{ isSignal: true, alias: "showEditAction", required: false }] }], showDuplicateAction: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDuplicateAction", required: false }] }], showDeleteAction: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDeleteAction", required: false }] }], rowActionState: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActionState", required: false }] }], showDensityControl: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDensityControl", required: false }] }], showColumnPicker: [{ type: i0.Input, args: [{ isSignal: true, alias: "showColumnPicker", required: false }] }], showExportControl: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExportControl", required: false }] }], exporting: [{ type: i0.Input, args: [{ isSignal: true, alias: "exporting", required: false }] }], screenContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "screenContext", required: false }] }], searchTerm: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchTerm", required: false }] }], tableKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableKey", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], pageSizeChange: [{ type: i0.Output, args: ["pageSizeChange"] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], rowDoubleClick: [{ type: i0.Output, args: ["rowDoubleClick"] }], rowAction: [{ type: i0.Output, args: ["rowAction"] }], exportRequest: [{ type: i0.Output, args: ["exportRequest"] }], clearSearch: [{ type: i0.Output, args: ["clearSearch"] }], retry: [{ type: i0.Output, args: ["retry"] }], bodyTemplates: [{
|
|
6104
6382
|
type: ContentChildren,
|
|
6105
6383
|
args: [EfColumnTemplateDirective]
|
|
@@ -6197,6 +6475,11 @@ class EfOrderBuilderComponent {
|
|
|
6197
6475
|
* desktop width and inside the drawer below that or in focus mode.
|
|
6198
6476
|
*/
|
|
6199
6477
|
auditEntries = input([], ...(ngDevMode ? [{ debugName: "auditEntries" }] : /* istanbul ignore next */ []));
|
|
6478
|
+
/** Passed to the history's `valueLists`: field path to reference items, so
|
|
6479
|
+
* raw statuses show as translated labels. */
|
|
6480
|
+
auditValueLists = input(null, ...(ngDevMode ? [{ debugName: "auditValueLists" }] : /* istanbul ignore next */ []));
|
|
6481
|
+
/** Passed to the history's `noteFormatter`: returns the text for a note, or null for the raw note. */
|
|
6482
|
+
auditNoteFormatter = input(null, ...(ngDevMode ? [{ debugName: "auditNoteFormatter" }] : /* istanbul ignore next */ []));
|
|
6200
6483
|
openCatalogue = output();
|
|
6201
6484
|
orderChanged = output();
|
|
6202
6485
|
orderValidated = output();
|
|
@@ -6674,7 +6957,7 @@ class EfOrderBuilderComponent {
|
|
|
6674
6957
|
return OrderEntityHelper.calculateTotals(fee > 0 ? { ...order, shippingTotal: fee } : order);
|
|
6675
6958
|
}
|
|
6676
6959
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfOrderBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6677
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfOrderBuilderComponent, isStandalone: true, selector: "ef-order-builder", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, order: { classPropertyName: "order", publicName: "order", isSignal: true, isRequired: true, transformFunction: null }, products: { classPropertyName: "products", publicName: "products", isSignal: true, isRequired: true, transformFunction: null }, customers: { classPropertyName: "customers", publicName: "customers", isSignal: true, isRequired: true, transformFunction: null }, recipient: { classPropertyName: "recipient", publicName: "recipient", isSignal: true, isRequired: false, transformFunction: null }, defaultCallingCode: { classPropertyName: "defaultCallingCode", publicName: "defaultCallingCode", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, countGroupLabels: { classPropertyName: "countGroupLabels", publicName: "countGroupLabels", isSignal: true, isRequired: false, transformFunction: null }, productKeyField: { classPropertyName: "productKeyField", publicName: "productKeyField", isSignal: true, isRequired: false, transformFunction: null }, priceList: { classPropertyName: "priceList", publicName: "priceList", isSignal: true, isRequired: false, transformFunction: null }, backendErrors: { classPropertyName: "backendErrors", publicName: "backendErrors", isSignal: true, isRequired: false, transformFunction: null }, shippingTotal: { classPropertyName: "shippingTotal", publicName: "shippingTotal", isSignal: true, isRequired: false, transformFunction: null }, auditEntries: { classPropertyName: "auditEntries", publicName: "auditEntries", isSignal: true, isRequired: false, transformFunction: null }, focusMode: { classPropertyName: "focusMode", publicName: "focusMode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { order: "orderChange", openCatalogue: "openCatalogue", orderChanged: "orderChanged", orderValidated: "orderValidated", productsAdded: "productsAdded", focusMode: "focusModeChange" }, viewQueries: [{ propertyName: "typeahead", first: true, predicate: EfProductTypeaheadComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"order-builder\"\n [class.ob-collapsed]=\"focusMode()\"\n [class.ob-rail-open]=\"railOpen()\">\n <div class=\"ob-grid\">\n <div class=\"ob-main\">\n <!-- Order header: customer + date + express add (one card) -->\n <ef-card\n titleKey=\"order_builder.section_order\"\n [metaKey]=\"readonly() ? '' : 'order_builder.quick_add_hint'\"\n [overflowVisible]=\"true\">\n <div class=\"ob-order-head\">\n <div class=\"form-grid\">\n @if (recipient(); as r) {\n <!-- No client to pick (a storefront order): who it goes to,\n read-only, in the customer's place. -->\n <div class=\"field col-6\">\n <ef-label labelKey=\"order_builder.recipient\" />\n <div\n class=\"ob-recipient\"\n role=\"group\"\n [attr.aria-label]=\"'order_builder.recipient' | translate\">\n <span class=\"ob-recipient-name\">{{ r.name }}</span>\n @if (recipientTel(); as tel) {\n <a class=\"ob-recipient-phone\" [href]=\"tel\" dir=\"ltr\">{{ r.phone }}</a>\n }\n @if (r.city) {\n <span class=\"ob-recipient-city\">{{ r.city }}</span>\n }\n @if (r.address) {\n <span class=\"ob-recipient-address\" [title]=\"r.address\">{{ r.address }}</span>\n }\n </div>\n </div>\n } @else {\n <div class=\"field col-6\">\n <ef-label labelKey=\"label.customer\" for=\"ob-cust\" [required]=\"true\" />\n <ef-select\n inputId=\"ob-cust\"\n [options]=\"customers()\"\n optionLabel=\"companyName\"\n optionValue=\"customerId\"\n [ngModel]=\"order().customerId\"\n (ngModelChange)=\"updateCustomer($event)\"\n [disabled]=\"readonly()\"\n [showClear]=\"true\"\n [filter]=\"true\"\n filterBy=\"companyName\"\n filterPlaceholderKey=\"label.search_customer\"\n appendTo=\"body\"\n placeholderKey=\"label.select_customer\" />\n </div>\n }\n <div class=\"field col-3\">\n <ef-label labelKey=\"label.order_date\" for=\"ob-date\" [required]=\"true\" />\n <ef-datepicker-advanced\n mode=\"field\"\n selectionMode=\"single\"\n inputId=\"ob-date\"\n placeholderKey=\"date_select_date\"\n [ngModel]=\"order().orderDate\"\n (ngModelChange)=\"updateOrderDate($event)\"\n [disabled]=\"readonly()\" />\n </div>\n </div>\n\n @if (!readonly()) {\n <div class=\"qa-grid\">\n <div class=\"qa-combo-wrap\">\n <ef-label labelKey=\"label.article\" />\n <ef-product-typeahead\n [products]=\"products()\"\n [productKeyField]=\"productKeyField()\"\n placeholderKey=\"label.search_product\"\n [currency]=\"currency()\"\n [locale]=\"locale()\"\n [priceList]=\"priceList()\"\n (productSelect)=\"onTypeaheadSelect($event)\"\n [showVariants]=\"false\" />\n </div>\n <div class=\"qa-price\">\n <ef-label labelKey=\"label.sale_price\" />\n <ef-inputnumber\n variant=\"comptoir\"\n [value]=\"quickAddPrice()\"\n (valueChangeEvent)=\"quickAddPrice.set($event ?? 0)\"\n [minFractionDigits]=\"2\"\n [maxFractionDigits]=\"2\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\" />\n </div>\n <div class=\"qa-qtyf\">\n <ef-label labelKey=\"label.quantity\" />\n <ef-inputnumber\n variant=\"comptoir\"\n [ngModel]=\"quickAddQuantity()\"\n (ngModelChange)=\"quickAddQuantity.set($event)\"\n [min]=\"0\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\"\n />\n </div>\n <div class=\"qa-go\">\n <!-- Ghost while the preconditions are unmet, tenant once the\n line is addable. A disabled control painted in a faded\n tenant fill reads as an enabled button that lost its\n colour, which is the greyed-out accent the design brief\n bans; ghost reads as \"not yet\". -->\n <ef-button\n labelKey=\"action.add\"\n icon=\"pi pi-plus\"\n variant=\"comptoir\"\n size=\"base\"\n [severity]=\"quickAddDisabled() ? 'ghost' : 'tenant'\"\n [disabled]=\"quickAddDisabled()\"\n (clickEvent)=\"addQuickProduct()\" />\n </div>\n </div>\n }\n </div>\n </ef-card>\n\n <!-- Lines.\n\n ef-data-card owns the panel here instead of sitting inside an\n ef-card: `.tbl-wrap` already draws a bordered, rounded surface, so\n nesting it in another one would be a card inside a card. The title\n and the card's actions move into the head slots it provides.\n\n The columns are declared in TS (`lineColumns`) so discount and tax\n follow the document config, and so one definition drives both the\n desktop table and the mobile cards. Only the article column is\n elastic; every other column holds a number of known magnitude and\n gets a fixed track, which keeps the decimals aligned down the\n table rather than shifting with the longest description. -->\n <ef-data-card\n [columns]=\"lineColumns()\"\n [rows]=\"orderLines()\"\n trackByField=\"id\"\n [hidePager]=\"true\"\n defaultDensity=\"compact\"\n [reorderable]=\"!readonly()\"\n [fixedLayout]=\"true\"\n (rowReorder)=\"reorderLine($event)\"\n mobileLayout=\"card\">\n\n <div tbl-head-info class=\"ob-lines-head\">\n <span class=\"strong\">{{ 'order_builder.lines' | translate }}</span>\n <span class=\"meta\">\u00B7 {{ orderLines().length }}</span>\n </div>\n\n <div tbl-head-actions class=\"head-actions\">\n @if (config().showCatalogue && !readonly()) {\n <ef-button\n labelKey=\"action.open_catalogue\"\n icon=\"pi pi-box\"\n severity=\"secondary\"\n [text]=\"true\"\n (clickEvent)=\"handleOpenCatalogue()\" />\n }\n <ef-button\n [labelKey]=\"focusMode() ? 'order_builder.show_summary' : 'order_builder.fullscreen'\"\n icon=\"pi pi-window-maximize\"\n severity=\"secondary\"\n [text]=\"true\"\n (clickEvent)=\"toggleFocus()\" />\n </div>\n\n <span empty-state>{{ 'order_builder.empty' | translate }}</span>\n <span empty-state-mobile>{{ 'order_builder.empty' | translate }}</span>\n\n <ng-template efColumnTemplate=\"product\" let-item let-i=\"index\">\n <div class=\"p\">\n <span class=\"ln\">{{ i + 1 }}</span>\n <span class=\"pp\">\n <span class=\"nm\">{{ item.productDescription }}</span>\n </span>\n </div>\n </ng-template>\n\n <ng-template efColumnTemplate=\"price\" let-item>\n @if (!readonly() && isEditingCell(item, 'price')) {\n <ef-inputnumber\n [(ngModel)]=\"item.productUnitPrice\"\n (ngModelChange)=\"onFieldChange(item)\"\n [showClear]=\"false\"\n [autofocus]=\"true\"\n (blurEvent)=\"stopEditCell()\"\n (keyDownEvent)=\"onEditCellKeydown($event)\"\n [min]=\"0\"\n [minFractionDigits]=\"2\"\n [maxFractionDigits]=\"2\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\" />\n } @else {\n <button type=\"button\" class=\"ln-val\" [disabled]=\"readonly()\" (click)=\"startEditCell(item, 'price')\">\n {{ item.productUnitPrice | number: '1.2-2' : locale() }}\n </button>\n }\n </ng-template>\n\n <ng-template efColumnTemplate=\"qty\" let-item>\n @if (!readonly() && isEditingCell(item, 'qty')) {\n <ef-inputnumber\n [(ngModel)]=\"item.quantity\"\n (ngModelChange)=\"onFieldChange(item)\"\n [showClear]=\"false\"\n [autofocus]=\"true\"\n (blurEvent)=\"stopEditCell()\"\n (keyDownEvent)=\"onEditCellKeydown($event)\"\n [min]=\"0\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\" />\n } @else {\n <button type=\"button\" class=\"ln-val\" [disabled]=\"readonly()\" (click)=\"startEditCell(item, 'qty')\">\n {{ item.quantity | number: '1.0-4' : locale() }}\n </button>\n }\n </ng-template>\n\n <ng-template efColumnTemplate=\"discount\" let-item>\n @if (!readonly() && isEditingCell(item, 'discount')) {\n <ef-inputnumber\n [(ngModel)]=\"item.discount\"\n (ngModelChange)=\"onFieldChange(item)\"\n [showClear]=\"false\"\n [autofocus]=\"true\"\n (blurEvent)=\"stopEditCell()\"\n (keyDownEvent)=\"onEditCellKeydown($event)\"\n [min]=\"0\"\n [max]=\"100\"\n [maxFractionDigits]=\"2\"\n suffix=\" %\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\" />\n } @else {\n <button type=\"button\" class=\"ln-val\" [disabled]=\"readonly()\" (click)=\"startEditCell(item, 'discount')\">\n {{ item.discount | number: '1.0-2' : locale() }} %\n </button>\n }\n </ng-template>\n\n <ng-template efColumnTemplate=\"tax\" let-item>\n @if (!readonly() && isEditingCell(item, 'tva')) {\n <ef-inputnumber\n [(ngModel)]=\"item.taxRate\"\n (ngModelChange)=\"onFieldChange(item)\"\n [showClear]=\"false\"\n [autofocus]=\"true\"\n (blurEvent)=\"stopEditCell()\"\n (keyDownEvent)=\"onEditCellKeydown($event)\"\n [min]=\"0\"\n [max]=\"100\"\n [maxFractionDigits]=\"2\"\n suffix=\" %\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\" />\n } @else {\n <button type=\"button\" class=\"ln-val\" [disabled]=\"readonly()\" (click)=\"startEditCell(item, 'tva')\">\n {{ (item.taxRate ?? 0) | number: '1.0-2' : locale() }} %\n </button>\n }\n </ng-template>\n\n <ng-template efColumnTemplate=\"subtotal\" let-item>\n {{ calculateSubtotal(item) | number: '1.2-2' : locale() }}\n </ng-template>\n\n <ng-template efColumnTemplate=\"remove\" let-item>\n <ef-button\n icon=\"pi pi-trash\"\n ariaLabelKey=\"common_delete\"\n severity=\"danger\"\n [text]=\"true\"\n styleClass=\"ob-del\"\n (clickEvent)=\"removeOrderLine(item.id)\" />\n </ng-template>\n </ef-data-card>\n\n </div>\n\n <!-- CONTEXT RAIL (1/3) -->\n <aside class=\"ob-rail\">\n <!-- Totaux (top) -->\n <ef-card titleKey=\"order_builder.totals\" [meta]=\"currency()\">\n <div class=\"ob-totals\">\n <div class=\"row\">\n <span class=\"lbl\">{{ 'order_builder.gross' | translate }}</span>\n <span class=\"v\">{{ grossTotal() | number: '1.2-2' : locale() }}</span>\n </div>\n <div class=\"row discount\">\n <span class=\"lbl\">{{ 'order_builder.discount' | translate }}</span>\n <span class=\"v\">\u2212{{ discountTotal() | number: '1.2-2' : locale() }}</span>\n </div>\n <div class=\"row\">\n <span class=\"lbl\">{{ 'order_builder.tax' | translate }}</span>\n <span class=\"v\">{{ tvaTotal() | number: '1.2-2' : locale() }}</span>\n </div>\n @if (shippingTotal() > 0) {\n <div class=\"row shipping\">\n <span class=\"lbl\">{{ 'order_builder.shipping' | translate }}</span>\n <span class=\"v\">{{ shippingTotal() | number: '1.2-2' : locale() }}</span>\n </div>\n }\n <hr />\n <div class=\"row grand\">\n <span class=\"lbl\">{{ 'order_builder.grand_total' | translate }}</span>\n <span class=\"v\">{{ finalTotal() | number: '1.2-2' : locale() }}<span class=\"cur\">{{ currency() }}</span></span>\n </div>\n </div>\n </ef-card>\n\n <!-- R\u00E9capitulatif par format -->\n @if (productsSummary().items.length > 0) {\n <ef-card titleKey=\"order_builder.recap\" metaKey=\"order_builder.auto\">\n <div class=\"ob-recap\">\n <div class=\"cols\">\n @for (col of recapColumns(); track $index) {\n <div class=\"grp\">\n @for (seg of col; track seg.code) {\n <div class=\"seg\">\n <span class=\"fmt\">{{ seg.label }}</span>\n <span class=\"n\">{{ seg.count | number: '1.0-0' : locale() }}</span>\n </div>\n }\n </div>\n }\n </div>\n <div class=\"tot\">\n <span class=\"l\">{{ 'order_builder.total_units' | translate }}</span>\n <span class=\"n\">{{ productsSummary().totalCount | number: '1.0-0' : locale() }}</span>\n </div>\n </div>\n </ef-card>\n }\n\n <!-- Host cards that belong beside the totals (delivery, notes): a column\n at desktop width, below the totals in the drawer otherwise. -->\n <ng-content select=\"[obRail]\" />\n\n <!-- Historique (optional, component-owned) -->\n @if (hasAudit()) {\n <ef-change-history [entries]=\"auditEntries()\" />\n }\n </aside>\n </div>\n\n <!-- Floating summary FAB + backdrop -->\n <button type=\"button\" class=\"ob-backdrop\" aria-hidden=\"true\" tabindex=\"-1\" (click)=\"toggleRail(false)\"></button>\n <div class=\"ob-fab\" role=\"status\">\n <div class=\"ft\">\n <span class=\"k\">{{ 'order_builder.total_units' | translate }} \u00B7 {{ productsSummary().totalCount }}</span>\n <span class=\"vv\">{{ finalTotal() | number: '1.2-2' : locale() }}<span class=\"cur\">{{ currency() }}</span></span>\n </div>\n <button type=\"button\" class=\"open\" (click)=\"toggleRail(true)\">\n {{ 'order_builder.summary' | translate }}\n </button>\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block}:host .order-builder{display:grid;gap:var(--s-3)}:host .order-builder .head-actions{display:flex;align-items:center;gap:6px}:host .order-builder .form-grid{display:grid;gap:var(--s-3);grid-template-columns:repeat(12,minmax(0,1fr))}:host .order-builder .form-grid>.col-3{grid-column:span 3}:host .order-builder .form-grid>.col-4{grid-column:span 4}:host .order-builder .form-grid>.col-5{grid-column:span 5}:host .order-builder .form-grid>.col-6{grid-column:span 6}@media(max-width:767px){:host .order-builder .form-grid>[class*=col-]{grid-column:span 12}}:host .order-builder .field{display:grid;gap:4px}:host .order-builder .field>label{font-size:11px;font-weight:700;color:var(--text-mute);text-transform:uppercase;letter-spacing:.06em}:host .order-builder .ob-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s-3);align-items:start}@media(min-width:1280px){:host .order-builder .ob-grid{grid-template-columns:minmax(0,2fr) minmax(0,1fr)}}:host .order-builder .ob-main{display:grid;gap:var(--s-3);min-width:0}:host .order-builder .ob-rail{display:grid;gap:var(--s-3);align-content:start}@media(min-width:1280px){:host .order-builder .ob-rail{position:sticky;top:44px}}:host .order-builder .ob-order-head{display:grid;gap:var(--s-4)}:host .order-builder .ob-recipient{display:flex;flex-wrap:wrap;align-items:baseline;column-gap:var(--s-3);row-gap:2px;min-width:0;font-size:13px;line-height:1.45;color:var(--text-soft)}:host .order-builder .ob-recipient>*{flex:0 1 auto;min-width:0;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;unicode-bidi:plaintext}:host .order-builder .ob-recipient-name{font-weight:600;color:var(--text)}:host .order-builder .ob-recipient-phone{color:var(--tenant-700);text-decoration:none;font-variant-numeric:tabular-nums}:host .order-builder .ob-recipient-phone:hover{text-decoration:underline}:host .order-builder .ob-recipient-address{flex-basis:100%;color:var(--text-mute)}:host .order-builder .qa-grid{display:flex;flex-wrap:wrap;gap:10px;align-items:end;padding-top:var(--s-3);border-top:1px solid var(--rule-soft)}:host .order-builder .qa-combo-wrap{flex:1 1 260px;min-width:0}:host .order-builder .qa-price{flex:0 1 132px}:host .order-builder .qa-qtyf{flex:0 0 132px}:host .order-builder .qa-go{flex:0 0 auto}@media(max-width:560px){:host .order-builder .qa-combo-wrap,:host .order-builder .qa-price,:host .order-builder .qa-qtyf,:host .order-builder .qa-go{flex:1 1 100%}}:host .order-builder ef-data-card{font-size:13px}:host .order-builder ef-data-card .p{display:flex;align-items:baseline;gap:9px;line-height:1.25}:host .order-builder ef-data-card .p .ln{flex:0 0 auto;min-width:15px;text-align:end;font-family:var(--font-mono);font-size:10.5px;font-weight:700;color:var(--text-soft)}:host .order-builder ef-data-card .p .pp{display:grid;gap:1px;min-width:0}:host .order-builder ef-data-card .p .nm{font-weight:600;letter-spacing:-.005em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .order-builder ef-data-card .p .nm em{font-style:normal;color:var(--text-mute);font-weight:600}:host .order-builder ef-data-card ef-inputnumber,:host .order-builder ef-data-card ef-inputnumber .ef-field-control,:host .order-builder ef-data-card ef-inputnumber input{width:100%;min-width:0}:host .order-builder ef-data-card .num,:host .order-builder ef-data-card .total{font-family:var(--font-mono);font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums}:host .order-builder ef-data-card .ln-val{appearance:none;width:100%;margin:0;padding:5px 8px;border:1px solid transparent;border-radius:7px;background:transparent;font:inherit;font-family:var(--font-mono);font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums;color:inherit;text-align:end;cursor:text;transition:background .12s ease,border-color .12s ease}:host .order-builder ef-data-card .ln-val:hover:not(:disabled){background:var(--paper);border-color:var(--rule)}:host .order-builder ef-data-card .ln-val:focus-visible{outline:none;background:var(--paper);border-color:var(--tenant, var(--rule))}:host .order-builder ef-data-card .ln-val:disabled{cursor:default;background:transparent;border-color:transparent}:host .order-builder ef-data-card .total{font-family:var(--font-display);font-variation-settings:\"opsz\" 12,\"wght\" 700,\"wdth\" 85;font-size:13.5px}:host .order-builder ef-data-card .cur{font-size:9.5px;color:var(--text-soft);margin-inline-start:3px}:host .order-builder .ob-lines-head{display:flex;align-items:baseline;gap:6px;min-width:0}:host .order-builder .ob-lines-head .strong{font-weight:700;color:var(--text)}:host .order-builder .ob-lines-head .meta{color:var(--text-soft)}:host .order-builder .ob-del{color:var(--text-soft)}:host .order-builder .ob-del:hover{color:var(--st-cancelled-fg);border-color:var(--st-cancelled-bg)}:host .order-builder .ob-totals{display:grid;gap:6px}:host .order-builder .ob-totals .row{display:flex;justify-content:space-between;align-items:baseline;gap:14px}:host .order-builder .ob-totals .row .lbl{font-size:12px;font-weight:600;color:var(--text-mute)}:host .order-builder .ob-totals .row .v{font-family:var(--font-mono);font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums;font-weight:700;font-size:12.5px}:host .order-builder .ob-totals .row.discount .v{color:var(--st-pending-fg)}:host .order-builder .ob-totals hr{border:0;border-top:1px solid var(--rule);margin:5px 0}:host .order-builder .ob-totals .grand .lbl{font-family:var(--font-display);font-variation-settings:\"opsz\" 12,\"wght\" 700,\"wdth\" 85;font-size:13.5px;color:var(--text)}:host .order-builder .ob-totals .grand .v{font-family:var(--font-display);font-variation-settings:\"opsz\" 96,\"wght\" 700,\"wdth\" 80;font-size:26px;letter-spacing:-.03em;color:var(--text)}:host .order-builder .ob-totals .grand .cur{font-family:var(--font-mono);font-size:.4em;color:var(--text-mute);margin-inline-start:4px}:host .order-builder .ob-recap .cols{display:grid;grid-template-columns:1fr 1fr}:host .order-builder .ob-recap .grp{display:flex;flex-direction:column;gap:3px;min-width:0}:host .order-builder .ob-recap .grp:first-child{padding-inline-end:14px}:host .order-builder .ob-recap .grp+.grp{padding-inline-start:14px;border-inline-start:1px solid var(--rule)}:host .order-builder .ob-recap .seg{display:flex;justify-content:space-between;align-items:baseline;gap:8px}:host .order-builder .ob-recap .seg .fmt{font-size:11.5px;font-weight:700;color:var(--text);letter-spacing:-.005em;min-width:0}:host .order-builder .ob-recap .seg .fmt:after{content:\":\";color:var(--text-soft)}:host .order-builder .ob-recap .seg .n{font-family:var(--font-mono);font-weight:700;font-size:12px;color:var(--text);font-variant-numeric:tabular-nums;flex-shrink:0}:host .order-builder .ob-recap .tot{display:flex;justify-content:space-between;align-items:baseline;margin-top:12px;padding-top:10px;border-top:1px solid var(--rule)}:host .order-builder .ob-recap .tot .l{font-weight:700;font-size:12px}:host .order-builder .ob-recap .tot .n{font-family:var(--font-display);font-variation-settings:\"opsz\" 12,\"wght\" 700,\"wdth\" 85;font-size:18px;font-variant-numeric:tabular-nums}:host .order-builder .ob-fab{display:none;position:fixed;inset-inline-end:20px;bottom:calc(var(--app-bottom-bar, 0px) + env(safe-area-inset-bottom,0px) + 20px);z-index:30;align-items:center;gap:10px;box-sizing:border-box;block-size:var(--ef-order-fab-h);--ob-fab-ground: var(--ink-active);--ob-fab-on: var(--paper);background:var(--ob-fab-ground);color:var(--ob-fab-on);border-radius:var(--r-pill);padding-block:0;padding-inline:16px 10px;box-shadow:var(--shadow-3)}:host .order-builder .ob-fab .ft{display:grid;line-height:1.1}:host .order-builder .ob-fab .ft .k{font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;opacity:.65}:host .order-builder .ob-fab .ft .vv{font-family:var(--font-display);font-variation-settings:\"opsz\" 12,\"wght\" 700,\"wdth\" 82;font-size:16px}:host .order-builder .ob-fab .ft .vv .cur{font-family:var(--font-mono);font-size:.6em;opacity:.7;margin-inline-start:3px}:host .order-builder .ob-fab .open{appearance:none;cursor:pointer;border:0;background:color-mix(in srgb,var(--ob-fab-on) 14%,transparent);color:var(--ob-fab-on);font:inherit;font-size:12px;font-weight:700;border-radius:var(--r-pill);padding:8px 14px;display:inline-flex;align-items:center;gap:6px}:host .order-builder .ob-fab .open:hover{background:color-mix(in srgb,var(--ob-fab-on) 24%,transparent)}:host .order-builder .ob-fab .open:focus-visible{outline:2px solid var(--ob-fab-on);outline-offset:2px}:host .order-builder .ob-backdrop{display:none;position:fixed;inset:0;z-index:38;background:#0f11156b;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}:host .order-builder.ob-collapsed .ob-grid{grid-template-columns:minmax(0,1fr)}:host .order-builder.ob-collapsed .ob-fab{display:flex}:host .order-builder.ob-collapsed .ob-rail{position:fixed;top:0;inset-inline-end:0;height:100dvh;width:min(360px,92vw);z-index:40;background:var(--paper);border-inline-start:1px solid var(--rule);box-shadow:var(--shadow-3);padding:14px;overflow:auto;gap:var(--s-3);transform:translate(101%);transition:transform var(--t-base) var(--ease-out)}[dir=rtl] :host .order-builder.ob-collapsed .ob-rail{transform:translate(-101%)}:host .order-builder.ob-collapsed.ob-rail-open .ob-rail{transform:none}:host .order-builder.ob-collapsed.ob-rail-open .ob-backdrop{display:block}@media(max-width:1279px){:host .order-builder .ob-grid{grid-template-columns:minmax(0,1fr)}:host .order-builder .ob-fab{display:flex}:host .order-builder .ob-rail{position:fixed;top:0;inset-inline-end:0;height:100dvh;width:min(360px,92vw);z-index:40;background:var(--paper);border-inline-start:1px solid var(--rule);box-shadow:var(--shadow-3);padding:14px;overflow:auto;gap:var(--s-3);transform:translate(101%);transition:transform var(--t-base) var(--ease-out)}[dir=rtl] :host .order-builder .ob-rail{transform:translate(-101%)}:host .order-builder.ob-rail-open .ob-rail{transform:none}:host .order-builder.ob-rail-open .ob-backdrop{display:block}}:host ef-data-card .nm,:host ef-data-card .num,:host ef-data-card .ln-val,:host .ob-totals .v{unicode-bidi:plaintext}@media(pointer:coarse){:host .order-builder .ob-fab .open{min-block-size:var(--hit-touch)}:host ef-data-card .ln-val{min-block-size:var(--hit-field)}:host .order-builder .ob-recipient-phone{line-height:var(--hit-touch)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfSelectComponent, selector: "ef-select", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "dataKey", "group", "optionGroupLabel", "optionGroupChildren", "size", "fluid", "inline", "appendTo", "multiple", "filter", "filterBy", "filterPlaceholder", "filterPlaceholderKey", "autofocusFilter", "autofocus", "editable", "loading", "virtualScroll", "virtualScrollItemSize", "scrollHeight", "multiDisplay", "showToggleAll", "emptyMessage", "emptyMessageKey", "emptyFilterMessage", "emptyFilterMessageKey", "styleClass", "panelStyleClass", "filterByKeys", "variant", "value"], outputs: ["selectionChangeEvent", "filterChangeEvent", "panelShowEvent", "panelHideEvent"] }, { kind: "component", type: EfDatepickerAdvancedComponent, selector: "ef-datepicker-advanced", inputs: ["triggerLabelKey", "triggerLabel", "presets", "activePreset", "start", "end", "allowCustomRange", "allowAllTime", "mode", "selectionMode", "variant", "showTime", "valueFormat"], outputs: ["rangeChange", "opened", "closed"] }, { kind: "component", type: EfInputNumberComponent, selector: "ef-inputnumber", inputs: ["value", "invalid", "format", "showButtons", "buttonLayout", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "prefix", "suffix", "currency", "currencyDisplay", "locale", "localeMatcher", "mode", "useGrouping", "minFractionDigits", "maxFractionDigits", "min", "max", "step", "allowEmpty", "inputSize", "size", "maxlength", "minlength", "pattern", "tabindex", "title", "ariaLabelledBy", "inputStyle", "inputStyleClass", "style", "styleClass", "primeNgVariant", "autofocus", "autocomplete", "fluid", "inline", "variant", "pt", "ptOptions", "dt", "unstyled"], outputs: ["inputEvent", "valueChangeEvent", "focusEvent", "blurEvent", "keyDownEvent", "clearEvent"] }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "for", "styleClass", "required"] }, { kind: "component", type: EfButtonComponent, selector: "ef-button", inputs: ["label", "labelKey", "icon", "ariaLabelKey", "variant", "severity", "size", "disabled", "rounded", "outlined", "raised", "text", "loading", "styleClass", "type", "badge", "badgeSeverity", "tooltip", "tooltipKey", "tooltipPosition", "autofocus"], outputs: ["clickEvent"] }, { kind: "component", type: EfCardComponent, selector: "ef-card", inputs: ["titleKey", "title", "metaKey", "meta", "tone", "forceHead", "hasFoot", "collapsible", "overflowVisible", "loading", "skeleton", "skeletonRows", "skeletonHeight", "collapsed"], outputs: ["collapsedChange"] }, { kind: "component", type: EfChangeHistoryComponent, selector: "ef-change-history", inputs: ["entries", "titleKey", "emptyKey", "limit", "previewLimit", "collapsible", "startCollapsed"] }, { kind: "component", type: EfProductTypeaheadComponent, selector: "ef-product-typeahead", inputs: ["products", "productKeyField", "placeholderKey", "disabled", "currency", "locale", "showVariants", "priceList"], outputs: ["productSelect"] }, { kind: "component", type: EfDataCardComponent, selector: "ef-data-card", inputs: ["mobileLayout", "columns", "rows", "trackByField", "pageNumber", "pageSize", "totalCount", "pageSizeOptions", "hidePager", "sort", "loading", "errorMsg", "loadingKey", "rowDoubleClickable", "rowPressed", "rowDisabled", "hasActionsColumn", "rowActionsContext", "showViewAction", "showEditAction", "showDuplicateAction", "showDeleteAction", "rowActionState", "showDensityControl", "showColumnPicker", "showExportControl", "exporting", "screenContext", "searchTerm", "tableKey", "defaultDensity", "reorderable", "fixedLayout"], outputs: ["pageChange", "pageSizeChange", "sortChange", "rowDoubleClick", "rowAction", "exportRequest", "clearSearch", "retry", "rowReorder"] }, { kind: "directive", type: EfColumnTemplateDirective, selector: "[efColumnTemplate]", inputs: ["efColumnTemplate"] }, { kind: "pipe", type: i1$3.DecimalPipe, name: "number" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
6960
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfOrderBuilderComponent, isStandalone: true, selector: "ef-order-builder", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, order: { classPropertyName: "order", publicName: "order", isSignal: true, isRequired: true, transformFunction: null }, products: { classPropertyName: "products", publicName: "products", isSignal: true, isRequired: true, transformFunction: null }, customers: { classPropertyName: "customers", publicName: "customers", isSignal: true, isRequired: true, transformFunction: null }, recipient: { classPropertyName: "recipient", publicName: "recipient", isSignal: true, isRequired: false, transformFunction: null }, defaultCallingCode: { classPropertyName: "defaultCallingCode", publicName: "defaultCallingCode", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, countGroupLabels: { classPropertyName: "countGroupLabels", publicName: "countGroupLabels", isSignal: true, isRequired: false, transformFunction: null }, productKeyField: { classPropertyName: "productKeyField", publicName: "productKeyField", isSignal: true, isRequired: false, transformFunction: null }, priceList: { classPropertyName: "priceList", publicName: "priceList", isSignal: true, isRequired: false, transformFunction: null }, backendErrors: { classPropertyName: "backendErrors", publicName: "backendErrors", isSignal: true, isRequired: false, transformFunction: null }, shippingTotal: { classPropertyName: "shippingTotal", publicName: "shippingTotal", isSignal: true, isRequired: false, transformFunction: null }, auditEntries: { classPropertyName: "auditEntries", publicName: "auditEntries", isSignal: true, isRequired: false, transformFunction: null }, auditValueLists: { classPropertyName: "auditValueLists", publicName: "auditValueLists", isSignal: true, isRequired: false, transformFunction: null }, auditNoteFormatter: { classPropertyName: "auditNoteFormatter", publicName: "auditNoteFormatter", isSignal: true, isRequired: false, transformFunction: null }, focusMode: { classPropertyName: "focusMode", publicName: "focusMode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { order: "orderChange", openCatalogue: "openCatalogue", orderChanged: "orderChanged", orderValidated: "orderValidated", productsAdded: "productsAdded", focusMode: "focusModeChange" }, viewQueries: [{ propertyName: "typeahead", first: true, predicate: EfProductTypeaheadComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"order-builder\"\n [class.ob-collapsed]=\"focusMode()\"\n [class.ob-rail-open]=\"railOpen()\">\n <div class=\"ob-grid\">\n <div class=\"ob-main\">\n <!-- Order header: customer + date + express add (one card) -->\n <ef-card\n titleKey=\"order_builder.section_order\"\n [metaKey]=\"readonly() ? '' : 'order_builder.quick_add_hint'\"\n [overflowVisible]=\"true\">\n <div class=\"ob-order-head\">\n <div class=\"form-grid\">\n @if (recipient(); as r) {\n <!-- No client to pick (a storefront order): who it goes to,\n read-only, in the customer's place. -->\n <div class=\"field col-6\">\n <ef-label labelKey=\"order_builder.recipient\" />\n <div\n class=\"ob-recipient\"\n role=\"group\"\n [attr.aria-label]=\"'order_builder.recipient' | translate\">\n <span class=\"ob-recipient-name\">{{ r.name }}</span>\n @if (recipientTel(); as tel) {\n <a class=\"ob-recipient-phone\" [href]=\"tel\" dir=\"ltr\">{{ r.phone }}</a>\n }\n @if (r.city) {\n <span class=\"ob-recipient-city\">{{ r.city }}</span>\n }\n @if (r.address) {\n <span class=\"ob-recipient-address\" [title]=\"r.address\">{{ r.address }}</span>\n }\n </div>\n </div>\n } @else {\n <div class=\"field col-6\">\n <ef-label labelKey=\"label.customer\" for=\"ob-cust\" [required]=\"true\" />\n <ef-select\n inputId=\"ob-cust\"\n [options]=\"customers()\"\n optionLabel=\"companyName\"\n optionValue=\"customerId\"\n [ngModel]=\"order().customerId\"\n (ngModelChange)=\"updateCustomer($event)\"\n [disabled]=\"readonly()\"\n [showClear]=\"true\"\n [filter]=\"true\"\n filterBy=\"companyName\"\n filterPlaceholderKey=\"label.search_customer\"\n appendTo=\"body\"\n placeholderKey=\"label.select_customer\" />\n </div>\n }\n <div class=\"field col-3\">\n <ef-label labelKey=\"label.order_date\" for=\"ob-date\" [required]=\"true\" />\n <ef-datepicker-advanced\n mode=\"field\"\n selectionMode=\"single\"\n inputId=\"ob-date\"\n placeholderKey=\"date_select_date\"\n [ngModel]=\"order().orderDate\"\n (ngModelChange)=\"updateOrderDate($event)\"\n [disabled]=\"readonly()\" />\n </div>\n </div>\n\n @if (!readonly()) {\n <div class=\"qa-grid\">\n <div class=\"qa-combo-wrap\">\n <ef-label labelKey=\"label.article\" />\n <ef-product-typeahead\n [products]=\"products()\"\n [productKeyField]=\"productKeyField()\"\n placeholderKey=\"label.search_product\"\n [currency]=\"currency()\"\n [locale]=\"locale()\"\n [priceList]=\"priceList()\"\n (productSelect)=\"onTypeaheadSelect($event)\"\n [showVariants]=\"false\" />\n </div>\n <div class=\"qa-price\">\n <ef-label labelKey=\"label.sale_price\" />\n <ef-inputnumber\n variant=\"comptoir\"\n [value]=\"quickAddPrice()\"\n (valueChangeEvent)=\"quickAddPrice.set($event ?? 0)\"\n [minFractionDigits]=\"2\"\n [maxFractionDigits]=\"2\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\" />\n </div>\n <div class=\"qa-qtyf\">\n <ef-label labelKey=\"label.quantity\" />\n <ef-inputnumber\n variant=\"comptoir\"\n [ngModel]=\"quickAddQuantity()\"\n (ngModelChange)=\"quickAddQuantity.set($event)\"\n [min]=\"0\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\"\n />\n </div>\n <div class=\"qa-go\">\n <!-- Ghost while the preconditions are unmet, tenant once the\n line is addable. A disabled control painted in a faded\n tenant fill reads as an enabled button that lost its\n colour, which is the greyed-out accent the design brief\n bans; ghost reads as \"not yet\". -->\n <ef-button\n labelKey=\"action.add\"\n icon=\"pi pi-plus\"\n variant=\"comptoir\"\n size=\"base\"\n [severity]=\"quickAddDisabled() ? 'ghost' : 'tenant'\"\n [disabled]=\"quickAddDisabled()\"\n (clickEvent)=\"addQuickProduct()\" />\n </div>\n </div>\n }\n </div>\n </ef-card>\n\n <!-- Lines.\n\n ef-data-card owns the panel here instead of sitting inside an\n ef-card: `.tbl-wrap` already draws a bordered, rounded surface, so\n nesting it in another one would be a card inside a card. The title\n and the card's actions move into the head slots it provides.\n\n The columns are declared in TS (`lineColumns`) so discount and tax\n follow the document config, and so one definition drives both the\n desktop table and the mobile cards. Only the article column is\n elastic; every other column holds a number of known magnitude and\n gets a fixed track, which keeps the decimals aligned down the\n table rather than shifting with the longest description. -->\n <ef-data-card\n [columns]=\"lineColumns()\"\n [rows]=\"orderLines()\"\n trackByField=\"id\"\n [hidePager]=\"true\"\n defaultDensity=\"compact\"\n [reorderable]=\"!readonly()\"\n [fixedLayout]=\"true\"\n (rowReorder)=\"reorderLine($event)\"\n mobileLayout=\"card\">\n\n <div tbl-head-info class=\"ob-lines-head\">\n <span class=\"strong\">{{ 'order_builder.lines' | translate }}</span>\n <span class=\"meta\">\u00B7 {{ orderLines().length }}</span>\n </div>\n\n <div tbl-head-actions class=\"head-actions\">\n @if (config().showCatalogue && !readonly()) {\n <ef-button\n labelKey=\"action.open_catalogue\"\n icon=\"pi pi-box\"\n severity=\"secondary\"\n [text]=\"true\"\n (clickEvent)=\"handleOpenCatalogue()\" />\n }\n <ef-button\n [labelKey]=\"focusMode() ? 'order_builder.show_summary' : 'order_builder.fullscreen'\"\n icon=\"pi pi-window-maximize\"\n severity=\"secondary\"\n [text]=\"true\"\n (clickEvent)=\"toggleFocus()\" />\n </div>\n\n <span empty-state>{{ 'order_builder.empty' | translate }}</span>\n <span empty-state-mobile>{{ 'order_builder.empty' | translate }}</span>\n\n <ng-template efColumnTemplate=\"product\" let-item let-i=\"index\">\n <div class=\"p\">\n <span class=\"ln\">{{ i + 1 }}</span>\n <span class=\"pp\">\n <span class=\"nm\">{{ item.productDescription }}</span>\n </span>\n </div>\n </ng-template>\n\n <ng-template efColumnTemplate=\"price\" let-item>\n @if (!readonly() && isEditingCell(item, 'price')) {\n <ef-inputnumber\n [(ngModel)]=\"item.productUnitPrice\"\n (ngModelChange)=\"onFieldChange(item)\"\n [showClear]=\"false\"\n [autofocus]=\"true\"\n (blurEvent)=\"stopEditCell()\"\n (keyDownEvent)=\"onEditCellKeydown($event)\"\n [min]=\"0\"\n [minFractionDigits]=\"2\"\n [maxFractionDigits]=\"2\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\" />\n } @else {\n <button type=\"button\" class=\"ln-val\" [disabled]=\"readonly()\" (click)=\"startEditCell(item, 'price')\">\n {{ item.productUnitPrice | number: '1.2-2' : locale() }}\n </button>\n }\n </ng-template>\n\n <ng-template efColumnTemplate=\"qty\" let-item>\n @if (!readonly() && isEditingCell(item, 'qty')) {\n <ef-inputnumber\n [(ngModel)]=\"item.quantity\"\n (ngModelChange)=\"onFieldChange(item)\"\n [showClear]=\"false\"\n [autofocus]=\"true\"\n (blurEvent)=\"stopEditCell()\"\n (keyDownEvent)=\"onEditCellKeydown($event)\"\n [min]=\"0\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\" />\n } @else {\n <button type=\"button\" class=\"ln-val\" [disabled]=\"readonly()\" (click)=\"startEditCell(item, 'qty')\">\n {{ item.quantity | number: '1.0-4' : locale() }}\n </button>\n }\n </ng-template>\n\n <ng-template efColumnTemplate=\"discount\" let-item>\n @if (!readonly() && isEditingCell(item, 'discount')) {\n <ef-inputnumber\n [(ngModel)]=\"item.discount\"\n (ngModelChange)=\"onFieldChange(item)\"\n [showClear]=\"false\"\n [autofocus]=\"true\"\n (blurEvent)=\"stopEditCell()\"\n (keyDownEvent)=\"onEditCellKeydown($event)\"\n [min]=\"0\"\n [max]=\"100\"\n [maxFractionDigits]=\"2\"\n suffix=\" %\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\" />\n } @else {\n <button type=\"button\" class=\"ln-val\" [disabled]=\"readonly()\" (click)=\"startEditCell(item, 'discount')\">\n {{ item.discount | number: '1.0-2' : locale() }} %\n </button>\n }\n </ng-template>\n\n <ng-template efColumnTemplate=\"tax\" let-item>\n @if (!readonly() && isEditingCell(item, 'tva')) {\n <ef-inputnumber\n [(ngModel)]=\"item.taxRate\"\n (ngModelChange)=\"onFieldChange(item)\"\n [showClear]=\"false\"\n [autofocus]=\"true\"\n (blurEvent)=\"stopEditCell()\"\n (keyDownEvent)=\"onEditCellKeydown($event)\"\n [min]=\"0\"\n [max]=\"100\"\n [maxFractionDigits]=\"2\"\n suffix=\" %\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\" />\n } @else {\n <button type=\"button\" class=\"ln-val\" [disabled]=\"readonly()\" (click)=\"startEditCell(item, 'tva')\">\n {{ (item.taxRate ?? 0) | number: '1.0-2' : locale() }} %\n </button>\n }\n </ng-template>\n\n <ng-template efColumnTemplate=\"subtotal\" let-item>\n {{ calculateSubtotal(item) | number: '1.2-2' : locale() }}\n </ng-template>\n\n <ng-template efColumnTemplate=\"remove\" let-item>\n <ef-button\n icon=\"pi pi-trash\"\n ariaLabelKey=\"common_delete\"\n severity=\"danger\"\n [text]=\"true\"\n styleClass=\"ob-del\"\n (clickEvent)=\"removeOrderLine(item.id)\" />\n </ng-template>\n </ef-data-card>\n\n </div>\n\n <!-- CONTEXT RAIL (1/3) -->\n <aside class=\"ob-rail\">\n <!-- Totaux (top) -->\n <ef-card titleKey=\"order_builder.totals\" [meta]=\"currency()\">\n <div class=\"ob-totals\">\n <div class=\"row\">\n <span class=\"lbl\">{{ 'order_builder.gross' | translate }}</span>\n <span class=\"v\">{{ grossTotal() | number: '1.2-2' : locale() }}</span>\n </div>\n <div class=\"row discount\">\n <span class=\"lbl\">{{ 'order_builder.discount' | translate }}</span>\n <span class=\"v\">\u2212{{ discountTotal() | number: '1.2-2' : locale() }}</span>\n </div>\n <div class=\"row\">\n <span class=\"lbl\">{{ 'order_builder.tax' | translate }}</span>\n <span class=\"v\">{{ tvaTotal() | number: '1.2-2' : locale() }}</span>\n </div>\n @if (shippingTotal() > 0) {\n <div class=\"row shipping\">\n <span class=\"lbl\">{{ 'order_builder.shipping' | translate }}</span>\n <span class=\"v\">{{ shippingTotal() | number: '1.2-2' : locale() }}</span>\n </div>\n }\n <hr />\n <div class=\"row grand\">\n <span class=\"lbl\">{{ 'order_builder.grand_total' | translate }}</span>\n <span class=\"v\">{{ finalTotal() | number: '1.2-2' : locale() }}<span class=\"cur\">{{ currency() }}</span></span>\n </div>\n </div>\n </ef-card>\n\n <!-- R\u00E9capitulatif par format -->\n @if (productsSummary().items.length > 0) {\n <ef-card titleKey=\"order_builder.recap\" metaKey=\"order_builder.auto\">\n <div class=\"ob-recap\">\n <div class=\"cols\">\n @for (col of recapColumns(); track $index) {\n <div class=\"grp\">\n @for (seg of col; track seg.code) {\n <div class=\"seg\">\n <span class=\"fmt\">{{ seg.label }}</span>\n <span class=\"n\">{{ seg.count | number: '1.0-0' : locale() }}</span>\n </div>\n }\n </div>\n }\n </div>\n <div class=\"tot\">\n <span class=\"l\">{{ 'order_builder.total_units' | translate }}</span>\n <span class=\"n\">{{ productsSummary().totalCount | number: '1.0-0' : locale() }}</span>\n </div>\n </div>\n </ef-card>\n }\n\n <!-- Host cards that belong beside the totals (delivery, notes): a column\n at desktop width, below the totals in the drawer otherwise. -->\n <ng-content select=\"[obRail]\" />\n\n <!-- Historique (optional, component-owned) -->\n @if (hasAudit()) {\n <ef-change-history\n [entries]=\"auditEntries()\"\n [valueLists]=\"auditValueLists()\"\n [noteFormatter]=\"auditNoteFormatter()\"\n />\n }\n </aside>\n </div>\n\n <!-- Floating summary FAB + backdrop -->\n <button type=\"button\" class=\"ob-backdrop\" aria-hidden=\"true\" tabindex=\"-1\" (click)=\"toggleRail(false)\"></button>\n <div class=\"ob-fab\" role=\"status\">\n <div class=\"ft\">\n <span class=\"k\">{{ 'order_builder.total_units' | translate }} \u00B7 {{ productsSummary().totalCount }}</span>\n <span class=\"vv\">{{ finalTotal() | number: '1.2-2' : locale() }}<span class=\"cur\">{{ currency() }}</span></span>\n </div>\n <button type=\"button\" class=\"open\" (click)=\"toggleRail(true)\">\n {{ 'order_builder.summary' | translate }}\n </button>\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block}:host .order-builder{display:grid;gap:var(--s-3)}:host .order-builder .head-actions{display:flex;align-items:center;gap:6px}:host .order-builder .form-grid{display:grid;gap:var(--s-3);grid-template-columns:repeat(12,minmax(0,1fr))}:host .order-builder .form-grid>.col-3{grid-column:span 3}:host .order-builder .form-grid>.col-4{grid-column:span 4}:host .order-builder .form-grid>.col-5{grid-column:span 5}:host .order-builder .form-grid>.col-6{grid-column:span 6}@media(max-width:767px){:host .order-builder .form-grid>[class*=col-]{grid-column:span 12}}:host .order-builder .field{display:grid;gap:4px}:host .order-builder .field>label{font-size:11px;font-weight:700;color:var(--text-mute);text-transform:uppercase;letter-spacing:.06em}:host .order-builder .ob-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s-3);align-items:start}@media(min-width:1280px){:host .order-builder .ob-grid{grid-template-columns:minmax(0,2fr) minmax(0,1fr)}}:host .order-builder .ob-main{display:grid;gap:var(--s-3);min-width:0}:host .order-builder .ob-rail{display:grid;gap:var(--s-3);align-content:start}@media(min-width:1280px){:host .order-builder .ob-rail{position:sticky;top:44px}}:host .order-builder .ob-order-head{display:grid;gap:var(--s-4)}:host .order-builder .ob-recipient{display:flex;flex-wrap:wrap;align-items:baseline;column-gap:var(--s-3);row-gap:2px;min-width:0;font-size:13px;line-height:1.45;color:var(--text-soft)}:host .order-builder .ob-recipient>*{flex:0 1 auto;min-width:0;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;unicode-bidi:plaintext}:host .order-builder .ob-recipient-name{font-weight:600;color:var(--text)}:host .order-builder .ob-recipient-phone{color:var(--tenant-700);text-decoration:none;font-variant-numeric:tabular-nums}:host .order-builder .ob-recipient-phone:hover{text-decoration:underline}:host .order-builder .ob-recipient-address{flex-basis:100%;color:var(--text-mute)}:host .order-builder .qa-grid{display:flex;flex-wrap:wrap;gap:10px;align-items:end;padding-top:var(--s-3);border-top:1px solid var(--rule-soft)}:host .order-builder .qa-combo-wrap{flex:1 1 260px;min-width:0}:host .order-builder .qa-price{flex:0 1 132px}:host .order-builder .qa-qtyf{flex:0 0 132px}:host .order-builder .qa-go{flex:0 0 auto}@media(max-width:560px){:host .order-builder .qa-combo-wrap,:host .order-builder .qa-price,:host .order-builder .qa-qtyf,:host .order-builder .qa-go{flex:1 1 100%}}:host .order-builder ef-data-card{font-size:13px}:host .order-builder ef-data-card .p{display:flex;align-items:baseline;gap:9px;line-height:1.25}:host .order-builder ef-data-card .p .ln{flex:0 0 auto;min-width:15px;text-align:end;font-family:var(--font-mono);font-size:10.5px;font-weight:700;color:var(--text-soft)}:host .order-builder ef-data-card .p .pp{display:grid;gap:1px;min-width:0}:host .order-builder ef-data-card .p .nm{font-weight:600;letter-spacing:-.005em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .order-builder ef-data-card .p .nm em{font-style:normal;color:var(--text-mute);font-weight:600}:host .order-builder ef-data-card ef-inputnumber,:host .order-builder ef-data-card ef-inputnumber .ef-field-control,:host .order-builder ef-data-card ef-inputnumber input{width:100%;min-width:0}:host .order-builder ef-data-card .num,:host .order-builder ef-data-card .total{font-family:var(--font-mono);font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums}:host .order-builder ef-data-card .ln-val{appearance:none;width:100%;margin:0;padding:5px 8px;border:1px solid transparent;border-radius:7px;background:transparent;font:inherit;font-family:var(--font-mono);font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums;color:inherit;text-align:end;cursor:text;transition:background .12s ease,border-color .12s ease}:host .order-builder ef-data-card .ln-val:hover:not(:disabled){background:var(--paper);border-color:var(--rule)}:host .order-builder ef-data-card .ln-val:focus-visible{outline:none;background:var(--paper);border-color:var(--tenant, var(--rule))}:host .order-builder ef-data-card .ln-val:disabled{cursor:default;background:transparent;border-color:transparent}:host .order-builder ef-data-card .total{font-family:var(--font-display);font-variation-settings:\"opsz\" 12,\"wght\" 700,\"wdth\" 85;font-size:13.5px}:host .order-builder ef-data-card .cur{font-size:9.5px;color:var(--text-soft);margin-inline-start:3px}:host .order-builder .ob-lines-head{display:flex;align-items:baseline;gap:6px;min-width:0}:host .order-builder .ob-lines-head .strong{font-weight:700;color:var(--text)}:host .order-builder .ob-lines-head .meta{color:var(--text-soft)}:host .order-builder .ob-del{color:var(--text-soft)}:host .order-builder .ob-del:hover{color:var(--st-cancelled-fg);border-color:var(--st-cancelled-bg)}:host .order-builder .ob-totals{display:grid;gap:6px}:host .order-builder .ob-totals .row{display:flex;justify-content:space-between;align-items:baseline;gap:14px}:host .order-builder .ob-totals .row .lbl{font-size:12px;font-weight:600;color:var(--text-mute)}:host .order-builder .ob-totals .row .v{font-family:var(--font-mono);font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums;font-weight:700;font-size:12.5px}:host .order-builder .ob-totals .row.discount .v{color:var(--st-pending-fg)}:host .order-builder .ob-totals hr{border:0;border-top:1px solid var(--rule);margin:5px 0}:host .order-builder .ob-totals .grand .lbl{font-family:var(--font-display);font-variation-settings:\"opsz\" 12,\"wght\" 700,\"wdth\" 85;font-size:13.5px;color:var(--text)}:host .order-builder .ob-totals .grand .v{font-family:var(--font-display);font-variation-settings:\"opsz\" 96,\"wght\" 700,\"wdth\" 80;font-size:26px;letter-spacing:-.03em;color:var(--text)}:host .order-builder .ob-totals .grand .cur{font-family:var(--font-mono);font-size:.4em;color:var(--text-mute);margin-inline-start:4px}:host .order-builder .ob-recap .cols{display:grid;grid-template-columns:1fr 1fr}:host .order-builder .ob-recap .grp{display:flex;flex-direction:column;gap:3px;min-width:0}:host .order-builder .ob-recap .grp:first-child{padding-inline-end:14px}:host .order-builder .ob-recap .grp+.grp{padding-inline-start:14px;border-inline-start:1px solid var(--rule)}:host .order-builder .ob-recap .seg{display:flex;justify-content:space-between;align-items:baseline;gap:8px}:host .order-builder .ob-recap .seg .fmt{font-size:11.5px;font-weight:700;color:var(--text);letter-spacing:-.005em;min-width:0}:host .order-builder .ob-recap .seg .fmt:after{content:\":\";color:var(--text-soft)}:host .order-builder .ob-recap .seg .n{font-family:var(--font-mono);font-weight:700;font-size:12px;color:var(--text);font-variant-numeric:tabular-nums;flex-shrink:0}:host .order-builder .ob-recap .tot{display:flex;justify-content:space-between;align-items:baseline;margin-top:12px;padding-top:10px;border-top:1px solid var(--rule)}:host .order-builder .ob-recap .tot .l{font-weight:700;font-size:12px}:host .order-builder .ob-recap .tot .n{font-family:var(--font-display);font-variation-settings:\"opsz\" 12,\"wght\" 700,\"wdth\" 85;font-size:18px;font-variant-numeric:tabular-nums}:host .order-builder .ob-fab{display:none;position:fixed;inset-inline-end:20px;bottom:calc(var(--app-bottom-bar, 0px) + env(safe-area-inset-bottom,0px) + 20px);z-index:30;align-items:center;gap:10px;box-sizing:border-box;block-size:var(--ef-order-fab-h);--ob-fab-ground: var(--ink-active);--ob-fab-on: var(--paper);background:var(--ob-fab-ground);color:var(--ob-fab-on);border-radius:var(--r-pill);padding-block:0;padding-inline:16px 10px;box-shadow:var(--shadow-3)}:host .order-builder .ob-fab .ft{display:grid;line-height:1.1}:host .order-builder .ob-fab .ft .k{font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;opacity:.65}:host .order-builder .ob-fab .ft .vv{font-family:var(--font-display);font-variation-settings:\"opsz\" 12,\"wght\" 700,\"wdth\" 82;font-size:16px}:host .order-builder .ob-fab .ft .vv .cur{font-family:var(--font-mono);font-size:.6em;opacity:.7;margin-inline-start:3px}:host .order-builder .ob-fab .open{appearance:none;cursor:pointer;border:0;background:color-mix(in srgb,var(--ob-fab-on) 14%,transparent);color:var(--ob-fab-on);font:inherit;font-size:12px;font-weight:700;border-radius:var(--r-pill);padding:8px 14px;display:inline-flex;align-items:center;gap:6px}:host .order-builder .ob-fab .open:hover{background:color-mix(in srgb,var(--ob-fab-on) 24%,transparent)}:host .order-builder .ob-fab .open:focus-visible{outline:2px solid var(--ob-fab-on);outline-offset:2px}:host .order-builder .ob-backdrop{display:none;position:fixed;inset:0;z-index:38;background:#0f11156b;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}:host .order-builder.ob-collapsed .ob-grid{grid-template-columns:minmax(0,1fr)}:host .order-builder.ob-collapsed .ob-fab{display:flex}:host .order-builder.ob-collapsed .ob-rail{position:fixed;top:0;inset-inline-end:0;height:100dvh;width:min(360px,92vw);z-index:40;background:var(--paper);border-inline-start:1px solid var(--rule);box-shadow:var(--shadow-3);padding:14px;overflow:auto;gap:var(--s-3);transform:translate(101%);transition:transform var(--t-base) var(--ease-out)}[dir=rtl] :host .order-builder.ob-collapsed .ob-rail{transform:translate(-101%)}:host .order-builder.ob-collapsed.ob-rail-open .ob-rail{transform:none}:host .order-builder.ob-collapsed.ob-rail-open .ob-backdrop{display:block}@media(max-width:1279px){:host .order-builder .ob-grid{grid-template-columns:minmax(0,1fr)}:host .order-builder .ob-fab{display:flex}:host .order-builder .ob-rail{position:fixed;top:0;inset-inline-end:0;height:100dvh;width:min(360px,92vw);z-index:40;background:var(--paper);border-inline-start:1px solid var(--rule);box-shadow:var(--shadow-3);padding:14px;overflow:auto;gap:var(--s-3);transform:translate(101%);transition:transform var(--t-base) var(--ease-out)}[dir=rtl] :host .order-builder .ob-rail{transform:translate(-101%)}:host .order-builder.ob-rail-open .ob-rail{transform:none}:host .order-builder.ob-rail-open .ob-backdrop{display:block}}:host ef-data-card .nm,:host ef-data-card .num,:host ef-data-card .ln-val,:host .ob-totals .v{unicode-bidi:plaintext}@media(pointer:coarse){:host .order-builder .ob-fab .open{min-block-size:var(--hit-touch)}:host ef-data-card .ln-val{min-block-size:var(--hit-field)}:host .order-builder .ob-recipient-phone{line-height:var(--hit-touch)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfSelectComponent, selector: "ef-select", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "dataKey", "group", "optionGroupLabel", "optionGroupChildren", "size", "fluid", "inline", "appendTo", "multiple", "filter", "filterBy", "filterPlaceholder", "filterPlaceholderKey", "autofocusFilter", "autofocus", "editable", "loading", "virtualScroll", "virtualScrollItemSize", "scrollHeight", "multiDisplay", "showToggleAll", "emptyMessage", "emptyMessageKey", "emptyFilterMessage", "emptyFilterMessageKey", "styleClass", "panelStyleClass", "filterByKeys", "variant", "value"], outputs: ["selectionChangeEvent", "filterChangeEvent", "panelShowEvent", "panelHideEvent"] }, { kind: "component", type: EfDatepickerAdvancedComponent, selector: "ef-datepicker-advanced", inputs: ["triggerLabelKey", "triggerLabel", "presets", "activePreset", "start", "end", "allowCustomRange", "allowAllTime", "mode", "selectionMode", "variant", "showTime", "valueFormat"], outputs: ["rangeChange", "opened", "closed"] }, { kind: "component", type: EfInputNumberComponent, selector: "ef-inputnumber", inputs: ["value", "invalid", "format", "showButtons", "buttonLayout", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "prefix", "suffix", "currency", "currencyDisplay", "locale", "localeMatcher", "mode", "useGrouping", "minFractionDigits", "maxFractionDigits", "min", "max", "step", "allowEmpty", "inputSize", "size", "maxlength", "minlength", "pattern", "tabindex", "title", "ariaLabelledBy", "inputStyle", "inputStyleClass", "style", "styleClass", "primeNgVariant", "autofocus", "autocomplete", "fluid", "inline", "variant", "pt", "ptOptions", "dt", "unstyled"], outputs: ["inputEvent", "valueChangeEvent", "focusEvent", "blurEvent", "keyDownEvent", "clearEvent"] }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "labelParams", "for", "styleClass", "required"] }, { kind: "component", type: EfButtonComponent, selector: "ef-button", inputs: ["label", "labelKey", "icon", "ariaLabelKey", "variant", "severity", "size", "disabled", "rounded", "outlined", "raised", "text", "loading", "styleClass", "type", "badge", "badgeSeverity", "tooltip", "tooltipKey", "tooltipPosition", "autofocus"], outputs: ["clickEvent"] }, { kind: "component", type: EfCardComponent, selector: "ef-card", inputs: ["titleKey", "title", "metaKey", "meta", "tone", "forceHead", "hasFoot", "collapsible", "overflowVisible", "loading", "skeleton", "skeletonRows", "skeletonHeight", "collapsed"], outputs: ["collapsedChange"] }, { kind: "component", type: EfChangeHistoryComponent, selector: "ef-change-history", inputs: ["entries", "titleKey", "emptyKey", "limit", "previewLimit", "collapsible", "startCollapsed", "valueLists", "noteFormatter"] }, { kind: "component", type: EfProductTypeaheadComponent, selector: "ef-product-typeahead", inputs: ["products", "productKeyField", "placeholderKey", "disabled", "currency", "locale", "showVariants", "priceList"], outputs: ["productSelect"] }, { kind: "component", type: EfDataCardComponent, selector: "ef-data-card", inputs: ["mobileLayout", "columns", "rows", "trackByField", "pageNumber", "pageSize", "totalCount", "pageSizeOptions", "hidePager", "sort", "loading", "errorMsg", "loadingKey", "rowDoubleClickable", "rowPressed", "rowDisabled", "hasActionsColumn", "rowActionsContext", "showViewAction", "showEditAction", "showDuplicateAction", "showDeleteAction", "rowActionState", "showDensityControl", "showColumnPicker", "showExportControl", "exporting", "screenContext", "searchTerm", "tableKey", "defaultDensity", "reorderable", "fixedLayout"], outputs: ["pageChange", "pageSizeChange", "sortChange", "rowDoubleClick", "rowAction", "exportRequest", "clearSearch", "retry", "rowReorder"] }, { kind: "directive", type: EfColumnTemplateDirective, selector: "[efColumnTemplate]", inputs: ["efColumnTemplate"] }, { kind: "pipe", type: i1$3.DecimalPipe, name: "number" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
6678
6961
|
}
|
|
6679
6962
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfOrderBuilderComponent, decorators: [{
|
|
6680
6963
|
type: Component,
|
|
@@ -6693,8 +6976,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
6693
6976
|
EfProductTypeaheadComponent,
|
|
6694
6977
|
EfDataCardComponent,
|
|
6695
6978
|
EfColumnTemplateDirective,
|
|
6696
|
-
], template: "<div\n class=\"order-builder\"\n [class.ob-collapsed]=\"focusMode()\"\n [class.ob-rail-open]=\"railOpen()\">\n <div class=\"ob-grid\">\n <div class=\"ob-main\">\n <!-- Order header: customer + date + express add (one card) -->\n <ef-card\n titleKey=\"order_builder.section_order\"\n [metaKey]=\"readonly() ? '' : 'order_builder.quick_add_hint'\"\n [overflowVisible]=\"true\">\n <div class=\"ob-order-head\">\n <div class=\"form-grid\">\n @if (recipient(); as r) {\n <!-- No client to pick (a storefront order): who it goes to,\n read-only, in the customer's place. -->\n <div class=\"field col-6\">\n <ef-label labelKey=\"order_builder.recipient\" />\n <div\n class=\"ob-recipient\"\n role=\"group\"\n [attr.aria-label]=\"'order_builder.recipient' | translate\">\n <span class=\"ob-recipient-name\">{{ r.name }}</span>\n @if (recipientTel(); as tel) {\n <a class=\"ob-recipient-phone\" [href]=\"tel\" dir=\"ltr\">{{ r.phone }}</a>\n }\n @if (r.city) {\n <span class=\"ob-recipient-city\">{{ r.city }}</span>\n }\n @if (r.address) {\n <span class=\"ob-recipient-address\" [title]=\"r.address\">{{ r.address }}</span>\n }\n </div>\n </div>\n } @else {\n <div class=\"field col-6\">\n <ef-label labelKey=\"label.customer\" for=\"ob-cust\" [required]=\"true\" />\n <ef-select\n inputId=\"ob-cust\"\n [options]=\"customers()\"\n optionLabel=\"companyName\"\n optionValue=\"customerId\"\n [ngModel]=\"order().customerId\"\n (ngModelChange)=\"updateCustomer($event)\"\n [disabled]=\"readonly()\"\n [showClear]=\"true\"\n [filter]=\"true\"\n filterBy=\"companyName\"\n filterPlaceholderKey=\"label.search_customer\"\n appendTo=\"body\"\n placeholderKey=\"label.select_customer\" />\n </div>\n }\n <div class=\"field col-3\">\n <ef-label labelKey=\"label.order_date\" for=\"ob-date\" [required]=\"true\" />\n <ef-datepicker-advanced\n mode=\"field\"\n selectionMode=\"single\"\n inputId=\"ob-date\"\n placeholderKey=\"date_select_date\"\n [ngModel]=\"order().orderDate\"\n (ngModelChange)=\"updateOrderDate($event)\"\n [disabled]=\"readonly()\" />\n </div>\n </div>\n\n @if (!readonly()) {\n <div class=\"qa-grid\">\n <div class=\"qa-combo-wrap\">\n <ef-label labelKey=\"label.article\" />\n <ef-product-typeahead\n [products]=\"products()\"\n [productKeyField]=\"productKeyField()\"\n placeholderKey=\"label.search_product\"\n [currency]=\"currency()\"\n [locale]=\"locale()\"\n [priceList]=\"priceList()\"\n (productSelect)=\"onTypeaheadSelect($event)\"\n [showVariants]=\"false\" />\n </div>\n <div class=\"qa-price\">\n <ef-label labelKey=\"label.sale_price\" />\n <ef-inputnumber\n variant=\"comptoir\"\n [value]=\"quickAddPrice()\"\n (valueChangeEvent)=\"quickAddPrice.set($event ?? 0)\"\n [minFractionDigits]=\"2\"\n [maxFractionDigits]=\"2\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\" />\n </div>\n <div class=\"qa-qtyf\">\n <ef-label labelKey=\"label.quantity\" />\n <ef-inputnumber\n variant=\"comptoir\"\n [ngModel]=\"quickAddQuantity()\"\n (ngModelChange)=\"quickAddQuantity.set($event)\"\n [min]=\"0\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\"\n />\n </div>\n <div class=\"qa-go\">\n <!-- Ghost while the preconditions are unmet, tenant once the\n line is addable. A disabled control painted in a faded\n tenant fill reads as an enabled button that lost its\n colour, which is the greyed-out accent the design brief\n bans; ghost reads as \"not yet\". -->\n <ef-button\n labelKey=\"action.add\"\n icon=\"pi pi-plus\"\n variant=\"comptoir\"\n size=\"base\"\n [severity]=\"quickAddDisabled() ? 'ghost' : 'tenant'\"\n [disabled]=\"quickAddDisabled()\"\n (clickEvent)=\"addQuickProduct()\" />\n </div>\n </div>\n }\n </div>\n </ef-card>\n\n <!-- Lines.\n\n ef-data-card owns the panel here instead of sitting inside an\n ef-card: `.tbl-wrap` already draws a bordered, rounded surface, so\n nesting it in another one would be a card inside a card. The title\n and the card's actions move into the head slots it provides.\n\n The columns are declared in TS (`lineColumns`) so discount and tax\n follow the document config, and so one definition drives both the\n desktop table and the mobile cards. Only the article column is\n elastic; every other column holds a number of known magnitude and\n gets a fixed track, which keeps the decimals aligned down the\n table rather than shifting with the longest description. -->\n <ef-data-card\n [columns]=\"lineColumns()\"\n [rows]=\"orderLines()\"\n trackByField=\"id\"\n [hidePager]=\"true\"\n defaultDensity=\"compact\"\n [reorderable]=\"!readonly()\"\n [fixedLayout]=\"true\"\n (rowReorder)=\"reorderLine($event)\"\n mobileLayout=\"card\">\n\n <div tbl-head-info class=\"ob-lines-head\">\n <span class=\"strong\">{{ 'order_builder.lines' | translate }}</span>\n <span class=\"meta\">\u00B7 {{ orderLines().length }}</span>\n </div>\n\n <div tbl-head-actions class=\"head-actions\">\n @if (config().showCatalogue && !readonly()) {\n <ef-button\n labelKey=\"action.open_catalogue\"\n icon=\"pi pi-box\"\n severity=\"secondary\"\n [text]=\"true\"\n (clickEvent)=\"handleOpenCatalogue()\" />\n }\n <ef-button\n [labelKey]=\"focusMode() ? 'order_builder.show_summary' : 'order_builder.fullscreen'\"\n icon=\"pi pi-window-maximize\"\n severity=\"secondary\"\n [text]=\"true\"\n (clickEvent)=\"toggleFocus()\" />\n </div>\n\n <span empty-state>{{ 'order_builder.empty' | translate }}</span>\n <span empty-state-mobile>{{ 'order_builder.empty' | translate }}</span>\n\n <ng-template efColumnTemplate=\"product\" let-item let-i=\"index\">\n <div class=\"p\">\n <span class=\"ln\">{{ i + 1 }}</span>\n <span class=\"pp\">\n <span class=\"nm\">{{ item.productDescription }}</span>\n </span>\n </div>\n </ng-template>\n\n <ng-template efColumnTemplate=\"price\" let-item>\n @if (!readonly() && isEditingCell(item, 'price')) {\n <ef-inputnumber\n [(ngModel)]=\"item.productUnitPrice\"\n (ngModelChange)=\"onFieldChange(item)\"\n [showClear]=\"false\"\n [autofocus]=\"true\"\n (blurEvent)=\"stopEditCell()\"\n (keyDownEvent)=\"onEditCellKeydown($event)\"\n [min]=\"0\"\n [minFractionDigits]=\"2\"\n [maxFractionDigits]=\"2\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\" />\n } @else {\n <button type=\"button\" class=\"ln-val\" [disabled]=\"readonly()\" (click)=\"startEditCell(item, 'price')\">\n {{ item.productUnitPrice | number: '1.2-2' : locale() }}\n </button>\n }\n </ng-template>\n\n <ng-template efColumnTemplate=\"qty\" let-item>\n @if (!readonly() && isEditingCell(item, 'qty')) {\n <ef-inputnumber\n [(ngModel)]=\"item.quantity\"\n (ngModelChange)=\"onFieldChange(item)\"\n [showClear]=\"false\"\n [autofocus]=\"true\"\n (blurEvent)=\"stopEditCell()\"\n (keyDownEvent)=\"onEditCellKeydown($event)\"\n [min]=\"0\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\" />\n } @else {\n <button type=\"button\" class=\"ln-val\" [disabled]=\"readonly()\" (click)=\"startEditCell(item, 'qty')\">\n {{ item.quantity | number: '1.0-4' : locale() }}\n </button>\n }\n </ng-template>\n\n <ng-template efColumnTemplate=\"discount\" let-item>\n @if (!readonly() && isEditingCell(item, 'discount')) {\n <ef-inputnumber\n [(ngModel)]=\"item.discount\"\n (ngModelChange)=\"onFieldChange(item)\"\n [showClear]=\"false\"\n [autofocus]=\"true\"\n (blurEvent)=\"stopEditCell()\"\n (keyDownEvent)=\"onEditCellKeydown($event)\"\n [min]=\"0\"\n [max]=\"100\"\n [maxFractionDigits]=\"2\"\n suffix=\" %\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\" />\n } @else {\n <button type=\"button\" class=\"ln-val\" [disabled]=\"readonly()\" (click)=\"startEditCell(item, 'discount')\">\n {{ item.discount | number: '1.0-2' : locale() }} %\n </button>\n }\n </ng-template>\n\n <ng-template efColumnTemplate=\"tax\" let-item>\n @if (!readonly() && isEditingCell(item, 'tva')) {\n <ef-inputnumber\n [(ngModel)]=\"item.taxRate\"\n (ngModelChange)=\"onFieldChange(item)\"\n [showClear]=\"false\"\n [autofocus]=\"true\"\n (blurEvent)=\"stopEditCell()\"\n (keyDownEvent)=\"onEditCellKeydown($event)\"\n [min]=\"0\"\n [max]=\"100\"\n [maxFractionDigits]=\"2\"\n suffix=\" %\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\" />\n } @else {\n <button type=\"button\" class=\"ln-val\" [disabled]=\"readonly()\" (click)=\"startEditCell(item, 'tva')\">\n {{ (item.taxRate ?? 0) | number: '1.0-2' : locale() }} %\n </button>\n }\n </ng-template>\n\n <ng-template efColumnTemplate=\"subtotal\" let-item>\n {{ calculateSubtotal(item) | number: '1.2-2' : locale() }}\n </ng-template>\n\n <ng-template efColumnTemplate=\"remove\" let-item>\n <ef-button\n icon=\"pi pi-trash\"\n ariaLabelKey=\"common_delete\"\n severity=\"danger\"\n [text]=\"true\"\n styleClass=\"ob-del\"\n (clickEvent)=\"removeOrderLine(item.id)\" />\n </ng-template>\n </ef-data-card>\n\n </div>\n\n <!-- CONTEXT RAIL (1/3) -->\n <aside class=\"ob-rail\">\n <!-- Totaux (top) -->\n <ef-card titleKey=\"order_builder.totals\" [meta]=\"currency()\">\n <div class=\"ob-totals\">\n <div class=\"row\">\n <span class=\"lbl\">{{ 'order_builder.gross' | translate }}</span>\n <span class=\"v\">{{ grossTotal() | number: '1.2-2' : locale() }}</span>\n </div>\n <div class=\"row discount\">\n <span class=\"lbl\">{{ 'order_builder.discount' | translate }}</span>\n <span class=\"v\">\u2212{{ discountTotal() | number: '1.2-2' : locale() }}</span>\n </div>\n <div class=\"row\">\n <span class=\"lbl\">{{ 'order_builder.tax' | translate }}</span>\n <span class=\"v\">{{ tvaTotal() | number: '1.2-2' : locale() }}</span>\n </div>\n @if (shippingTotal() > 0) {\n <div class=\"row shipping\">\n <span class=\"lbl\">{{ 'order_builder.shipping' | translate }}</span>\n <span class=\"v\">{{ shippingTotal() | number: '1.2-2' : locale() }}</span>\n </div>\n }\n <hr />\n <div class=\"row grand\">\n <span class=\"lbl\">{{ 'order_builder.grand_total' | translate }}</span>\n <span class=\"v\">{{ finalTotal() | number: '1.2-2' : locale() }}<span class=\"cur\">{{ currency() }}</span></span>\n </div>\n </div>\n </ef-card>\n\n <!-- R\u00E9capitulatif par format -->\n @if (productsSummary().items.length > 0) {\n <ef-card titleKey=\"order_builder.recap\" metaKey=\"order_builder.auto\">\n <div class=\"ob-recap\">\n <div class=\"cols\">\n @for (col of recapColumns(); track $index) {\n <div class=\"grp\">\n @for (seg of col; track seg.code) {\n <div class=\"seg\">\n <span class=\"fmt\">{{ seg.label }}</span>\n <span class=\"n\">{{ seg.count | number: '1.0-0' : locale() }}</span>\n </div>\n }\n </div>\n }\n </div>\n <div class=\"tot\">\n <span class=\"l\">{{ 'order_builder.total_units' | translate }}</span>\n <span class=\"n\">{{ productsSummary().totalCount | number: '1.0-0' : locale() }}</span>\n </div>\n </div>\n </ef-card>\n }\n\n <!-- Host cards that belong beside the totals (delivery, notes): a column\n at desktop width, below the totals in the drawer otherwise. -->\n <ng-content select=\"[obRail]\" />\n\n <!-- Historique (optional, component-owned) -->\n @if (hasAudit()) {\n <ef-change-history [entries]=\"auditEntries()\" />\n }\n </aside>\n </div>\n\n <!-- Floating summary FAB + backdrop -->\n <button type=\"button\" class=\"ob-backdrop\" aria-hidden=\"true\" tabindex=\"-1\" (click)=\"toggleRail(false)\"></button>\n <div class=\"ob-fab\" role=\"status\">\n <div class=\"ft\">\n <span class=\"k\">{{ 'order_builder.total_units' | translate }} \u00B7 {{ productsSummary().totalCount }}</span>\n <span class=\"vv\">{{ finalTotal() | number: '1.2-2' : locale() }}<span class=\"cur\">{{ currency() }}</span></span>\n </div>\n <button type=\"button\" class=\"open\" (click)=\"toggleRail(true)\">\n {{ 'order_builder.summary' | translate }}\n </button>\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block}:host .order-builder{display:grid;gap:var(--s-3)}:host .order-builder .head-actions{display:flex;align-items:center;gap:6px}:host .order-builder .form-grid{display:grid;gap:var(--s-3);grid-template-columns:repeat(12,minmax(0,1fr))}:host .order-builder .form-grid>.col-3{grid-column:span 3}:host .order-builder .form-grid>.col-4{grid-column:span 4}:host .order-builder .form-grid>.col-5{grid-column:span 5}:host .order-builder .form-grid>.col-6{grid-column:span 6}@media(max-width:767px){:host .order-builder .form-grid>[class*=col-]{grid-column:span 12}}:host .order-builder .field{display:grid;gap:4px}:host .order-builder .field>label{font-size:11px;font-weight:700;color:var(--text-mute);text-transform:uppercase;letter-spacing:.06em}:host .order-builder .ob-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s-3);align-items:start}@media(min-width:1280px){:host .order-builder .ob-grid{grid-template-columns:minmax(0,2fr) minmax(0,1fr)}}:host .order-builder .ob-main{display:grid;gap:var(--s-3);min-width:0}:host .order-builder .ob-rail{display:grid;gap:var(--s-3);align-content:start}@media(min-width:1280px){:host .order-builder .ob-rail{position:sticky;top:44px}}:host .order-builder .ob-order-head{display:grid;gap:var(--s-4)}:host .order-builder .ob-recipient{display:flex;flex-wrap:wrap;align-items:baseline;column-gap:var(--s-3);row-gap:2px;min-width:0;font-size:13px;line-height:1.45;color:var(--text-soft)}:host .order-builder .ob-recipient>*{flex:0 1 auto;min-width:0;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;unicode-bidi:plaintext}:host .order-builder .ob-recipient-name{font-weight:600;color:var(--text)}:host .order-builder .ob-recipient-phone{color:var(--tenant-700);text-decoration:none;font-variant-numeric:tabular-nums}:host .order-builder .ob-recipient-phone:hover{text-decoration:underline}:host .order-builder .ob-recipient-address{flex-basis:100%;color:var(--text-mute)}:host .order-builder .qa-grid{display:flex;flex-wrap:wrap;gap:10px;align-items:end;padding-top:var(--s-3);border-top:1px solid var(--rule-soft)}:host .order-builder .qa-combo-wrap{flex:1 1 260px;min-width:0}:host .order-builder .qa-price{flex:0 1 132px}:host .order-builder .qa-qtyf{flex:0 0 132px}:host .order-builder .qa-go{flex:0 0 auto}@media(max-width:560px){:host .order-builder .qa-combo-wrap,:host .order-builder .qa-price,:host .order-builder .qa-qtyf,:host .order-builder .qa-go{flex:1 1 100%}}:host .order-builder ef-data-card{font-size:13px}:host .order-builder ef-data-card .p{display:flex;align-items:baseline;gap:9px;line-height:1.25}:host .order-builder ef-data-card .p .ln{flex:0 0 auto;min-width:15px;text-align:end;font-family:var(--font-mono);font-size:10.5px;font-weight:700;color:var(--text-soft)}:host .order-builder ef-data-card .p .pp{display:grid;gap:1px;min-width:0}:host .order-builder ef-data-card .p .nm{font-weight:600;letter-spacing:-.005em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .order-builder ef-data-card .p .nm em{font-style:normal;color:var(--text-mute);font-weight:600}:host .order-builder ef-data-card ef-inputnumber,:host .order-builder ef-data-card ef-inputnumber .ef-field-control,:host .order-builder ef-data-card ef-inputnumber input{width:100%;min-width:0}:host .order-builder ef-data-card .num,:host .order-builder ef-data-card .total{font-family:var(--font-mono);font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums}:host .order-builder ef-data-card .ln-val{appearance:none;width:100%;margin:0;padding:5px 8px;border:1px solid transparent;border-radius:7px;background:transparent;font:inherit;font-family:var(--font-mono);font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums;color:inherit;text-align:end;cursor:text;transition:background .12s ease,border-color .12s ease}:host .order-builder ef-data-card .ln-val:hover:not(:disabled){background:var(--paper);border-color:var(--rule)}:host .order-builder ef-data-card .ln-val:focus-visible{outline:none;background:var(--paper);border-color:var(--tenant, var(--rule))}:host .order-builder ef-data-card .ln-val:disabled{cursor:default;background:transparent;border-color:transparent}:host .order-builder ef-data-card .total{font-family:var(--font-display);font-variation-settings:\"opsz\" 12,\"wght\" 700,\"wdth\" 85;font-size:13.5px}:host .order-builder ef-data-card .cur{font-size:9.5px;color:var(--text-soft);margin-inline-start:3px}:host .order-builder .ob-lines-head{display:flex;align-items:baseline;gap:6px;min-width:0}:host .order-builder .ob-lines-head .strong{font-weight:700;color:var(--text)}:host .order-builder .ob-lines-head .meta{color:var(--text-soft)}:host .order-builder .ob-del{color:var(--text-soft)}:host .order-builder .ob-del:hover{color:var(--st-cancelled-fg);border-color:var(--st-cancelled-bg)}:host .order-builder .ob-totals{display:grid;gap:6px}:host .order-builder .ob-totals .row{display:flex;justify-content:space-between;align-items:baseline;gap:14px}:host .order-builder .ob-totals .row .lbl{font-size:12px;font-weight:600;color:var(--text-mute)}:host .order-builder .ob-totals .row .v{font-family:var(--font-mono);font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums;font-weight:700;font-size:12.5px}:host .order-builder .ob-totals .row.discount .v{color:var(--st-pending-fg)}:host .order-builder .ob-totals hr{border:0;border-top:1px solid var(--rule);margin:5px 0}:host .order-builder .ob-totals .grand .lbl{font-family:var(--font-display);font-variation-settings:\"opsz\" 12,\"wght\" 700,\"wdth\" 85;font-size:13.5px;color:var(--text)}:host .order-builder .ob-totals .grand .v{font-family:var(--font-display);font-variation-settings:\"opsz\" 96,\"wght\" 700,\"wdth\" 80;font-size:26px;letter-spacing:-.03em;color:var(--text)}:host .order-builder .ob-totals .grand .cur{font-family:var(--font-mono);font-size:.4em;color:var(--text-mute);margin-inline-start:4px}:host .order-builder .ob-recap .cols{display:grid;grid-template-columns:1fr 1fr}:host .order-builder .ob-recap .grp{display:flex;flex-direction:column;gap:3px;min-width:0}:host .order-builder .ob-recap .grp:first-child{padding-inline-end:14px}:host .order-builder .ob-recap .grp+.grp{padding-inline-start:14px;border-inline-start:1px solid var(--rule)}:host .order-builder .ob-recap .seg{display:flex;justify-content:space-between;align-items:baseline;gap:8px}:host .order-builder .ob-recap .seg .fmt{font-size:11.5px;font-weight:700;color:var(--text);letter-spacing:-.005em;min-width:0}:host .order-builder .ob-recap .seg .fmt:after{content:\":\";color:var(--text-soft)}:host .order-builder .ob-recap .seg .n{font-family:var(--font-mono);font-weight:700;font-size:12px;color:var(--text);font-variant-numeric:tabular-nums;flex-shrink:0}:host .order-builder .ob-recap .tot{display:flex;justify-content:space-between;align-items:baseline;margin-top:12px;padding-top:10px;border-top:1px solid var(--rule)}:host .order-builder .ob-recap .tot .l{font-weight:700;font-size:12px}:host .order-builder .ob-recap .tot .n{font-family:var(--font-display);font-variation-settings:\"opsz\" 12,\"wght\" 700,\"wdth\" 85;font-size:18px;font-variant-numeric:tabular-nums}:host .order-builder .ob-fab{display:none;position:fixed;inset-inline-end:20px;bottom:calc(var(--app-bottom-bar, 0px) + env(safe-area-inset-bottom,0px) + 20px);z-index:30;align-items:center;gap:10px;box-sizing:border-box;block-size:var(--ef-order-fab-h);--ob-fab-ground: var(--ink-active);--ob-fab-on: var(--paper);background:var(--ob-fab-ground);color:var(--ob-fab-on);border-radius:var(--r-pill);padding-block:0;padding-inline:16px 10px;box-shadow:var(--shadow-3)}:host .order-builder .ob-fab .ft{display:grid;line-height:1.1}:host .order-builder .ob-fab .ft .k{font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;opacity:.65}:host .order-builder .ob-fab .ft .vv{font-family:var(--font-display);font-variation-settings:\"opsz\" 12,\"wght\" 700,\"wdth\" 82;font-size:16px}:host .order-builder .ob-fab .ft .vv .cur{font-family:var(--font-mono);font-size:.6em;opacity:.7;margin-inline-start:3px}:host .order-builder .ob-fab .open{appearance:none;cursor:pointer;border:0;background:color-mix(in srgb,var(--ob-fab-on) 14%,transparent);color:var(--ob-fab-on);font:inherit;font-size:12px;font-weight:700;border-radius:var(--r-pill);padding:8px 14px;display:inline-flex;align-items:center;gap:6px}:host .order-builder .ob-fab .open:hover{background:color-mix(in srgb,var(--ob-fab-on) 24%,transparent)}:host .order-builder .ob-fab .open:focus-visible{outline:2px solid var(--ob-fab-on);outline-offset:2px}:host .order-builder .ob-backdrop{display:none;position:fixed;inset:0;z-index:38;background:#0f11156b;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}:host .order-builder.ob-collapsed .ob-grid{grid-template-columns:minmax(0,1fr)}:host .order-builder.ob-collapsed .ob-fab{display:flex}:host .order-builder.ob-collapsed .ob-rail{position:fixed;top:0;inset-inline-end:0;height:100dvh;width:min(360px,92vw);z-index:40;background:var(--paper);border-inline-start:1px solid var(--rule);box-shadow:var(--shadow-3);padding:14px;overflow:auto;gap:var(--s-3);transform:translate(101%);transition:transform var(--t-base) var(--ease-out)}[dir=rtl] :host .order-builder.ob-collapsed .ob-rail{transform:translate(-101%)}:host .order-builder.ob-collapsed.ob-rail-open .ob-rail{transform:none}:host .order-builder.ob-collapsed.ob-rail-open .ob-backdrop{display:block}@media(max-width:1279px){:host .order-builder .ob-grid{grid-template-columns:minmax(0,1fr)}:host .order-builder .ob-fab{display:flex}:host .order-builder .ob-rail{position:fixed;top:0;inset-inline-end:0;height:100dvh;width:min(360px,92vw);z-index:40;background:var(--paper);border-inline-start:1px solid var(--rule);box-shadow:var(--shadow-3);padding:14px;overflow:auto;gap:var(--s-3);transform:translate(101%);transition:transform var(--t-base) var(--ease-out)}[dir=rtl] :host .order-builder .ob-rail{transform:translate(-101%)}:host .order-builder.ob-rail-open .ob-rail{transform:none}:host .order-builder.ob-rail-open .ob-backdrop{display:block}}:host ef-data-card .nm,:host ef-data-card .num,:host ef-data-card .ln-val,:host .ob-totals .v{unicode-bidi:plaintext}@media(pointer:coarse){:host .order-builder .ob-fab .open{min-block-size:var(--hit-touch)}:host ef-data-card .ln-val{min-block-size:var(--hit-field)}:host .order-builder .ob-recipient-phone{line-height:var(--hit-touch)}}\n"] }]
|
|
6697
|
-
}], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], order: [{ type: i0.Input, args: [{ isSignal: true, alias: "order", required: true }] }, { type: i0.Output, args: ["orderChange"] }], products: [{ type: i0.Input, args: [{ isSignal: true, alias: "products", required: true }] }], customers: [{ type: i0.Input, args: [{ isSignal: true, alias: "customers", required: true }] }], recipient: [{ type: i0.Input, args: [{ isSignal: true, alias: "recipient", required: false }] }], defaultCallingCode: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultCallingCode", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], countGroupLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "countGroupLabels", required: false }] }], productKeyField: [{ type: i0.Input, args: [{ isSignal: true, alias: "productKeyField", required: false }] }], priceList: [{ type: i0.Input, args: [{ isSignal: true, alias: "priceList", required: false }] }], backendErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "backendErrors", required: false }] }], shippingTotal: [{ type: i0.Input, args: [{ isSignal: true, alias: "shippingTotal", required: false }] }], auditEntries: [{ type: i0.Input, args: [{ isSignal: true, alias: "auditEntries", required: false }] }], openCatalogue: [{ type: i0.Output, args: ["openCatalogue"] }], orderChanged: [{ type: i0.Output, args: ["orderChanged"] }], orderValidated: [{ type: i0.Output, args: ["orderValidated"] }], productsAdded: [{ type: i0.Output, args: ["productsAdded"] }], focusMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "focusMode", required: false }] }, { type: i0.Output, args: ["focusModeChange"] }], typeahead: [{ type: i0.ViewChild, args: [i0.forwardRef(() => EfProductTypeaheadComponent), { isSignal: true }] }] } });
|
|
6979
|
+
], template: "<div\n class=\"order-builder\"\n [class.ob-collapsed]=\"focusMode()\"\n [class.ob-rail-open]=\"railOpen()\">\n <div class=\"ob-grid\">\n <div class=\"ob-main\">\n <!-- Order header: customer + date + express add (one card) -->\n <ef-card\n titleKey=\"order_builder.section_order\"\n [metaKey]=\"readonly() ? '' : 'order_builder.quick_add_hint'\"\n [overflowVisible]=\"true\">\n <div class=\"ob-order-head\">\n <div class=\"form-grid\">\n @if (recipient(); as r) {\n <!-- No client to pick (a storefront order): who it goes to,\n read-only, in the customer's place. -->\n <div class=\"field col-6\">\n <ef-label labelKey=\"order_builder.recipient\" />\n <div\n class=\"ob-recipient\"\n role=\"group\"\n [attr.aria-label]=\"'order_builder.recipient' | translate\">\n <span class=\"ob-recipient-name\">{{ r.name }}</span>\n @if (recipientTel(); as tel) {\n <a class=\"ob-recipient-phone\" [href]=\"tel\" dir=\"ltr\">{{ r.phone }}</a>\n }\n @if (r.city) {\n <span class=\"ob-recipient-city\">{{ r.city }}</span>\n }\n @if (r.address) {\n <span class=\"ob-recipient-address\" [title]=\"r.address\">{{ r.address }}</span>\n }\n </div>\n </div>\n } @else {\n <div class=\"field col-6\">\n <ef-label labelKey=\"label.customer\" for=\"ob-cust\" [required]=\"true\" />\n <ef-select\n inputId=\"ob-cust\"\n [options]=\"customers()\"\n optionLabel=\"companyName\"\n optionValue=\"customerId\"\n [ngModel]=\"order().customerId\"\n (ngModelChange)=\"updateCustomer($event)\"\n [disabled]=\"readonly()\"\n [showClear]=\"true\"\n [filter]=\"true\"\n filterBy=\"companyName\"\n filterPlaceholderKey=\"label.search_customer\"\n appendTo=\"body\"\n placeholderKey=\"label.select_customer\" />\n </div>\n }\n <div class=\"field col-3\">\n <ef-label labelKey=\"label.order_date\" for=\"ob-date\" [required]=\"true\" />\n <ef-datepicker-advanced\n mode=\"field\"\n selectionMode=\"single\"\n inputId=\"ob-date\"\n placeholderKey=\"date_select_date\"\n [ngModel]=\"order().orderDate\"\n (ngModelChange)=\"updateOrderDate($event)\"\n [disabled]=\"readonly()\" />\n </div>\n </div>\n\n @if (!readonly()) {\n <div class=\"qa-grid\">\n <div class=\"qa-combo-wrap\">\n <ef-label labelKey=\"label.article\" />\n <ef-product-typeahead\n [products]=\"products()\"\n [productKeyField]=\"productKeyField()\"\n placeholderKey=\"label.search_product\"\n [currency]=\"currency()\"\n [locale]=\"locale()\"\n [priceList]=\"priceList()\"\n (productSelect)=\"onTypeaheadSelect($event)\"\n [showVariants]=\"false\" />\n </div>\n <div class=\"qa-price\">\n <ef-label labelKey=\"label.sale_price\" />\n <ef-inputnumber\n variant=\"comptoir\"\n [value]=\"quickAddPrice()\"\n (valueChangeEvent)=\"quickAddPrice.set($event ?? 0)\"\n [minFractionDigits]=\"2\"\n [maxFractionDigits]=\"2\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\" />\n </div>\n <div class=\"qa-qtyf\">\n <ef-label labelKey=\"label.quantity\" />\n <ef-inputnumber\n variant=\"comptoir\"\n [ngModel]=\"quickAddQuantity()\"\n (ngModelChange)=\"quickAddQuantity.set($event)\"\n [min]=\"0\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n [disabled]=\"readonly()\"\n />\n </div>\n <div class=\"qa-go\">\n <!-- Ghost while the preconditions are unmet, tenant once the\n line is addable. A disabled control painted in a faded\n tenant fill reads as an enabled button that lost its\n colour, which is the greyed-out accent the design brief\n bans; ghost reads as \"not yet\". -->\n <ef-button\n labelKey=\"action.add\"\n icon=\"pi pi-plus\"\n variant=\"comptoir\"\n size=\"base\"\n [severity]=\"quickAddDisabled() ? 'ghost' : 'tenant'\"\n [disabled]=\"quickAddDisabled()\"\n (clickEvent)=\"addQuickProduct()\" />\n </div>\n </div>\n }\n </div>\n </ef-card>\n\n <!-- Lines.\n\n ef-data-card owns the panel here instead of sitting inside an\n ef-card: `.tbl-wrap` already draws a bordered, rounded surface, so\n nesting it in another one would be a card inside a card. The title\n and the card's actions move into the head slots it provides.\n\n The columns are declared in TS (`lineColumns`) so discount and tax\n follow the document config, and so one definition drives both the\n desktop table and the mobile cards. Only the article column is\n elastic; every other column holds a number of known magnitude and\n gets a fixed track, which keeps the decimals aligned down the\n table rather than shifting with the longest description. -->\n <ef-data-card\n [columns]=\"lineColumns()\"\n [rows]=\"orderLines()\"\n trackByField=\"id\"\n [hidePager]=\"true\"\n defaultDensity=\"compact\"\n [reorderable]=\"!readonly()\"\n [fixedLayout]=\"true\"\n (rowReorder)=\"reorderLine($event)\"\n mobileLayout=\"card\">\n\n <div tbl-head-info class=\"ob-lines-head\">\n <span class=\"strong\">{{ 'order_builder.lines' | translate }}</span>\n <span class=\"meta\">\u00B7 {{ orderLines().length }}</span>\n </div>\n\n <div tbl-head-actions class=\"head-actions\">\n @if (config().showCatalogue && !readonly()) {\n <ef-button\n labelKey=\"action.open_catalogue\"\n icon=\"pi pi-box\"\n severity=\"secondary\"\n [text]=\"true\"\n (clickEvent)=\"handleOpenCatalogue()\" />\n }\n <ef-button\n [labelKey]=\"focusMode() ? 'order_builder.show_summary' : 'order_builder.fullscreen'\"\n icon=\"pi pi-window-maximize\"\n severity=\"secondary\"\n [text]=\"true\"\n (clickEvent)=\"toggleFocus()\" />\n </div>\n\n <span empty-state>{{ 'order_builder.empty' | translate }}</span>\n <span empty-state-mobile>{{ 'order_builder.empty' | translate }}</span>\n\n <ng-template efColumnTemplate=\"product\" let-item let-i=\"index\">\n <div class=\"p\">\n <span class=\"ln\">{{ i + 1 }}</span>\n <span class=\"pp\">\n <span class=\"nm\">{{ item.productDescription }}</span>\n </span>\n </div>\n </ng-template>\n\n <ng-template efColumnTemplate=\"price\" let-item>\n @if (!readonly() && isEditingCell(item, 'price')) {\n <ef-inputnumber\n [(ngModel)]=\"item.productUnitPrice\"\n (ngModelChange)=\"onFieldChange(item)\"\n [showClear]=\"false\"\n [autofocus]=\"true\"\n (blurEvent)=\"stopEditCell()\"\n (keyDownEvent)=\"onEditCellKeydown($event)\"\n [min]=\"0\"\n [minFractionDigits]=\"2\"\n [maxFractionDigits]=\"2\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\" />\n } @else {\n <button type=\"button\" class=\"ln-val\" [disabled]=\"readonly()\" (click)=\"startEditCell(item, 'price')\">\n {{ item.productUnitPrice | number: '1.2-2' : locale() }}\n </button>\n }\n </ng-template>\n\n <ng-template efColumnTemplate=\"qty\" let-item>\n @if (!readonly() && isEditingCell(item, 'qty')) {\n <ef-inputnumber\n [(ngModel)]=\"item.quantity\"\n (ngModelChange)=\"onFieldChange(item)\"\n [showClear]=\"false\"\n [autofocus]=\"true\"\n (blurEvent)=\"stopEditCell()\"\n (keyDownEvent)=\"onEditCellKeydown($event)\"\n [min]=\"0\"\n [maxFractionDigits]=\"4\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\" />\n } @else {\n <button type=\"button\" class=\"ln-val\" [disabled]=\"readonly()\" (click)=\"startEditCell(item, 'qty')\">\n {{ item.quantity | number: '1.0-4' : locale() }}\n </button>\n }\n </ng-template>\n\n <ng-template efColumnTemplate=\"discount\" let-item>\n @if (!readonly() && isEditingCell(item, 'discount')) {\n <ef-inputnumber\n [(ngModel)]=\"item.discount\"\n (ngModelChange)=\"onFieldChange(item)\"\n [showClear]=\"false\"\n [autofocus]=\"true\"\n (blurEvent)=\"stopEditCell()\"\n (keyDownEvent)=\"onEditCellKeydown($event)\"\n [min]=\"0\"\n [max]=\"100\"\n [maxFractionDigits]=\"2\"\n suffix=\" %\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\" />\n } @else {\n <button type=\"button\" class=\"ln-val\" [disabled]=\"readonly()\" (click)=\"startEditCell(item, 'discount')\">\n {{ item.discount | number: '1.0-2' : locale() }} %\n </button>\n }\n </ng-template>\n\n <ng-template efColumnTemplate=\"tax\" let-item>\n @if (!readonly() && isEditingCell(item, 'tva')) {\n <ef-inputnumber\n [(ngModel)]=\"item.taxRate\"\n (ngModelChange)=\"onFieldChange(item)\"\n [showClear]=\"false\"\n [autofocus]=\"true\"\n (blurEvent)=\"stopEditCell()\"\n (keyDownEvent)=\"onEditCellKeydown($event)\"\n [min]=\"0\"\n [max]=\"100\"\n [maxFractionDigits]=\"2\"\n suffix=\" %\"\n [locale]=\"locale()\"\n size=\"small\"\n class=\"!text-end\" />\n } @else {\n <button type=\"button\" class=\"ln-val\" [disabled]=\"readonly()\" (click)=\"startEditCell(item, 'tva')\">\n {{ (item.taxRate ?? 0) | number: '1.0-2' : locale() }} %\n </button>\n }\n </ng-template>\n\n <ng-template efColumnTemplate=\"subtotal\" let-item>\n {{ calculateSubtotal(item) | number: '1.2-2' : locale() }}\n </ng-template>\n\n <ng-template efColumnTemplate=\"remove\" let-item>\n <ef-button\n icon=\"pi pi-trash\"\n ariaLabelKey=\"common_delete\"\n severity=\"danger\"\n [text]=\"true\"\n styleClass=\"ob-del\"\n (clickEvent)=\"removeOrderLine(item.id)\" />\n </ng-template>\n </ef-data-card>\n\n </div>\n\n <!-- CONTEXT RAIL (1/3) -->\n <aside class=\"ob-rail\">\n <!-- Totaux (top) -->\n <ef-card titleKey=\"order_builder.totals\" [meta]=\"currency()\">\n <div class=\"ob-totals\">\n <div class=\"row\">\n <span class=\"lbl\">{{ 'order_builder.gross' | translate }}</span>\n <span class=\"v\">{{ grossTotal() | number: '1.2-2' : locale() }}</span>\n </div>\n <div class=\"row discount\">\n <span class=\"lbl\">{{ 'order_builder.discount' | translate }}</span>\n <span class=\"v\">\u2212{{ discountTotal() | number: '1.2-2' : locale() }}</span>\n </div>\n <div class=\"row\">\n <span class=\"lbl\">{{ 'order_builder.tax' | translate }}</span>\n <span class=\"v\">{{ tvaTotal() | number: '1.2-2' : locale() }}</span>\n </div>\n @if (shippingTotal() > 0) {\n <div class=\"row shipping\">\n <span class=\"lbl\">{{ 'order_builder.shipping' | translate }}</span>\n <span class=\"v\">{{ shippingTotal() | number: '1.2-2' : locale() }}</span>\n </div>\n }\n <hr />\n <div class=\"row grand\">\n <span class=\"lbl\">{{ 'order_builder.grand_total' | translate }}</span>\n <span class=\"v\">{{ finalTotal() | number: '1.2-2' : locale() }}<span class=\"cur\">{{ currency() }}</span></span>\n </div>\n </div>\n </ef-card>\n\n <!-- R\u00E9capitulatif par format -->\n @if (productsSummary().items.length > 0) {\n <ef-card titleKey=\"order_builder.recap\" metaKey=\"order_builder.auto\">\n <div class=\"ob-recap\">\n <div class=\"cols\">\n @for (col of recapColumns(); track $index) {\n <div class=\"grp\">\n @for (seg of col; track seg.code) {\n <div class=\"seg\">\n <span class=\"fmt\">{{ seg.label }}</span>\n <span class=\"n\">{{ seg.count | number: '1.0-0' : locale() }}</span>\n </div>\n }\n </div>\n }\n </div>\n <div class=\"tot\">\n <span class=\"l\">{{ 'order_builder.total_units' | translate }}</span>\n <span class=\"n\">{{ productsSummary().totalCount | number: '1.0-0' : locale() }}</span>\n </div>\n </div>\n </ef-card>\n }\n\n <!-- Host cards that belong beside the totals (delivery, notes): a column\n at desktop width, below the totals in the drawer otherwise. -->\n <ng-content select=\"[obRail]\" />\n\n <!-- Historique (optional, component-owned) -->\n @if (hasAudit()) {\n <ef-change-history\n [entries]=\"auditEntries()\"\n [valueLists]=\"auditValueLists()\"\n [noteFormatter]=\"auditNoteFormatter()\"\n />\n }\n </aside>\n </div>\n\n <!-- Floating summary FAB + backdrop -->\n <button type=\"button\" class=\"ob-backdrop\" aria-hidden=\"true\" tabindex=\"-1\" (click)=\"toggleRail(false)\"></button>\n <div class=\"ob-fab\" role=\"status\">\n <div class=\"ft\">\n <span class=\"k\">{{ 'order_builder.total_units' | translate }} \u00B7 {{ productsSummary().totalCount }}</span>\n <span class=\"vv\">{{ finalTotal() | number: '1.2-2' : locale() }}<span class=\"cur\">{{ currency() }}</span></span>\n </div>\n <button type=\"button\" class=\"open\" (click)=\"toggleRail(true)\">\n {{ 'order_builder.summary' | translate }}\n </button>\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block}:host .order-builder{display:grid;gap:var(--s-3)}:host .order-builder .head-actions{display:flex;align-items:center;gap:6px}:host .order-builder .form-grid{display:grid;gap:var(--s-3);grid-template-columns:repeat(12,minmax(0,1fr))}:host .order-builder .form-grid>.col-3{grid-column:span 3}:host .order-builder .form-grid>.col-4{grid-column:span 4}:host .order-builder .form-grid>.col-5{grid-column:span 5}:host .order-builder .form-grid>.col-6{grid-column:span 6}@media(max-width:767px){:host .order-builder .form-grid>[class*=col-]{grid-column:span 12}}:host .order-builder .field{display:grid;gap:4px}:host .order-builder .field>label{font-size:11px;font-weight:700;color:var(--text-mute);text-transform:uppercase;letter-spacing:.06em}:host .order-builder .ob-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s-3);align-items:start}@media(min-width:1280px){:host .order-builder .ob-grid{grid-template-columns:minmax(0,2fr) minmax(0,1fr)}}:host .order-builder .ob-main{display:grid;gap:var(--s-3);min-width:0}:host .order-builder .ob-rail{display:grid;gap:var(--s-3);align-content:start}@media(min-width:1280px){:host .order-builder .ob-rail{position:sticky;top:44px}}:host .order-builder .ob-order-head{display:grid;gap:var(--s-4)}:host .order-builder .ob-recipient{display:flex;flex-wrap:wrap;align-items:baseline;column-gap:var(--s-3);row-gap:2px;min-width:0;font-size:13px;line-height:1.45;color:var(--text-soft)}:host .order-builder .ob-recipient>*{flex:0 1 auto;min-width:0;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;unicode-bidi:plaintext}:host .order-builder .ob-recipient-name{font-weight:600;color:var(--text)}:host .order-builder .ob-recipient-phone{color:var(--tenant-700);text-decoration:none;font-variant-numeric:tabular-nums}:host .order-builder .ob-recipient-phone:hover{text-decoration:underline}:host .order-builder .ob-recipient-address{flex-basis:100%;color:var(--text-mute)}:host .order-builder .qa-grid{display:flex;flex-wrap:wrap;gap:10px;align-items:end;padding-top:var(--s-3);border-top:1px solid var(--rule-soft)}:host .order-builder .qa-combo-wrap{flex:1 1 260px;min-width:0}:host .order-builder .qa-price{flex:0 1 132px}:host .order-builder .qa-qtyf{flex:0 0 132px}:host .order-builder .qa-go{flex:0 0 auto}@media(max-width:560px){:host .order-builder .qa-combo-wrap,:host .order-builder .qa-price,:host .order-builder .qa-qtyf,:host .order-builder .qa-go{flex:1 1 100%}}:host .order-builder ef-data-card{font-size:13px}:host .order-builder ef-data-card .p{display:flex;align-items:baseline;gap:9px;line-height:1.25}:host .order-builder ef-data-card .p .ln{flex:0 0 auto;min-width:15px;text-align:end;font-family:var(--font-mono);font-size:10.5px;font-weight:700;color:var(--text-soft)}:host .order-builder ef-data-card .p .pp{display:grid;gap:1px;min-width:0}:host .order-builder ef-data-card .p .nm{font-weight:600;letter-spacing:-.005em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .order-builder ef-data-card .p .nm em{font-style:normal;color:var(--text-mute);font-weight:600}:host .order-builder ef-data-card ef-inputnumber,:host .order-builder ef-data-card ef-inputnumber .ef-field-control,:host .order-builder ef-data-card ef-inputnumber input{width:100%;min-width:0}:host .order-builder ef-data-card .num,:host .order-builder ef-data-card .total{font-family:var(--font-mono);font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums}:host .order-builder ef-data-card .ln-val{appearance:none;width:100%;margin:0;padding:5px 8px;border:1px solid transparent;border-radius:7px;background:transparent;font:inherit;font-family:var(--font-mono);font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums;color:inherit;text-align:end;cursor:text;transition:background .12s ease,border-color .12s ease}:host .order-builder ef-data-card .ln-val:hover:not(:disabled){background:var(--paper);border-color:var(--rule)}:host .order-builder ef-data-card .ln-val:focus-visible{outline:none;background:var(--paper);border-color:var(--tenant, var(--rule))}:host .order-builder ef-data-card .ln-val:disabled{cursor:default;background:transparent;border-color:transparent}:host .order-builder ef-data-card .total{font-family:var(--font-display);font-variation-settings:\"opsz\" 12,\"wght\" 700,\"wdth\" 85;font-size:13.5px}:host .order-builder ef-data-card .cur{font-size:9.5px;color:var(--text-soft);margin-inline-start:3px}:host .order-builder .ob-lines-head{display:flex;align-items:baseline;gap:6px;min-width:0}:host .order-builder .ob-lines-head .strong{font-weight:700;color:var(--text)}:host .order-builder .ob-lines-head .meta{color:var(--text-soft)}:host .order-builder .ob-del{color:var(--text-soft)}:host .order-builder .ob-del:hover{color:var(--st-cancelled-fg);border-color:var(--st-cancelled-bg)}:host .order-builder .ob-totals{display:grid;gap:6px}:host .order-builder .ob-totals .row{display:flex;justify-content:space-between;align-items:baseline;gap:14px}:host .order-builder .ob-totals .row .lbl{font-size:12px;font-weight:600;color:var(--text-mute)}:host .order-builder .ob-totals .row .v{font-family:var(--font-mono);font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums;font-weight:700;font-size:12.5px}:host .order-builder .ob-totals .row.discount .v{color:var(--st-pending-fg)}:host .order-builder .ob-totals hr{border:0;border-top:1px solid var(--rule);margin:5px 0}:host .order-builder .ob-totals .grand .lbl{font-family:var(--font-display);font-variation-settings:\"opsz\" 12,\"wght\" 700,\"wdth\" 85;font-size:13.5px;color:var(--text)}:host .order-builder .ob-totals .grand .v{font-family:var(--font-display);font-variation-settings:\"opsz\" 96,\"wght\" 700,\"wdth\" 80;font-size:26px;letter-spacing:-.03em;color:var(--text)}:host .order-builder .ob-totals .grand .cur{font-family:var(--font-mono);font-size:.4em;color:var(--text-mute);margin-inline-start:4px}:host .order-builder .ob-recap .cols{display:grid;grid-template-columns:1fr 1fr}:host .order-builder .ob-recap .grp{display:flex;flex-direction:column;gap:3px;min-width:0}:host .order-builder .ob-recap .grp:first-child{padding-inline-end:14px}:host .order-builder .ob-recap .grp+.grp{padding-inline-start:14px;border-inline-start:1px solid var(--rule)}:host .order-builder .ob-recap .seg{display:flex;justify-content:space-between;align-items:baseline;gap:8px}:host .order-builder .ob-recap .seg .fmt{font-size:11.5px;font-weight:700;color:var(--text);letter-spacing:-.005em;min-width:0}:host .order-builder .ob-recap .seg .fmt:after{content:\":\";color:var(--text-soft)}:host .order-builder .ob-recap .seg .n{font-family:var(--font-mono);font-weight:700;font-size:12px;color:var(--text);font-variant-numeric:tabular-nums;flex-shrink:0}:host .order-builder .ob-recap .tot{display:flex;justify-content:space-between;align-items:baseline;margin-top:12px;padding-top:10px;border-top:1px solid var(--rule)}:host .order-builder .ob-recap .tot .l{font-weight:700;font-size:12px}:host .order-builder .ob-recap .tot .n{font-family:var(--font-display);font-variation-settings:\"opsz\" 12,\"wght\" 700,\"wdth\" 85;font-size:18px;font-variant-numeric:tabular-nums}:host .order-builder .ob-fab{display:none;position:fixed;inset-inline-end:20px;bottom:calc(var(--app-bottom-bar, 0px) + env(safe-area-inset-bottom,0px) + 20px);z-index:30;align-items:center;gap:10px;box-sizing:border-box;block-size:var(--ef-order-fab-h);--ob-fab-ground: var(--ink-active);--ob-fab-on: var(--paper);background:var(--ob-fab-ground);color:var(--ob-fab-on);border-radius:var(--r-pill);padding-block:0;padding-inline:16px 10px;box-shadow:var(--shadow-3)}:host .order-builder .ob-fab .ft{display:grid;line-height:1.1}:host .order-builder .ob-fab .ft .k{font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;opacity:.65}:host .order-builder .ob-fab .ft .vv{font-family:var(--font-display);font-variation-settings:\"opsz\" 12,\"wght\" 700,\"wdth\" 82;font-size:16px}:host .order-builder .ob-fab .ft .vv .cur{font-family:var(--font-mono);font-size:.6em;opacity:.7;margin-inline-start:3px}:host .order-builder .ob-fab .open{appearance:none;cursor:pointer;border:0;background:color-mix(in srgb,var(--ob-fab-on) 14%,transparent);color:var(--ob-fab-on);font:inherit;font-size:12px;font-weight:700;border-radius:var(--r-pill);padding:8px 14px;display:inline-flex;align-items:center;gap:6px}:host .order-builder .ob-fab .open:hover{background:color-mix(in srgb,var(--ob-fab-on) 24%,transparent)}:host .order-builder .ob-fab .open:focus-visible{outline:2px solid var(--ob-fab-on);outline-offset:2px}:host .order-builder .ob-backdrop{display:none;position:fixed;inset:0;z-index:38;background:#0f11156b;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}:host .order-builder.ob-collapsed .ob-grid{grid-template-columns:minmax(0,1fr)}:host .order-builder.ob-collapsed .ob-fab{display:flex}:host .order-builder.ob-collapsed .ob-rail{position:fixed;top:0;inset-inline-end:0;height:100dvh;width:min(360px,92vw);z-index:40;background:var(--paper);border-inline-start:1px solid var(--rule);box-shadow:var(--shadow-3);padding:14px;overflow:auto;gap:var(--s-3);transform:translate(101%);transition:transform var(--t-base) var(--ease-out)}[dir=rtl] :host .order-builder.ob-collapsed .ob-rail{transform:translate(-101%)}:host .order-builder.ob-collapsed.ob-rail-open .ob-rail{transform:none}:host .order-builder.ob-collapsed.ob-rail-open .ob-backdrop{display:block}@media(max-width:1279px){:host .order-builder .ob-grid{grid-template-columns:minmax(0,1fr)}:host .order-builder .ob-fab{display:flex}:host .order-builder .ob-rail{position:fixed;top:0;inset-inline-end:0;height:100dvh;width:min(360px,92vw);z-index:40;background:var(--paper);border-inline-start:1px solid var(--rule);box-shadow:var(--shadow-3);padding:14px;overflow:auto;gap:var(--s-3);transform:translate(101%);transition:transform var(--t-base) var(--ease-out)}[dir=rtl] :host .order-builder .ob-rail{transform:translate(-101%)}:host .order-builder.ob-rail-open .ob-rail{transform:none}:host .order-builder.ob-rail-open .ob-backdrop{display:block}}:host ef-data-card .nm,:host ef-data-card .num,:host ef-data-card .ln-val,:host .ob-totals .v{unicode-bidi:plaintext}@media(pointer:coarse){:host .order-builder .ob-fab .open{min-block-size:var(--hit-touch)}:host ef-data-card .ln-val{min-block-size:var(--hit-field)}:host .order-builder .ob-recipient-phone{line-height:var(--hit-touch)}}\n"] }]
|
|
6980
|
+
}], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], order: [{ type: i0.Input, args: [{ isSignal: true, alias: "order", required: true }] }, { type: i0.Output, args: ["orderChange"] }], products: [{ type: i0.Input, args: [{ isSignal: true, alias: "products", required: true }] }], customers: [{ type: i0.Input, args: [{ isSignal: true, alias: "customers", required: true }] }], recipient: [{ type: i0.Input, args: [{ isSignal: true, alias: "recipient", required: false }] }], defaultCallingCode: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultCallingCode", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], countGroupLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "countGroupLabels", required: false }] }], productKeyField: [{ type: i0.Input, args: [{ isSignal: true, alias: "productKeyField", required: false }] }], priceList: [{ type: i0.Input, args: [{ isSignal: true, alias: "priceList", required: false }] }], backendErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "backendErrors", required: false }] }], shippingTotal: [{ type: i0.Input, args: [{ isSignal: true, alias: "shippingTotal", required: false }] }], auditEntries: [{ type: i0.Input, args: [{ isSignal: true, alias: "auditEntries", required: false }] }], auditValueLists: [{ type: i0.Input, args: [{ isSignal: true, alias: "auditValueLists", required: false }] }], auditNoteFormatter: [{ type: i0.Input, args: [{ isSignal: true, alias: "auditNoteFormatter", required: false }] }], openCatalogue: [{ type: i0.Output, args: ["openCatalogue"] }], orderChanged: [{ type: i0.Output, args: ["orderChanged"] }], orderValidated: [{ type: i0.Output, args: ["orderValidated"] }], productsAdded: [{ type: i0.Output, args: ["productsAdded"] }], focusMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "focusMode", required: false }] }, { type: i0.Output, args: ["focusModeChange"] }], typeahead: [{ type: i0.ViewChild, args: [i0.forwardRef(() => EfProductTypeaheadComponent), { isSignal: true }] }] } });
|
|
6698
6981
|
|
|
6699
6982
|
/**
|
|
6700
6983
|
* Comptoir smart bar — the search/filter card every list screen
|
|
@@ -7476,7 +7759,7 @@ class EfProductCatalogueFilterComponent {
|
|
|
7476
7759
|
return (group.children?.length || 0) > 5;
|
|
7477
7760
|
}
|
|
7478
7761
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfProductCatalogueFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7479
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfProductCatalogueFilterComponent, isStandalone: true, selector: "ef-product-catalogue-filter", inputs: { categoryGroups: { classPropertyName: "categoryGroups", publicName: "categoryGroups", isSignal: true, isRequired: true, transformFunction: null }, products: { classPropertyName: "products", publicName: "products", isSignal: true, isRequired: true, transformFunction: null }, selectedFilters: { classPropertyName: "selectedFilters", publicName: "selectedFilters", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedFilters: "selectedFiltersChange", filtersChanged: "filtersChanged" }, ngImport: i0, template: "<div class=\"flex w-80 shrink-0 flex-col items-start justify-start sticky top-4 max-h-[calc(100vh-2rem)] overflow-y-auto\">\n <div\n class=\"mb-4 flex flex-col items-start justify-center gap-2 self-stretch border-b border-surface-200 px-2 pb-4 dark:border-surface-700\"\n >\n <div class=\"flex items-center justify-between gap-2 self-stretch\">\n <div\n class=\"text-base font-semibold leading-tight text-surface-900 dark:text-surface-0\"\n >\n Filters\n </div>\n <p-button\n [autofocus]=\"false\"\n [buttonProps]=\"noAutofocus\"\n size=\"small\"\n severity=\"secondary\"\n [text]=\"true\"\n class=\"py-1! px-2! text-base! font-normal!\"\n (clickEvent)=\"clearAllFilters()\"\n >\n <span pButtonLabel>\n <i class=\"pi pi-eraser\"></i>\n Clear\n </span>\n </p-button>\n </div>\n @if (selectedFilters().length > 0) {\n <div\n class=\"flex flex-wrap items-start justify-start gap-2 self-stretch\"\n >\n @for (filter of selectedFilters(); track filter.type + filter.code) {\n <p-chip\n [label]=\"filter.label\"\n [removable]=\"true\"\n size=\"small\"\n class=\"h-auto rounded-full px-6\"\n removeIcon=\"pi pi-times\"\n (onRemove)=\"removeFilter(filter)\"\n ></p-chip>\n }\n </div>\n }\n </div>\n\n <p-accordion\n [multiple]=\"true\"\n [value]=\"expandedPanels()\"\n class=\"w-full\"\n >\n @for (group of categoryGroups(); track group.code) {\n @if (group.children && group.children.length > 0) {\n <p-accordion-panel [value]=\"group.code\">\n <p-accordion-header>\n <span\n class=\"text-base font-semibold leading-tight text-surface-900 dark:text-surface-0\"\n >{{ group.label }}</span>\n </p-accordion-header>\n <p-accordion-content>\n <div class=\"flex flex-col gap-4\">\n @if (shouldShowSearch(group)) {\n <p-iconfield iconPosition=\"left\" class=\"w-full\">\n <p-inputicon class=\"pi pi-search\"></p-inputicon>\n <input\n pInputText\n size=\"small\"\n [ngModel]=\"searchTerms()[group.code] || ''\"\n (ngModelChange)=\"updateSearchTerm(group.code, $event)\"\n [placeholder]=\"'Rechercher ' + group.label.toLowerCase()\"\n class=\"w-full\"\n />\n </p-iconfield>\n }\n\n @for (item of getFilteredChildren(group); track item.code) {\n <div class=\"flex items-center justify-between gap-2\">\n <div class=\"flex flex-1 items-center gap-2\">\n <p-checkbox\n size=\"small\"\n [inputId]=\"group.code + '_' + item.code\"\n [binary]=\"true\"\n [ngModel]=\"isFilterSelected(group.code, item.code)\"\n (ngModelChange)=\"toggleFilter(group, item)\"\n ></p-checkbox>\n @if (getColorClass(item)) {\n <div\n class=\"h-5 w-5 rounded-full\"\n [class]=\"getColorClass(item)\"\n ></div>\n }\n <ef-label\n [label]=\"item.label\"\n [for]=\"group.code + '_' + item.code\"\n styleClass=\"cursor-pointer text-base font-normal leading-tight text-surface-700 dark:text-surface-300\"\n />\n </div>\n @if (getItemCount(group.code, item.code) > 0) {\n <p-badge\n [value]=\"getItemCount(group.code, item.code)\"\n size=\"small\"\n class=\"rounded-xl! h-6! min-w-6!\"\n severity=\"secondary\"\n ></p-badge>\n }\n </div>\n }\n </div>\n </p-accordion-content>\n </p-accordion-panel>\n }\n }\n </p-accordion>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AccordionModule }, { kind: "component", type: i2$2.Accordion, selector: "p-accordion", inputs: ["value", "multiple", "styleClass", "expandIcon", "collapseIcon", "selectOnFocus", "transitionOptions", "motionOptions"], outputs: ["valueChange", "onClose", "onOpen"] }, { kind: "component", type: i2$2.AccordionPanel, selector: "p-accordion-panel, p-accordionpanel", inputs: ["value", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: i2$2.AccordionHeader, selector: "p-accordion-header, p-accordionheader" }, { kind: "component", type: i2$2.AccordionContent, selector: "p-accordion-content, p-accordioncontent" }, { kind: "ngmodule", type: BadgeModule }, { kind: "component", type: i3.Badge, selector: "p-badge", inputs: ["styleClass", "badgeSize", "size", "severity", "value", "badgeDisabled"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i1.ButtonLabel, selector: "[pButtonLabel]", inputs: ["ptButtonLabel", "pButtonLabelPT", "pButtonLabelUnstyled"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i5.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ChipModule }, { kind: "component", type: i6.Chip, selector: "p-chip", inputs: ["label", "icon", "image", "alt", "styleClass", "disabled", "removable", "removeIcon", "chipProps"], outputs: ["onRemove", "onImageError"] }, { kind: "ngmodule", type: IconFieldModule }, { kind: "component", type: i7.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "ngmodule", type: InputIconModule }, { kind: "component", type: i8.InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i9.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "for", "styleClass", "required"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7762
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfProductCatalogueFilterComponent, isStandalone: true, selector: "ef-product-catalogue-filter", inputs: { categoryGroups: { classPropertyName: "categoryGroups", publicName: "categoryGroups", isSignal: true, isRequired: true, transformFunction: null }, products: { classPropertyName: "products", publicName: "products", isSignal: true, isRequired: true, transformFunction: null }, selectedFilters: { classPropertyName: "selectedFilters", publicName: "selectedFilters", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedFilters: "selectedFiltersChange", filtersChanged: "filtersChanged" }, ngImport: i0, template: "<div class=\"flex w-80 shrink-0 flex-col items-start justify-start sticky top-4 max-h-[calc(100vh-2rem)] overflow-y-auto\">\n <div\n class=\"mb-4 flex flex-col items-start justify-center gap-2 self-stretch border-b border-surface-200 px-2 pb-4 dark:border-surface-700\"\n >\n <div class=\"flex items-center justify-between gap-2 self-stretch\">\n <div\n class=\"text-base font-semibold leading-tight text-surface-900 dark:text-surface-0\"\n >\n Filters\n </div>\n <p-button\n [autofocus]=\"false\"\n [buttonProps]=\"noAutofocus\"\n size=\"small\"\n severity=\"secondary\"\n [text]=\"true\"\n class=\"py-1! px-2! text-base! font-normal!\"\n (clickEvent)=\"clearAllFilters()\"\n >\n <span pButtonLabel>\n <i class=\"pi pi-eraser\"></i>\n Clear\n </span>\n </p-button>\n </div>\n @if (selectedFilters().length > 0) {\n <div\n class=\"flex flex-wrap items-start justify-start gap-2 self-stretch\"\n >\n @for (filter of selectedFilters(); track filter.type + filter.code) {\n <p-chip\n [label]=\"filter.label\"\n [removable]=\"true\"\n size=\"small\"\n class=\"h-auto rounded-full px-6\"\n removeIcon=\"pi pi-times\"\n (onRemove)=\"removeFilter(filter)\"\n ></p-chip>\n }\n </div>\n }\n </div>\n\n <p-accordion\n [multiple]=\"true\"\n [value]=\"expandedPanels()\"\n class=\"w-full\"\n >\n @for (group of categoryGroups(); track group.code) {\n @if (group.children && group.children.length > 0) {\n <p-accordion-panel [value]=\"group.code\">\n <p-accordion-header>\n <span\n class=\"text-base font-semibold leading-tight text-surface-900 dark:text-surface-0\"\n >{{ group.label }}</span>\n </p-accordion-header>\n <p-accordion-content>\n <div class=\"flex flex-col gap-4\">\n @if (shouldShowSearch(group)) {\n <p-iconfield iconPosition=\"left\" class=\"w-full\">\n <p-inputicon class=\"pi pi-search\"></p-inputicon>\n <input\n pInputText\n size=\"small\"\n [ngModel]=\"searchTerms()[group.code] || ''\"\n (ngModelChange)=\"updateSearchTerm(group.code, $event)\"\n [placeholder]=\"'Rechercher ' + group.label.toLowerCase()\"\n class=\"w-full\"\n />\n </p-iconfield>\n }\n\n @for (item of getFilteredChildren(group); track item.code) {\n <div class=\"flex items-center justify-between gap-2\">\n <div class=\"flex flex-1 items-center gap-2\">\n <p-checkbox\n size=\"small\"\n [inputId]=\"group.code + '_' + item.code\"\n [binary]=\"true\"\n [ngModel]=\"isFilterSelected(group.code, item.code)\"\n (ngModelChange)=\"toggleFilter(group, item)\"\n ></p-checkbox>\n @if (getColorClass(item)) {\n <div\n class=\"h-5 w-5 rounded-full\"\n [class]=\"getColorClass(item)\"\n ></div>\n }\n <ef-label\n [label]=\"item.label\"\n [for]=\"group.code + '_' + item.code\"\n styleClass=\"cursor-pointer text-base font-normal leading-tight text-surface-700 dark:text-surface-300\"\n />\n </div>\n @if (getItemCount(group.code, item.code) > 0) {\n <p-badge\n [value]=\"getItemCount(group.code, item.code)\"\n size=\"small\"\n class=\"rounded-xl! h-6! min-w-6!\"\n severity=\"secondary\"\n ></p-badge>\n }\n </div>\n }\n </div>\n </p-accordion-content>\n </p-accordion-panel>\n }\n }\n </p-accordion>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AccordionModule }, { kind: "component", type: i2$2.Accordion, selector: "p-accordion", inputs: ["value", "multiple", "styleClass", "expandIcon", "collapseIcon", "selectOnFocus", "transitionOptions", "motionOptions"], outputs: ["valueChange", "onClose", "onOpen"] }, { kind: "component", type: i2$2.AccordionPanel, selector: "p-accordion-panel, p-accordionpanel", inputs: ["value", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: i2$2.AccordionHeader, selector: "p-accordion-header, p-accordionheader" }, { kind: "component", type: i2$2.AccordionContent, selector: "p-accordion-content, p-accordioncontent" }, { kind: "ngmodule", type: BadgeModule }, { kind: "component", type: i3.Badge, selector: "p-badge", inputs: ["styleClass", "badgeSize", "size", "severity", "value", "badgeDisabled"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i1.ButtonLabel, selector: "[pButtonLabel]", inputs: ["ptButtonLabel", "pButtonLabelPT", "pButtonLabelUnstyled"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i5.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ChipModule }, { kind: "component", type: i6.Chip, selector: "p-chip", inputs: ["label", "icon", "image", "alt", "styleClass", "disabled", "removable", "removeIcon", "chipProps"], outputs: ["onRemove", "onImageError"] }, { kind: "ngmodule", type: IconFieldModule }, { kind: "component", type: i7.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "ngmodule", type: InputIconModule }, { kind: "component", type: i8.InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i9.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "labelParams", "for", "styleClass", "required"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7480
7763
|
}
|
|
7481
7764
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfProductCatalogueFilterComponent, decorators: [{
|
|
7482
7765
|
type: Component,
|
|
@@ -9602,7 +9885,7 @@ class EfTabsComponent {
|
|
|
9602
9885
|
return item.value;
|
|
9603
9886
|
}
|
|
9604
9887
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9605
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfTabsComponent, isStandalone: true, selector: "ef-tabs", inputs: { items: "items", value: "value", variant: "variant", module: "module", overflow: ["overflow", "overflow", booleanAttribute], hideCounts: ["hideCounts", "hideCounts", booleanAttribute], ariaLabel: "ariaLabel", ariaLabelKey: "ariaLabelKey", dirtyLabelKey: "dirtyLabelKey" }, outputs: { valueChange: "valueChange" }, host: { properties: { "class.ef-tabs--pill": "variant === 'pill'", "class.ef-tabs--underline": "variant === 'underline'", "class.ef-tabs--overflow": "overflow", "attr.data-module": "module ?? null" } }, queries: [{ propertyName: "panels", predicate: EfTabPanelDirective }], viewQueries: [{ propertyName: "tabEls", predicate: ["tabEl"], descendants: true }], ngImport: i0, template: "<div class=\"ef-tabs__list\" role=\"tablist\" [attr.aria-label]=\"ariaLabelKey ? (ariaLabelKey | translate) : ariaLabel\">\n @for (item of items; track trackValue($index, item); let i = $index) {\n <button\n #tabEl\n type=\"button\"\n class=\"ef-tabs__tab\"\n role=\"tab\"\n [class.is-active]=\"item.value === value\"\n [class.is-disabled]=\"!!item.disabled\"\n [attr.aria-selected]=\"item.value === value\"\n [attr.aria-disabled]=\"item.disabled ? 'true' : null\"\n [disabled]=\"item.disabled\"\n [tabindex]=\"item.value === value ? 0 : -1\"\n (click)=\"select(item)\"\n (keydown)=\"onKeydown($event, i)\"\n >\n @if (item.icon) {\n <i class=\"ef-tabs__ico\" [class]=\"item.icon\" aria-hidden=\"true\"></i>\n }\n <span class=\"ef-tabs__label\">\n @if (item.labelKey) {\n {{ item.labelKey | translate }}\n } @else {\n {{ item.label }}\n }\n </span>\n @if (!hideCounts && item.count !== undefined && item.count !== null) {\n <span class=\"ef-tabs__count\">{{ item.count }}</span>\n }\n @if (item.dirty) {\n <span class=\"ef-tabs__dot\" [attr.aria-label]=\"dirtyLabelKey | translate\"></span>\n }\n </button>\n }\n</div>\n\n@if (hasPanels()) {\n <div class=\"ef-tabs__panels\" role=\"tabpanel\">\n @if (activePanel(); as tpl) {\n <ng-container [ngTemplateOutlet]=\"tpl\"></ng-container>\n }\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:block;min-width:0}.ef-tabs__list{position:relative;display:flex;align-items:stretch;gap:2px;min-width:0;max-width:100%;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}.ef-tabs__list>*{flex:0 0 auto}.ef-tabs__panels{display:block;padding-top:var(--s-5);min-width:0;animation:ef-tabs-panel-in var(--t-base, .26s) var(--ease-out, ease-out)}@keyframes ef-tabs-panel-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}.ef-tabs__list::-webkit-scrollbar{display:none}.ef-tabs__tab{appearance:none;background:transparent;border:0;font:inherit;font-size:12.5px;font-weight:600;letter-spacing:-.005em;color:var(--text-mute);cursor:pointer;display:inline-flex;align-items:center;gap:8px;white-space:nowrap;transition:color var(--t-fast, .14s) var(--ease-out, ease-out),background-color var(--t-fast, .14s) var(--ease-out, ease-out),border-color var(--t-base, .26s) var(--ease-spring, cubic-bezier(.34, 1.56, .64, 1))}.ef-tabs__tab:focus-visible{outline:0}.ef-tabs__tab.is-disabled,.ef-tabs__tab[disabled]{opacity:.4;cursor:not-allowed}.ef-tabs__ico{font-size:13px;opacity:.75;flex-shrink:0}.ef-tabs__tab.is-active .ef-tabs__ico{opacity:1}.ef-tabs__count{font-family:var(--font-mono);font-size:10.5px;font-weight:700;background:var(--paper);border:1px solid var(--rule);color:var(--text-mute);padding:1px 7px;border-radius:var(--r-pill);min-width:20px;text-align:center;line-height:1.5;font-variant-numeric:tabular-nums}.ef-tabs__dot{width:6px;height:6px;border-radius:50%;background:var(--tenant-500);flex-shrink:0}:host(.ef-tabs--pill) .ef-tabs__list{background:var(--paper-alt);border:1px solid var(--rule);border-radius:var(--r-pill);padding:4px;gap:2px}:host(.ef-tabs--pill) .ef-tabs__tab{min-height:var(--hit, 32px);padding:7px 14px;border-radius:var(--r-pill)}:host(.ef-tabs--pill) .ef-tabs__tab:hover:not(.is-active):not(.is-disabled){color:var(--text)}:host(.ef-tabs--pill) .ef-tabs__tab:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb,var(--ink-active, #0f1115) 40%,transparent)}:host(.ef-tabs--pill) .ef-tabs__tab.is-active{background:var(--ink-active, #0f1115);color:var(--paper)}:host(.ef-tabs--pill) .ef-tabs__tab.is-active .ef-tabs__count{background:#ffffff2e;border-color:transparent;color:var(--paper)}:host(.ef-tabs--pill) .ef-tabs__tab.is-active .ef-tabs__dot{background:var(--paper)}:host(.ef-tabs--underline) .ef-tabs__list{display:flex;border-bottom:1px solid var(--rule);padding:0;gap:2px}:host(.ef-tabs--underline) .ef-tabs__tab{min-height:42px;padding:11px 14px;border-bottom:2px solid transparent;margin-bottom:-1px;border-radius:var(--r-sm) var(--r-sm) 0 0;font-size:13.5px}:host(.ef-tabs--underline) .ef-tabs__tab:hover:not(.is-active):not(.is-disabled){color:var(--text);background:var(--paper)}:host(.ef-tabs--underline) .ef-tabs__tab:focus-visible{background:var(--paper-alt);box-shadow:inset 0 0 0 2px var(--tenant-200)}:host(.ef-tabs--underline) .ef-tabs__tab.is-active{color:var(--text);border-bottom-color:var(--module, var(--ink-active))}:host(.ef-tabs--underline) .ef-tabs__tab.is-active .ef-tabs__count{background:color-mix(in srgb,var(--module, var(--ink-active)) 14%,var(--paper-alt));border-color:transparent;color:var(--module, var(--ink-active))}:host(.ef-tabs--underline) .ef-tabs__tab.is-active .ef-tabs__dot{background:var(--module, var(--tenant-500))}:host[data-module=sales]{--module: var(--m-sales)}:host[data-module=purchase]{--module: var(--m-purchase)}:host[data-module=stock]{--module: var(--m-stock)}:host[data-module=pos]{--module: var(--m-pos)}:host[data-module=marketing]{--module: var(--m-marketing)}:host[data-module=store]{--module: var(--m-store)}:host[data-module=finance]{--module: var(--m-finance)}:host[data-module=admin]{--module: var(--m-admin)}:host(.ef-tabs--overflow){position:relative;display:block}:host(.ef-tabs--overflow) .ef-tabs__list{display:flex;width:100%}:host(.ef-tabs--overflow):before,:host(.ef-tabs--overflow):after{content:\"\";position:absolute;top:0;bottom:0;width:36px;pointer-events:none;z-index:2}:host(.ef-tabs--overflow):before{inset-inline-start:0;background:linear-gradient(to right,var(--paper-alt),transparent)}:host(.ef-tabs--overflow):after{inset-inline-end:0;background:linear-gradient(to left,var(--paper-alt),transparent)}[data-theme=dark] :host(.ef-tabs--underline) .ef-tabs__tab.is-active .ef-tabs__count{background:color-mix(in srgb,var(--module, var(--ink-active)) 28%,transparent)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
9888
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfTabsComponent, isStandalone: true, selector: "ef-tabs", inputs: { items: "items", value: "value", variant: "variant", module: "module", overflow: ["overflow", "overflow", booleanAttribute], hideCounts: ["hideCounts", "hideCounts", booleanAttribute], ariaLabel: "ariaLabel", ariaLabelKey: "ariaLabelKey", dirtyLabelKey: "dirtyLabelKey" }, outputs: { valueChange: "valueChange" }, host: { properties: { "class.ef-tabs--pill": "variant === 'pill'", "class.ef-tabs--underline": "variant === 'underline'", "class.ef-tabs--overflow": "overflow", "attr.data-module": "module ?? null" } }, queries: [{ propertyName: "panels", predicate: EfTabPanelDirective }], viewQueries: [{ propertyName: "tabEls", predicate: ["tabEl"], descendants: true }], ngImport: i0, template: "<div class=\"ef-tabs__list\" role=\"tablist\" [attr.aria-label]=\"ariaLabelKey ? (ariaLabelKey | translate) : ariaLabel\">\n @for (item of items; track trackValue($index, item); let i = $index) {\n <button\n #tabEl\n type=\"button\"\n class=\"ef-tabs__tab\"\n role=\"tab\"\n [class.is-active]=\"item.value === value\"\n [class.is-disabled]=\"!!item.disabled\"\n [attr.aria-selected]=\"item.value === value\"\n [attr.aria-disabled]=\"item.disabled ? 'true' : null\"\n [disabled]=\"item.disabled\"\n [tabindex]=\"item.value === value ? 0 : -1\"\n (click)=\"select(item)\"\n (keydown)=\"onKeydown($event, i)\"\n >\n @if (item.icon) {\n <i class=\"ef-tabs__ico\" [class]=\"item.icon\" aria-hidden=\"true\"></i>\n }\n <span class=\"ef-tabs__label\">\n @if (item.labelKey) {\n {{ item.labelKey | translate }}\n } @else {\n {{ item.label }}\n }\n </span>\n @if (!hideCounts && item.count !== undefined && item.count !== null) {\n <span class=\"ef-tabs__count\">{{ item.count }}</span>\n }\n @if (item.dirty) {\n <span class=\"ef-tabs__dot\" [attr.aria-label]=\"dirtyLabelKey | translate\"></span>\n }\n </button>\n }\n</div>\n\n@if (hasPanels()) {\n <div class=\"ef-tabs__panels\" role=\"tabpanel\">\n @if (activePanel(); as tpl) {\n <ng-container [ngTemplateOutlet]=\"tpl\"></ng-container>\n }\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:block;min-width:0}.ef-tabs__list{position:relative;display:flex;align-items:stretch;gap:2px;min-width:0;max-width:100%;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}.ef-tabs__list>*{flex:0 0 auto}.ef-tabs__panels{display:block;padding-top:var(--s-5);min-width:0;animation:ef-tabs-panel-in var(--t-base, .26s) var(--ease-out, ease-out)}@keyframes ef-tabs-panel-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}.ef-tabs__list::-webkit-scrollbar{display:none}.ef-tabs__tab{appearance:none;background:transparent;border:0;font:inherit;font-size:12.5px;font-weight:600;letter-spacing:-.005em;color:var(--text-mute);cursor:pointer;display:inline-flex;align-items:center;gap:8px;white-space:nowrap;transition:color var(--t-fast, .14s) var(--ease-out, ease-out),background-color var(--t-fast, .14s) var(--ease-out, ease-out),border-color var(--t-base, .26s) var(--ease-spring, cubic-bezier(.34, 1.56, .64, 1))}.ef-tabs__tab:focus-visible{outline:0}.ef-tabs__tab.is-disabled,.ef-tabs__tab[disabled]{opacity:.4;cursor:not-allowed}.ef-tabs__ico{font-size:13px;opacity:.75;flex-shrink:0}.ef-tabs__tab.is-active .ef-tabs__ico{opacity:1}.ef-tabs__count{font-family:var(--font-mono);font-size:10.5px;font-weight:700;background:var(--paper);border:1px solid var(--rule);color:var(--text-mute);padding:1px 7px;border-radius:var(--r-pill);min-width:20px;text-align:center;line-height:1.5;font-variant-numeric:tabular-nums}.ef-tabs__dot{width:6px;height:6px;border-radius:50%;background:var(--tenant-500);flex-shrink:0}:host(.ef-tabs--pill) .ef-tabs__list{background:var(--paper-alt);border:1px solid var(--rule);border-radius:var(--r-pill);padding:4px;gap:2px}:host(.ef-tabs--pill) .ef-tabs__tab{min-height:var(--hit, 32px);padding:7px 14px;border-radius:var(--r-pill)}:host(.ef-tabs--pill) .ef-tabs__tab:hover:not(.is-active):not(.is-disabled){color:var(--text)}:host(.ef-tabs--pill) .ef-tabs__tab:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb,var(--ink-active, #0f1115) 40%,transparent)}:host(.ef-tabs--pill) .ef-tabs__tab.is-active{background:var(--ink-active, #0f1115);color:var(--paper)}:host(.ef-tabs--pill) .ef-tabs__tab.is-active .ef-tabs__count{background:#ffffff2e;border-color:transparent;color:var(--paper)}:host(.ef-tabs--pill) .ef-tabs__tab.is-active .ef-tabs__dot{background:var(--paper)}:host(.ef-tabs--underline) .ef-tabs__list{display:flex;border-bottom:1px solid var(--rule);padding:0;gap:2px}:host(.ef-tabs--underline) .ef-tabs__tab{min-height:42px;padding:11px 14px;border-bottom:2px solid transparent;margin-bottom:-1px;border-radius:var(--r-sm) var(--r-sm) 0 0;font-size:13.5px}:host(.ef-tabs--underline) .ef-tabs__tab:hover:not(.is-active):not(.is-disabled){color:var(--text);background:var(--paper)}:host(.ef-tabs--underline) .ef-tabs__tab:focus-visible{background:var(--paper-alt);box-shadow:inset 0 0 0 2px var(--tenant-200)}:host(.ef-tabs--underline) .ef-tabs__tab.is-active{color:var(--text);border-bottom-color:var(--module, var(--ink-active))}:host(.ef-tabs--underline) .ef-tabs__tab.is-active .ef-tabs__count{background:color-mix(in srgb,var(--module, var(--ink-active)) 14%,var(--paper-alt));border-color:transparent;color:var(--module, var(--ink-active))}:host(.ef-tabs--underline) .ef-tabs__tab.is-active .ef-tabs__dot{background:var(--module, var(--tenant-500))}:host[data-module=sales]{--module: var(--m-sales)}:host[data-module=purchase]{--module: var(--m-purchase)}:host[data-module=stock]{--module: var(--m-stock)}:host[data-module=pos]{--module: var(--m-pos)}:host[data-module=marketing]{--module: var(--m-marketing)}:host[data-module=store]{--module: var(--m-store)}:host[data-module=finance]{--module: var(--m-finance)}:host[data-module=admin]{--module: var(--m-admin)}:host(.ef-tabs--overflow){position:relative;display:block}:host(.ef-tabs--overflow) .ef-tabs__list{display:flex;width:100%}:host(.ef-tabs--overflow):before,:host(.ef-tabs--overflow):after{content:\"\";position:absolute;top:0;bottom:0;width:36px;pointer-events:none;z-index:2}:host(.ef-tabs--overflow):before{inset-inline-start:0;background:linear-gradient(to right,var(--paper-alt),transparent)}:host(.ef-tabs--overflow):after{inset-inline-end:0;background:linear-gradient(to left,var(--paper-alt),transparent)}[data-theme=dark] :host(.ef-tabs--underline) .ef-tabs__tab.is-active .ef-tabs__count{background:color-mix(in srgb,var(--module, var(--ink-active)) 28%,transparent)}@media(pointer:coarse){:host(.ef-tabs--pill) .ef-tabs__tab,:host(.ef-tabs--underline) .ef-tabs__tab{min-height:var(--hit-touch)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
9606
9889
|
}
|
|
9607
9890
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfTabsComponent, decorators: [{
|
|
9608
9891
|
type: Component,
|
|
@@ -9611,7 +9894,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
9611
9894
|
'[class.ef-tabs--underline]': "variant === 'underline'",
|
|
9612
9895
|
'[class.ef-tabs--overflow]': 'overflow',
|
|
9613
9896
|
'[attr.data-module]': 'module ?? null',
|
|
9614
|
-
}, template: "<div class=\"ef-tabs__list\" role=\"tablist\" [attr.aria-label]=\"ariaLabelKey ? (ariaLabelKey | translate) : ariaLabel\">\n @for (item of items; track trackValue($index, item); let i = $index) {\n <button\n #tabEl\n type=\"button\"\n class=\"ef-tabs__tab\"\n role=\"tab\"\n [class.is-active]=\"item.value === value\"\n [class.is-disabled]=\"!!item.disabled\"\n [attr.aria-selected]=\"item.value === value\"\n [attr.aria-disabled]=\"item.disabled ? 'true' : null\"\n [disabled]=\"item.disabled\"\n [tabindex]=\"item.value === value ? 0 : -1\"\n (click)=\"select(item)\"\n (keydown)=\"onKeydown($event, i)\"\n >\n @if (item.icon) {\n <i class=\"ef-tabs__ico\" [class]=\"item.icon\" aria-hidden=\"true\"></i>\n }\n <span class=\"ef-tabs__label\">\n @if (item.labelKey) {\n {{ item.labelKey | translate }}\n } @else {\n {{ item.label }}\n }\n </span>\n @if (!hideCounts && item.count !== undefined && item.count !== null) {\n <span class=\"ef-tabs__count\">{{ item.count }}</span>\n }\n @if (item.dirty) {\n <span class=\"ef-tabs__dot\" [attr.aria-label]=\"dirtyLabelKey | translate\"></span>\n }\n </button>\n }\n</div>\n\n@if (hasPanels()) {\n <div class=\"ef-tabs__panels\" role=\"tabpanel\">\n @if (activePanel(); as tpl) {\n <ng-container [ngTemplateOutlet]=\"tpl\"></ng-container>\n }\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:block;min-width:0}.ef-tabs__list{position:relative;display:flex;align-items:stretch;gap:2px;min-width:0;max-width:100%;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}.ef-tabs__list>*{flex:0 0 auto}.ef-tabs__panels{display:block;padding-top:var(--s-5);min-width:0;animation:ef-tabs-panel-in var(--t-base, .26s) var(--ease-out, ease-out)}@keyframes ef-tabs-panel-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}.ef-tabs__list::-webkit-scrollbar{display:none}.ef-tabs__tab{appearance:none;background:transparent;border:0;font:inherit;font-size:12.5px;font-weight:600;letter-spacing:-.005em;color:var(--text-mute);cursor:pointer;display:inline-flex;align-items:center;gap:8px;white-space:nowrap;transition:color var(--t-fast, .14s) var(--ease-out, ease-out),background-color var(--t-fast, .14s) var(--ease-out, ease-out),border-color var(--t-base, .26s) var(--ease-spring, cubic-bezier(.34, 1.56, .64, 1))}.ef-tabs__tab:focus-visible{outline:0}.ef-tabs__tab.is-disabled,.ef-tabs__tab[disabled]{opacity:.4;cursor:not-allowed}.ef-tabs__ico{font-size:13px;opacity:.75;flex-shrink:0}.ef-tabs__tab.is-active .ef-tabs__ico{opacity:1}.ef-tabs__count{font-family:var(--font-mono);font-size:10.5px;font-weight:700;background:var(--paper);border:1px solid var(--rule);color:var(--text-mute);padding:1px 7px;border-radius:var(--r-pill);min-width:20px;text-align:center;line-height:1.5;font-variant-numeric:tabular-nums}.ef-tabs__dot{width:6px;height:6px;border-radius:50%;background:var(--tenant-500);flex-shrink:0}:host(.ef-tabs--pill) .ef-tabs__list{background:var(--paper-alt);border:1px solid var(--rule);border-radius:var(--r-pill);padding:4px;gap:2px}:host(.ef-tabs--pill) .ef-tabs__tab{min-height:var(--hit, 32px);padding:7px 14px;border-radius:var(--r-pill)}:host(.ef-tabs--pill) .ef-tabs__tab:hover:not(.is-active):not(.is-disabled){color:var(--text)}:host(.ef-tabs--pill) .ef-tabs__tab:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb,var(--ink-active, #0f1115) 40%,transparent)}:host(.ef-tabs--pill) .ef-tabs__tab.is-active{background:var(--ink-active, #0f1115);color:var(--paper)}:host(.ef-tabs--pill) .ef-tabs__tab.is-active .ef-tabs__count{background:#ffffff2e;border-color:transparent;color:var(--paper)}:host(.ef-tabs--pill) .ef-tabs__tab.is-active .ef-tabs__dot{background:var(--paper)}:host(.ef-tabs--underline) .ef-tabs__list{display:flex;border-bottom:1px solid var(--rule);padding:0;gap:2px}:host(.ef-tabs--underline) .ef-tabs__tab{min-height:42px;padding:11px 14px;border-bottom:2px solid transparent;margin-bottom:-1px;border-radius:var(--r-sm) var(--r-sm) 0 0;font-size:13.5px}:host(.ef-tabs--underline) .ef-tabs__tab:hover:not(.is-active):not(.is-disabled){color:var(--text);background:var(--paper)}:host(.ef-tabs--underline) .ef-tabs__tab:focus-visible{background:var(--paper-alt);box-shadow:inset 0 0 0 2px var(--tenant-200)}:host(.ef-tabs--underline) .ef-tabs__tab.is-active{color:var(--text);border-bottom-color:var(--module, var(--ink-active))}:host(.ef-tabs--underline) .ef-tabs__tab.is-active .ef-tabs__count{background:color-mix(in srgb,var(--module, var(--ink-active)) 14%,var(--paper-alt));border-color:transparent;color:var(--module, var(--ink-active))}:host(.ef-tabs--underline) .ef-tabs__tab.is-active .ef-tabs__dot{background:var(--module, var(--tenant-500))}:host[data-module=sales]{--module: var(--m-sales)}:host[data-module=purchase]{--module: var(--m-purchase)}:host[data-module=stock]{--module: var(--m-stock)}:host[data-module=pos]{--module: var(--m-pos)}:host[data-module=marketing]{--module: var(--m-marketing)}:host[data-module=store]{--module: var(--m-store)}:host[data-module=finance]{--module: var(--m-finance)}:host[data-module=admin]{--module: var(--m-admin)}:host(.ef-tabs--overflow){position:relative;display:block}:host(.ef-tabs--overflow) .ef-tabs__list{display:flex;width:100%}:host(.ef-tabs--overflow):before,:host(.ef-tabs--overflow):after{content:\"\";position:absolute;top:0;bottom:0;width:36px;pointer-events:none;z-index:2}:host(.ef-tabs--overflow):before{inset-inline-start:0;background:linear-gradient(to right,var(--paper-alt),transparent)}:host(.ef-tabs--overflow):after{inset-inline-end:0;background:linear-gradient(to left,var(--paper-alt),transparent)}[data-theme=dark] :host(.ef-tabs--underline) .ef-tabs__tab.is-active .ef-tabs__count{background:color-mix(in srgb,var(--module, var(--ink-active)) 28%,transparent)}\n"] }]
|
|
9897
|
+
}, template: "<div class=\"ef-tabs__list\" role=\"tablist\" [attr.aria-label]=\"ariaLabelKey ? (ariaLabelKey | translate) : ariaLabel\">\n @for (item of items; track trackValue($index, item); let i = $index) {\n <button\n #tabEl\n type=\"button\"\n class=\"ef-tabs__tab\"\n role=\"tab\"\n [class.is-active]=\"item.value === value\"\n [class.is-disabled]=\"!!item.disabled\"\n [attr.aria-selected]=\"item.value === value\"\n [attr.aria-disabled]=\"item.disabled ? 'true' : null\"\n [disabled]=\"item.disabled\"\n [tabindex]=\"item.value === value ? 0 : -1\"\n (click)=\"select(item)\"\n (keydown)=\"onKeydown($event, i)\"\n >\n @if (item.icon) {\n <i class=\"ef-tabs__ico\" [class]=\"item.icon\" aria-hidden=\"true\"></i>\n }\n <span class=\"ef-tabs__label\">\n @if (item.labelKey) {\n {{ item.labelKey | translate }}\n } @else {\n {{ item.label }}\n }\n </span>\n @if (!hideCounts && item.count !== undefined && item.count !== null) {\n <span class=\"ef-tabs__count\">{{ item.count }}</span>\n }\n @if (item.dirty) {\n <span class=\"ef-tabs__dot\" [attr.aria-label]=\"dirtyLabelKey | translate\"></span>\n }\n </button>\n }\n</div>\n\n@if (hasPanels()) {\n <div class=\"ef-tabs__panels\" role=\"tabpanel\">\n @if (activePanel(); as tpl) {\n <ng-container [ngTemplateOutlet]=\"tpl\"></ng-container>\n }\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:block;min-width:0}.ef-tabs__list{position:relative;display:flex;align-items:stretch;gap:2px;min-width:0;max-width:100%;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}.ef-tabs__list>*{flex:0 0 auto}.ef-tabs__panels{display:block;padding-top:var(--s-5);min-width:0;animation:ef-tabs-panel-in var(--t-base, .26s) var(--ease-out, ease-out)}@keyframes ef-tabs-panel-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}.ef-tabs__list::-webkit-scrollbar{display:none}.ef-tabs__tab{appearance:none;background:transparent;border:0;font:inherit;font-size:12.5px;font-weight:600;letter-spacing:-.005em;color:var(--text-mute);cursor:pointer;display:inline-flex;align-items:center;gap:8px;white-space:nowrap;transition:color var(--t-fast, .14s) var(--ease-out, ease-out),background-color var(--t-fast, .14s) var(--ease-out, ease-out),border-color var(--t-base, .26s) var(--ease-spring, cubic-bezier(.34, 1.56, .64, 1))}.ef-tabs__tab:focus-visible{outline:0}.ef-tabs__tab.is-disabled,.ef-tabs__tab[disabled]{opacity:.4;cursor:not-allowed}.ef-tabs__ico{font-size:13px;opacity:.75;flex-shrink:0}.ef-tabs__tab.is-active .ef-tabs__ico{opacity:1}.ef-tabs__count{font-family:var(--font-mono);font-size:10.5px;font-weight:700;background:var(--paper);border:1px solid var(--rule);color:var(--text-mute);padding:1px 7px;border-radius:var(--r-pill);min-width:20px;text-align:center;line-height:1.5;font-variant-numeric:tabular-nums}.ef-tabs__dot{width:6px;height:6px;border-radius:50%;background:var(--tenant-500);flex-shrink:0}:host(.ef-tabs--pill) .ef-tabs__list{background:var(--paper-alt);border:1px solid var(--rule);border-radius:var(--r-pill);padding:4px;gap:2px}:host(.ef-tabs--pill) .ef-tabs__tab{min-height:var(--hit, 32px);padding:7px 14px;border-radius:var(--r-pill)}:host(.ef-tabs--pill) .ef-tabs__tab:hover:not(.is-active):not(.is-disabled){color:var(--text)}:host(.ef-tabs--pill) .ef-tabs__tab:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb,var(--ink-active, #0f1115) 40%,transparent)}:host(.ef-tabs--pill) .ef-tabs__tab.is-active{background:var(--ink-active, #0f1115);color:var(--paper)}:host(.ef-tabs--pill) .ef-tabs__tab.is-active .ef-tabs__count{background:#ffffff2e;border-color:transparent;color:var(--paper)}:host(.ef-tabs--pill) .ef-tabs__tab.is-active .ef-tabs__dot{background:var(--paper)}:host(.ef-tabs--underline) .ef-tabs__list{display:flex;border-bottom:1px solid var(--rule);padding:0;gap:2px}:host(.ef-tabs--underline) .ef-tabs__tab{min-height:42px;padding:11px 14px;border-bottom:2px solid transparent;margin-bottom:-1px;border-radius:var(--r-sm) var(--r-sm) 0 0;font-size:13.5px}:host(.ef-tabs--underline) .ef-tabs__tab:hover:not(.is-active):not(.is-disabled){color:var(--text);background:var(--paper)}:host(.ef-tabs--underline) .ef-tabs__tab:focus-visible{background:var(--paper-alt);box-shadow:inset 0 0 0 2px var(--tenant-200)}:host(.ef-tabs--underline) .ef-tabs__tab.is-active{color:var(--text);border-bottom-color:var(--module, var(--ink-active))}:host(.ef-tabs--underline) .ef-tabs__tab.is-active .ef-tabs__count{background:color-mix(in srgb,var(--module, var(--ink-active)) 14%,var(--paper-alt));border-color:transparent;color:var(--module, var(--ink-active))}:host(.ef-tabs--underline) .ef-tabs__tab.is-active .ef-tabs__dot{background:var(--module, var(--tenant-500))}:host[data-module=sales]{--module: var(--m-sales)}:host[data-module=purchase]{--module: var(--m-purchase)}:host[data-module=stock]{--module: var(--m-stock)}:host[data-module=pos]{--module: var(--m-pos)}:host[data-module=marketing]{--module: var(--m-marketing)}:host[data-module=store]{--module: var(--m-store)}:host[data-module=finance]{--module: var(--m-finance)}:host[data-module=admin]{--module: var(--m-admin)}:host(.ef-tabs--overflow){position:relative;display:block}:host(.ef-tabs--overflow) .ef-tabs__list{display:flex;width:100%}:host(.ef-tabs--overflow):before,:host(.ef-tabs--overflow):after{content:\"\";position:absolute;top:0;bottom:0;width:36px;pointer-events:none;z-index:2}:host(.ef-tabs--overflow):before{inset-inline-start:0;background:linear-gradient(to right,var(--paper-alt),transparent)}:host(.ef-tabs--overflow):after{inset-inline-end:0;background:linear-gradient(to left,var(--paper-alt),transparent)}[data-theme=dark] :host(.ef-tabs--underline) .ef-tabs__tab.is-active .ef-tabs__count{background:color-mix(in srgb,var(--module, var(--ink-active)) 28%,transparent)}@media(pointer:coarse){:host(.ef-tabs--pill) .ef-tabs__tab,:host(.ef-tabs--underline) .ef-tabs__tab{min-height:var(--hit-touch)}}\n"] }]
|
|
9615
9898
|
}], propDecorators: { items: [{
|
|
9616
9899
|
type: Input,
|
|
9617
9900
|
args: [{ required: true }]
|
|
@@ -10048,6 +10331,73 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
10048
10331
|
args: [{ selector: 'ef-pdf-preview', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, TranslateModule, EfDialogComponent], template: "<ef-dialog\n [(visible)]=\"visible\"\n [headerKey]=\"titleKey()\"\n [subtitle]=\"fileName()\"\n icon=\"pi pi-file-pdf\"\n [style]=\"{ width: '92vw', 'max-width': '1280px' }\"\n styleClass=\"ef-pdf-preview-dialog\"\n [actions]=\"actions()\"\n>\n <div class=\"pdf-frame-wrap\">\n @if (safeUrl()) {\n <iframe class=\"pdf-frame\" [src]=\"safeUrl()\" [title]=\"fileName()\"></iframe>\n } @else {\n <div class=\"pdf-empty\">{{ 'pdf_preview.empty' | translate }}</div>\n }\n </div>\n</ef-dialog>\n", styles: [":host{display:contents}.pdf-frame-wrap{height:75vh;min-height:320px}.pdf-frame{width:100%;height:100%;border:0;border-radius:var(--r-md);background:#525659}.pdf-empty{display:grid;place-items:center;height:100%;color:var(--text-mute);font-size:13px}\n"] }]
|
|
10049
10332
|
}], ctorParameters: () => [], propDecorators: { blob: [{ type: i0.Input, args: [{ isSignal: true, alias: "blob", required: false }] }], fileName: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileName", required: false }] }], visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }, { type: i0.Output, args: ["visibleChange"] }], titleKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleKey", required: false }] }], showDownload: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDownload", required: false }] }], showPrint: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPrint", required: false }] }], showOpenInNewTab: [{ type: i0.Input, args: [{ isSignal: true, alias: "showOpenInNewTab", required: false }] }], showWhatsappShare: [{ type: i0.Input, args: [{ isSignal: true, alias: "showWhatsappShare", required: false }] }], whatsappPhone: [{ type: i0.Input, args: [{ isSignal: true, alias: "whatsappPhone", required: false }] }], defaultCallingCode: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultCallingCode", required: false }] }] } });
|
|
10050
10333
|
|
|
10334
|
+
/**
|
|
10335
|
+
* Shows a complete HTML document (an email template, rendered with sample
|
|
10336
|
+
* data) inside a frame that cannot act.
|
|
10337
|
+
*
|
|
10338
|
+
* The frame's `sandbox` attribute is written in the template and empty, and
|
|
10339
|
+
* no input reaches it: the document runs no script, has an opaque origin
|
|
10340
|
+
* (no cookies, no storage, no access to the parent), cannot submit a form,
|
|
10341
|
+
* open a popup or navigate the top window. The document comes from
|
|
10342
|
+
* `srcdoc`, so there is no URL to load and no new origin for the CSP; it
|
|
10343
|
+
* inherits the host page's policy, whose `style-src 'unsafe-inline'` lets
|
|
10344
|
+
* the template's own styles apply.
|
|
10345
|
+
*
|
|
10346
|
+
* The frame always has an accessible name (WCAG 4.1.2): `titleKey`, or the
|
|
10347
|
+
* shared `ef_html_preview_title` key when none is given. Consuming apps define it:
|
|
10348
|
+
* fr "Aperçu", ar "معاينة", en "Preview".
|
|
10349
|
+
*
|
|
10350
|
+
* `width: 100%` and `min-width: 0` keep a 600px email table scrolling inside
|
|
10351
|
+
* the frame on a phone instead of widening the page.
|
|
10352
|
+
*
|
|
10353
|
+
* ```html
|
|
10354
|
+
* <ef-html-preview [html]="previewHtml()" titleKey="email_templates_preview_title" [height]="560" />
|
|
10355
|
+
* ```
|
|
10356
|
+
*/
|
|
10357
|
+
/** The title key the frame falls back to; apps add it to fr.json, ar.json and en.json. */
|
|
10358
|
+
const EF_HTML_PREVIEW_DEFAULT_TITLE_KEY = 'ef_html_preview_title';
|
|
10359
|
+
class EfHtmlPreviewComponent {
|
|
10360
|
+
/** The full document to show. Sanitized by the server before it gets here. */
|
|
10361
|
+
html = input('', ...(ngDevMode ? [{ debugName: "html" }] : /* istanbul ignore next */ []));
|
|
10362
|
+
/** i18n key of the frame's accessible title; `ef_html_preview_title` when empty. */
|
|
10363
|
+
titleKey = input('', ...(ngDevMode ? [{ debugName: "titleKey" }] : /* istanbul ignore next */ []));
|
|
10364
|
+
/** Frame height in pixels. */
|
|
10365
|
+
height = input(480, ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
|
|
10366
|
+
sanitizer = inject(DomSanitizer);
|
|
10367
|
+
effectiveTitleKey = computed(() => this.titleKey() || EF_HTML_PREVIEW_DEFAULT_TITLE_KEY, ...(ngDevMode ? [{ debugName: "effectiveTitleKey" }] : /* istanbul ignore next */ []));
|
|
10368
|
+
/**
|
|
10369
|
+
* Angular's sanitizer strips every `style` attribute and `<style>` block
|
|
10370
|
+
* from a bound `srcdoc`, which would preview an email without its
|
|
10371
|
+
* layout, so the HTML is trusted explicitly. The guarantee is the frame's
|
|
10372
|
+
* empty sandbox: nothing in the document can run, submit, open a window
|
|
10373
|
+
* or reach this page, even for raw author HTML of an unsaved edit. The
|
|
10374
|
+
* server's sanitization of stored templates is a second layer, not the
|
|
10375
|
+
* reason this is safe. Never reuse this bypass for markup rendered
|
|
10376
|
+
* outside such a frame.
|
|
10377
|
+
*/
|
|
10378
|
+
trustedHtml = computed(() => this.sanitizer.bypassSecurityTrustHtml(this.html() ?? ''), ...(ngDevMode ? [{ debugName: "trustedHtml" }] : /* istanbul ignore next */ []));
|
|
10379
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfHtmlPreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10380
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.11", type: EfHtmlPreviewComponent, isStandalone: true, selector: "ef-html-preview", inputs: { html: { classPropertyName: "html", publicName: "html", isSignal: true, isRequired: false, transformFunction: null }, titleKey: { classPropertyName: "titleKey", publicName: "titleKey", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `<iframe
|
|
10381
|
+
class="ef-html-preview__frame"
|
|
10382
|
+
sandbox=""
|
|
10383
|
+
referrerpolicy="no-referrer"
|
|
10384
|
+
[srcdoc]="trustedHtml()"
|
|
10385
|
+
[attr.title]="effectiveTitleKey() | translate"
|
|
10386
|
+
[style.height.px]="height()"
|
|
10387
|
+
></iframe>`, isInline: true, styles: [":host{display:block;min-width:0;max-width:100%}.ef-html-preview__frame{display:block;width:100%;max-width:100%;min-width:0;border:1px solid var(--rule, #e5e5e5);border-radius:var(--r-2, 8px);background:#fff}\n"], dependencies: [{ kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
10388
|
+
}
|
|
10389
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfHtmlPreviewComponent, decorators: [{
|
|
10390
|
+
type: Component,
|
|
10391
|
+
args: [{ selector: 'ef-html-preview', standalone: true, imports: [TranslateModule], template: `<iframe
|
|
10392
|
+
class="ef-html-preview__frame"
|
|
10393
|
+
sandbox=""
|
|
10394
|
+
referrerpolicy="no-referrer"
|
|
10395
|
+
[srcdoc]="trustedHtml()"
|
|
10396
|
+
[attr.title]="effectiveTitleKey() | translate"
|
|
10397
|
+
[style.height.px]="height()"
|
|
10398
|
+
></iframe>`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;min-width:0;max-width:100%}.ef-html-preview__frame{display:block;width:100%;max-width:100%;min-width:0;border:1px solid var(--rule, #e5e5e5);border-radius:var(--r-2, 8px);background:#fff}\n"] }]
|
|
10399
|
+
}], propDecorators: { html: [{ type: i0.Input, args: [{ isSignal: true, alias: "html", required: false }] }], titleKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleKey", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }] } });
|
|
10400
|
+
|
|
10051
10401
|
/**
|
|
10052
10402
|
* Comptoir toast region — V2 successor to PrimeNG `<p-toast>`.
|
|
10053
10403
|
*
|
|
@@ -11147,6 +11497,45 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
11147
11497
|
}, template: "<p class=\"ef-seo-preview__label\" data-role=\"preview-label\" [id]=\"labelId\">{{ labelKey() | translate }}</p>\n<div class=\"ef-seo-preview__box\" data-role=\"preview-box\" [attr.lang]=\"contentLang()\">\n <p class=\"ef-seo-preview__url\" data-role=\"preview-url\" dir=\"ltr\">{{ shownUrl() }}</p>\n <p class=\"ef-seo-preview__title\" data-role=\"preview-title\" dir=\"auto\">{{ shownTitle() }}</p>\n <p class=\"ef-seo-preview__description\" data-role=\"preview-description\" dir=\"auto\">{{ shownDescription() }}</p>\n</div>\n", styles: [":host{display:grid;gap:var(--s-2);min-width:0}.ef-seo-preview__label{margin:0;font-size:12px;color:var(--text-mute)}.ef-seo-preview__box{display:grid;gap:2px;min-width:0;padding:var(--s-3);border:1px solid var(--rule-soft);border-radius:var(--r-md, 8px);background:#fff;color:#4d5156}.ef-seo-preview__box p{margin:0;min-width:0;overflow-wrap:anywhere}.ef-seo-preview__url{color:#006621;font-family:var(--font-mono, monospace);font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:left}.ef-seo-preview__title{color:#1a0dab;font-size:16px;font-weight:500;letter-spacing:-.005em;line-height:1.3}.ef-seo-preview__description{font-size:13px;line-height:1.4;margin-top:4px}\n"] }]
|
|
11148
11498
|
}], propDecorators: { pageTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageTitle", required: false }] }], url: [{ type: i0.Input, args: [{ isSignal: true, alias: "url", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], labelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelKey", required: false }] }], titleMaxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleMaxLength", required: false }] }], descriptionMaxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "descriptionMaxLength", required: false }] }], contentLang: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentLang", required: false }] }] } });
|
|
11149
11499
|
|
|
11500
|
+
/**
|
|
11501
|
+
* The value a text field takes when `text` is inserted where the user left
|
|
11502
|
+
* the caret, and where the caret goes next.
|
|
11503
|
+
*
|
|
11504
|
+
* `selectionStart` is only the user's choice once the field has had focus:
|
|
11505
|
+
* before that the browser reports wherever writing `value` left it. A field
|
|
11506
|
+
* never entered, or one whose type has no selection (`email`, `number`),
|
|
11507
|
+
* gets the text appended. A field that lost focus keeps its last selection,
|
|
11508
|
+
* which is what a toolbar button beside it relies on.
|
|
11509
|
+
*/
|
|
11510
|
+
function insertAtSelection(field, text, wasFocused) {
|
|
11511
|
+
const current = field.value;
|
|
11512
|
+
let start = null;
|
|
11513
|
+
let end = null;
|
|
11514
|
+
try {
|
|
11515
|
+
start = field.selectionStart;
|
|
11516
|
+
end = field.selectionEnd;
|
|
11517
|
+
}
|
|
11518
|
+
catch {
|
|
11519
|
+
// Some input types throw on selection access instead of returning null.
|
|
11520
|
+
}
|
|
11521
|
+
if (!wasFocused || start === null || end === null) {
|
|
11522
|
+
return { value: current + text, caret: current.length + text.length };
|
|
11523
|
+
}
|
|
11524
|
+
const from = Math.min(start, end, current.length);
|
|
11525
|
+
const to = Math.min(Math.max(start, end), current.length);
|
|
11526
|
+
return { value: current.slice(0, from) + text + current.slice(to), caret: from + text.length };
|
|
11527
|
+
}
|
|
11528
|
+
/** Moves the caret, ignoring input types that have no selection. */
|
|
11529
|
+
function placeCaret(field, caret) {
|
|
11530
|
+
field.focus();
|
|
11531
|
+
try {
|
|
11532
|
+
field.setSelectionRange(caret, caret);
|
|
11533
|
+
}
|
|
11534
|
+
catch {
|
|
11535
|
+
// `email` / `number` inputs: no selection API, the caret stays at the end.
|
|
11536
|
+
}
|
|
11537
|
+
}
|
|
11538
|
+
|
|
11150
11539
|
class EfInputTextComponent extends AbstractEfFormControl {
|
|
11151
11540
|
/* Identity / label / placeholder / aria / required / readonly /
|
|
11152
11541
|
disabled all inherited from AbstractEfFormControl. */
|
|
@@ -11156,6 +11545,11 @@ class EfInputTextComponent extends AbstractEfFormControl {
|
|
|
11156
11545
|
fluid = true;
|
|
11157
11546
|
inline = false;
|
|
11158
11547
|
autocomplete;
|
|
11548
|
+
/** Direction of the typed value, applied to the native input. Use
|
|
11549
|
+
* `'ltr'` for IBAN / RIB / phone values on an Arabic screen, `'auto'` to
|
|
11550
|
+
* follow the value's first strong character. The label and messages keep
|
|
11551
|
+
* the page direction. Null or absent leaves the input unchanged. */
|
|
11552
|
+
dir = null;
|
|
11159
11553
|
/** Clearable (showClear / canClear) is inherited from AbstractEfFormControl;
|
|
11160
11554
|
* we only supply the value predicate + the reset. */
|
|
11161
11555
|
get hasValue() {
|
|
@@ -11189,6 +11583,7 @@ class EfInputTextComponent extends AbstractEfFormControl {
|
|
|
11189
11583
|
onChange = () => { };
|
|
11190
11584
|
onTouched = () => { };
|
|
11191
11585
|
ngControl = inject(NgControl, { self: true, optional: true });
|
|
11586
|
+
hostRef = inject(ElementRef);
|
|
11192
11587
|
constructor() {
|
|
11193
11588
|
super();
|
|
11194
11589
|
if (this.ngControl) {
|
|
@@ -11225,15 +11620,52 @@ class EfInputTextComponent extends AbstractEfFormControl {
|
|
|
11225
11620
|
this.onChange(value);
|
|
11226
11621
|
this.valueChangeEvent.emit(value);
|
|
11227
11622
|
}
|
|
11623
|
+
/** Set once the field itself has had focus; see {@link insertAtSelection}. */
|
|
11624
|
+
wasFocused = false;
|
|
11625
|
+
markFocused(event) {
|
|
11626
|
+
// Only the native field counts: focus on the clear button says nothing
|
|
11627
|
+
// about where the user left the caret.
|
|
11628
|
+
if (event.target === this.nativeField())
|
|
11629
|
+
this.wasFocused = true;
|
|
11630
|
+
}
|
|
11631
|
+
/**
|
|
11632
|
+
* Inserts `text` at the caret (replacing a selection), or at the end when
|
|
11633
|
+
* the field was never focused, then puts the caret after it. Emits the
|
|
11634
|
+
* change once, exactly as typing would. Does nothing when read-only or
|
|
11635
|
+
* disabled, and refuses the whole insert when the result would pass
|
|
11636
|
+
* `maxlength` (a cut placeholder token is worse than none).
|
|
11637
|
+
*
|
|
11638
|
+
* The value is written programmatically, so the browser's undo history for
|
|
11639
|
+
* the field is reset and no DOM `input` event fires; the form control
|
|
11640
|
+
* (`onChange`) and `valueChangeEvent` are the contract.
|
|
11641
|
+
*/
|
|
11642
|
+
insertAtCursor(text) {
|
|
11643
|
+
if (this.readonly() || this.isDisabled())
|
|
11644
|
+
return;
|
|
11645
|
+
const field = this.nativeField();
|
|
11646
|
+
if (!field)
|
|
11647
|
+
return;
|
|
11648
|
+
const { value, caret } = insertAtSelection(field, text, this.wasFocused);
|
|
11649
|
+
if (this.maxlength != null && value.length > this.maxlength)
|
|
11650
|
+
return;
|
|
11651
|
+
// Write the DOM first so the later [value] binding finds it unchanged
|
|
11652
|
+
// and leaves the caret where we put it.
|
|
11653
|
+
field.value = value;
|
|
11654
|
+
this.handleInput(value);
|
|
11655
|
+
placeCaret(field, caret);
|
|
11656
|
+
}
|
|
11657
|
+
nativeField() {
|
|
11658
|
+
return this.hostRef.nativeElement.querySelector('input');
|
|
11659
|
+
}
|
|
11228
11660
|
handleBlur() {
|
|
11229
11661
|
this.onTouched();
|
|
11230
11662
|
}
|
|
11231
11663
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfInputTextComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11232
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfInputTextComponent, isStandalone: true, selector: "ef-input-text", inputs: { size: "size", type: "type", maxlength: "maxlength", fluid: ["fluid", "fluid", booleanAttribute], inline: ["inline", "inline", booleanAttribute], autocomplete: "autocomplete", variant: "variant", prefix: "prefix", suffix: "suffix", value: "value" }, outputs: { valueChangeEvent: "valueChangeEvent" }, host: { properties: { "class.ef-inline": "this.isInline" } }, usesInheritance: true, ngImport: i0, template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n @if (variant === 'comptoir') {\n @if (prefix || suffix) {\n <!-- Comptoir variant with prefix/suffix \u2014 wraps the input in\n an .ef-input-group so the addons share the border + focus\n ring with the input. -->\n <div class=\"ef-input-group\" [class.is-invalid]=\"isInvalid\">\n @if (prefix) {\n <span class=\"ef-input-addon is-prefix\" aria-hidden=\"true\">{{ prefix }}</span>\n }\n <input\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [type]=\"type\"\n [value]=\"value\"\n (input)=\"handleInput($any($event.target).value)\"\n (blur)=\"handleBlur()\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [attr.maxlength]=\"maxlength ?? null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? null\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n />\n @if (canClear) {\n <ef-clear-button\n class=\"ef-group-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n @if (suffix) {\n <span class=\"ef-input-addon is-suffix\" aria-hidden=\"true\">{{ suffix }}</span>\n }\n </div>\n } @else {\n <!-- Comptoir variant \u2014 bare native input (+ optional inline clear). -->\n <div class=\"ef-input-wrap\">\n <input\n class=\"ef-input\"\n [class.has-clear]=\"canClear\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [type]=\"type\"\n [value]=\"value\"\n (input)=\"handleInput($any($event.target).value)\"\n (blur)=\"handleBlur()\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [attr.maxlength]=\"maxlength ?? null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? null\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n />\n @if (canClear) {\n <ef-clear-button\n class=\"ef-input-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n </div>\n }\n } @else {\n <!-- PrimeNG variant (default) \u2014 v1 styling. -->\n <input\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [type]=\"type\"\n pInputText\n [pSize]=\"size\"\n [value]=\"value\"\n (input)=\"handleInput($any($event.target).value)\"\n (blur)=\"handleBlur()\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [attr.maxlength]=\"maxlength ?? null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? null\"\n [class.ng-invalid]=\"isInvalid\"\n [class.ng-touched]=\"ngControl?.touched\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n fluid\n />\n }\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}\n"], dependencies: [{ kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i9.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "for", "styleClass", "required"] }, { kind: "component", type: EfClearButtonComponent, selector: "ef-clear-button", inputs: ["ariaLabel", "disabled"], outputs: ["clear"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
11664
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfInputTextComponent, isStandalone: true, selector: "ef-input-text", inputs: { size: "size", type: "type", maxlength: "maxlength", fluid: ["fluid", "fluid", booleanAttribute], inline: ["inline", "inline", booleanAttribute], autocomplete: "autocomplete", dir: "dir", variant: "variant", prefix: "prefix", suffix: "suffix", value: "value" }, outputs: { valueChangeEvent: "valueChangeEvent" }, host: { listeners: { "focusin": "markFocused($event)" }, properties: { "class.ef-inline": "this.isInline" } }, usesInheritance: true, ngImport: i0, template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n @if (variant === 'comptoir') {\n @if (prefix || suffix) {\n <!-- Comptoir variant with prefix/suffix \u2014 wraps the input in\n an .ef-input-group so the addons share the border + focus\n ring with the input. -->\n <div class=\"ef-input-group\" [class.is-invalid]=\"isInvalid\">\n @if (prefix) {\n <span class=\"ef-input-addon is-prefix\" aria-hidden=\"true\">{{ prefix }}</span>\n }\n <input\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [attr.dir]=\"dir || null\"\n [type]=\"type\"\n [value]=\"value\"\n (input)=\"handleInput($any($event.target).value)\"\n (blur)=\"handleBlur()\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [attr.maxlength]=\"maxlength ?? null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? null\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n />\n @if (canClear) {\n <ef-clear-button\n class=\"ef-group-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n @if (suffix) {\n <span class=\"ef-input-addon is-suffix\" aria-hidden=\"true\">{{ suffix }}</span>\n }\n </div>\n } @else {\n <!-- Comptoir variant \u2014 bare native input (+ optional inline clear). -->\n <div class=\"ef-input-wrap\">\n <input\n class=\"ef-input\"\n [class.has-clear]=\"canClear\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [attr.dir]=\"dir || null\"\n [type]=\"type\"\n [value]=\"value\"\n (input)=\"handleInput($any($event.target).value)\"\n (blur)=\"handleBlur()\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [attr.maxlength]=\"maxlength ?? null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? null\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n />\n @if (canClear) {\n <ef-clear-button\n class=\"ef-input-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n </div>\n }\n } @else {\n <!-- PrimeNG variant (default) \u2014 v1 styling. -->\n <input\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [attr.dir]=\"dir || null\"\n [type]=\"type\"\n pInputText\n [pSize]=\"size\"\n [value]=\"value\"\n (input)=\"handleInput($any($event.target).value)\"\n (blur)=\"handleBlur()\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [attr.maxlength]=\"maxlength ?? null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? null\"\n [class.ng-invalid]=\"isInvalid\"\n [class.ng-touched]=\"ngControl?.touched\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n fluid\n />\n }\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}\n"], dependencies: [{ kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i9.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "labelParams", "for", "styleClass", "required"] }, { kind: "component", type: EfClearButtonComponent, selector: "ef-clear-button", inputs: ["ariaLabel", "disabled"], outputs: ["clear"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
11233
11665
|
}
|
|
11234
11666
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfInputTextComponent, decorators: [{
|
|
11235
11667
|
type: Component,
|
|
11236
|
-
args: [{ selector: 'ef-input-text', standalone: true, imports: [InputTextModule, FormsModule, TranslateModule, EfLabelComponent, EfClearButtonComponent], template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n @if (variant === 'comptoir') {\n @if (prefix || suffix) {\n <!-- Comptoir variant with prefix/suffix \u2014 wraps the input in\n an .ef-input-group so the addons share the border + focus\n ring with the input. -->\n <div class=\"ef-input-group\" [class.is-invalid]=\"isInvalid\">\n @if (prefix) {\n <span class=\"ef-input-addon is-prefix\" aria-hidden=\"true\">{{ prefix }}</span>\n }\n <input\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [type]=\"type\"\n [value]=\"value\"\n (input)=\"handleInput($any($event.target).value)\"\n (blur)=\"handleBlur()\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [attr.maxlength]=\"maxlength ?? null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? null\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n />\n @if (canClear) {\n <ef-clear-button\n class=\"ef-group-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n @if (suffix) {\n <span class=\"ef-input-addon is-suffix\" aria-hidden=\"true\">{{ suffix }}</span>\n }\n </div>\n } @else {\n <!-- Comptoir variant \u2014 bare native input (+ optional inline clear). -->\n <div class=\"ef-input-wrap\">\n <input\n class=\"ef-input\"\n [class.has-clear]=\"canClear\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [type]=\"type\"\n [value]=\"value\"\n (input)=\"handleInput($any($event.target).value)\"\n (blur)=\"handleBlur()\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [attr.maxlength]=\"maxlength ?? null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? null\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n />\n @if (canClear) {\n <ef-clear-button\n class=\"ef-input-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n </div>\n }\n } @else {\n <!-- PrimeNG variant (default) \u2014 v1 styling. -->\n <input\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [type]=\"type\"\n pInputText\n [pSize]=\"size\"\n [value]=\"value\"\n (input)=\"handleInput($any($event.target).value)\"\n (blur)=\"handleBlur()\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [attr.maxlength]=\"maxlength ?? null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? null\"\n [class.ng-invalid]=\"isInvalid\"\n [class.ng-touched]=\"ngControl?.touched\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n fluid\n />\n }\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}\n"] }]
|
|
11668
|
+
args: [{ selector: 'ef-input-text', standalone: true, imports: [InputTextModule, FormsModule, TranslateModule, EfLabelComponent, EfClearButtonComponent], template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n @if (variant === 'comptoir') {\n @if (prefix || suffix) {\n <!-- Comptoir variant with prefix/suffix \u2014 wraps the input in\n an .ef-input-group so the addons share the border + focus\n ring with the input. -->\n <div class=\"ef-input-group\" [class.is-invalid]=\"isInvalid\">\n @if (prefix) {\n <span class=\"ef-input-addon is-prefix\" aria-hidden=\"true\">{{ prefix }}</span>\n }\n <input\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [attr.dir]=\"dir || null\"\n [type]=\"type\"\n [value]=\"value\"\n (input)=\"handleInput($any($event.target).value)\"\n (blur)=\"handleBlur()\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [attr.maxlength]=\"maxlength ?? null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? null\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n />\n @if (canClear) {\n <ef-clear-button\n class=\"ef-group-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n @if (suffix) {\n <span class=\"ef-input-addon is-suffix\" aria-hidden=\"true\">{{ suffix }}</span>\n }\n </div>\n } @else {\n <!-- Comptoir variant \u2014 bare native input (+ optional inline clear). -->\n <div class=\"ef-input-wrap\">\n <input\n class=\"ef-input\"\n [class.has-clear]=\"canClear\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [attr.dir]=\"dir || null\"\n [type]=\"type\"\n [value]=\"value\"\n (input)=\"handleInput($any($event.target).value)\"\n (blur)=\"handleBlur()\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [attr.maxlength]=\"maxlength ?? null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? null\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n />\n @if (canClear) {\n <ef-clear-button\n class=\"ef-input-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n </div>\n }\n } @else {\n <!-- PrimeNG variant (default) \u2014 v1 styling. -->\n <input\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [attr.dir]=\"dir || null\"\n [type]=\"type\"\n pInputText\n [pSize]=\"size\"\n [value]=\"value\"\n (input)=\"handleInput($any($event.target).value)\"\n (blur)=\"handleBlur()\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [attr.maxlength]=\"maxlength ?? null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete ?? null\"\n [class.ng-invalid]=\"isInvalid\"\n [class.ng-touched]=\"ngControl?.touched\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n fluid\n />\n }\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}\n"] }]
|
|
11237
11669
|
}], ctorParameters: () => [], propDecorators: { size: [{
|
|
11238
11670
|
type: Input
|
|
11239
11671
|
}], type: [{
|
|
@@ -11248,6 +11680,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
11248
11680
|
args: [{ transform: booleanAttribute }]
|
|
11249
11681
|
}], autocomplete: [{
|
|
11250
11682
|
type: Input
|
|
11683
|
+
}], dir: [{
|
|
11684
|
+
type: Input
|
|
11251
11685
|
}], variant: [{
|
|
11252
11686
|
type: Input
|
|
11253
11687
|
}], prefix: [{
|
|
@@ -11261,6 +11695,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
11261
11695
|
type: Output
|
|
11262
11696
|
}], value: [{
|
|
11263
11697
|
type: Input
|
|
11698
|
+
}], markFocused: [{
|
|
11699
|
+
type: HostListener,
|
|
11700
|
+
args: ['focusin', ['$event']]
|
|
11264
11701
|
}] } });
|
|
11265
11702
|
|
|
11266
11703
|
/**
|
|
@@ -11372,11 +11809,11 @@ class EfCheckboxComponent extends AbstractEfFormControl {
|
|
|
11372
11809
|
this.onTouched();
|
|
11373
11810
|
}
|
|
11374
11811
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfCheckboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11375
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfCheckboxComponent, isStandalone: true, selector: "ef-checkbox", inputs: { value: "value", binary: ["binary", "binary", booleanAttribute], variant: "variant", inline: ["inline", "inline", booleanAttribute], checked: "checked" }, outputs: { checkedChange: "checkedChange" }, host: { properties: { "class.ef-stacked": "this.isStacked" } }, usesInheritance: true, ngImport: i0, template: "@if (!inline && hasLabel) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-checkbox-control\">\n @if (variant === 'comptoir') {\n <label class=\"ef-checkbox-native\" [class.is-disabled]=\"isDisabled()\">\n <input\n type=\"checkbox\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [checked]=\"!!checked\"\n [disabled]=\"isDisabled()\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"(showRequired || serverErrors) ? errorsId : null\"\n (change)=\"handleNativeChange($any($event.target).checked)\"\n />\n <span class=\"ef-checkbox-box\" aria-hidden=\"true\"></span>\n @if (inline && hasLabel) {\n <span class=\"ef-checkbox-label\">\n {{ labelKey() ? (labelKey() | translate) : label() }}\n @if (required()) {\n <span class=\"ef-checkbox-required\" aria-hidden=\"true\">*</span>\n }\n </span>\n }\n </label>\n } @else {\n <p-checkbox\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [ngModel]=\"checked\"\n [ngModelOptions]=\"{standalone: true}\"\n (onChange)=\"handleChange($event)\"\n [value]=\"value\"\n [binary]=\"binary\"\n [disabled]=\"isDisabled()\"\n [ariaLabel]=\"effectiveAriaLabel || undefined\"\n />\n @if (inline && hasLabel) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" [for]=\"effectiveId\" styleClass=\"form-label-left cursor-pointer\" />\n }\n }\n</div>\n@if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n}\n", styles: [":host{display:inline-flex;flex-wrap:wrap;align-items:center;gap:.5rem;min-width:0}.ef-checkbox-control{display:inline-flex;align-items:center;gap:.5rem;min-width:0}:host(.ef-stacked){display:block}:host(.ef-stacked)>.ef-checkbox-control{display:flex;min-height:var(--hit-field)}\n"], dependencies: [{ kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i5.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "for", "styleClass", "required"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
11812
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfCheckboxComponent, isStandalone: true, selector: "ef-checkbox", inputs: { value: "value", binary: ["binary", "binary", booleanAttribute], variant: "variant", inline: ["inline", "inline", booleanAttribute], checked: "checked" }, outputs: { checkedChange: "checkedChange" }, host: { properties: { "class.ef-stacked": "this.isStacked" } }, usesInheritance: true, ngImport: i0, template: "@if (!inline && hasLabel) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-checkbox-control\">\n @if (variant === 'comptoir') {\n <label class=\"ef-checkbox-native\" [class.is-disabled]=\"isDisabled()\">\n <input\n type=\"checkbox\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [checked]=\"!!checked\"\n [disabled]=\"isDisabled()\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"(showRequired || serverErrors) ? errorsId : null\"\n (change)=\"handleNativeChange($any($event.target).checked)\"\n />\n <span class=\"ef-checkbox-box\" aria-hidden=\"true\"></span>\n @if (inline && hasLabel) {\n <span class=\"ef-checkbox-label\">\n {{ labelKey() ? (labelKey() | translate) : label() }}\n @if (required()) {\n <span class=\"ef-checkbox-required\" aria-hidden=\"true\">*</span>\n }\n </span>\n }\n </label>\n } @else {\n <p-checkbox\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [ngModel]=\"checked\"\n [ngModelOptions]=\"{standalone: true}\"\n (onChange)=\"handleChange($event)\"\n [value]=\"value\"\n [binary]=\"binary\"\n [disabled]=\"isDisabled()\"\n [ariaLabel]=\"effectiveAriaLabel || undefined\"\n />\n @if (inline && hasLabel) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" [for]=\"effectiveId\" styleClass=\"form-label-left cursor-pointer\" />\n }\n }\n</div>\n@if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n}\n", styles: [":host{display:inline-flex;flex-wrap:wrap;align-items:center;gap:.5rem;min-width:0}.ef-checkbox-control{display:inline-flex;align-items:center;gap:.5rem;min-width:0}:host(.ef-stacked){display:block}:host(.ef-stacked)>.ef-checkbox-control{display:flex;min-height:var(--hit-field)}\n"], dependencies: [{ kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i5.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "labelParams", "for", "styleClass", "required"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
11376
11813
|
}
|
|
11377
11814
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfCheckboxComponent, decorators: [{
|
|
11378
11815
|
type: Component,
|
|
11379
|
-
args: [{ selector: 'ef-checkbox', standalone: true, imports: [CheckboxModule, FormsModule, TranslateModule, EfLabelComponent], template: "@if (!inline && hasLabel) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-checkbox-control\">\n @if (variant === 'comptoir') {\n <label class=\"ef-checkbox-native\" [class.is-disabled]=\"isDisabled()\">\n <input\n type=\"checkbox\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [checked]=\"!!checked\"\n [disabled]=\"isDisabled()\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"(showRequired || serverErrors) ? errorsId : null\"\n (change)=\"handleNativeChange($any($event.target).checked)\"\n />\n <span class=\"ef-checkbox-box\" aria-hidden=\"true\"></span>\n @if (inline && hasLabel) {\n <span class=\"ef-checkbox-label\">\n {{ labelKey() ? (labelKey() | translate) : label() }}\n @if (required()) {\n <span class=\"ef-checkbox-required\" aria-hidden=\"true\">*</span>\n }\n </span>\n }\n </label>\n } @else {\n <p-checkbox\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [ngModel]=\"checked\"\n [ngModelOptions]=\"{standalone: true}\"\n (onChange)=\"handleChange($event)\"\n [value]=\"value\"\n [binary]=\"binary\"\n [disabled]=\"isDisabled()\"\n [ariaLabel]=\"effectiveAriaLabel || undefined\"\n />\n @if (inline && hasLabel) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" [for]=\"effectiveId\" styleClass=\"form-label-left cursor-pointer\" />\n }\n }\n</div>\n@if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n}\n", styles: [":host{display:inline-flex;flex-wrap:wrap;align-items:center;gap:.5rem;min-width:0}.ef-checkbox-control{display:inline-flex;align-items:center;gap:.5rem;min-width:0}:host(.ef-stacked){display:block}:host(.ef-stacked)>.ef-checkbox-control{display:flex;min-height:var(--hit-field)}\n"] }]
|
|
11816
|
+
args: [{ selector: 'ef-checkbox', standalone: true, imports: [CheckboxModule, FormsModule, TranslateModule, EfLabelComponent], template: "@if (!inline && hasLabel) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-checkbox-control\">\n @if (variant === 'comptoir') {\n <label class=\"ef-checkbox-native\" [class.is-disabled]=\"isDisabled()\">\n <input\n type=\"checkbox\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [checked]=\"!!checked\"\n [disabled]=\"isDisabled()\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"(showRequired || serverErrors) ? errorsId : null\"\n (change)=\"handleNativeChange($any($event.target).checked)\"\n />\n <span class=\"ef-checkbox-box\" aria-hidden=\"true\"></span>\n @if (inline && hasLabel) {\n <span class=\"ef-checkbox-label\">\n {{ labelKey() ? (labelKey() | translate) : label() }}\n @if (required()) {\n <span class=\"ef-checkbox-required\" aria-hidden=\"true\">*</span>\n }\n </span>\n }\n </label>\n } @else {\n <p-checkbox\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [ngModel]=\"checked\"\n [ngModelOptions]=\"{standalone: true}\"\n (onChange)=\"handleChange($event)\"\n [value]=\"value\"\n [binary]=\"binary\"\n [disabled]=\"isDisabled()\"\n [ariaLabel]=\"effectiveAriaLabel || undefined\"\n />\n @if (inline && hasLabel) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" [for]=\"effectiveId\" styleClass=\"form-label-left cursor-pointer\" />\n }\n }\n</div>\n@if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n}\n", styles: [":host{display:inline-flex;flex-wrap:wrap;align-items:center;gap:.5rem;min-width:0}.ef-checkbox-control{display:inline-flex;align-items:center;gap:.5rem;min-width:0}:host(.ef-stacked){display:block}:host(.ef-stacked)>.ef-checkbox-control{display:flex;min-height:var(--hit-field)}\n"] }]
|
|
11380
11817
|
}], ctorParameters: () => [], propDecorators: { value: [{
|
|
11381
11818
|
type: Input
|
|
11382
11819
|
}], binary: [{
|
|
@@ -11476,7 +11913,7 @@ class EfToggleSwitchComponent extends AbstractEfFormControl {
|
|
|
11476
11913
|
styleClass="form-label-left cursor-pointer"
|
|
11477
11914
|
/>
|
|
11478
11915
|
}
|
|
11479
|
-
`, isInline: true, styles: [":host(.ef-toggleswitch--row){display:flex;align-items:center;gap:var(--s-3);min-width:0}:host(.ef-toggleswitch--row) p-toggleswitch{flex-shrink:0}:host(.ef-toggleswitch--row) ef-label{display:block;min-width:0;overflow-wrap:anywhere}\n"], dependencies: [{ kind: "ngmodule", type: ToggleSwitchModule }, { kind: "component", type: i1$c.ToggleSwitch, selector: "p-toggleswitch, p-toggleSwitch, p-toggle-switch", inputs: ["styleClass", "tabindex", "inputId", "readonly", "trueValue", "falseValue", "ariaLabel", "size", "ariaLabelledBy", "autofocus"], outputs: ["onChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "for", "styleClass", "required"] }] });
|
|
11916
|
+
`, isInline: true, styles: [":host(.ef-toggleswitch--row){display:flex;align-items:center;gap:var(--s-3);min-width:0}:host(.ef-toggleswitch--row) p-toggleswitch{flex-shrink:0}:host(.ef-toggleswitch--row) ef-label{display:block;min-width:0;overflow-wrap:anywhere}\n"], dependencies: [{ kind: "ngmodule", type: ToggleSwitchModule }, { kind: "component", type: i1$c.ToggleSwitch, selector: "p-toggleswitch, p-toggleSwitch, p-toggle-switch", inputs: ["styleClass", "tabindex", "inputId", "readonly", "trueValue", "falseValue", "ariaLabel", "size", "ariaLabelledBy", "autofocus"], outputs: ["onChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "labelParams", "for", "styleClass", "required"] }] });
|
|
11480
11917
|
}
|
|
11481
11918
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfToggleSwitchComponent, decorators: [{
|
|
11482
11919
|
type: Component,
|
|
@@ -11606,7 +12043,7 @@ class EfDatepickerComponent {
|
|
|
11606
12043
|
this.onTouched();
|
|
11607
12044
|
}
|
|
11608
12045
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfDatepickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11609
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfDatepickerComponent, isStandalone: true, selector: "ef-datepicker", inputs: { label: "label", labelKey: "labelKey", inputId: "inputId", name: "name", placeholder: "placeholder", placeholderKey: "placeholderKey", size: "size", required: ["required", "required", booleanAttribute], fluid: ["fluid", "fluid", booleanAttribute], inline: ["inline", "inline", booleanAttribute], disabled: ["disabled", "disabled", booleanAttribute], showIcon: ["showIcon", "showIcon", booleanAttribute], iconDisplay: "iconDisplay", selectionMode: "selectionMode", dateFormat: "dateFormat", appendTo: "appendTo", showTime: ["showTime", "showTime", booleanAttribute], showButtonBar: ["showButtonBar", "showButtonBar", booleanAttribute], minDate: "minDate", maxDate: "maxDate", autofocus: ["autofocus", "autofocus", booleanAttribute] }, outputs: { dateSelectEvent: "dateSelectEvent" }, host: { properties: { "class.ef-inline": "this.isInline" } }, ngImport: i0, template: "@if (labelKey || label) {\n <ef-label [labelKey]=\"labelKey\" [label]=\"label\" [for]=\"effectiveId\" [required]=\"required\" />\n}\n<div class=\"ef-field-control\">\n <p-datepicker\n [autofocus]=\"autofocus\"\n [inputId]=\"effectiveId\"\n [name]=\"$any(name)\"\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"handleChange($event)\"\n (onBlur)=\"handleBlur()\"\n [placeholder]=\"effectivePlaceholder\"\n [size]=\"size\"\n [disabled]=\"isDisabled\"\n [showIcon]=\"showIcon\"\n [iconDisplay]=\"iconDisplay\"\n [selectionMode]=\"selectionMode\"\n [dateFormat]=\"dateFormat\"\n [appendTo]=\"appendTo\"\n [showTime]=\"showTime\"\n [showButtonBar]=\"showButtonBar\"\n [minDate]=\"minDate\"\n [maxDate]=\"maxDate\"\n [fluid]=\"fluid\"\n [class.ng-invalid]=\"isInvalid\"\n [class.ng-touched]=\"ngControl?.touched\"\n />\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}\n"], dependencies: [{ kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i1$d.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "for", "styleClass", "required"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
12046
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfDatepickerComponent, isStandalone: true, selector: "ef-datepicker", inputs: { label: "label", labelKey: "labelKey", inputId: "inputId", name: "name", placeholder: "placeholder", placeholderKey: "placeholderKey", size: "size", required: ["required", "required", booleanAttribute], fluid: ["fluid", "fluid", booleanAttribute], inline: ["inline", "inline", booleanAttribute], disabled: ["disabled", "disabled", booleanAttribute], showIcon: ["showIcon", "showIcon", booleanAttribute], iconDisplay: "iconDisplay", selectionMode: "selectionMode", dateFormat: "dateFormat", appendTo: "appendTo", showTime: ["showTime", "showTime", booleanAttribute], showButtonBar: ["showButtonBar", "showButtonBar", booleanAttribute], minDate: "minDate", maxDate: "maxDate", autofocus: ["autofocus", "autofocus", booleanAttribute] }, outputs: { dateSelectEvent: "dateSelectEvent" }, host: { properties: { "class.ef-inline": "this.isInline" } }, ngImport: i0, template: "@if (labelKey || label) {\n <ef-label [labelKey]=\"labelKey\" [label]=\"label\" [for]=\"effectiveId\" [required]=\"required\" />\n}\n<div class=\"ef-field-control\">\n <p-datepicker\n [autofocus]=\"autofocus\"\n [inputId]=\"effectiveId\"\n [name]=\"$any(name)\"\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"handleChange($event)\"\n (onBlur)=\"handleBlur()\"\n [placeholder]=\"effectivePlaceholder\"\n [size]=\"size\"\n [disabled]=\"isDisabled\"\n [showIcon]=\"showIcon\"\n [iconDisplay]=\"iconDisplay\"\n [selectionMode]=\"selectionMode\"\n [dateFormat]=\"dateFormat\"\n [appendTo]=\"appendTo\"\n [showTime]=\"showTime\"\n [showButtonBar]=\"showButtonBar\"\n [minDate]=\"minDate\"\n [maxDate]=\"maxDate\"\n [fluid]=\"fluid\"\n [class.ng-invalid]=\"isInvalid\"\n [class.ng-touched]=\"ngControl?.touched\"\n />\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}\n"], dependencies: [{ kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i1$d.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "labelParams", "for", "styleClass", "required"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
11610
12047
|
}
|
|
11611
12048
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfDatepickerComponent, decorators: [{
|
|
11612
12049
|
type: Component,
|
|
@@ -11678,6 +12115,17 @@ class EfPasswordComponent extends AbstractEfFormControl {
|
|
|
11678
12115
|
toggleMask = true;
|
|
11679
12116
|
feedback = false;
|
|
11680
12117
|
autocomplete;
|
|
12118
|
+
/**
|
|
12119
|
+
* Rendering variant, as `ef-input-text`. `'comptoir'` skins the PrimeNG
|
|
12120
|
+
* input to the native `.ef-input` look (--hit-field height, --r-md radius,
|
|
12121
|
+
* 12px padding, --paper-alt fill) so it lines up with the comptoir inputs
|
|
12122
|
+
* beside it; the show/hide toggle becomes a --hit-field square target.
|
|
12123
|
+
* `'primeng'` (default) keeps the stock PrimeNG look.
|
|
12124
|
+
*/
|
|
12125
|
+
variant = 'primeng';
|
|
12126
|
+
get fieldStyleClass() {
|
|
12127
|
+
return this.variant === 'comptoir' ? 'ef-password-field ef-password--comptoir' : 'ef-password-field';
|
|
12128
|
+
}
|
|
11681
12129
|
/**
|
|
11682
12130
|
* Take focus on load. Bound explicitly rather than left unset: PrimeNG's
|
|
11683
12131
|
* AutoFocus directive tests `autofocus === false`, and the control declares
|
|
@@ -11693,8 +12141,24 @@ class EfPasswordComponent extends AbstractEfFormControl {
|
|
|
11693
12141
|
onChange = () => { };
|
|
11694
12142
|
onTouched = () => { };
|
|
11695
12143
|
ngControl = inject(NgControl, { self: true, optional: true });
|
|
12144
|
+
host = inject(ElementRef);
|
|
11696
12145
|
constructor() {
|
|
11697
12146
|
super();
|
|
12147
|
+
// p-password has no describedby input, so link the hint (and any extra ids)
|
|
12148
|
+
// to the native input here, as the other controls do in their templates.
|
|
12149
|
+
afterEveryRender(() => {
|
|
12150
|
+
const input = this.host.nativeElement.querySelector('input');
|
|
12151
|
+
const ids = this.describedBy(false);
|
|
12152
|
+
if (!input)
|
|
12153
|
+
return;
|
|
12154
|
+
// Write only on change: this runs after every render in the app.
|
|
12155
|
+
if ((input.getAttribute('aria-describedby') || null) === (ids || null))
|
|
12156
|
+
return;
|
|
12157
|
+
if (ids)
|
|
12158
|
+
input.setAttribute('aria-describedby', ids);
|
|
12159
|
+
else
|
|
12160
|
+
input.removeAttribute('aria-describedby');
|
|
12161
|
+
});
|
|
11698
12162
|
if (this.ngControl) {
|
|
11699
12163
|
this.ngControl.valueAccessor = this;
|
|
11700
12164
|
}
|
|
@@ -11740,11 +12204,11 @@ class EfPasswordComponent extends AbstractEfFormControl {
|
|
|
11740
12204
|
this.onTouched();
|
|
11741
12205
|
}
|
|
11742
12206
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfPasswordComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11743
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfPasswordComponent, isStandalone: true, selector: "ef-password", inputs: { size: "size", fluid: ["fluid", "fluid", booleanAttribute], inline: ["inline", "inline", booleanAttribute], toggleMask: ["toggleMask", "toggleMask", booleanAttribute], feedback: ["feedback", "feedback", booleanAttribute], autocomplete: "autocomplete", autofocus: ["autofocus", "autofocus", booleanAttribute] }, outputs: { valueChangeEvent: "valueChangeEvent" }, host: { properties: { "class.ef-inline": "this.isInline" } }, usesInheritance: true, ngImport: i0, template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n <p-password\n [autofocus]=\"autofocus\"\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"handleChange($event)\"\n (onBlur)=\"handleBlur()\"\n [toggleMask]=\"toggleMask\"\n [feedback]=\"feedback\"\n [placeholder]=\"effectivePlaceholder\"\n [size]=\"size\"\n [disabled]=\"isDisabled()\"\n [
|
|
12207
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfPasswordComponent, isStandalone: true, selector: "ef-password", inputs: { size: "size", fluid: ["fluid", "fluid", booleanAttribute], inline: ["inline", "inline", booleanAttribute], toggleMask: ["toggleMask", "toggleMask", booleanAttribute], feedback: ["feedback", "feedback", booleanAttribute], autocomplete: "autocomplete", variant: "variant", autofocus: ["autofocus", "autofocus", booleanAttribute] }, outputs: { valueChangeEvent: "valueChangeEvent" }, host: { properties: { "class.ef-inline": "this.isInline" } }, usesInheritance: true, ngImport: i0, template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n <p-password\n [autofocus]=\"autofocus\"\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"handleChange($event)\"\n (onBlur)=\"handleBlur()\"\n [toggleMask]=\"toggleMask\"\n [feedback]=\"feedback\"\n [placeholder]=\"effectivePlaceholder\"\n [size]=\"size\"\n [disabled]=\"isDisabled()\"\n [autocomplete]=\"autocomplete\"\n [class.ng-invalid]=\"isInvalid\"\n [class.ng-touched]=\"ngControl?.touched\"\n [showClear]=\"canClear\"\n (onClear)=\"clearValue()\"\n [styleClass]=\"fieldStyleClass\"\n fluid\n >\n <ng-template #clearicon>\n <span class=\"ef-clear\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\">\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </span>\n </ng-template>\n </p-password>\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}\n"], dependencies: [{ kind: "ngmodule", type: PasswordModule }, { kind: "component", type: i1$e.Password, selector: "p-password", inputs: ["ariaLabel", "ariaLabelledBy", "label", "promptLabel", "mediumRegex", "strongRegex", "weakLabel", "mediumLabel", "maxLength", "strongLabel", "inputId", "feedback", "toggleMask", "inputStyleClass", "styleClass", "inputStyle", "showTransitionOptions", "hideTransitionOptions", "autocomplete", "placeholder", "showClear", "autofocus", "tabindex", "appendTo", "motionOptions", "overlayOptions"], outputs: ["onFocus", "onBlur", "onClear"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "labelParams", "for", "styleClass", "required"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
11744
12208
|
}
|
|
11745
12209
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfPasswordComponent, decorators: [{
|
|
11746
12210
|
type: Component,
|
|
11747
|
-
args: [{ selector: 'ef-password', standalone: true, imports: [PasswordModule, FormsModule, TranslateModule, EfLabelComponent], template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n <p-password\n [autofocus]=\"autofocus\"\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"handleChange($event)\"\n (onBlur)=\"handleBlur()\"\n [toggleMask]=\"toggleMask\"\n [feedback]=\"feedback\"\n [placeholder]=\"effectivePlaceholder\"\n [size]=\"size\"\n [disabled]=\"isDisabled()\"\n [
|
|
12211
|
+
args: [{ selector: 'ef-password', standalone: true, imports: [PasswordModule, FormsModule, TranslateModule, EfLabelComponent], template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n <p-password\n [autofocus]=\"autofocus\"\n [inputId]=\"effectiveId\"\n [name]=\"name()\"\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"handleChange($event)\"\n (onBlur)=\"handleBlur()\"\n [toggleMask]=\"toggleMask\"\n [feedback]=\"feedback\"\n [placeholder]=\"effectivePlaceholder\"\n [size]=\"size\"\n [disabled]=\"isDisabled()\"\n [autocomplete]=\"autocomplete\"\n [class.ng-invalid]=\"isInvalid\"\n [class.ng-touched]=\"ngControl?.touched\"\n [showClear]=\"canClear\"\n (onClear)=\"clearValue()\"\n [styleClass]=\"fieldStyleClass\"\n fluid\n >\n <ng-template #clearicon>\n <span class=\"ef-clear\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\">\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </span>\n </ng-template>\n </p-password>\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}\n"] }]
|
|
11748
12212
|
}], ctorParameters: () => [], propDecorators: { size: [{
|
|
11749
12213
|
type: Input
|
|
11750
12214
|
}], fluid: [{
|
|
@@ -11761,6 +12225,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
11761
12225
|
args: [{ transform: booleanAttribute }]
|
|
11762
12226
|
}], autocomplete: [{
|
|
11763
12227
|
type: Input
|
|
12228
|
+
}], variant: [{
|
|
12229
|
+
type: Input
|
|
11764
12230
|
}], autofocus: [{
|
|
11765
12231
|
type: Input,
|
|
11766
12232
|
args: [{ transform: booleanAttribute }]
|
|
@@ -11853,11 +12319,11 @@ class EfMultiSelectComponent extends AbstractEfFormControl {
|
|
|
11853
12319
|
this.selectedValues = [];
|
|
11854
12320
|
}
|
|
11855
12321
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfMultiSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11856
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfMultiSelectComponent, isStandalone: true, selector: "ef-multi-select", inputs: { optionLabel: "optionLabel", optionValue: "optionValue", options: "options", filteredOptions: "filteredOptions", maxSelectedLabels: "maxSelectedLabels", display: "display", size: "size", selectionLimit: "selectionLimit", filters: "filters", inline: ["inline", "inline", booleanAttribute] }, outputs: { changeEvent: "changeEvent" }, host: { properties: { "class.ef-inline": "this.isInline", "class.ef-required": "this.isRequired" } }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n <p-multiSelect\n [id]=\"effectiveId\"\n [name]=\"name()\"\n [placeholder]=\"effectivePlaceholder\"\n [options]=\"filteredOptions\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [maxSelectedLabels]=\"maxSelectedLabels\"\n [ngModel]=\"selectedValues\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"onInternalModelChange($event)\"\n (onClear)=\"handleClear()\"\n [disabled]=\"isDisabled()\"\n [display]=\"display\"\n [size]=\"size\"\n [selectionLimit]=\"selectionLimit\"\n [showClear]=\"showClear()\"\n fluid\n />\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block}:host:not(.ef-required) ::ng-deep .p-multiselect.ng-invalid.ng-dirty{border-color:var(--p-multiselect-border-color, #d1d5db)}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i2.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "for", "styleClass", "required"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
12322
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfMultiSelectComponent, isStandalone: true, selector: "ef-multi-select", inputs: { optionLabel: "optionLabel", optionValue: "optionValue", options: "options", filteredOptions: "filteredOptions", maxSelectedLabels: "maxSelectedLabels", display: "display", size: "size", selectionLimit: "selectionLimit", filters: "filters", inline: ["inline", "inline", booleanAttribute] }, outputs: { changeEvent: "changeEvent" }, host: { properties: { "class.ef-inline": "this.isInline", "class.ef-required": "this.isRequired" } }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n <p-multiSelect\n [id]=\"effectiveId\"\n [name]=\"name()\"\n [placeholder]=\"effectivePlaceholder\"\n [options]=\"filteredOptions\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [maxSelectedLabels]=\"maxSelectedLabels\"\n [ngModel]=\"selectedValues\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"onInternalModelChange($event)\"\n (onClear)=\"handleClear()\"\n [disabled]=\"isDisabled()\"\n [display]=\"display\"\n [size]=\"size\"\n [selectionLimit]=\"selectionLimit\"\n [showClear]=\"showClear()\"\n fluid\n />\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block}:host:not(.ef-required) ::ng-deep .p-multiselect.ng-invalid.ng-dirty{border-color:var(--p-multiselect-border-color, #d1d5db)}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i2.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "labelParams", "for", "styleClass", "required"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
11857
12323
|
}
|
|
11858
12324
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfMultiSelectComponent, decorators: [{
|
|
11859
12325
|
type: Component,
|
|
11860
|
-
args: [{ selector: 'ef-multi-select', standalone: true, imports: [FormsModule, MultiSelectModule, TranslateModule, EfLabelComponent], template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n <p-multiSelect\n [id]=\"effectiveId\"\n [name]=\"name()\"\n [placeholder]=\"effectivePlaceholder\"\n [options]=\"filteredOptions\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [maxSelectedLabels]=\"maxSelectedLabels\"\n [ngModel]=\"selectedValues\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"onInternalModelChange($event)\"\n (onClear)=\"handleClear()\"\n [disabled]=\"isDisabled()\"\n [display]=\"display\"\n [size]=\"size\"\n [selectionLimit]=\"selectionLimit\"\n [showClear]=\"showClear()\"\n fluid\n />\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block}:host:not(.ef-required) ::ng-deep .p-multiselect.ng-invalid.ng-dirty{border-color:var(--p-multiselect-border-color, #d1d5db)}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}\n"] }]
|
|
12326
|
+
args: [{ selector: 'ef-multi-select', standalone: true, imports: [FormsModule, MultiSelectModule, TranslateModule, EfLabelComponent], template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n <p-multiSelect\n [id]=\"effectiveId\"\n [name]=\"name()\"\n [placeholder]=\"effectivePlaceholder\"\n [options]=\"filteredOptions\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [maxSelectedLabels]=\"maxSelectedLabels\"\n [ngModel]=\"selectedValues\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"onInternalModelChange($event)\"\n (onClear)=\"handleClear()\"\n [disabled]=\"isDisabled()\"\n [display]=\"display\"\n [size]=\"size\"\n [selectionLimit]=\"selectionLimit\"\n [showClear]=\"showClear()\"\n fluid\n />\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}:host{display:block}:host:not(.ef-required) ::ng-deep .p-multiselect.ng-invalid.ng-dirty{border-color:var(--p-multiselect-border-color, #d1d5db)}:host(.ef-inline){display:flex;align-items:baseline;gap:.5rem}:host(.ef-inline)>label{white-space:nowrap;flex-shrink:0}:host(.ef-inline)>.ef-field-control{flex:1;min-width:0}\n"] }]
|
|
11861
12327
|
}], ctorParameters: () => [], propDecorators: { changeEvent: [{
|
|
11862
12328
|
type: Output
|
|
11863
12329
|
}], optionLabel: [{
|
|
@@ -11929,11 +12395,11 @@ class EfSelectButtonComponent extends AbstractEfFormControl {
|
|
|
11929
12395
|
this.onTouched();
|
|
11930
12396
|
}
|
|
11931
12397
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfSelectButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11932
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfSelectButtonComponent, isStandalone: true, selector: "ef-selectbutton", inputs: { options: "options", optionLabel: "optionLabel", optionValue: "optionValue", multiple: ["multiple", "multiple", booleanAttribute], allowEmpty: ["allowEmpty", "allowEmpty", booleanAttribute], size: "size", styleClass: "styleClass" }, queries: [{ propertyName: "itemTemplate", first: true, predicate: ["item"], descendants: true }], usesInheritance: true, ngImport: i0, template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" />\n}\n<p-selectbutton\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (onChange)=\"handleChange($event)\"\n [options]=\"options\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [multiple]=\"multiple\"\n [allowEmpty]=\"allowEmpty\"\n [disabled]=\"isDisabled()\"\n [size]=\"size\"\n [styleClass]=\"styleClass\"\n>\n @if (itemTemplate) {\n <ng-template let-item pTemplate=\"item\">\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\" />\n </ng-template>\n }\n</p-selectbutton>\n", dependencies: [{ kind: "ngmodule", type: SelectButtonModule }, { kind: "component", type: i1$f.SelectButton, selector: "p-selectButton, p-selectbutton, p-select-button", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "styleClass", "ariaLabelledBy", "dataKey", "autofocus", "size", "fluid"], outputs: ["onOptionClick", "onChange"] }, { kind: "directive", type: i2$3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "for", "styleClass", "required"] }] });
|
|
12398
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfSelectButtonComponent, isStandalone: true, selector: "ef-selectbutton", inputs: { options: "options", optionLabel: "optionLabel", optionValue: "optionValue", multiple: ["multiple", "multiple", booleanAttribute], allowEmpty: ["allowEmpty", "allowEmpty", booleanAttribute], size: "size", styleClass: "styleClass" }, queries: [{ propertyName: "itemTemplate", first: true, predicate: ["item"], descendants: true }], usesInheritance: true, ngImport: i0, template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" />\n}\n<p-selectbutton\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (onChange)=\"handleChange($event)\"\n [options]=\"options\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [multiple]=\"multiple\"\n [allowEmpty]=\"allowEmpty\"\n [disabled]=\"isDisabled()\"\n [size]=\"size\"\n [styleClass]=\"styleClass\"\n>\n @if (itemTemplate) {\n <ng-template let-item pTemplate=\"item\">\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\" />\n </ng-template>\n }\n</p-selectbutton>\n", dependencies: [{ kind: "ngmodule", type: SelectButtonModule }, { kind: "component", type: i1$f.SelectButton, selector: "p-selectButton, p-selectbutton, p-select-button", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "styleClass", "ariaLabelledBy", "dataKey", "autofocus", "size", "fluid"], outputs: ["onOptionClick", "onChange"] }, { kind: "directive", type: i2$3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "labelParams", "for", "styleClass", "required"] }] });
|
|
11933
12399
|
}
|
|
11934
12400
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfSelectButtonComponent, decorators: [{
|
|
11935
12401
|
type: Component,
|
|
11936
|
-
args: [{ selector: 'ef-selectbutton', standalone: true, imports: [SelectButtonModule, FormsModule, TranslateModule, CommonModule, EfLabelComponent], template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" />\n}\n<p-selectbutton\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (onChange)=\"handleChange($event)\"\n [options]=\"options\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [multiple]=\"multiple\"\n [allowEmpty]=\"allowEmpty\"\n [disabled]=\"isDisabled()\"\n [size]=\"size\"\n [styleClass]=\"styleClass\"\n>\n @if (itemTemplate) {\n <ng-template let-item pTemplate=\"item\">\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\" />\n </ng-template>\n }\n</p-selectbutton>\n" }]
|
|
12402
|
+
args: [{ selector: 'ef-selectbutton', standalone: true, imports: [SelectButtonModule, FormsModule, TranslateModule, CommonModule, EfLabelComponent], template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" />\n}\n<p-selectbutton\n [ngModel]=\"value\"\n [ngModelOptions]=\"{standalone: true}\"\n (onChange)=\"handleChange($event)\"\n [options]=\"options\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [multiple]=\"multiple\"\n [allowEmpty]=\"allowEmpty\"\n [disabled]=\"isDisabled()\"\n [size]=\"size\"\n [styleClass]=\"styleClass\"\n>\n @if (itemTemplate) {\n <ng-template let-item pTemplate=\"item\">\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\" />\n </ng-template>\n }\n</p-selectbutton>\n" }]
|
|
11937
12403
|
}], ctorParameters: () => [], propDecorators: { options: [{
|
|
11938
12404
|
type: Input
|
|
11939
12405
|
}], optionLabel: [{
|
|
@@ -12609,7 +13075,7 @@ class EfChangePasswordComponent {
|
|
|
12609
13075
|
});
|
|
12610
13076
|
}
|
|
12611
13077
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfChangePasswordComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12612
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfChangePasswordComponent, isStandalone: true, selector: "ef-change-password", inputs: { requireCurrent: { classPropertyName: "requireCurrent", publicName: "requireCurrent", isSignal: true, isRequired: false, transformFunction: null }, busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, minLength: { classPropertyName: "minLength", publicName: "minLength", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { submitted: "submitted" }, ngImport: i0, template: "<ef-form-grid>\n @if (requireCurrent()) {\n <ef-password\n class=\"col-12\"\n name=\"currentPassword\"\n labelKey=\"ef_change_password_current\"\n autocomplete=\"current-password\"\n required\n (valueChangeEvent)=\"currentPassword.set($event)\"\n />\n }\n <ef-password\n class=\"col-6\"\n name=\"newPassword\"\n labelKey=\"ef_change_password_new\"\n autocomplete=\"new-password\"\n required\n (valueChangeEvent)=\"newPassword.set($event)\"\n />\n <ef-password\n class=\"col-6\"\n name=\"newPasswordConfirmation\"\n labelKey=\"ef_change_password_confirm\"\n autocomplete=\"new-password\"\n required\n (valueChangeEvent)=\"confirmation.set($event)\"\n />\n</ef-form-grid>\n\n@if (validationKeys().length || errors().length) {\n <ul class=\"ef-change-password__errors\">\n @for (key of validationKeys(); track $index) {\n <li>{{ key | translate }}</li>\n }\n <!-- Server messages arrive already translated (Identity returns prose,\n not keys), so they are printed verbatim. -->\n @for (error of errors(); track $index) {\n <li>{{ error }}</li>\n }\n </ul>\n}\n", styles: [":host{display:grid;gap:var(--s-2, 8px);min-width:0}.ef-change-password__errors{display:grid;gap:4px;margin:0;padding:0;list-style:none;min-width:0;font-size:12px;line-height:1.4;color:var(--st-cancelled-fg, #b42318)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfPasswordComponent, selector: "ef-password", inputs: ["size", "fluid", "inline", "toggleMask", "feedback", "autocomplete", "autofocus"], outputs: ["valueChangeEvent"] }, { kind: "component", type: EfFormGridComponent, selector: "ef-form-grid" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
13078
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfChangePasswordComponent, isStandalone: true, selector: "ef-change-password", inputs: { requireCurrent: { classPropertyName: "requireCurrent", publicName: "requireCurrent", isSignal: true, isRequired: false, transformFunction: null }, busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, minLength: { classPropertyName: "minLength", publicName: "minLength", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { submitted: "submitted" }, ngImport: i0, template: "<ef-form-grid>\n @if (requireCurrent()) {\n <ef-password\n class=\"col-12\"\n name=\"currentPassword\"\n labelKey=\"ef_change_password_current\"\n autocomplete=\"current-password\"\n required\n (valueChangeEvent)=\"currentPassword.set($event)\"\n />\n }\n <ef-password\n class=\"col-6\"\n name=\"newPassword\"\n labelKey=\"ef_change_password_new\"\n autocomplete=\"new-password\"\n required\n (valueChangeEvent)=\"newPassword.set($event)\"\n />\n <ef-password\n class=\"col-6\"\n name=\"newPasswordConfirmation\"\n labelKey=\"ef_change_password_confirm\"\n autocomplete=\"new-password\"\n required\n (valueChangeEvent)=\"confirmation.set($event)\"\n />\n</ef-form-grid>\n\n@if (validationKeys().length || errors().length) {\n <ul class=\"ef-change-password__errors\">\n @for (key of validationKeys(); track $index) {\n <li>{{ key | translate }}</li>\n }\n <!-- Server messages arrive already translated (Identity returns prose,\n not keys), so they are printed verbatim. -->\n @for (error of errors(); track $index) {\n <li>{{ error }}</li>\n }\n </ul>\n}\n", styles: [":host{display:grid;gap:var(--s-2, 8px);min-width:0}.ef-change-password__errors{display:grid;gap:4px;margin:0;padding:0;list-style:none;min-width:0;font-size:12px;line-height:1.4;color:var(--st-cancelled-fg, #b42318)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfPasswordComponent, selector: "ef-password", inputs: ["size", "fluid", "inline", "toggleMask", "feedback", "autocomplete", "variant", "autofocus"], outputs: ["valueChangeEvent"] }, { kind: "component", type: EfFormGridComponent, selector: "ef-form-grid" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
12613
13079
|
}
|
|
12614
13080
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfChangePasswordComponent, decorators: [{
|
|
12615
13081
|
type: Component,
|
|
@@ -12722,7 +13188,7 @@ class EfRadioComponent extends AbstractEfFormControl {
|
|
|
12722
13188
|
this.onTouched();
|
|
12723
13189
|
}
|
|
12724
13190
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfRadioComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12725
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfRadioComponent, isStandalone: true, selector: "ef-radio", inputs: { options: "options", optionLabel: "optionLabel", optionValue: "optionValue", optionDisabled: "optionDisabled", inline: ["inline", "inline", booleanAttribute], value: "value" }, outputs: { selectionChangeEvent: "selectionChangeEvent" }, host: { properties: { "class.ef-stacked": "this.isStacked" } }, usesInheritance: true, ngImport: i0, template: "@if (hasLabel) {\n <span class=\"ef-radio-group-label\" [id]=\"labelId\">\n <ef-label\n [labelKey]=\"labelKey()\"\n [label]=\"label()\"\n [required]=\"required()\"\n [styleClass]=\"inline ? 'form-label-left' : 'form-label'\"\n />\n </span>\n}\n<div\n class=\"ef-radio-options\"\n role=\"radiogroup\"\n [attr.aria-labelledby]=\"hasLabel ? labelId : null\"\n [attr.aria-label]=\"hasLabel ? null : effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"(showRequired || serverErrors) ? errorsId : null\"\n>\n @for (opt of options; track $index; let i = $index) {\n <div class=\"ef-radio-option\">\n <p-radiobutton\n [inputId]=\"optionId(i)\"\n [name]=\"groupName\"\n [value]=\"optionValueOf(opt)\"\n [ngModel]=\"value\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"handleChange($event)\"\n [disabled]=\"isOptionDisabled(opt)\"\n />\n <label class=\"ef-radio-option-label\" [for]=\"optionId(i)\" [class.is-disabled]=\"isOptionDisabled(opt)\">\n {{ optionLabelOf(opt) }}\n </label>\n </div>\n }\n</div>\n@if (showRequired || serverErrors) {\n <div class=\"ef-radio-errors\" [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n}\n", styles: [":host{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem 1rem;min-width:0}.ef-radio-options{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem 1.25rem;min-width:0}.ef-radio-option{display:inline-flex;align-items:center;gap:.5rem;min-width:0}.ef-radio-option-label{font-size:13.5px;font-weight:600;color:var(--text);cursor:pointer;-webkit-user-select:none;user-select:none}.ef-radio-option-label.is-disabled{color:var(--text-mute);cursor:not-allowed}.ef-radio-errors{flex-basis:100%}:host(.ef-stacked){display:block}:host(.ef-stacked)>.ef-radio-group-label{display:block}:host(.ef-stacked)>.ef-radio-options{min-height:var(--hit-field)}\n"], dependencies: [{ kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i1$g.RadioButton, selector: "p-radioButton, p-radiobutton, p-radio-button", inputs: ["value", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "styleClass", "autofocus", "binary", "variant", "size"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "for", "styleClass", "required"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
13191
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfRadioComponent, isStandalone: true, selector: "ef-radio", inputs: { options: "options", optionLabel: "optionLabel", optionValue: "optionValue", optionDisabled: "optionDisabled", inline: ["inline", "inline", booleanAttribute], value: "value" }, outputs: { selectionChangeEvent: "selectionChangeEvent" }, host: { properties: { "class.ef-stacked": "this.isStacked" } }, usesInheritance: true, ngImport: i0, template: "@if (hasLabel) {\n <span class=\"ef-radio-group-label\" [id]=\"labelId\">\n <ef-label\n [labelKey]=\"labelKey()\"\n [label]=\"label()\"\n [required]=\"required()\"\n [styleClass]=\"inline ? 'form-label-left' : 'form-label'\"\n />\n </span>\n}\n<div\n class=\"ef-radio-options\"\n role=\"radiogroup\"\n [attr.aria-labelledby]=\"hasLabel ? labelId : null\"\n [attr.aria-label]=\"hasLabel ? null : effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"(showRequired || serverErrors) ? errorsId : null\"\n>\n @for (opt of options; track $index; let i = $index) {\n <div class=\"ef-radio-option\">\n <p-radiobutton\n [inputId]=\"optionId(i)\"\n [name]=\"groupName\"\n [value]=\"optionValueOf(opt)\"\n [ngModel]=\"value\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"handleChange($event)\"\n [disabled]=\"isOptionDisabled(opt)\"\n />\n <label class=\"ef-radio-option-label\" [for]=\"optionId(i)\" [class.is-disabled]=\"isOptionDisabled(opt)\">\n {{ optionLabelOf(opt) }}\n </label>\n </div>\n }\n</div>\n@if (showRequired || serverErrors) {\n <div class=\"ef-radio-errors\" [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n}\n", styles: [":host{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem 1rem;min-width:0}.ef-radio-options{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem 1.25rem;min-width:0}.ef-radio-option{display:inline-flex;align-items:center;gap:.5rem;min-width:0}.ef-radio-option-label{font-size:13.5px;font-weight:600;color:var(--text);cursor:pointer;-webkit-user-select:none;user-select:none}.ef-radio-option-label.is-disabled{color:var(--text-mute);cursor:not-allowed}.ef-radio-errors{flex-basis:100%}:host(.ef-stacked){display:block}:host(.ef-stacked)>.ef-radio-group-label{display:block}:host(.ef-stacked)>.ef-radio-options{min-height:var(--hit-field)}\n"], dependencies: [{ kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i1$g.RadioButton, selector: "p-radioButton, p-radiobutton, p-radio-button", inputs: ["value", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "styleClass", "autofocus", "binary", "variant", "size"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "labelParams", "for", "styleClass", "required"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
12726
13192
|
}
|
|
12727
13193
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfRadioComponent, decorators: [{
|
|
12728
13194
|
type: Component,
|
|
@@ -12792,6 +13258,7 @@ class EfTextareaComponent extends AbstractEfFormControl {
|
|
|
12792
13258
|
/* noop */
|
|
12793
13259
|
};
|
|
12794
13260
|
ngControl = inject(NgControl, { self: true, optional: true });
|
|
13261
|
+
hostRef = inject(ElementRef);
|
|
12795
13262
|
constructor() {
|
|
12796
13263
|
super();
|
|
12797
13264
|
if (this.ngControl) {
|
|
@@ -12828,15 +13295,52 @@ class EfTextareaComponent extends AbstractEfFormControl {
|
|
|
12828
13295
|
this.onChange(value);
|
|
12829
13296
|
this.valueChangeEvent.emit(value);
|
|
12830
13297
|
}
|
|
13298
|
+
/** Set once the field itself has had focus; see {@link insertAtSelection}. */
|
|
13299
|
+
wasFocused = false;
|
|
13300
|
+
markFocused(event) {
|
|
13301
|
+
// Only the native field counts: focus on the clear button says nothing
|
|
13302
|
+
// about where the user left the caret.
|
|
13303
|
+
if (event.target === this.nativeField())
|
|
13304
|
+
this.wasFocused = true;
|
|
13305
|
+
}
|
|
13306
|
+
/**
|
|
13307
|
+
* Inserts `text` at the caret (replacing a selection), or at the end when
|
|
13308
|
+
* the field was never focused, then puts the caret after it. Emits the
|
|
13309
|
+
* change once, exactly as typing would. Does nothing when read-only or
|
|
13310
|
+
* disabled, and refuses the whole insert when the result would pass
|
|
13311
|
+
* `maxlength` (a cut placeholder token is worse than none).
|
|
13312
|
+
*
|
|
13313
|
+
* The value is written programmatically, so the browser's undo history for
|
|
13314
|
+
* the field is reset and no DOM `input` event fires; the form control
|
|
13315
|
+
* (`onChange`) and `valueChangeEvent` are the contract.
|
|
13316
|
+
*/
|
|
13317
|
+
insertAtCursor(text) {
|
|
13318
|
+
if (this.readonly() || this.isDisabled())
|
|
13319
|
+
return;
|
|
13320
|
+
const field = this.nativeField();
|
|
13321
|
+
if (!field)
|
|
13322
|
+
return;
|
|
13323
|
+
const { value, caret } = insertAtSelection(field, text, this.wasFocused);
|
|
13324
|
+
if (this.maxlength != null && value.length > this.maxlength)
|
|
13325
|
+
return;
|
|
13326
|
+
// Write the DOM first so the later [value] binding finds it unchanged
|
|
13327
|
+
// and leaves the caret where we put it.
|
|
13328
|
+
field.value = value;
|
|
13329
|
+
this.handleInput(value);
|
|
13330
|
+
placeCaret(field, caret);
|
|
13331
|
+
}
|
|
13332
|
+
nativeField() {
|
|
13333
|
+
return this.hostRef.nativeElement.querySelector('textarea');
|
|
13334
|
+
}
|
|
12831
13335
|
handleBlur() {
|
|
12832
13336
|
this.onTouched();
|
|
12833
13337
|
}
|
|
12834
13338
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfTextareaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12835
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfTextareaComponent, isStandalone: true, selector: "ef-textarea", inputs: { rows: "rows", maxlength: "maxlength", autofocus: ["autofocus", "autofocus", booleanAttribute], mono: ["mono", "mono", booleanAttribute], value: "value" }, outputs: { valueChangeEvent: "valueChangeEvent" }, usesInheritance: true, ngImport: i0, template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n <div class=\"ef-textarea-wrap\">\n <textarea\n class=\"ef-textarea\"\n [class.has-clear]=\"canClear\"\n [class.ef-textarea--mono]=\"mono\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [rows]=\"rows\"\n [value]=\"value\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [attr.maxlength]=\"maxlength ?? null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n (input)=\"handleInput($any($event.target).value)\"\n (blur)=\"handleBlur()\"\n ></textarea>\n @if (canClear) {\n <ef-clear-button\n class=\"ef-textarea-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n </div>\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}.ef-textarea.ef-textarea--mono{font-family:var(--font-mono);font-size:12.5px;line-height:1.55}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "for", "styleClass", "required"] }, { kind: "component", type: EfClearButtonComponent, selector: "ef-clear-button", inputs: ["ariaLabel", "disabled"], outputs: ["clear"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
13339
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfTextareaComponent, isStandalone: true, selector: "ef-textarea", inputs: { rows: "rows", maxlength: "maxlength", autofocus: ["autofocus", "autofocus", booleanAttribute], mono: ["mono", "mono", booleanAttribute], value: "value" }, outputs: { valueChangeEvent: "valueChangeEvent" }, host: { listeners: { "focusin": "markFocused($event)" } }, usesInheritance: true, ngImport: i0, template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n <div class=\"ef-textarea-wrap\">\n <textarea\n class=\"ef-textarea\"\n [class.has-clear]=\"canClear\"\n [class.ef-textarea--mono]=\"mono\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [rows]=\"rows\"\n [value]=\"value\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [attr.maxlength]=\"maxlength ?? null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n (input)=\"handleInput($any($event.target).value)\"\n (blur)=\"handleBlur()\"\n ></textarea>\n @if (canClear) {\n <ef-clear-button\n class=\"ef-textarea-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n </div>\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}.ef-textarea.ef-textarea--mono{font-family:var(--font-mono);font-size:12.5px;line-height:1.55}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "labelParams", "for", "styleClass", "required"] }, { kind: "component", type: EfClearButtonComponent, selector: "ef-clear-button", inputs: ["ariaLabel", "disabled"], outputs: ["clear"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
|
|
12836
13340
|
}
|
|
12837
13341
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfTextareaComponent, decorators: [{
|
|
12838
13342
|
type: Component,
|
|
12839
|
-
args: [{ selector: 'ef-textarea', standalone: true, imports: [FormsModule, TranslateModule, EfLabelComponent, EfClearButtonComponent], template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n <div class=\"ef-textarea-wrap\">\n <textarea\n class=\"ef-textarea\"\n [class.has-clear]=\"canClear\"\n [class.ef-textarea--mono]=\"mono\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [rows]=\"rows\"\n [value]=\"value\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [attr.maxlength]=\"maxlength ?? null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n (input)=\"handleInput($any($event.target).value)\"\n (blur)=\"handleBlur()\"\n ></textarea>\n @if (canClear) {\n <ef-clear-button\n class=\"ef-textarea-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n </div>\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}.ef-textarea.ef-textarea--mono{font-family:var(--font-mono);font-size:12.5px;line-height:1.55}\n"] }]
|
|
13343
|
+
args: [{ selector: 'ef-textarea', standalone: true, imports: [FormsModule, TranslateModule, EfLabelComponent, EfClearButtonComponent], template: "@if (labelKey() || label()) {\n <ef-label [labelKey]=\"labelKey()\" [labelParams]=\"labelParams()\" [label]=\"label()\" [for]=\"effectiveId\" [required]=\"required()\" />\n}\n<div class=\"ef-field-control\">\n <div class=\"ef-textarea-wrap\">\n <textarea\n class=\"ef-textarea\"\n [class.has-clear]=\"canClear\"\n [class.ef-textarea--mono]=\"mono\"\n [id]=\"effectiveId\"\n [attr.name]=\"name() || null\"\n [rows]=\"rows\"\n [value]=\"value\"\n [attr.placeholder]=\"effectivePlaceholder || null\"\n [attr.maxlength]=\"maxlength ?? null\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [class.is-invalid]=\"isInvalid\"\n [attr.aria-label]=\"effectiveAriaLabel\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy(showRequired || !!serverErrors)\"\n (input)=\"handleInput($any($event.target).value)\"\n (blur)=\"handleBlur()\"\n ></textarea>\n @if (canClear) {\n <ef-clear-button\n class=\"ef-textarea-clear\"\n [ariaLabel]=\"'action.clear' | translate\"\n (clear)=\"clearValue()\"\n />\n }\n </div>\n @if (effectiveHint) {\n <small class=\"ef-field-hint\" [id]=\"hintId\">{{ effectiveHint }}</small>\n }\n @if (showRequired || serverErrors) {\n <div [id]=\"errorsId\" role=\"alert\" aria-live=\"polite\">\n @if (showRequired) {\n <small class=\"text-danger\">{{ 'validation.required' | translate }}</small>\n }\n @if (serverErrors; as errors) {\n @for (error of errors; track error) {\n <small class=\"text-danger block\">{{ error }}</small>\n }\n }\n </div>\n }\n</div>\n", styles: [":host([dir]){direction:inherit}:host([dir=rtl]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:rtl}:host([dir=ltr]) .ef-field-control>:not([role=alert]):not(.ef-field-hint){direction:ltr}:host([dir=auto]) .ef-field-control input,:host([dir=auto]) .ef-field-control textarea{unicode-bidi:plaintext}.ef-textarea.ef-textarea--mono{font-family:var(--font-mono);font-size:12.5px;line-height:1.55}\n"] }]
|
|
12840
13344
|
}], ctorParameters: () => [], propDecorators: { rows: [{
|
|
12841
13345
|
type: Input
|
|
12842
13346
|
}], maxlength: [{
|
|
@@ -12851,6 +13355,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
12851
13355
|
type: Output
|
|
12852
13356
|
}], value: [{
|
|
12853
13357
|
type: Input
|
|
13358
|
+
}], markFocused: [{
|
|
13359
|
+
type: HostListener,
|
|
13360
|
+
args: ['focusin', ['$event']]
|
|
12854
13361
|
}] } });
|
|
12855
13362
|
|
|
12856
13363
|
let nextId = 0;
|
|
@@ -12923,7 +13430,7 @@ class EfSeoFieldsComponent {
|
|
|
12923
13430
|
previewTitle = computed(() => (this.seoTitle() || this.fallbackTitle() || '').trim(), ...(ngDevMode ? [{ debugName: "previewTitle" }] : /* istanbul ignore next */ []));
|
|
12924
13431
|
previewDescription = computed(() => (this.seoDescription() || this.fallbackDescription() || '').trim(), ...(ngDevMode ? [{ debugName: "previewDescription" }] : /* istanbul ignore next */ []));
|
|
12925
13432
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfSeoFieldsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12926
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfSeoFieldsComponent, isStandalone: true, selector: "ef-seo-fields", inputs: { seoTitle: { classPropertyName: "seoTitle", publicName: "seoTitle", isSignal: true, isRequired: false, transformFunction: null }, seoDescription: { classPropertyName: "seoDescription", publicName: "seoDescription", isSignal: true, isRequired: false, transformFunction: null }, seoTitleTranslations: { classPropertyName: "seoTitleTranslations", publicName: "seoTitleTranslations", isSignal: true, isRequired: false, transformFunction: null }, seoDescriptionTranslations: { classPropertyName: "seoDescriptionTranslations", publicName: "seoDescriptionTranslations", isSignal: true, isRequired: false, transformFunction: null }, fallbackTitle: { classPropertyName: "fallbackTitle", publicName: "fallbackTitle", isSignal: true, isRequired: false, transformFunction: null }, fallbackDescription: { classPropertyName: "fallbackDescription", publicName: "fallbackDescription", isSignal: true, isRequired: false, transformFunction: null }, previewUrl: { classPropertyName: "previewUrl", publicName: "previewUrl", isSignal: true, isRequired: false, transformFunction: null }, titleMax: { classPropertyName: "titleMax", publicName: "titleMax", isSignal: true, isRequired: false, transformFunction: null }, descriptionMax: { classPropertyName: "descriptionMax", publicName: "descriptionMax", isSignal: true, isRequired: false, transformFunction: null }, titleAdvised: { classPropertyName: "titleAdvised", publicName: "titleAdvised", isSignal: true, isRequired: false, transformFunction: null }, descriptionAdvised: { classPropertyName: "descriptionAdvised", publicName: "descriptionAdvised", isSignal: true, isRequired: false, transformFunction: null }, locales: { classPropertyName: "locales", publicName: "locales", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { seoChange: "seoChange" }, ngImport: i0, template: "<div class=\"seo-body\">\n <p class=\"seo-note text-mute\">{{ 'ef_seo_fields_fallback_hint' | translate }}</p>\n\n @for (locale of locales(); track locale.code) {\n <ef-fieldset variant=\"plain\" [legendKey]=\"locale.labelKey\" [attr.lang]=\"locale.code\">\n <ef-form-grid>\n <ef-input-text\n class=\"col-12\"\n variant=\"comptoir\"\n [dir]=\"locale.dir\"\n [name]=\"name(locale.code, 'title')\"\n [describedByExtra]=\"counterId(locale.code, 'title')\"\n labelKey=\"ef_seo_fields_title_label\"\n [maxlength]=\"titleMax()\"\n [value]=\"text(locale.code, 'title')\"\n (valueChangeEvent)=\"setText(locale.code, 'title', $event)\"\n />\n <span\n class=\"col-12 seo-counter\"\n [id]=\"counterId(locale.code, 'title')\"\n [attr.data-counter]=\"locale.code + '-title'\"\n [class.is-over]=\"text(locale.code, 'title').length > titleAdvised()\"\n >\n <bdi dir=\"ltr\">{{ text(locale.code, 'title').length }} / {{ titleAdvised() }}</bdi>\n <span class=\"seo-counter__sr\">\n {{ 'ef_seo_fields_counter_hint' | translate: { max: titleAdvised() } }}\n @if (text(locale.code, 'title').length > titleAdvised()) {\n {{ 'ef_seo_fields_over_hint' | translate: { max: titleAdvised() } }}\n }\n </span>\n </span>\n\n <ef-textarea\n class=\"col-12\"\n [dir]=\"locale.dir\"\n [name]=\"name(locale.code, 'description')\"\n [describedByExtra]=\"counterId(locale.code, 'description')\"\n labelKey=\"ef_seo_fields_description_label\"\n [rows]=\"3\"\n [maxlength]=\"descriptionMax()\"\n [value]=\"text(locale.code, 'description')\"\n (valueChangeEvent)=\"setText(locale.code, 'description', $event)\"\n />\n <span\n class=\"col-12 seo-counter\"\n [id]=\"counterId(locale.code, 'description')\"\n [attr.data-counter]=\"locale.code + '-description'\"\n [class.is-over]=\"text(locale.code, 'description').length > descriptionAdvised()\"\n >\n <bdi dir=\"ltr\">{{ text(locale.code, 'description').length }} / {{ descriptionAdvised() }}</bdi>\n <span class=\"seo-counter__sr\">\n {{ 'ef_seo_fields_counter_hint' | translate: { max: descriptionAdvised() } }}\n @if (text(locale.code, 'description').length > descriptionAdvised()) {\n {{ 'ef_seo_fields_over_hint' | translate: { max: descriptionAdvised() } }}\n }\n </span>\n </span>\n </ef-form-grid>\n </ef-fieldset>\n }\n\n <ef-seo-preview\n labelKey=\"ef_seo_fields_preview\"\n contentLang=\"fr\"\n [pageTitle]=\"previewTitle()\"\n [url]=\"previewUrl()\"\n [description]=\"previewDescription()\"\n />\n</div>\n", styles: [":host{display:block;min-width:0}.seo-body{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s-3);min-width:0}.seo-note{margin:0;font-size:12px}.seo-counter{margin-top:calc(-1 * var(--s-2));font-size:12px;color:var(--text-mute);text-align:end;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.seo-counter.is-over{color:var(--st-pending-fg);font-weight:600}.seo-counter__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfFieldsetComponent, selector: "ef-fieldset", inputs: ["legend", "legendKey", "variant", "toggleable", "collapsed", "styleClass"] }, { kind: "component", type: EfFormGridComponent, selector: "ef-form-grid" }, { kind: "component", type: EfInputTextComponent, selector: "ef-input-text", inputs: ["size", "type", "maxlength", "fluid", "inline", "autocomplete", "variant", "prefix", "suffix", "value"], outputs: ["valueChangeEvent"] }, { kind: "component", type: EfTextareaComponent, selector: "ef-textarea", inputs: ["rows", "maxlength", "autofocus", "mono", "value"], outputs: ["valueChangeEvent"] }, { kind: "component", type: EfSeoPreviewComponent, selector: "ef-seo-preview", inputs: ["pageTitle", "url", "description", "labelKey", "titleMaxLength", "descriptionMaxLength", "contentLang"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
13433
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfSeoFieldsComponent, isStandalone: true, selector: "ef-seo-fields", inputs: { seoTitle: { classPropertyName: "seoTitle", publicName: "seoTitle", isSignal: true, isRequired: false, transformFunction: null }, seoDescription: { classPropertyName: "seoDescription", publicName: "seoDescription", isSignal: true, isRequired: false, transformFunction: null }, seoTitleTranslations: { classPropertyName: "seoTitleTranslations", publicName: "seoTitleTranslations", isSignal: true, isRequired: false, transformFunction: null }, seoDescriptionTranslations: { classPropertyName: "seoDescriptionTranslations", publicName: "seoDescriptionTranslations", isSignal: true, isRequired: false, transformFunction: null }, fallbackTitle: { classPropertyName: "fallbackTitle", publicName: "fallbackTitle", isSignal: true, isRequired: false, transformFunction: null }, fallbackDescription: { classPropertyName: "fallbackDescription", publicName: "fallbackDescription", isSignal: true, isRequired: false, transformFunction: null }, previewUrl: { classPropertyName: "previewUrl", publicName: "previewUrl", isSignal: true, isRequired: false, transformFunction: null }, titleMax: { classPropertyName: "titleMax", publicName: "titleMax", isSignal: true, isRequired: false, transformFunction: null }, descriptionMax: { classPropertyName: "descriptionMax", publicName: "descriptionMax", isSignal: true, isRequired: false, transformFunction: null }, titleAdvised: { classPropertyName: "titleAdvised", publicName: "titleAdvised", isSignal: true, isRequired: false, transformFunction: null }, descriptionAdvised: { classPropertyName: "descriptionAdvised", publicName: "descriptionAdvised", isSignal: true, isRequired: false, transformFunction: null }, locales: { classPropertyName: "locales", publicName: "locales", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { seoChange: "seoChange" }, ngImport: i0, template: "<div class=\"seo-body\">\n <p class=\"seo-note text-mute\">{{ 'ef_seo_fields_fallback_hint' | translate }}</p>\n\n @for (locale of locales(); track locale.code) {\n <ef-fieldset variant=\"plain\" [legendKey]=\"locale.labelKey\" [attr.lang]=\"locale.code\">\n <ef-form-grid>\n <ef-input-text\n class=\"col-12\"\n variant=\"comptoir\"\n [dir]=\"locale.dir\"\n [name]=\"name(locale.code, 'title')\"\n [describedByExtra]=\"counterId(locale.code, 'title')\"\n labelKey=\"ef_seo_fields_title_label\"\n [maxlength]=\"titleMax()\"\n [value]=\"text(locale.code, 'title')\"\n (valueChangeEvent)=\"setText(locale.code, 'title', $event)\"\n />\n <span\n class=\"col-12 seo-counter\"\n [id]=\"counterId(locale.code, 'title')\"\n [attr.data-counter]=\"locale.code + '-title'\"\n [class.is-over]=\"text(locale.code, 'title').length > titleAdvised()\"\n >\n <bdi dir=\"ltr\">{{ text(locale.code, 'title').length }} / {{ titleAdvised() }}</bdi>\n <span class=\"seo-counter__sr\">\n {{ 'ef_seo_fields_counter_hint' | translate: { max: titleAdvised() } }}\n @if (text(locale.code, 'title').length > titleAdvised()) {\n {{ 'ef_seo_fields_over_hint' | translate: { max: titleAdvised() } }}\n }\n </span>\n </span>\n\n <ef-textarea\n class=\"col-12\"\n [dir]=\"locale.dir\"\n [name]=\"name(locale.code, 'description')\"\n [describedByExtra]=\"counterId(locale.code, 'description')\"\n labelKey=\"ef_seo_fields_description_label\"\n [rows]=\"3\"\n [maxlength]=\"descriptionMax()\"\n [value]=\"text(locale.code, 'description')\"\n (valueChangeEvent)=\"setText(locale.code, 'description', $event)\"\n />\n <span\n class=\"col-12 seo-counter\"\n [id]=\"counterId(locale.code, 'description')\"\n [attr.data-counter]=\"locale.code + '-description'\"\n [class.is-over]=\"text(locale.code, 'description').length > descriptionAdvised()\"\n >\n <bdi dir=\"ltr\">{{ text(locale.code, 'description').length }} / {{ descriptionAdvised() }}</bdi>\n <span class=\"seo-counter__sr\">\n {{ 'ef_seo_fields_counter_hint' | translate: { max: descriptionAdvised() } }}\n @if (text(locale.code, 'description').length > descriptionAdvised()) {\n {{ 'ef_seo_fields_over_hint' | translate: { max: descriptionAdvised() } }}\n }\n </span>\n </span>\n </ef-form-grid>\n </ef-fieldset>\n }\n\n <ef-seo-preview\n labelKey=\"ef_seo_fields_preview\"\n contentLang=\"fr\"\n [pageTitle]=\"previewTitle()\"\n [url]=\"previewUrl()\"\n [description]=\"previewDescription()\"\n />\n</div>\n", styles: [":host{display:block;min-width:0}.seo-body{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s-3);min-width:0}.seo-note{margin:0;font-size:12px}.seo-counter{margin-top:calc(-1 * var(--s-2));font-size:12px;color:var(--text-mute);text-align:end;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.seo-counter.is-over{color:var(--st-pending-fg);font-weight:600}.seo-counter__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfFieldsetComponent, selector: "ef-fieldset", inputs: ["legend", "legendKey", "variant", "toggleable", "collapsed", "styleClass"] }, { kind: "component", type: EfFormGridComponent, selector: "ef-form-grid" }, { kind: "component", type: EfInputTextComponent, selector: "ef-input-text", inputs: ["size", "type", "maxlength", "fluid", "inline", "autocomplete", "dir", "variant", "prefix", "suffix", "value"], outputs: ["valueChangeEvent"] }, { kind: "component", type: EfTextareaComponent, selector: "ef-textarea", inputs: ["rows", "maxlength", "autofocus", "mono", "value"], outputs: ["valueChangeEvent"] }, { kind: "component", type: EfSeoPreviewComponent, selector: "ef-seo-preview", inputs: ["pageTitle", "url", "description", "labelKey", "titleMaxLength", "descriptionMaxLength", "contentLang"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
12927
13434
|
}
|
|
12928
13435
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfSeoFieldsComponent, decorators: [{
|
|
12929
13436
|
type: Component,
|
|
@@ -13838,5 +14345,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
13838
14345
|
* Generated bundle index. Do not edit.
|
|
13839
14346
|
*/
|
|
13840
14347
|
|
|
13841
|
-
export { EF_BADGE_BG_COLORS, EF_BADGE_BG_LIGHT_COLORS, EF_BADGE_BORDER_COLORS, EF_BADGE_DEFAULTS, EF_BADGE_TEXT_COLORS, EF_CHART_PALETTE, EF_DATATABLE_ACTIONS_DEFAULTS, EF_DATATABLE_COLUMN_DEFAULTS, EF_DATATABLE_DEFAULTS, EF_DATA_CARD_MOBILE_LAYOUT, EF_SEO_DEFAULT_LOCALES, EF_STATUS_COLORS, EF_STATUS_COLOR_ALIASES, EfAboutDialogComponent, EfActivationFilterComponent, EfAppMainComponent, EfAppShellComponent, EfAppShellMobileComponent, EfAppTopComponent, EfBadgeComponent, EfBlockUiComponent, EfBlockableDivComponent, EfBottomSheetComponent, EfBuildStampComponent, EfBulkBarComponent, EfButtonComponent, EfButtonGroupComponent, EfCardComponent, EfChangeHistoryComponent, EfChangePasswordComponent, EfChartComponent, EfCheckboxComponent, EfClearButtonComponent, EfColumnHeaderTemplateDirective, EfColumnTemplateDirective, EfCompactPipe, EfConfirmDialogComponent, EfCurrencyPipe, EfDataCardComponent, EfDatatableActionBarComponent, EfDatatableComponent, EfDatepickerAdvancedComponent, EfDatepickerComponent, EfDetailToolbarComponent, EfDialogComponent, EfEmailMetricsCardComponent, EfEmptyStateComponent, EfFabComponent, EfFieldsetComponent, EfFileUploadComponent, EfFilterDrawerComponent, EfFilterPillComponent, EfFormGridComponent, EfIdentityThumbComponent, EfInputNumberComponent, EfInputTextComponent, EfKpiCardComponent, EfLabelComponent, EfModuleRailComponent, EfModuleSideComponent, EfMultiSelectComponent, EfOrderBuilderComponent, EfOrderSummaryComponent, EfOverflowTooltipDirective, EfPagerComponent, EfPasswordComponent, EfPdfPreviewComponent, EfPillGroupComponent, EfPresetPillComponent, EfPriceDisplayComponent, EfProductCatalogueComponent, EfProductCatalogueFilterComponent, EfProductPickerComponent, EfProductTypeaheadComponent, EfProfileChipComponent, EfProfileMenuComponent, EfPulseComponent, EfQuantitySelectorComponent, EfQuantityStepperComponent, EfRadioComponent, EfRankListComponent, EfReadOnlyDirective, EfRowActionsComponent, EfSearchToolbarComponent, EfSelectButtonComponent, EfSelectComponent, EfSeoFieldsComponent, EfSeoPreviewComponent, EfServerErrorsDirective, EfSessionIdleDialogComponent, EfShortcutsDialogComponent, EfSkeletonComponent, EfSmartBarComponent, EfStatsComponent, EfStatusChipComponent, EfTabPanelDirective, EfTabsComponent, EfTextareaComponent, EfThemeConfiguratorComponent, EfThemeToggleComponent, EfTimelineComponent, EfToastRegionComponent, EfToggleSwitchComponent, EfToolbarComponent, EfTooltipDirective, EfTotalsComponent, OrderEntityHelper, OrderLineItemHelper, TruncatePipe, buildChartConfig, defaultUnitPrice, getColumnAlignment, mergeColumnDefaults, resolveEfStatusColor };
|
|
14348
|
+
export { EF_BADGE_BG_COLORS, EF_BADGE_BG_LIGHT_COLORS, EF_BADGE_BORDER_COLORS, EF_BADGE_DEFAULTS, EF_BADGE_TEXT_COLORS, EF_CHART_PALETTE, EF_DATATABLE_ACTIONS_DEFAULTS, EF_DATATABLE_COLUMN_DEFAULTS, EF_DATATABLE_DEFAULTS, EF_DATA_CARD_MOBILE_LAYOUT, EF_HTML_PREVIEW_DEFAULT_TITLE_KEY, EF_SEO_DEFAULT_LOCALES, EF_STATUS_COLORS, EF_STATUS_COLOR_ALIASES, EfAboutDialogComponent, EfActivationFilterComponent, EfAppMainComponent, EfAppShellComponent, EfAppShellMobileComponent, EfAppTopComponent, EfBadgeComponent, EfBlockUiComponent, EfBlockableDivComponent, EfBottomSheetComponent, EfBuildStampComponent, EfBulkBarComponent, EfButtonComponent, EfButtonGroupComponent, EfCardComponent, EfChangeHistoryComponent, EfChangePasswordComponent, EfChartComponent, EfCheckboxComponent, EfClearButtonComponent, EfColumnHeaderTemplateDirective, EfColumnTemplateDirective, EfCompactPipe, EfConfirmDialogComponent, EfCurrencyPipe, EfDataCardComponent, EfDatatableActionBarComponent, EfDatatableComponent, EfDatepickerAdvancedComponent, EfDatepickerComponent, EfDetailToolbarComponent, EfDialogComponent, EfEmailMetricsCardComponent, EfEmptyStateComponent, EfFabComponent, EfFieldsetComponent, EfFileUploadComponent, EfFilterDrawerComponent, EfFilterPillComponent, EfFormGridComponent, EfHtmlPreviewComponent, EfIdentityThumbComponent, EfInputNumberComponent, EfInputTextComponent, EfKpiCardComponent, EfLabelComponent, EfModuleRailComponent, EfModuleSideComponent, EfMultiSelectComponent, EfOrderBuilderComponent, EfOrderSummaryComponent, EfOverflowTooltipDirective, EfPagerComponent, EfPasswordComponent, EfPdfPreviewComponent, EfPillGroupComponent, EfPresetPillComponent, EfPriceDisplayComponent, EfProductCatalogueComponent, EfProductCatalogueFilterComponent, EfProductPickerComponent, EfProductTypeaheadComponent, EfProfileChipComponent, EfProfileMenuComponent, EfPulseComponent, EfQuantitySelectorComponent, EfQuantityStepperComponent, EfRadioComponent, EfRankListComponent, EfReadOnlyDirective, EfRowActionsComponent, EfSearchToolbarComponent, EfSelectButtonComponent, EfSelectComponent, EfSeoFieldsComponent, EfSeoPreviewComponent, EfServerErrorsDirective, EfSessionIdleDialogComponent, EfShortcutsDialogComponent, EfSkeletonComponent, EfSmartBarComponent, EfStatsComponent, EfStatusChipComponent, EfTabPanelDirective, EfTabsComponent, EfTextareaComponent, EfThemeConfiguratorComponent, EfThemeToggleComponent, EfTimelineComponent, EfToastRegionComponent, EfToggleSwitchComponent, EfToolbarComponent, EfTooltipDirective, EfTotalsComponent, OrderEntityHelper, OrderLineItemHelper, TruncatePipe, buildChartConfig, defaultUnitPrice, getColumnAlignment, mergeColumnDefaults, resolveEfStatusColor };
|
|
13842
14349
|
//# sourceMappingURL=elasticias-ui.mjs.map
|