@elasticias/ui 1.0.52 → 1.0.54
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-rich-text.mjs +336 -0
- package/fesm2022/elasticias-ui-rich-text.mjs.map +1 -0
- package/fesm2022/elasticias-ui.mjs +690 -112
- package/fesm2022/elasticias-ui.mjs.map +1 -1
- package/package.json +12 -2
- package/styles/_patterns.scss +51 -2
- package/types/elasticias-ui-rich-text.d.ts +151 -0
- package/types/elasticias-ui.d.ts +380 -61
|
@@ -1,22 +1,22 @@
|
|
|
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, afterEveryRender, 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, inputBinding, outputBinding, 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';
|
|
6
6
|
import * as i1$3 from '@angular/common';
|
|
7
7
|
import { CommonModule, formatDate, CurrencyPipe, DatePipe, DecimalPipe } from '@angular/common';
|
|
8
8
|
import * as i2$1 from '@angular/forms';
|
|
9
|
-
import { NgControl, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
9
|
+
import { NgControl, Validators, FormControl, FormsModule, NG_VALUE_ACCESSOR, FormGroupDirective, NgForm, NG_VALIDATORS } from '@angular/forms';
|
|
10
10
|
import * as i1$1 from '@ngx-translate/core';
|
|
11
11
|
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
|
12
12
|
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 { ObjectUtils, ZIndexUtils } from 'primeng/utils';
|
|
16
17
|
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
|
17
18
|
import { merge, Subject, tap, switchMap, catchError, EMPTY } from 'rxjs';
|
|
18
|
-
import {
|
|
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';
|
|
19
|
+
import { EfViewportService, EfShortcutService, EF_PREFERENCE_STORE, formatShortcut, efTablePrefsKey, EfThemeConfigService, EF_BUILD_INFO, EfSessionIdleService, EF_SESSION, EfPermissionService, EfActiveModuleService, EF_SIDE_COLLAPSED_KEY, EfToastService, EF_CONTENT_CULTURES, cleanLocalizedText, EF_DEFAULT_CONTENT_CULTURES } from '@elasticias/core';
|
|
20
20
|
import * as i1$4 from 'primeng/inputnumber';
|
|
21
21
|
import { InputNumberModule } from 'primeng/inputnumber';
|
|
22
22
|
import * as i1$5 from 'primeng/tooltip';
|
|
@@ -674,6 +674,24 @@ class EfServerErrorsDirective {
|
|
|
674
674
|
return null;
|
|
675
675
|
return map[name] ?? null;
|
|
676
676
|
}
|
|
677
|
+
/**
|
|
678
|
+
* Messages for every key that starts with `prefix`, keyed by what follows
|
|
679
|
+
* it: `errorsWithPrefix('title.')` turns `title.ar` into `ar`. For a control
|
|
680
|
+
* that owns several backend keys, such as a text per language. Empty lists
|
|
681
|
+
* are left out.
|
|
682
|
+
*/
|
|
683
|
+
errorsWithPrefix(prefix) {
|
|
684
|
+
const out = {};
|
|
685
|
+
const map = this.errors();
|
|
686
|
+
if (!prefix || !map)
|
|
687
|
+
return out;
|
|
688
|
+
for (const [key, messages] of Object.entries(map)) {
|
|
689
|
+
if (key.length > prefix.length && key.startsWith(prefix) && messages?.length) {
|
|
690
|
+
out[key.slice(prefix.length)] = messages;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
return out;
|
|
694
|
+
}
|
|
677
695
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfServerErrorsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
678
696
|
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.11", type: EfServerErrorsDirective, isStandalone: true, selector: "[efServerErrors]", inputs: { errors: { classPropertyName: "errors", publicName: "efServerErrors", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
679
697
|
}
|
|
@@ -701,6 +719,9 @@ function resolveExternalErrors(explicit, scope, name) {
|
|
|
701
719
|
}
|
|
702
720
|
|
|
703
721
|
/**
|
|
722
|
+
* Exported for `@elasticias/ui/rich-text` only, not a supported extension
|
|
723
|
+
* point: it may change without a major version.
|
|
724
|
+
*
|
|
704
725
|
* Shared base for every Comptoir form control (`ef-input-text`,
|
|
705
726
|
* `ef-textarea`, `ef-select`, `ef-input-number`, `ef-checkbox`, …).
|
|
706
727
|
*
|
|
@@ -924,6 +945,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
924
945
|
args: [{ template: '', standalone: true }]
|
|
925
946
|
}], 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 }] }] } });
|
|
926
947
|
|
|
948
|
+
/**
|
|
949
|
+
* Past this many options an `ef-select` whose `filter` is left unset shows
|
|
950
|
+
* a search box. Eight is about what the panel shows without scrolling;
|
|
951
|
+
* beyond that, finding a name by scrolling is slower than typing it.
|
|
952
|
+
*/
|
|
953
|
+
const EF_SELECT_FILTER_THRESHOLD = 8;
|
|
954
|
+
/** `filter` keeps "not bound" apart from `false`, which `booleanAttribute`
|
|
955
|
+
* folds together, so the threshold applies only when the caller is silent. */
|
|
956
|
+
function optionalBoolean(value) {
|
|
957
|
+
return value === undefined || value === null ? undefined : booleanAttribute(value);
|
|
958
|
+
}
|
|
927
959
|
/**
|
|
928
960
|
* Comptoir select — wraps PrimeNG `p-select` (single) or `p-multiselect`
|
|
929
961
|
* (multi) and skins it against the `.es` / `.es__panel` patterns lifted
|
|
@@ -934,10 +966,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
934
966
|
* Elasticias API surface (i18n `*Key` inputs, `selectionChangeEvent`,
|
|
935
967
|
* `filterByKeys`, content-projected templates).
|
|
936
968
|
*
|
|
937
|
-
*
|
|
938
|
-
*
|
|
939
|
-
*
|
|
940
|
-
*
|
|
969
|
+
* Both variants render PrimeNG. `'comptoir'` used to be a native
|
|
970
|
+
* `<select>`, which opened the operating system's own list (a blank,
|
|
971
|
+
* checked first row on macOS, no search); it now gets the same `.es`
|
|
972
|
+
* dropdown, sized for dense rows and appended to `body` so a scrolling
|
|
973
|
+
* card or table cannot clip its panel.
|
|
974
|
+
*
|
|
975
|
+
* The search box turns itself on past `EF_SELECT_FILTER_THRESHOLD`
|
|
976
|
+
* options unless the caller binds `[filter]` either way.
|
|
941
977
|
*
|
|
942
978
|
* ```html
|
|
943
979
|
* <ef-select
|
|
@@ -988,7 +1024,10 @@ class EfSelectComponent extends AbstractEfFormControl {
|
|
|
988
1024
|
/* ── Behavior ────────────────────────────────────────────────── */
|
|
989
1025
|
/** Multi-selection — internally swaps `<p-select>` for `<p-multiselect>`. */
|
|
990
1026
|
multiple = false;
|
|
991
|
-
|
|
1027
|
+
/** Show the search box in the panel. Unset (the default) turns it on
|
|
1028
|
+
* when there are more than `EF_SELECT_FILTER_THRESHOLD` options; bind
|
|
1029
|
+
* `true` or `false` to decide for the screen. */
|
|
1030
|
+
filter = undefined;
|
|
992
1031
|
/** Filter the list against these option fields (string or csv).
|
|
993
1032
|
* PrimeNG default: `optionLabel`. */
|
|
994
1033
|
filterBy;
|
|
@@ -1009,8 +1048,9 @@ class EfSelectComponent extends AbstractEfFormControl {
|
|
|
1009
1048
|
* branch and removes the attribute.
|
|
1010
1049
|
*/
|
|
1011
1050
|
autofocus = false;
|
|
1012
|
-
/* showClear is inherited from AbstractEfFormControl
|
|
1013
|
-
|
|
1051
|
+
/* showClear is inherited from AbstractEfFormControl. Here it is an
|
|
1052
|
+
override only: left unbound, the ✕ follows whether the field is
|
|
1053
|
+
optional (see `effectiveShowClear`). */
|
|
1014
1054
|
editable = false;
|
|
1015
1055
|
loading = false;
|
|
1016
1056
|
/** Virtual-scroll for long lists (>100 items). Pair with
|
|
@@ -1045,12 +1085,13 @@ class EfSelectComponent extends AbstractEfFormControl {
|
|
|
1045
1085
|
* to the canonical `--hit-field` (40px, 48px on touch) trigger
|
|
1046
1086
|
* height (`.es.p-select { min-height: --hit-field }`),
|
|
1047
1087
|
* so it aligns with inputs/buttons in a row.
|
|
1048
|
-
* - `'comptoir'`
|
|
1049
|
-
*
|
|
1050
|
-
*
|
|
1051
|
-
*
|
|
1052
|
-
*
|
|
1053
|
-
* `
|
|
1088
|
+
* - `'comptoir'`: the same `p-select` / `p-multiselect`, for dense
|
|
1089
|
+
* rows and table-row editors: no 200px minimum width,
|
|
1090
|
+
* so it shrinks with its cell, and the panel goes to
|
|
1091
|
+
* `body` unless `appendTo` says otherwise. It no
|
|
1092
|
+
* longer renders a native `<select>`.
|
|
1093
|
+
* Both share the `--hit-field` height (40px, 48px on a coarse pointer), so a
|
|
1094
|
+
* select lines up with comptoir inputs and buttons in a row. See ADR-009.
|
|
1054
1095
|
*/
|
|
1055
1096
|
variant = 'primeng';
|
|
1056
1097
|
get isInline() { return this.inline; }
|
|
@@ -1076,6 +1117,10 @@ class EfSelectComponent extends AbstractEfFormControl {
|
|
|
1076
1117
|
filteredOptions = [];
|
|
1077
1118
|
onChange = () => { };
|
|
1078
1119
|
onTouched = () => { };
|
|
1120
|
+
/** Set once the host binds `[showClear]`, so its value wins over the
|
|
1121
|
+
* required rule. The inherited signal input cannot tell "bound" from its
|
|
1122
|
+
* default, but a bound signal input does show up in `ngOnChanges`. */
|
|
1123
|
+
showClearBound = false;
|
|
1079
1124
|
/** Resolved lazily in ngOnInit to avoid circular DI with NG_VALUE_ACCESSOR */
|
|
1080
1125
|
ngControl = null;
|
|
1081
1126
|
injector = inject(Injector);
|
|
@@ -1083,6 +1128,69 @@ class EfSelectComponent extends AbstractEfFormControl {
|
|
|
1083
1128
|
this.ngControl = this.injector.get(NgControl, null);
|
|
1084
1129
|
this.applyFilters();
|
|
1085
1130
|
}
|
|
1131
|
+
/** Whether the panel shows its search box: the bound `filter`, or past
|
|
1132
|
+
* the threshold when the caller left it unset. */
|
|
1133
|
+
get effectiveFilter() {
|
|
1134
|
+
return this.filter ?? (this.filteredOptions?.length ?? 0) > EF_SELECT_FILTER_THRESHOLD;
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Whether the clear ✕ is offered. A bound `[showClear]` decides. Otherwise
|
|
1138
|
+
* only an optional select gets one: on a required select the ✕ would just
|
|
1139
|
+
* lead the user into the "required" error, since clearing is never a valid
|
|
1140
|
+
* answer there.
|
|
1141
|
+
*/
|
|
1142
|
+
get effectiveShowClear() {
|
|
1143
|
+
return this.showClearBound ? this.showClear() : !this.isRequiredField;
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Required by the `[required]` input or by the bound control: a reactive
|
|
1147
|
+
* `Validators.required`, or any validator (template-driven `required`,
|
|
1148
|
+
* a composed list) that rejects an empty value as required.
|
|
1149
|
+
*/
|
|
1150
|
+
get isRequiredField() {
|
|
1151
|
+
if (this.required())
|
|
1152
|
+
return true;
|
|
1153
|
+
const ctrl = this.ngControl?.control;
|
|
1154
|
+
if (!ctrl)
|
|
1155
|
+
return false;
|
|
1156
|
+
if (ctrl.hasValidator(Validators.required))
|
|
1157
|
+
return true;
|
|
1158
|
+
if (!ctrl.validator)
|
|
1159
|
+
return false;
|
|
1160
|
+
try {
|
|
1161
|
+
return !!ctrl.validator(new FormControl(this.multiple ? [] : null))?.['required'];
|
|
1162
|
+
}
|
|
1163
|
+
catch {
|
|
1164
|
+
return false;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Whether the current value is one of the options, compared the way
|
|
1169
|
+
* PrimeNG compares (`optionValue`, else the whole option or its
|
|
1170
|
+
* `dataKey`). An option whose value is null or '' counts: "Aucune" with a
|
|
1171
|
+
* null id is a real choice and must read as a value, not as the
|
|
1172
|
+
* placeholder.
|
|
1173
|
+
*/
|
|
1174
|
+
get hasSelectedOption() {
|
|
1175
|
+
if (this.multiple)
|
|
1176
|
+
return false;
|
|
1177
|
+
const value = this.value ?? null;
|
|
1178
|
+
const key = this.optionValue ? undefined : this.dataKey;
|
|
1179
|
+
return this.flatOptions().some((option) => option != null && ObjectUtils.equals(value, this.optionValueOf(option), key));
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* The placeholder handed to PrimeNG. Empty while an option is selected:
|
|
1183
|
+
* PrimeNG greys the trigger whenever its label equals the placeholder, so
|
|
1184
|
+
* a selected "Aucune" beside a placeholder "Aucune" rendered as unset.
|
|
1185
|
+
*/
|
|
1186
|
+
get triggerPlaceholder() {
|
|
1187
|
+
return this.hasSelectedOption ? '' : this.placeholderText;
|
|
1188
|
+
}
|
|
1189
|
+
/** The overlay target. A comptoir select lives in cards and table rows
|
|
1190
|
+
* that scroll, which would clip a panel attached in place. */
|
|
1191
|
+
get effectiveAppendTo() {
|
|
1192
|
+
return this.appendTo ?? (this.variant === 'comptoir' ? 'body' : undefined);
|
|
1193
|
+
}
|
|
1086
1194
|
/* effectiveId / effectivePlaceholder / effectiveAriaLabel /
|
|
1087
1195
|
errorsId all inherited from AbstractEfFormControl. */
|
|
1088
1196
|
/**
|
|
@@ -1134,7 +1242,12 @@ class EfSelectComponent extends AbstractEfFormControl {
|
|
|
1134
1242
|
* `_patterns.scss` always finds a hook, regardless of what the
|
|
1135
1243
|
* consumer passes via `styleClass`. */
|
|
1136
1244
|
get effectiveStyleClass() {
|
|
1137
|
-
return [
|
|
1245
|
+
return [
|
|
1246
|
+
'es',
|
|
1247
|
+
this.size === 'small' ? 'es--sm' : null,
|
|
1248
|
+
this.variant === 'comptoir' ? 'es--comptoir' : null,
|
|
1249
|
+
this.styleClass,
|
|
1250
|
+
]
|
|
1138
1251
|
.filter(Boolean).join(' ');
|
|
1139
1252
|
}
|
|
1140
1253
|
get effectivePanelStyleClass() {
|
|
@@ -1154,6 +1267,9 @@ class EfSelectComponent extends AbstractEfFormControl {
|
|
|
1154
1267
|
return !!(ctrl?.invalid && (ctrl?.touched || ctrl?.dirty));
|
|
1155
1268
|
}
|
|
1156
1269
|
ngOnChanges(changes) {
|
|
1270
|
+
if (changes['showClear']) {
|
|
1271
|
+
this.showClearBound = true;
|
|
1272
|
+
}
|
|
1157
1273
|
if (changes['options'] || changes['filterByKeys']) {
|
|
1158
1274
|
this.applyFilters();
|
|
1159
1275
|
}
|
|
@@ -1193,26 +1309,14 @@ class EfSelectComponent extends AbstractEfFormControl {
|
|
|
1193
1309
|
this.panelHideEvent.emit();
|
|
1194
1310
|
this.onTouched();
|
|
1195
1311
|
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
return;
|
|
1203
|
-
|
|
1204
|
-
let resolved = rawValue;
|
|
1205
|
-
if (this.optionValue) {
|
|
1206
|
-
// Map the string back to its option's value (which may be a
|
|
1207
|
-
// number, object key, etc. — preserves the original type).
|
|
1208
|
-
const match = this.filteredOptions.find((opt) => String(opt[this.optionValue]) === rawValue);
|
|
1209
|
-
if (match)
|
|
1210
|
-
resolved = match[this.optionValue];
|
|
1211
|
-
}
|
|
1212
|
-
this.value = resolved;
|
|
1213
|
-
this.onChange(resolved);
|
|
1214
|
-
this.onTouched();
|
|
1215
|
-
this.selectionChangeEvent.emit(resolved);
|
|
1312
|
+
optionValueOf(option) {
|
|
1313
|
+
return this.optionValue ? ObjectUtils.resolveFieldData(option, this.optionValue) : option;
|
|
1314
|
+
}
|
|
1315
|
+
flatOptions() {
|
|
1316
|
+
const options = this.filteredOptions ?? [];
|
|
1317
|
+
if (!this.group)
|
|
1318
|
+
return options;
|
|
1319
|
+
return options.flatMap((g) => ObjectUtils.resolveFieldData(g, this.optionGroupChildren) ?? []);
|
|
1216
1320
|
}
|
|
1217
1321
|
applyFilters() {
|
|
1218
1322
|
if (this.options && this.filterByKeys && typeof this.filterByKeys === 'object') {
|
|
@@ -1223,11 +1327,11 @@ class EfSelectComponent extends AbstractEfFormControl {
|
|
|
1223
1327
|
}
|
|
1224
1328
|
}
|
|
1225
1329
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfSelectComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1226
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfSelectComponent, isStandalone: true, selector: "ef-select", inputs: { options: "options", optionLabel: "optionLabel", optionValue: "optionValue", optionDisabled: "optionDisabled", dataKey: "dataKey", group: ["group", "group", booleanAttribute], optionGroupLabel: "optionGroupLabel", optionGroupChildren: "optionGroupChildren", size: "size", fluid: ["fluid", "fluid", booleanAttribute], inline: ["inline", "inline", booleanAttribute], appendTo: "appendTo", multiple: ["multiple", "multiple", booleanAttribute], filter: ["filter", "filter",
|
|
1330
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfSelectComponent, isStandalone: true, selector: "ef-select", inputs: { options: "options", optionLabel: "optionLabel", optionValue: "optionValue", optionDisabled: "optionDisabled", dataKey: "dataKey", group: ["group", "group", booleanAttribute], optionGroupLabel: "optionGroupLabel", optionGroupChildren: "optionGroupChildren", size: "size", fluid: ["fluid", "fluid", booleanAttribute], inline: ["inline", "inline", booleanAttribute], appendTo: "appendTo", multiple: ["multiple", "multiple", booleanAttribute], filter: ["filter", "filter", optionalBoolean], filterBy: "filterBy", filterPlaceholder: "filterPlaceholder", filterPlaceholderKey: "filterPlaceholderKey", autofocusFilter: ["autofocusFilter", "autofocusFilter", booleanAttribute], autofocus: ["autofocus", "autofocus", booleanAttribute], editable: ["editable", "editable", booleanAttribute], loading: ["loading", "loading", booleanAttribute], virtualScroll: ["virtualScroll", "virtualScroll", booleanAttribute], virtualScrollItemSize: "virtualScrollItemSize", scrollHeight: "scrollHeight", multiDisplay: "multiDisplay", showToggleAll: ["showToggleAll", "showToggleAll", booleanAttribute], emptyMessage: "emptyMessage", emptyMessageKey: "emptyMessageKey", emptyFilterMessage: "emptyFilterMessage", emptyFilterMessageKey: "emptyFilterMessageKey", styleClass: "styleClass", panelStyleClass: "panelStyleClass", filterByKeys: "filterByKeys", variant: "variant", value: "value" }, outputs: { selectionChangeEvent: "selectionChangeEvent", filterChangeEvent: "filterChangeEvent", panelShowEvent: "panelShowEvent", panelHideEvent: "panelHideEvent" }, host: { properties: { "class.ef-inline": "this.isInline", "class.ef-select-host": "this.hostClass" } }, providers: [{
|
|
1227
1331
|
provide: NG_VALUE_ACCESSOR,
|
|
1228
1332
|
useExisting: forwardRef(() => EfSelectComponent),
|
|
1229
1333
|
multi: true,
|
|
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" }] });
|
|
1334
|
+
}], 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 (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]=\"effectiveFilter\"\n [filterPlaceHolder]=\"effectiveFilterPlaceholder\"\n [autofocusFilter]=\"autofocusFilter\"\n [autofocus]=\"autofocus\"\n [filterBy]=\"filterBy ?? optionLabel\"\n [showClear]=\"effectiveShowClear\"\n [disabled]=\"isDisabled()\"\n [appendTo]=\"effectiveAppendTo\"\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 ?? null\"\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]=\"triggerPlaceholder\"\n [size]=\"size\"\n [filter]=\"effectiveFilter\"\n [filterPlaceholder]=\"effectiveFilterPlaceholder\"\n [filterBy]=\"filterBy ?? optionLabel\"\n [autofocusFilter]=\"autofocusFilter\"\n [autofocus]=\"autofocus\"\n [showClear]=\"effectiveShowClear\"\n [editable]=\"editable\"\n [disabled]=\"isDisabled()\"\n [appendTo]=\"effectiveAppendTo\"\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.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" }] });
|
|
1231
1335
|
}
|
|
1232
1336
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfSelectComponent, decorators: [{
|
|
1233
1337
|
type: Component,
|
|
@@ -1242,7 +1346,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
1242
1346
|
provide: NG_VALUE_ACCESSOR,
|
|
1243
1347
|
useExisting: forwardRef(() => EfSelectComponent),
|
|
1244
1348
|
multi: true,
|
|
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"] }]
|
|
1349
|
+
}], 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 (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]=\"effectiveFilter\"\n [filterPlaceHolder]=\"effectiveFilterPlaceholder\"\n [autofocusFilter]=\"autofocusFilter\"\n [autofocus]=\"autofocus\"\n [filterBy]=\"filterBy ?? optionLabel\"\n [showClear]=\"effectiveShowClear\"\n [disabled]=\"isDisabled()\"\n [appendTo]=\"effectiveAppendTo\"\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 ?? null\"\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]=\"triggerPlaceholder\"\n [size]=\"size\"\n [filter]=\"effectiveFilter\"\n [filterPlaceholder]=\"effectiveFilterPlaceholder\"\n [filterBy]=\"filterBy ?? optionLabel\"\n [autofocusFilter]=\"autofocusFilter\"\n [autofocus]=\"autofocus\"\n [showClear]=\"effectiveShowClear\"\n [editable]=\"editable\"\n [disabled]=\"isDisabled()\"\n [appendTo]=\"effectiveAppendTo\"\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"] }]
|
|
1246
1350
|
}], propDecorators: { options: [{
|
|
1247
1351
|
type: Input
|
|
1248
1352
|
}], optionLabel: [{
|
|
@@ -1275,7 +1379,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
1275
1379
|
args: [{ transform: booleanAttribute }]
|
|
1276
1380
|
}], filter: [{
|
|
1277
1381
|
type: Input,
|
|
1278
|
-
args: [{ transform:
|
|
1382
|
+
args: [{ transform: optionalBoolean }]
|
|
1279
1383
|
}], filterBy: [{
|
|
1280
1384
|
type: Input
|
|
1281
1385
|
}], filterPlaceholder: [{
|
|
@@ -4712,6 +4816,7 @@ function resolveEfStatusColor(value) {
|
|
|
4712
4816
|
* - `[color]` forces a specific Comptoir tone, ignoring metadata.
|
|
4713
4817
|
* - `[label]` / `[labelKey]` force the text, ignoring reference data.
|
|
4714
4818
|
* - `[hideDot]` removes the leading ● glyph.
|
|
4819
|
+
* - `metadata.icon` (or `[icon]`) swaps the ● for a PrimeIcons glyph, e.g. `pi-check`.
|
|
4715
4820
|
* - `[showCode]` displays the raw code (useful for admin screens).
|
|
4716
4821
|
*/
|
|
4717
4822
|
class EfStatusChipComponent {
|
|
@@ -4726,6 +4831,8 @@ class EfStatusChipComponent {
|
|
|
4726
4831
|
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
4727
4832
|
/** Translation key — preferred when forcing a label. */
|
|
4728
4833
|
labelKey = input('', ...(ngDevMode ? [{ debugName: "labelKey" }] : /* istanbul ignore next */ []));
|
|
4834
|
+
/** PrimeIcons class (e.g. `pi-check`) shown instead of the ● dot. Overrides `metadata.icon`. */
|
|
4835
|
+
icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
|
|
4729
4836
|
/** Hide the leading ● dot. */
|
|
4730
4837
|
hideDot = input(false, { ...(ngDevMode ? { debugName: "hideDot" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4731
4838
|
/** Render the raw `code` after the label (admin-screen helper). */
|
|
@@ -4751,6 +4858,14 @@ class EfStatusChipComponent {
|
|
|
4751
4858
|
resolveEfStatusColor(this.code()) ??
|
|
4752
4859
|
'neutral');
|
|
4753
4860
|
}, ...(ngDevMode ? [{ debugName: "resolvedColor" }] : /* istanbul ignore next */ []));
|
|
4861
|
+
/** Leading icon: the `icon` input, else the entry's `metadata.icon`, else none (the ● dot shows). */
|
|
4862
|
+
resolvedIcon = computed(() => {
|
|
4863
|
+
const raw = this.icon() || this.entry()?.metadata?.icon || '';
|
|
4864
|
+
const trimmed = raw.trim();
|
|
4865
|
+
if (!trimmed)
|
|
4866
|
+
return '';
|
|
4867
|
+
return trimmed.startsWith('pi ') || trimmed.startsWith('pi-') ? trimmed : `pi-${trimmed}`;
|
|
4868
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedIcon" }] : /* istanbul ignore next */ []));
|
|
4754
4869
|
/** Resolved label — labelKey input → label input → reference label → code. */
|
|
4755
4870
|
resolvedLabel = computed(() => {
|
|
4756
4871
|
const entry = this.entry();
|
|
@@ -4761,12 +4876,12 @@ class EfStatusChipComponent {
|
|
|
4761
4876
|
return this.labelKey() || this.entry()?.labelKey || '';
|
|
4762
4877
|
}, ...(ngDevMode ? [{ debugName: "resolvedLabelKey" }] : /* istanbul ignore next */ []));
|
|
4763
4878
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfStatusChipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4764
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfStatusChipComponent, isStandalone: true, selector: "ef-status-chip", inputs: { referenceKey: { classPropertyName: "referenceKey", publicName: "referenceKey", isSignal: true, isRequired: false, transformFunction: null }, code: { classPropertyName: "code", publicName: "code", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", 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 }, hideDot: { classPropertyName: "hideDot", publicName: "hideDot", isSignal: true, isRequired: false, transformFunction: null }, showCode: { classPropertyName: "showCode", publicName: "showCode", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<span class=\"chip\" [class]=\"'chip chip-' + resolvedColor()\">\n @if (!hideDot()) {\n <span aria-hidden=\"true\">\u25CF</span>\n }\n @if (resolvedLabelKey()) {\n {{ resolvedLabelKey() | translate }}\n } @else {\n {{ resolvedLabel() }}\n }\n @if (showCode()) {\n <span class=\"text-mute\" style=\"margin-inline-start: 6px; font-family: 'JetBrains Mono', monospace; font-size: 10.5px;\">\n {{ code() }}\n </span>\n }\n</span>\n", styles: ["@charset \"UTF-8\";:host{display:inline-flex;vertical-align:middle}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4879
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfStatusChipComponent, isStandalone: true, selector: "ef-status-chip", inputs: { referenceKey: { classPropertyName: "referenceKey", publicName: "referenceKey", isSignal: true, isRequired: false, transformFunction: null }, code: { classPropertyName: "code", publicName: "code", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", 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 }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, hideDot: { classPropertyName: "hideDot", publicName: "hideDot", isSignal: true, isRequired: false, transformFunction: null }, showCode: { classPropertyName: "showCode", publicName: "showCode", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<span class=\"chip\" [class]=\"'chip chip-' + resolvedColor()\">\n @if (resolvedIcon(); as ico) {\n <i class=\"pi\" [ngClass]=\"ico\" aria-hidden=\"true\"></i>\n } @else if (!hideDot()) {\n <span aria-hidden=\"true\">\u25CF</span>\n }\n @if (resolvedLabelKey()) {\n {{ resolvedLabelKey() | translate }}\n } @else {\n {{ resolvedLabel() }}\n }\n @if (showCode()) {\n <span class=\"text-mute\" style=\"margin-inline-start: 6px; font-family: 'JetBrains Mono', monospace; font-size: 10.5px;\">\n {{ code() }}\n </span>\n }\n</span>\n", styles: ["@charset \"UTF-8\";:host{display:inline-flex;vertical-align:middle}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4765
4880
|
}
|
|
4766
4881
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfStatusChipComponent, decorators: [{
|
|
4767
4882
|
type: Component,
|
|
4768
|
-
args: [{ selector: 'ef-status-chip', standalone: true, imports: [CommonModule, TranslateModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<span class=\"chip\" [class]=\"'chip chip-' + resolvedColor()\">\n @if (!hideDot()) {\n <span aria-hidden=\"true\">\u25CF</span>\n }\n @if (resolvedLabelKey()) {\n {{ resolvedLabelKey() | translate }}\n } @else {\n {{ resolvedLabel() }}\n }\n @if (showCode()) {\n <span class=\"text-mute\" style=\"margin-inline-start: 6px; font-family: 'JetBrains Mono', monospace; font-size: 10.5px;\">\n {{ code() }}\n </span>\n }\n</span>\n", styles: ["@charset \"UTF-8\";:host{display:inline-flex;vertical-align:middle}\n"] }]
|
|
4769
|
-
}], propDecorators: { referenceKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "referenceKey", required: false }] }], code: [{ type: i0.Input, args: [{ isSignal: true, alias: "code", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], labelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelKey", required: false }] }], hideDot: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideDot", required: false }] }], showCode: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCode", required: false }] }] } });
|
|
4883
|
+
args: [{ selector: 'ef-status-chip', standalone: true, imports: [CommonModule, TranslateModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<span class=\"chip\" [class]=\"'chip chip-' + resolvedColor()\">\n @if (resolvedIcon(); as ico) {\n <i class=\"pi\" [ngClass]=\"ico\" aria-hidden=\"true\"></i>\n } @else if (!hideDot()) {\n <span aria-hidden=\"true\">\u25CF</span>\n }\n @if (resolvedLabelKey()) {\n {{ resolvedLabelKey() | translate }}\n } @else {\n {{ resolvedLabel() }}\n }\n @if (showCode()) {\n <span class=\"text-mute\" style=\"margin-inline-start: 6px; font-family: 'JetBrains Mono', monospace; font-size: 10.5px;\">\n {{ code() }}\n </span>\n }\n</span>\n", styles: ["@charset \"UTF-8\";:host{display:inline-flex;vertical-align:middle}\n"] }]
|
|
4884
|
+
}], propDecorators: { referenceKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "referenceKey", required: false }] }], code: [{ type: i0.Input, args: [{ isSignal: true, alias: "code", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], labelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelKey", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], hideDot: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideDot", required: false }] }], showCode: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCode", required: false }] }] } });
|
|
4770
4885
|
|
|
4771
4886
|
/** Row-scoped shortcuts, wired once per instance and gated on `open()` so
|
|
4772
4887
|
* they're inert until this row's menu is on screen. The action `id` is a
|
|
@@ -6364,7 +6479,7 @@ class EfDataCardComponent {
|
|
|
6364
6479
|
};
|
|
6365
6480
|
trackByColumn = (_, col) => col.id;
|
|
6366
6481
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfDataCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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 });
|
|
6482
|
+
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", "icon", "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 });
|
|
6368
6483
|
}
|
|
6369
6484
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfDataCardComponent, decorators: [{
|
|
6370
6485
|
type: Component,
|
|
@@ -9274,13 +9389,13 @@ class EfAppMainComponent {
|
|
|
9274
9389
|
/** `data-module` value bound to the host. Null when no module is active. */
|
|
9275
9390
|
moduleId = computed(() => this.activeModule.activeModuleId(), ...(ngDevMode ? [{ debugName: "moduleId" }] : /* istanbul ignore next */ []));
|
|
9276
9391
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfAppMainComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9277
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfAppMainComponent, isStandalone: true, selector: "ef-app-main", inputs: { stripe: ["stripe", "stripe", booleanAttribute] }, host: { properties: { "attr.data-module": "moduleId()" } }, ngImport: i0, template: "@if (stripe) {\n <div class=\"ef-app-main__stripe\" aria-hidden=\"true\"></div>\n}\n<div class=\"ef-app-main__content\">\n <ng-content></ng-content>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0;overflow:hidden;background:var(--paper);color:var(--text)}.ef-app-main__stripe{flex:0 0 auto;height:3px;background:var(--module)}.ef-app-main__content{flex:1 1 auto;overflow:auto;padding:var(--s-5) var(--s-6) var(--s-7);display:grid;grid-template-columns:minmax(0,1fr);align-content:start}@media(max-width:767px){.ef-app-main__content{padding:var(--s-4) var(--s-4) var(--s-7)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
9392
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfAppMainComponent, isStandalone: true, selector: "ef-app-main", inputs: { stripe: ["stripe", "stripe", booleanAttribute] }, host: { properties: { "attr.data-module": "moduleId()" } }, ngImport: i0, template: "@if (stripe) {\n <div class=\"ef-app-main__stripe\" aria-hidden=\"true\"></div>\n}\n<div class=\"ef-app-main__content\">\n <ng-content></ng-content>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0;overflow:hidden;background:var(--paper);color:var(--text)}.ef-app-main__stripe{flex:0 0 auto;height:3px;background:var(--module)}.ef-app-main__content{flex:1 1 auto;position:relative;overflow:auto;padding:var(--s-5) var(--s-6) var(--s-7);display:grid;grid-template-columns:minmax(0,1fr);align-content:start}@media(max-width:767px){.ef-app-main__content{padding:var(--s-4) var(--s-4) var(--s-7)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
9278
9393
|
}
|
|
9279
9394
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfAppMainComponent, decorators: [{
|
|
9280
9395
|
type: Component,
|
|
9281
9396
|
args: [{ selector: 'ef-app-main', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
9282
9397
|
'[attr.data-module]': 'moduleId()',
|
|
9283
|
-
}, template: "@if (stripe) {\n <div class=\"ef-app-main__stripe\" aria-hidden=\"true\"></div>\n}\n<div class=\"ef-app-main__content\">\n <ng-content></ng-content>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0;overflow:hidden;background:var(--paper);color:var(--text)}.ef-app-main__stripe{flex:0 0 auto;height:3px;background:var(--module)}.ef-app-main__content{flex:1 1 auto;overflow:auto;padding:var(--s-5) var(--s-6) var(--s-7);display:grid;grid-template-columns:minmax(0,1fr);align-content:start}@media(max-width:767px){.ef-app-main__content{padding:var(--s-4) var(--s-4) var(--s-7)}}\n"] }]
|
|
9398
|
+
}, template: "@if (stripe) {\n <div class=\"ef-app-main__stripe\" aria-hidden=\"true\"></div>\n}\n<div class=\"ef-app-main__content\">\n <ng-content></ng-content>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0;overflow:hidden;background:var(--paper);color:var(--text)}.ef-app-main__stripe{flex:0 0 auto;height:3px;background:var(--module)}.ef-app-main__content{flex:1 1 auto;position:relative;overflow:auto;padding:var(--s-5) var(--s-6) var(--s-7);display:grid;grid-template-columns:minmax(0,1fr);align-content:start}@media(max-width:767px){.ef-app-main__content{padding:var(--s-4) var(--s-4) var(--s-7)}}\n"] }]
|
|
9284
9399
|
}], propDecorators: { stripe: [{
|
|
9285
9400
|
type: Input,
|
|
9286
9401
|
args: [{ transform: booleanAttribute }]
|
|
@@ -9812,6 +9927,7 @@ class EfTabsComponent {
|
|
|
9812
9927
|
* `fr.json` / `ar.json`. */
|
|
9813
9928
|
dirtyLabelKey = 'common.dirty';
|
|
9814
9929
|
valueChange = new EventEmitter();
|
|
9930
|
+
host = inject(ElementRef);
|
|
9815
9931
|
tabEls;
|
|
9816
9932
|
/* ── Content children (optional projected panels) ────────────── */
|
|
9817
9933
|
panels;
|
|
@@ -9873,6 +9989,13 @@ class EfTabsComponent {
|
|
|
9873
9989
|
default:
|
|
9874
9990
|
return;
|
|
9875
9991
|
}
|
|
9992
|
+
// In a right-to-left tablist the next tab sits to the left, so the
|
|
9993
|
+
// arrows swap (WAI-ARIA tabs pattern); Home and End keep their meaning.
|
|
9994
|
+
if ((event.key === 'ArrowRight' || event.key === 'ArrowLeft') && this.isRtl(event.currentTarget)) {
|
|
9995
|
+
nextPos = event.key === 'ArrowRight'
|
|
9996
|
+
? enabledPositions[(here - 1 + enabledPositions.length) % enabledPositions.length]
|
|
9997
|
+
: enabledPositions[(here + 1) % enabledPositions.length];
|
|
9998
|
+
}
|
|
9876
9999
|
event.preventDefault();
|
|
9877
10000
|
const target = all[nextPos];
|
|
9878
10001
|
this.select(target);
|
|
@@ -9881,11 +10004,19 @@ class EfTabsComponent {
|
|
|
9881
10004
|
const el = this.tabEls?.toArray()[nextPos]?.nativeElement;
|
|
9882
10005
|
el?.focus();
|
|
9883
10006
|
}
|
|
10007
|
+
isRtl(target) {
|
|
10008
|
+
const el = target instanceof HTMLElement ? target : this.host.nativeElement;
|
|
10009
|
+
if (getComputedStyle(el).direction === 'rtl')
|
|
10010
|
+
return true;
|
|
10011
|
+
// Only reached in jsdom, which does not inherit `direction`; a browser
|
|
10012
|
+
// answers above.
|
|
10013
|
+
return el.closest('[dir]')?.getAttribute('dir') === 'rtl';
|
|
10014
|
+
}
|
|
9884
10015
|
trackValue(_, item) {
|
|
9885
10016
|
return item.value;
|
|
9886
10017
|
}
|
|
9887
10018
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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 });
|
|
10019
|
+
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 [class.is-danger]=\"item.tone === 'danger'\"\n [attr.id]=\"item.id || null\"\n [attr.aria-controls]=\"item.panelId || null\"\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.noteKey) {\n <span class=\"ef-tabs__note\">{{ item.noteKey | translate }}</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}.ef-tabs__note{font-size:11px;font-weight:500}: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))}.ef-tabs__tab.is-danger,:host(.ef-tabs--pill) .ef-tabs__tab.is-danger:hover:not(.is-active):not(.is-disabled),:host(.ef-tabs--underline) .ef-tabs__tab.is-danger:hover:not(.is-active):not(.is-disabled){color:var(--st-cancelled-fg)}:host(.ef-tabs--pill) .ef-tabs__tab.is-danger.is-active{color:var(--paper);box-shadow:inset 0 0 0 2px var(--st-cancelled-fg)}:host(.ef-tabs--underline) .ef-tabs__tab.is-danger.is-active{color:var(--st-cancelled-fg);border-bottom-color:var(--st-cancelled-fg)}:host(.ef-tabs--pill) .ef-tabs__tab.is-danger.is-active:focus-visible{box-shadow:inset 0 0 0 2px var(--st-cancelled-fg),0 0 0 2px color-mix(in srgb,var(--ink-active, #0f1115) 40%,transparent)}: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 });
|
|
9889
10020
|
}
|
|
9890
10021
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfTabsComponent, decorators: [{
|
|
9891
10022
|
type: Component,
|
|
@@ -9894,7 +10025,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
9894
10025
|
'[class.ef-tabs--underline]': "variant === 'underline'",
|
|
9895
10026
|
'[class.ef-tabs--overflow]': 'overflow',
|
|
9896
10027
|
'[attr.data-module]': 'module ?? null',
|
|
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"] }]
|
|
10028
|
+
}, 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 [class.is-danger]=\"item.tone === 'danger'\"\n [attr.id]=\"item.id || null\"\n [attr.aria-controls]=\"item.panelId || null\"\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.noteKey) {\n <span class=\"ef-tabs__note\">{{ item.noteKey | translate }}</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}.ef-tabs__note{font-size:11px;font-weight:500}: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))}.ef-tabs__tab.is-danger,:host(.ef-tabs--pill) .ef-tabs__tab.is-danger:hover:not(.is-active):not(.is-disabled),:host(.ef-tabs--underline) .ef-tabs__tab.is-danger:hover:not(.is-active):not(.is-disabled){color:var(--st-cancelled-fg)}:host(.ef-tabs--pill) .ef-tabs__tab.is-danger.is-active{color:var(--paper);box-shadow:inset 0 0 0 2px var(--st-cancelled-fg)}:host(.ef-tabs--underline) .ef-tabs__tab.is-danger.is-active{color:var(--st-cancelled-fg);border-bottom-color:var(--st-cancelled-fg)}:host(.ef-tabs--pill) .ef-tabs__tab.is-danger.is-active:focus-visible{box-shadow:inset 0 0 0 2px var(--st-cancelled-fg),0 0 0 2px color-mix(in srgb,var(--ink-active, #0f1115) 40%,transparent)}: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"] }]
|
|
9898
10029
|
}], propDecorators: { items: [{
|
|
9899
10030
|
type: Input,
|
|
9900
10031
|
args: [{ required: true }]
|
|
@@ -11550,6 +11681,10 @@ class EfInputTextComponent extends AbstractEfFormControl {
|
|
|
11550
11681
|
* follow the value's first strong character. The label and messages keep
|
|
11551
11682
|
* the page direction. Null or absent leaves the input unchanged. */
|
|
11552
11683
|
dir = null;
|
|
11684
|
+
/** Language of the typed value (`'ar'` for an Arabic title on a French
|
|
11685
|
+
* screen), applied to the native input only: the label and hint are page
|
|
11686
|
+
* text and keep the page language. Null or absent renders no attribute. */
|
|
11687
|
+
lang = null;
|
|
11553
11688
|
/** Clearable (showClear / canClear) is inherited from AbstractEfFormControl;
|
|
11554
11689
|
* we only supply the value predicate + the reset. */
|
|
11555
11690
|
get hasValue() {
|
|
@@ -11661,11 +11796,11 @@ class EfInputTextComponent extends AbstractEfFormControl {
|
|
|
11661
11796
|
this.onTouched();
|
|
11662
11797
|
}
|
|
11663
11798
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfInputTextComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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
|
|
11799
|
+
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", lang: "lang", variant: "variant", prefix: "prefix", suffix: "suffix", value: "value" }, outputs: { valueChangeEvent: "valueChangeEvent" }, host: { listeners: { "focusin": "markFocused($event)" }, properties: { "attr.lang": "null", "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 [attr.lang]=\"lang || 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 [attr.lang]=\"lang || 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 [attr.lang]=\"lang || 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" }] });
|
|
11665
11800
|
}
|
|
11666
11801
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfInputTextComponent, decorators: [{
|
|
11667
11802
|
type: Component,
|
|
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
|
|
11803
|
+
args: [{ selector: 'ef-input-text', standalone: true, imports: [InputTextModule, FormsModule, TranslateModule, EfLabelComponent, EfClearButtonComponent], host: { '[attr.lang]': 'null' }, 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 [attr.lang]=\"lang || 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 [attr.lang]=\"lang || 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 [attr.lang]=\"lang || 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"] }]
|
|
11669
11804
|
}], ctorParameters: () => [], propDecorators: { size: [{
|
|
11670
11805
|
type: Input
|
|
11671
11806
|
}], type: [{
|
|
@@ -11682,6 +11817,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
11682
11817
|
type: Input
|
|
11683
11818
|
}], dir: [{
|
|
11684
11819
|
type: Input
|
|
11820
|
+
}], lang: [{
|
|
11821
|
+
type: Input
|
|
11685
11822
|
}], variant: [{
|
|
11686
11823
|
type: Input
|
|
11687
11824
|
}], prefix: [{
|
|
@@ -12255,6 +12392,19 @@ class EfMultiSelectComponent extends AbstractEfFormControl {
|
|
|
12255
12392
|
selectionLimit;
|
|
12256
12393
|
filters;
|
|
12257
12394
|
inline = false;
|
|
12395
|
+
/**
|
|
12396
|
+
* Rendering variant, as on `ef-select`.
|
|
12397
|
+
* - `'primeng'` (default): PrimeNG's own multiselect skin, unchanged.
|
|
12398
|
+
* - `'comptoir'`: the shared `.es` skin with `.es--comptoir`, the same hook
|
|
12399
|
+
* `ef-select variant="comptoir"` uses: a trigger on
|
|
12400
|
+
* `--hit-field` (`--hit-base` 40px, `--hit-touch` 48px on a
|
|
12401
|
+
* coarse pointer) and no minimum width, so a select and a
|
|
12402
|
+
* multi-select in one row align and both shrink with it.
|
|
12403
|
+
*/
|
|
12404
|
+
variant = 'primeng';
|
|
12405
|
+
get effectiveStyleClass() {
|
|
12406
|
+
return this.variant === 'comptoir' ? 'es es--comptoir' : undefined;
|
|
12407
|
+
}
|
|
12258
12408
|
get isInline() { return this.inline; }
|
|
12259
12409
|
get isRequired() { return this.required(); }
|
|
12260
12410
|
selectedValues = [];
|
|
@@ -12319,11 +12469,11 @@ class EfMultiSelectComponent extends AbstractEfFormControl {
|
|
|
12319
12469
|
this.selectedValues = [];
|
|
12320
12470
|
}
|
|
12321
12471
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfMultiSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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" }] });
|
|
12472
|
+
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], variant: "variant" }, 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 [styleClass]=\"effectiveStyleClass\"\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" }] });
|
|
12323
12473
|
}
|
|
12324
12474
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfMultiSelectComponent, decorators: [{
|
|
12325
12475
|
type: Component,
|
|
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"] }]
|
|
12476
|
+
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 [styleClass]=\"effectiveStyleClass\"\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"] }]
|
|
12327
12477
|
}], ctorParameters: () => [], propDecorators: { changeEvent: [{
|
|
12328
12478
|
type: Output
|
|
12329
12479
|
}], optionLabel: [{
|
|
@@ -12347,6 +12497,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
12347
12497
|
}], inline: [{
|
|
12348
12498
|
type: Input,
|
|
12349
12499
|
args: [{ transform: booleanAttribute }]
|
|
12500
|
+
}], variant: [{
|
|
12501
|
+
type: Input
|
|
12350
12502
|
}], isInline: [{
|
|
12351
12503
|
type: HostBinding,
|
|
12352
12504
|
args: ['class.ef-inline']
|
|
@@ -13236,6 +13388,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
13236
13388
|
class EfTextareaComponent extends AbstractEfFormControl {
|
|
13237
13389
|
rows = 4;
|
|
13238
13390
|
maxlength;
|
|
13391
|
+
/** Language of the typed value, applied to the native textarea only (the
|
|
13392
|
+
* label and hint keep the page language). Null or absent renders none. */
|
|
13393
|
+
lang = null;
|
|
13239
13394
|
autofocus = false;
|
|
13240
13395
|
/** Monospace face for source text (an HTML body, a JSON snippet), where
|
|
13241
13396
|
* tags and indentation must stay readable. */
|
|
@@ -13336,15 +13491,17 @@ class EfTextareaComponent extends AbstractEfFormControl {
|
|
|
13336
13491
|
this.onTouched();
|
|
13337
13492
|
}
|
|
13338
13493
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfTextareaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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" }] });
|
|
13494
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfTextareaComponent, isStandalone: true, selector: "ef-textarea", inputs: { rows: "rows", maxlength: "maxlength", lang: "lang", autofocus: ["autofocus", "autofocus", booleanAttribute], mono: ["mono", "mono", booleanAttribute], value: "value" }, outputs: { valueChangeEvent: "valueChangeEvent" }, host: { listeners: { "focusin": "markFocused($event)" }, properties: { "attr.lang": "null" } }, 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 [attr.lang]=\"lang || 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" }] });
|
|
13340
13495
|
}
|
|
13341
13496
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfTextareaComponent, decorators: [{
|
|
13342
13497
|
type: Component,
|
|
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"] }]
|
|
13498
|
+
args: [{ selector: 'ef-textarea', standalone: true, imports: [FormsModule, TranslateModule, EfLabelComponent, EfClearButtonComponent], host: { '[attr.lang]': 'null' }, 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 [attr.lang]=\"lang || 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"] }]
|
|
13344
13499
|
}], ctorParameters: () => [], propDecorators: { rows: [{
|
|
13345
13500
|
type: Input
|
|
13346
13501
|
}], maxlength: [{
|
|
13347
13502
|
type: Input
|
|
13503
|
+
}], lang: [{
|
|
13504
|
+
type: Input
|
|
13348
13505
|
}], autofocus: [{
|
|
13349
13506
|
type: Input,
|
|
13350
13507
|
args: [{ transform: booleanAttribute }]
|
|
@@ -13360,82 +13517,503 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
13360
13517
|
args: ['focusin', ['$event']]
|
|
13361
13518
|
}] } });
|
|
13362
13519
|
|
|
13520
|
+
/**
|
|
13521
|
+
* Loads the rich text editor component that `ef-localized-input`'s `rich`
|
|
13522
|
+
* mode renders. The editor lives in the `@elasticias/ui/rich-text` entry
|
|
13523
|
+
* point so an app that never edits rich text neither bundles PrimeNG's Editor
|
|
13524
|
+
* nor needs `quill` installed. Provide it with `provideEfRichText()` from that
|
|
13525
|
+
* entry (the editor code is in the app's main bundle, Quill itself still loads
|
|
13526
|
+
* lazily the first time an editor renders), or with your own loader to defer
|
|
13527
|
+
* the editor code too:
|
|
13528
|
+
*
|
|
13529
|
+
* ```ts
|
|
13530
|
+
* { provide: EF_RICH_TEXT, useValue: () => import('@elasticias/ui/rich-text').then(m => m.EfRichTextComponent) }
|
|
13531
|
+
* ```
|
|
13532
|
+
*/
|
|
13533
|
+
const EF_RICH_TEXT = new InjectionToken('EF_RICH_TEXT');
|
|
13534
|
+
/** Length of the text a reader sees in an HTML fragment, tags not counted. Browser only: it parses with `document`. */
|
|
13535
|
+
function plainTextLength(html) {
|
|
13536
|
+
if (!html)
|
|
13537
|
+
return 0;
|
|
13538
|
+
const tpl = document.createElement('template');
|
|
13539
|
+
tpl.innerHTML = html;
|
|
13540
|
+
return (tpl.content.textContent ?? '').length;
|
|
13541
|
+
}
|
|
13542
|
+
|
|
13363
13543
|
let nextId = 0;
|
|
13364
|
-
|
|
13365
|
-
|
|
13366
|
-
|
|
13367
|
-
|
|
13368
|
-
|
|
13544
|
+
/** Same texts once blanks are dropped. */
|
|
13545
|
+
function sameText(a, b) {
|
|
13546
|
+
const ka = Object.keys(a);
|
|
13547
|
+
return ka.length === Object.keys(b).length && ka.every(k => a[k] === b[k]);
|
|
13548
|
+
}
|
|
13369
13549
|
/**
|
|
13370
|
-
*
|
|
13371
|
-
*
|
|
13372
|
-
* and the
|
|
13373
|
-
*
|
|
13374
|
-
*
|
|
13550
|
+
* One owner-written text (a title, a description, an HTML body) in every
|
|
13551
|
+
* content language: a pill tab per culture, the active culture's field below
|
|
13552
|
+
* it, and the whole object out on every edit with blank cultures removed
|
|
13553
|
+
* (ADR-050 section 7).
|
|
13554
|
+
*
|
|
13555
|
+
* - **Cultures** come from `EF_CONTENT_CULTURES` (the ERP provides them from
|
|
13556
|
+
* the tenant profile) unless `cultures` is set. The active tab is local and
|
|
13557
|
+
* starts on the default culture.
|
|
13558
|
+
* - **Tabs** carry the `ef_localized_input_missing` note on every culture with
|
|
13559
|
+
* no text, the error tone on the default culture of a `required` field left
|
|
13560
|
+
* empty once the field is touched, changed or its form submitted, and the
|
|
13561
|
+
* dirty dot on cultures edited since the value arrived. Dirty detection
|
|
13562
|
+
* relies on the parent storing the emitted object by reference (the V2
|
|
13563
|
+
* signal pattern): a parent that clones the value resets the dots on every
|
|
13564
|
+
* edit, though it never loses text.
|
|
13565
|
+
* - **Direction.** Each field gets its culture's `dir` and `lang`. In `html`
|
|
13566
|
+
* mode the source stays `ltr` for every culture (raw HTML in an `rtl`
|
|
13567
|
+
* textarea reads reversed) and the sanitised preview takes the culture's
|
|
13568
|
+
* direction.
|
|
13569
|
+
* - **Rich mode** (`mode="rich"`) edits each culture with `ef-rich-text`, the
|
|
13570
|
+
* shared editor in `@elasticias/ui/rich-text`. The app turns it on once with
|
|
13571
|
+
* `provideEfRichText()` (and installs `quill`); without it the field falls
|
|
13572
|
+
* back to the `html` source and logs why in development, as it does when
|
|
13573
|
+
* the editor's chunk fails to load. With `advisedLength` the counter
|
|
13574
|
+
* counts the text a reader sees; with only `maxLength` it counts the
|
|
13575
|
+
* stored HTML, which is what the server measures. Neither caps typing in
|
|
13576
|
+
* this mode.
|
|
13577
|
+
* - **Read-only cultures** are shown read-only (still focusable, so the hint
|
|
13578
|
+
* is read and the text can be copied) with
|
|
13579
|
+
* `ef_localized_input_read_only_hint` and the projected `[read-only-link]`;
|
|
13580
|
+
* their text still travels in the emitted object. `[efReadOnly]`, `[disabled]`
|
|
13581
|
+
* and a form's `disable()` disable every culture; the tabs stay usable so the
|
|
13582
|
+
* other languages can still be read.
|
|
13583
|
+
* - **Forms.** A ControlValueAccessor (`[formControl]`, `formControlName`,
|
|
13584
|
+
* `[(ngModel)]`) as well as `[value]` + `(valueChangeEvent)`. With
|
|
13585
|
+
* `required`, the form control reports `required` while the default culture
|
|
13586
|
+
* is empty.
|
|
13587
|
+
* - **Server errors** (ADR-010) come from the enclosing `[efServerErrors]`
|
|
13588
|
+
* scope. `<name>.<culture>` errors tint that culture's tab, show under its
|
|
13589
|
+
* field while it is active (the field links itself by name) and, from another
|
|
13590
|
+
* tab, show as a hint naming the languages in error; a new set of errors
|
|
13591
|
+
* moves to the first of them. `<name>` errors, or an explicit `[errors]`,
|
|
13592
|
+
* show under the field on every tab, as do errors for a language the field
|
|
13593
|
+
* does not list (`title.xx`). Messages are translated as keys. Keys: `ef_localized_input_errors_in_other`.
|
|
13594
|
+
* - **Spoken limit.** `counterHintKey` is read with the counter (through
|
|
13595
|
+
* `aria-describedby`) and `counterOverKey` is announced politely when the
|
|
13596
|
+
* text passes the limit; both take `{{max}}`.
|
|
13597
|
+
* - **Names.** Each culture's field is named `<name>.<culture>` (`title.ar`),
|
|
13598
|
+
* the key the backend reports a per-culture error under.
|
|
13599
|
+
*
|
|
13600
|
+
* i18n keys the app's bundles must define (the libs ship no JSON):
|
|
13601
|
+
*
|
|
13602
|
+
* | key | fr | ar | en |
|
|
13603
|
+
* |---|---|---|---|
|
|
13604
|
+
* | `ef_localized_input_culture_fr` | Français | الفرنسية | French |
|
|
13605
|
+
* | `ef_localized_input_culture_ar` | Arabe | العربية | Arabic |
|
|
13606
|
+
* | `ef_localized_input_culture_en` | Anglais | الإنجليزية | English |
|
|
13607
|
+
* | `ef_localized_input_missing` | manquant | غير مكتوب | missing |
|
|
13608
|
+
* | `ef_localized_input_cultures` | Langues du texte | لغات النص | Text languages |
|
|
13609
|
+
* | `ef_localized_input_errors_in_other` | Erreur dans : | خطأ في: | Error in: |
|
|
13610
|
+
* | `ef_localized_input_read_only_hint` | Ce texte se modifie ailleurs. | يُعدَّل هذا النص في مكان آخر. | This text is edited elsewhere. |
|
|
13375
13611
|
*
|
|
13376
|
-
*
|
|
13377
|
-
*
|
|
13378
|
-
*
|
|
13612
|
+
* ```html
|
|
13613
|
+
* <ef-localized-input name="title" labelKey="product_title" required
|
|
13614
|
+
* [value]="product().title" (valueChangeEvent)="patch({ title: $event })"
|
|
13615
|
+
* [advisedLength]="60" [maxLength]="256" />
|
|
13616
|
+
*
|
|
13617
|
+
* <ef-localized-input name="title" [readOnlyCultures]="['fr']" ...>
|
|
13618
|
+
* <a read-only-link [routerLink]="catalogLink()">{{ 'store_open_catalog' | translate }}</a>
|
|
13619
|
+
* </ef-localized-input>
|
|
13620
|
+
* ```
|
|
13621
|
+
*/
|
|
13622
|
+
class EfLocalizedInputComponent {
|
|
13623
|
+
/** The camelCase backend property (`title`); each field is `<name>.<culture>`. */
|
|
13624
|
+
name = input.required(...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
|
|
13625
|
+
value = input(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
13626
|
+
mode = input('text', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
13627
|
+
rows = input(4, ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
|
|
13628
|
+
/** Hard cap, the server's limit. */
|
|
13629
|
+
maxLength = input(undefined, ...(ngDevMode ? [{ debugName: "maxLength" }] : /* istanbul ignore next */ []));
|
|
13630
|
+
/** What the counter measures against (what a search engine shows, say). */
|
|
13631
|
+
advisedLength = input(undefined, ...(ngDevMode ? [{ debugName: "advisedLength" }] : /* istanbul ignore next */ []));
|
|
13632
|
+
/** The default culture must be filled. */
|
|
13633
|
+
required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
13634
|
+
/** Cultures shown but not editable here, with the `[read-only-link]` slot. */
|
|
13635
|
+
readOnlyCultures = input([], ...(ngDevMode ? [{ debugName: "readOnlyCultures" }] : /* istanbul ignore next */ []));
|
|
13636
|
+
/** Labels the field and names its tablist; without it the tablist is
|
|
13637
|
+
* named `ef_localized_input_cultures`. */
|
|
13638
|
+
labelKey = input('', ...(ngDevMode ? [{ debugName: "labelKey" }] : /* istanbul ignore next */ []));
|
|
13639
|
+
placeholderKey = input('', ...(ngDevMode ? [{ debugName: "placeholderKey" }] : /* istanbul ignore next */ []));
|
|
13640
|
+
hintKey = input('', ...(ngDevMode ? [{ debugName: "hintKey" }] : /* istanbul ignore next */ []));
|
|
13641
|
+
/** i18n key (with `{{max}}`) read out with the counter, for a limit that is advice rather than a cap. */
|
|
13642
|
+
counterHintKey = input('', ...(ngDevMode ? [{ debugName: "counterHintKey" }] : /* istanbul ignore next */ []));
|
|
13643
|
+
/** i18n key (with `{{max}}`) announced politely when the text goes past the limit. */
|
|
13644
|
+
counterOverKey = input('', ...(ngDevMode ? [{ debugName: "counterOverKey" }] : /* istanbul ignore next */ []));
|
|
13645
|
+
/** Field style in `text` mode only, named like ef-input-text's (`primeng`
|
|
13646
|
+
* or `comptoir`); the textarea, html and rich modes have one style.
|
|
13647
|
+
* `default` is the former name of `primeng`, kept for one release. */
|
|
13648
|
+
variant = input('comptoir', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
13649
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
13650
|
+
/**
|
|
13651
|
+
* Messages for the whole field. Without it they come from the enclosing
|
|
13652
|
+
* `[efServerErrors]` scope under `name`; an explicit list wins. The
|
|
13653
|
+
* per-language messages (`<name>.<culture>`) always come from the scope.
|
|
13654
|
+
*/
|
|
13655
|
+
errors = input(undefined, ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
|
|
13656
|
+
/** Overrides the `EF_CONTENT_CULTURES` list for this field. */
|
|
13657
|
+
cultures = input(undefined, ...(ngDevMode ? [{ debugName: "cultures" }] : /* istanbul ignore next */ []));
|
|
13658
|
+
/** The whole new object, trimmed, with blank cultures removed. */
|
|
13659
|
+
valueChangeEvent = output();
|
|
13660
|
+
contentCultures = inject(EF_CONTENT_CULTURES);
|
|
13661
|
+
readOnlyScope = inject(EfReadOnlyDirective, { optional: true });
|
|
13662
|
+
ngControl = inject(NgControl, { self: true, optional: true });
|
|
13663
|
+
serverErrorsScope = inject(EfServerErrorsDirective, { optional: true });
|
|
13664
|
+
richLoader = inject(EF_RICH_TEXT, { optional: true });
|
|
13665
|
+
richType = signal(null, ...(ngDevMode ? [{ debugName: "richType" }] : /* istanbul ignore next */ []));
|
|
13666
|
+
/** The editor's chunk failed to load (offline, a stale deploy). */
|
|
13667
|
+
richFailed = signal(false, ...(ngDevMode ? [{ debugName: "richFailed" }] : /* istanbul ignore next */ []));
|
|
13668
|
+
richHost = viewChild('richHost', { ...(ngDevMode ? { debugName: "richHost" } : /* istanbul ignore next */ {}), read: ViewContainerRef });
|
|
13669
|
+
/** `rich` without a provided editor renders the html source instead. */
|
|
13670
|
+
effectiveMode = computed(() => this.mode() === 'rich' && (!this.richLoader || this.richFailed()) ? 'html' : this.mode(), ...(ngDevMode ? [{ debugName: "effectiveMode" }] : /* istanbul ignore next */ []));
|
|
13671
|
+
parentForm = inject(FormGroupDirective, { optional: true }) ?? inject(NgForm, { optional: true });
|
|
13672
|
+
host = inject(ElementRef);
|
|
13673
|
+
translateService = inject(TranslateService);
|
|
13674
|
+
/** The user typed in, or left, the field. */
|
|
13675
|
+
interacted = signal(false, ...(ngDevMode ? [{ debugName: "interacted" }] : /* istanbul ignore next */ []));
|
|
13676
|
+
/** The form control is touched or dirty, or its form was submitted; read on every check. */
|
|
13677
|
+
formTouched = signal(false, ...(ngDevMode ? [{ debugName: "formTouched" }] : /* istanbul ignore next */ []));
|
|
13678
|
+
/** Errors wait for touch, a change or a submit, like every ef-* control. */
|
|
13679
|
+
showErrors = computed(() => this.interacted() || this.formTouched(), ...(ngDevMode ? [{ debugName: "showErrors" }] : /* istanbul ignore next */ []));
|
|
13680
|
+
uid = `ef-localized-input-${nextId++}`;
|
|
13681
|
+
/** The object this component last emitted; an echo of it is not new data. */
|
|
13682
|
+
lastEmitted = null;
|
|
13683
|
+
/** The text as typed, blanks and spaces kept, so an echo never rewrites a field mid-word. */
|
|
13684
|
+
raw = linkedSignal({ ...(ngDevMode ? { debugName: "raw" } : /* istanbul ignore next */ {}), source: this.value,
|
|
13685
|
+
computation: (v, prev) => {
|
|
13686
|
+
if (prev && (v === this.lastEmitted || sameText(cleanLocalizedText(v), cleanLocalizedText(prev.value)))) {
|
|
13687
|
+
return prev.value;
|
|
13688
|
+
}
|
|
13689
|
+
return { ...(v ?? {}) };
|
|
13690
|
+
} });
|
|
13691
|
+
/** What the dirty dots compare against: the last value that came from outside. */
|
|
13692
|
+
baseline = linkedSignal({ ...(ngDevMode ? { debugName: "baseline" } : /* istanbul ignore next */ {}), source: this.value,
|
|
13693
|
+
computation: (v, prev) => (prev && v === this.lastEmitted ? prev.value : cleanLocalizedText(v)) });
|
|
13694
|
+
cleaned = computed(() => cleanLocalizedText(this.raw()), ...(ngDevMode ? [{ debugName: "cleaned" }] : /* istanbul ignore next */ []));
|
|
13695
|
+
list = computed(() => this.cultures() ?? this.contentCultures().cultures, ...(ngDevMode ? [{ debugName: "list" }] : /* istanbul ignore next */ []));
|
|
13696
|
+
defaultCode = computed(() => {
|
|
13697
|
+
const preferred = this.contentCultures().defaultCulture;
|
|
13698
|
+
const list = this.list();
|
|
13699
|
+
return list.some(c => c.code === preferred) ? preferred : (list[0]?.code ?? preferred);
|
|
13700
|
+
}, ...(ngDevMode ? [{ debugName: "defaultCode" }] : /* istanbul ignore next */ []));
|
|
13701
|
+
selected = signal(null, ...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
|
|
13702
|
+
active = computed(() => {
|
|
13703
|
+
const list = this.list();
|
|
13704
|
+
const code = this.selected();
|
|
13705
|
+
return list.find(c => c.code === code) ?? list.find(c => c.code === this.defaultCode()) ?? list[0];
|
|
13706
|
+
}, ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
|
|
13707
|
+
/** The active culture's code: unlike active(), equal when an equal culture list arrives. */
|
|
13708
|
+
activeCode = computed(() => this.active()?.code, ...(ngDevMode ? [{ debugName: "activeCode" }] : /* istanbul ignore next */ []));
|
|
13709
|
+
/** Server messages for the whole field (`title`). */
|
|
13710
|
+
fieldErrors = computed(() => {
|
|
13711
|
+
const messages = [...(resolveExternalErrors(this.errors(), this.serverErrorsScope, this.name()) ?? []), ...this.unlistedErrors()];
|
|
13712
|
+
return messages.length ? messages : null;
|
|
13713
|
+
}, ...(ngDevMode ? [{ debugName: "fieldErrors" }] : /* istanbul ignore next */ []));
|
|
13714
|
+
/** Every `<name>.<culture>` message the scope holds. */
|
|
13715
|
+
allCultureErrors = computed(() => this.serverErrorsScope?.errorsWithPrefix(`${this.name()}.`) ?? {}, ...(ngDevMode ? [{ debugName: "allCultureErrors" }] : /* istanbul ignore next */ []));
|
|
13716
|
+
/** Server messages per language (`title.ar` gives `ar`), only for listed cultures. */
|
|
13717
|
+
cultureErrors = computed(() => {
|
|
13718
|
+
const all = this.allCultureErrors();
|
|
13719
|
+
const out = {};
|
|
13720
|
+
for (const c of this.list())
|
|
13721
|
+
if (all[c.code])
|
|
13722
|
+
out[c.code] = all[c.code];
|
|
13723
|
+
return out;
|
|
13724
|
+
}, ...(ngDevMode ? [{ debugName: "cultureErrors" }] : /* istanbul ignore next */ []));
|
|
13725
|
+
/** Messages for a language this field does not list (`title.xx`): shown with the whole field, never dropped. */
|
|
13726
|
+
unlistedErrors = computed(() => {
|
|
13727
|
+
const listed = new Set(this.list().map(c => c.code));
|
|
13728
|
+
return Object.entries(this.allCultureErrors()).flatMap(([code, messages]) => (listed.has(code) ? [] : messages));
|
|
13729
|
+
}, ...(ngDevMode ? [{ debugName: "unlistedErrors" }] : /* istanbul ignore next */ []));
|
|
13730
|
+
/** Bumps when texts change, so translated messages follow the language. */
|
|
13731
|
+
textsVersion = signal(0, ...(ngDevMode ? [{ debugName: "textsVersion" }] : /* istanbul ignore next */ []));
|
|
13732
|
+
/** The active language's messages, translated once here for the field below. */
|
|
13733
|
+
activeErrors = computed(() => {
|
|
13734
|
+
this.textsVersion();
|
|
13735
|
+
const messages = this.cultureErrors()[this.active()?.code ?? ''];
|
|
13736
|
+
return messages?.length ? messages.map(m => this.translateService.instant(m)) : null;
|
|
13737
|
+
}, ...(ngDevMode ? [{ debugName: "activeErrors" }] : /* istanbul ignore next */ []));
|
|
13738
|
+
/** Cultures with a server error, in tab order. */
|
|
13739
|
+
culturesInError = computed(() => this.list().filter(c => this.cultureErrors()[c.code]), ...(ngDevMode ? [{ debugName: "culturesInError" }] : /* istanbul ignore next */ []));
|
|
13740
|
+
/** Cultures in error other than the one on show, for the hint under the tabs. */
|
|
13741
|
+
errorsElsewhere = computed(() => this.culturesInError().filter(c => c.code !== this.active()?.code), ...(ngDevMode ? [{ debugName: "errorsElsewhere" }] : /* istanbul ignore next */ []));
|
|
13742
|
+
/** Changes only when the set of errors does, so a tab the user picked stays picked. */
|
|
13743
|
+
errorSignature = computed(() => JSON.stringify(this.cultureErrors()), ...(ngDevMode ? [{ debugName: "errorSignature" }] : /* istanbul ignore next */ []));
|
|
13744
|
+
tabItems = computed(() => {
|
|
13745
|
+
const text = this.cleaned();
|
|
13746
|
+
const base = this.baseline();
|
|
13747
|
+
return this.list().map(c => {
|
|
13748
|
+
const missing = !text[c.code];
|
|
13749
|
+
return {
|
|
13750
|
+
value: c.code,
|
|
13751
|
+
labelKey: c.labelKey,
|
|
13752
|
+
noteKey: missing ? 'ef_localized_input_missing' : undefined,
|
|
13753
|
+
tone: this.cultureErrors()[c.code] ||
|
|
13754
|
+
(missing && this.required() && c.code === this.defaultCode() && this.showErrors())
|
|
13755
|
+
? 'danger'
|
|
13756
|
+
: undefined,
|
|
13757
|
+
dirty: (text[c.code] ?? '') !== (base[c.code] ?? ''),
|
|
13758
|
+
id: this.tabId(c.code),
|
|
13759
|
+
panelId: this.panelId,
|
|
13760
|
+
};
|
|
13761
|
+
});
|
|
13762
|
+
}, ...(ngDevMode ? [{ debugName: "tabItems" }] : /* istanbul ignore next */ []));
|
|
13763
|
+
cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "cvaDisabled" }] : /* istanbul ignore next */ []));
|
|
13764
|
+
/** Every culture is disabled: `[disabled]`, a form's `disable()`, or `[efReadOnly]`. */
|
|
13765
|
+
isDisabled = computed(() => this.disabled() || this.cvaDisabled() || (this.readOnlyScope?.readOnly() ?? false), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
13766
|
+
activeReadOnly = computed(() => {
|
|
13767
|
+
const code = this.active()?.code;
|
|
13768
|
+
return !!code && this.readOnlyCultures().includes(code);
|
|
13769
|
+
}, ...(ngDevMode ? [{ debugName: "activeReadOnly" }] : /* istanbul ignore next */ []));
|
|
13770
|
+
activeText = computed(() => this.raw()[this.active()?.code ?? ''] ?? '', ...(ngDevMode ? [{ debugName: "activeText" }] : /* istanbul ignore next */ []));
|
|
13771
|
+
/**
|
|
13772
|
+
* What the counter counts. Rich text against `advisedLength` counts the
|
|
13773
|
+
* characters a reader sees; against `maxLength` alone it counts the stored
|
|
13774
|
+
* HTML, because that is what the server's limit measures.
|
|
13775
|
+
*/
|
|
13776
|
+
activeLength = computed(() => this.effectiveMode() === 'rich' && this.advisedLength() != null
|
|
13777
|
+
? plainTextLength(this.activeText())
|
|
13778
|
+
: this.activeText().length, ...(ngDevMode ? [{ debugName: "activeLength" }] : /* istanbul ignore next */ []));
|
|
13779
|
+
labelId = `${this.uid}-label`;
|
|
13780
|
+
counterLimit = computed(() => this.advisedLength() ?? this.maxLength(), ...(ngDevMode ? [{ debugName: "counterLimit" }] : /* istanbul ignore next */ []));
|
|
13781
|
+
panelId = `${this.uid}-panel`;
|
|
13782
|
+
counterId = `${this.uid}-counter`;
|
|
13783
|
+
onChange = () => { };
|
|
13784
|
+
touched = () => { };
|
|
13785
|
+
constructor() {
|
|
13786
|
+
if (this.ngControl)
|
|
13787
|
+
this.ngControl.valueAccessor = this;
|
|
13788
|
+
merge(this.translateService.onLangChange, this.translateService.onTranslationChange)
|
|
13789
|
+
.pipe(takeUntilDestroyed())
|
|
13790
|
+
.subscribe(() => this.textsVersion.update(v => v + 1));
|
|
13791
|
+
// The validator reads required and the default culture, which can
|
|
13792
|
+
// change without the value changing (tenant cultures arrive late).
|
|
13793
|
+
effect(() => {
|
|
13794
|
+
this.required();
|
|
13795
|
+
this.defaultCode();
|
|
13796
|
+
untracked(() => this.ngControl?.control?.updateValueAndValidity({ emitEvent: false }));
|
|
13797
|
+
});
|
|
13798
|
+
// A new set of server errors brings the first language in error into view.
|
|
13799
|
+
effect(() => {
|
|
13800
|
+
this.errorSignature();
|
|
13801
|
+
untracked(() => {
|
|
13802
|
+
const first = this.culturesInError()[0];
|
|
13803
|
+
if (first)
|
|
13804
|
+
this.selected.set(first.code);
|
|
13805
|
+
});
|
|
13806
|
+
});
|
|
13807
|
+
effect(() => {
|
|
13808
|
+
if (this.mode() !== 'rich')
|
|
13809
|
+
return;
|
|
13810
|
+
if (!this.richLoader) {
|
|
13811
|
+
if (isDevMode()) {
|
|
13812
|
+
console.error(`ef-localized-input "${untracked(this.name)}": mode="rich" needs provideEfRichText() from @elasticias/ui/rich-text; showing the html source instead.`);
|
|
13813
|
+
}
|
|
13814
|
+
return;
|
|
13815
|
+
}
|
|
13816
|
+
if (untracked(this.richType) || untracked(this.richFailed))
|
|
13817
|
+
return;
|
|
13818
|
+
this.richLoader()
|
|
13819
|
+
.then(t => this.richType.set(t))
|
|
13820
|
+
.catch((err) => {
|
|
13821
|
+
if (isDevMode()) {
|
|
13822
|
+
console.error(`ef-localized-input "${untracked(this.name)}": could not load the rich text editor; showing the html source instead.`, err);
|
|
13823
|
+
}
|
|
13824
|
+
this.richFailed.set(true);
|
|
13825
|
+
});
|
|
13826
|
+
});
|
|
13827
|
+
// One editor per active culture: the @for recreates the host on a
|
|
13828
|
+
// culture switch, and the editor is created in it with live bindings.
|
|
13829
|
+
effect(() => {
|
|
13830
|
+
const vcr = this.richHost();
|
|
13831
|
+
const type = this.richType();
|
|
13832
|
+
const code = this.activeCode();
|
|
13833
|
+
if (!vcr || !type || !code || this.effectiveMode() !== 'rich')
|
|
13834
|
+
return;
|
|
13835
|
+
untracked(() => this.createRich(vcr, type, code));
|
|
13836
|
+
});
|
|
13837
|
+
}
|
|
13838
|
+
createRich(vcr, type, code) {
|
|
13839
|
+
vcr.clear();
|
|
13840
|
+
const culture = () => this.list().find(c => c.code === code);
|
|
13841
|
+
const readOnly = () => this.readOnlyCultures().includes(code);
|
|
13842
|
+
vcr.createComponent(type, {
|
|
13843
|
+
bindings: [
|
|
13844
|
+
inputBinding('value', () => this.raw()[code] ?? ''),
|
|
13845
|
+
inputBinding('name', () => `${this.name()}.${code}`),
|
|
13846
|
+
inputBinding('inputId', () => this.controlId(code)),
|
|
13847
|
+
inputBinding('dir', () => culture()?.dir ?? null),
|
|
13848
|
+
inputBinding('lang', () => code),
|
|
13849
|
+
inputBinding('rows', () => this.rows()),
|
|
13850
|
+
inputBinding('labelledBy', () => (this.labelKey() ? this.labelId : null)),
|
|
13851
|
+
inputBinding('required', () => this.required() && code === this.defaultCode()),
|
|
13852
|
+
inputBinding('errors', () => this.activeErrors()),
|
|
13853
|
+
inputBinding('disabled', () => this.isDisabled()),
|
|
13854
|
+
inputBinding('readonly', readOnly),
|
|
13855
|
+
inputBinding('placeholderKey', () => this.placeholderKey()),
|
|
13856
|
+
inputBinding('hintKey', () => (readOnly() ? 'ef_localized_input_read_only_hint' : this.hintKey())),
|
|
13857
|
+
inputBinding('describedByExtra', () => (this.counterLimit() ? this.counterId : '')),
|
|
13858
|
+
outputBinding('valueChangeEvent', v => this.setText(code, v)),
|
|
13859
|
+
],
|
|
13860
|
+
});
|
|
13861
|
+
}
|
|
13862
|
+
tabId(code) {
|
|
13863
|
+
return `${this.uid}-tab-${code}`;
|
|
13864
|
+
}
|
|
13865
|
+
controlId(code) {
|
|
13866
|
+
return `${this.uid}-${code}`;
|
|
13867
|
+
}
|
|
13868
|
+
select(code) {
|
|
13869
|
+
this.selected.set(code);
|
|
13870
|
+
}
|
|
13871
|
+
setText(code, text) {
|
|
13872
|
+
this.raw.update(r => ({ ...r, [code]: text }));
|
|
13873
|
+
const out = this.cleaned();
|
|
13874
|
+
this.lastEmitted = out;
|
|
13875
|
+
this.interacted.set(true);
|
|
13876
|
+
this.onChange(out);
|
|
13877
|
+
this.valueChangeEvent.emit(out);
|
|
13878
|
+
}
|
|
13879
|
+
/** Touched when focus leaves the whole field, not when it moves from a tab to the text. */
|
|
13880
|
+
onFocusOut(event) {
|
|
13881
|
+
const next = event.relatedTarget;
|
|
13882
|
+
if (next instanceof Node && this.host.nativeElement.contains(next))
|
|
13883
|
+
return;
|
|
13884
|
+
this.interacted.set(true);
|
|
13885
|
+
this.touched();
|
|
13886
|
+
}
|
|
13887
|
+
ngDoCheck() {
|
|
13888
|
+
// A form's markAllAsTouched() or submit does not reach the accessor,
|
|
13889
|
+
// so the control's state is read each time the parent is checked.
|
|
13890
|
+
const c = this.ngControl;
|
|
13891
|
+
this.formTouched.set(!!(c?.touched || c?.dirty || this.parentForm?.submitted));
|
|
13892
|
+
}
|
|
13893
|
+
/** `{ required: true }` while a required field's default culture has no text. */
|
|
13894
|
+
validateRequired(control) {
|
|
13895
|
+
if (!this.required())
|
|
13896
|
+
return null;
|
|
13897
|
+
return cleanLocalizedText(control.value)[this.defaultCode()] ? null : { required: true };
|
|
13898
|
+
}
|
|
13899
|
+
writeValue(v) {
|
|
13900
|
+
this.raw.set({ ...(v ?? {}) });
|
|
13901
|
+
this.baseline.set(cleanLocalizedText(v));
|
|
13902
|
+
}
|
|
13903
|
+
registerOnChange(fn) {
|
|
13904
|
+
this.onChange = fn;
|
|
13905
|
+
}
|
|
13906
|
+
registerOnTouched(fn) {
|
|
13907
|
+
this.touched = fn;
|
|
13908
|
+
}
|
|
13909
|
+
setDisabledState(disabled) {
|
|
13910
|
+
this.cvaDisabled.set(disabled);
|
|
13911
|
+
}
|
|
13912
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfLocalizedInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
13913
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfLocalizedInputComponent, isStandalone: true, selector: "ef-localized-input", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, advisedLength: { classPropertyName: "advisedLength", publicName: "advisedLength", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, readOnlyCultures: { classPropertyName: "readOnlyCultures", publicName: "readOnlyCultures", isSignal: true, isRequired: false, transformFunction: null }, labelKey: { classPropertyName: "labelKey", publicName: "labelKey", isSignal: true, isRequired: false, transformFunction: null }, placeholderKey: { classPropertyName: "placeholderKey", publicName: "placeholderKey", isSignal: true, isRequired: false, transformFunction: null }, hintKey: { classPropertyName: "hintKey", publicName: "hintKey", isSignal: true, isRequired: false, transformFunction: null }, counterHintKey: { classPropertyName: "counterHintKey", publicName: "counterHintKey", isSignal: true, isRequired: false, transformFunction: null }, counterOverKey: { classPropertyName: "counterOverKey", publicName: "counterOverKey", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, cultures: { classPropertyName: "cultures", publicName: "cultures", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChangeEvent: "valueChangeEvent" }, host: { listeners: { "focusout": "onFocusOut($event)" } }, providers: [
|
|
13914
|
+
{
|
|
13915
|
+
// A form's required validator reads an empty object as filled (it
|
|
13916
|
+
// checks length), so the field validates its default culture
|
|
13917
|
+
// itself. The component is looked up when the form validates, not
|
|
13918
|
+
// here: it injects NgControl, which injects these validators.
|
|
13919
|
+
provide: NG_VALIDATORS,
|
|
13920
|
+
multi: true,
|
|
13921
|
+
useFactory: () => {
|
|
13922
|
+
const injector = inject(Injector);
|
|
13923
|
+
return (control) => injector.get(EfLocalizedInputComponent).validateRequired(control);
|
|
13924
|
+
},
|
|
13925
|
+
},
|
|
13926
|
+
], viewQueries: [{ propertyName: "richHost", first: true, predicate: ["richHost"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: "<div class=\"eli-head\">\n @if (labelKey()) {\n <span class=\"eli-label\" [id]=\"labelId\">\n <ef-label [labelKey]=\"labelKey()\" [for]=\"controlId(active()?.code ?? '')\" [required]=\"required()\" />\n </span>\n }\n <div class=\"eli-tabs\">\n <ef-tabs\n variant=\"pill\"\n [items]=\"tabItems()\"\n [value]=\"active()?.code ?? null\"\n [ariaLabelKey]=\"labelKey() || 'ef_localized_input_cultures'\"\n (valueChange)=\"select($event)\"\n />\n </div>\n</div>\n\n<div class=\"eli-panel\" role=\"tabpanel\" [id]=\"panelId\" [attr.aria-labelledby]=\"tabId(active()?.code ?? '')\">\n @for (c of active() ? [active()!] : []; track c.code) {\n @switch (effectiveMode()) {\n @case ('text') {\n <ef-input-text\n [variant]=\"variant() === 'comptoir' ? 'comptoir' : 'primeng'\"\n [inputId]=\"controlId(c.code)\"\n [name]=\"name() + '.' + c.code\"\n [dir]=\"c.dir\"\n [lang]=\"c.code\"\n [value]=\"activeText()\"\n [maxlength]=\"maxLength()\"\n [required]=\"required() && c.code === defaultCode()\"\n [errors]=\"activeErrors()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"activeReadOnly()\"\n [placeholderKey]=\"placeholderKey()\"\n [hintKey]=\"activeReadOnly() ? 'ef_localized_input_read_only_hint' : hintKey()\"\n [describedByExtra]=\"counterLimit() ? counterId : ''\"\n (valueChangeEvent)=\"setText(c.code, $event)\"\n />\n }\n @case ('textarea') {\n <ef-textarea\n [inputId]=\"controlId(c.code)\"\n [name]=\"name() + '.' + c.code\"\n [dir]=\"c.dir\"\n [lang]=\"c.code\"\n [rows]=\"rows()\"\n [value]=\"activeText()\"\n [maxlength]=\"maxLength()\"\n [required]=\"required() && c.code === defaultCode()\"\n [errors]=\"activeErrors()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"activeReadOnly()\"\n [placeholderKey]=\"placeholderKey()\"\n [hintKey]=\"activeReadOnly() ? 'ef_localized_input_read_only_hint' : hintKey()\"\n [describedByExtra]=\"counterLimit() ? counterId : ''\"\n (valueChangeEvent)=\"setText(c.code, $event)\"\n />\n }\n @case ('rich') {\n <!-- ef-rich-text, created from EF_RICH_TEXT (see createRich). -->\n <ng-container #richHost />\n }\n @case ('html') {\n <!-- The source stays ltr for every culture: raw HTML in an rtl\n textarea reads reversed (ADR-036). The preview takes the\n culture's direction. -->\n <ef-textarea\n mono\n dir=\"ltr\"\n [inputId]=\"controlId(c.code)\"\n [name]=\"name() + '.' + c.code\"\n [lang]=\"c.code\"\n [rows]=\"rows()\"\n [value]=\"activeText()\"\n [maxlength]=\"maxLength()\"\n [required]=\"required() && c.code === defaultCode()\"\n [errors]=\"activeErrors()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"activeReadOnly()\"\n [placeholderKey]=\"placeholderKey()\"\n [hintKey]=\"activeReadOnly() ? 'ef_localized_input_read_only_hint' : hintKey()\"\n [describedByExtra]=\"counterLimit() ? counterId : ''\"\n (valueChangeEvent)=\"setText(c.code, $event)\"\n />\n }\n }\n }\n\n @if (counterLimit(); as limit) {\n <span class=\"eli-counter\" data-counter [id]=\"counterId\" [class.is-over]=\"activeLength() > limit\">\n <bdi dir=\"ltr\">{{ activeLength() }} / {{ limit }}</bdi>\n @if (counterHintKey()) {\n <span class=\"eli-counter__sr\">{{ counterHintKey() | translate: { max: limit } }}</span>\n }\n </span>\n @if (counterOverKey()) {\n <!-- Always present, so a screen reader hears the text appear. -->\n <span class=\"eli-counter__sr\" data-over-message aria-live=\"polite\">\n @if (activeLength() > limit) {\n {{ counterOverKey() | translate: { max: limit } }}\n }\n </span>\n }\n }\n\n @if (effectiveMode() === 'html' && activeText().trim()) {\n @if (active(); as c) {\n <!-- Angular's default [innerHTML] sanitising strips scripts and handlers. -->\n <div class=\"eli-preview\" data-preview [attr.dir]=\"c.dir\" [attr.lang]=\"c.code\" [innerHTML]=\"activeText()\"></div>\n }\n }\n\n @if (fieldErrors(); as messages) {\n <div class=\"eli-errors\" data-field-errors role=\"alert\" aria-live=\"polite\">\n @for (message of messages; track message) {\n <small class=\"text-danger block\">{{ message | translate }}</small>\n }\n </div>\n }\n\n @if (errorsElsewhere(); as others) {\n @if (others.length) {\n <small class=\"eli-elsewhere text-danger block\" data-errors-elsewhere role=\"status\">\n {{ 'ef_localized_input_errors_in_other' | translate }}\n @for (c of others; track c.code; let last = $last) {\n {{ c.labelKey | translate }}{{ last ? '' : ',' }}\n }\n </small>\n }\n }\n\n @if (activeReadOnly()) {\n <div class=\"eli-link\">\n <ng-content select=\"[read-only-link]\" />\n </div>\n }\n</div>\n", styles: [":host{position:relative;display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s-2);min-width:0}.eli-head{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--s-2);min-width:0}.eli-label{min-width:0}.eli-tabs{min-width:0;max-width:100%;overflow-x:auto}.eli-panel{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s-2);min-width:0}.eli-counter{position:relative;font-size:12px;color:var(--text-mute);text-align:end;font-variant-numeric:tabular-nums}.eli-counter.is-over{color:var(--st-pending-fg);font-weight:600}.eli-counter__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.eli-preview{min-width:0;overflow-x:auto;overflow-wrap:anywhere;padding:var(--s-3);border:1px solid var(--rule);border-radius:var(--r-sm);background:var(--paper-alt);color:var(--text);font-size:13.5px;line-height:1.55}.eli-link{font-size:12px}@media(max-width:767px){.eli-tabs{--hit: var(--hit-touch);flex:1 1 100%}}\n"], dependencies: [{ kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLabelComponent, selector: "ef-label", inputs: ["label", "labelKey", "labelParams", "for", "styleClass", "required"] }, { kind: "component", type: EfTabsComponent, selector: "ef-tabs", inputs: ["items", "value", "variant", "module", "overflow", "hideCounts", "ariaLabel", "ariaLabelKey", "dirtyLabelKey"], outputs: ["valueChange"] }, { kind: "component", type: EfInputTextComponent, selector: "ef-input-text", inputs: ["size", "type", "maxlength", "fluid", "inline", "autocomplete", "dir", "lang", "variant", "prefix", "suffix", "value"], outputs: ["valueChangeEvent"] }, { kind: "component", type: EfTextareaComponent, selector: "ef-textarea", inputs: ["rows", "maxlength", "lang", "autofocus", "mono", "value"], outputs: ["valueChangeEvent"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
13927
|
+
}
|
|
13928
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfLocalizedInputComponent, decorators: [{
|
|
13929
|
+
type: Component,
|
|
13930
|
+
args: [{ selector: 'ef-localized-input', standalone: true, imports: [TranslateModule, EfLabelComponent, EfTabsComponent, EfInputTextComponent, EfTextareaComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: { '(focusout)': 'onFocusOut($event)' }, providers: [
|
|
13931
|
+
{
|
|
13932
|
+
// A form's required validator reads an empty object as filled (it
|
|
13933
|
+
// checks length), so the field validates its default culture
|
|
13934
|
+
// itself. The component is looked up when the form validates, not
|
|
13935
|
+
// here: it injects NgControl, which injects these validators.
|
|
13936
|
+
provide: NG_VALIDATORS,
|
|
13937
|
+
multi: true,
|
|
13938
|
+
useFactory: () => {
|
|
13939
|
+
const injector = inject(Injector);
|
|
13940
|
+
return (control) => injector.get(EfLocalizedInputComponent).validateRequired(control);
|
|
13941
|
+
},
|
|
13942
|
+
},
|
|
13943
|
+
], template: "<div class=\"eli-head\">\n @if (labelKey()) {\n <span class=\"eli-label\" [id]=\"labelId\">\n <ef-label [labelKey]=\"labelKey()\" [for]=\"controlId(active()?.code ?? '')\" [required]=\"required()\" />\n </span>\n }\n <div class=\"eli-tabs\">\n <ef-tabs\n variant=\"pill\"\n [items]=\"tabItems()\"\n [value]=\"active()?.code ?? null\"\n [ariaLabelKey]=\"labelKey() || 'ef_localized_input_cultures'\"\n (valueChange)=\"select($event)\"\n />\n </div>\n</div>\n\n<div class=\"eli-panel\" role=\"tabpanel\" [id]=\"panelId\" [attr.aria-labelledby]=\"tabId(active()?.code ?? '')\">\n @for (c of active() ? [active()!] : []; track c.code) {\n @switch (effectiveMode()) {\n @case ('text') {\n <ef-input-text\n [variant]=\"variant() === 'comptoir' ? 'comptoir' : 'primeng'\"\n [inputId]=\"controlId(c.code)\"\n [name]=\"name() + '.' + c.code\"\n [dir]=\"c.dir\"\n [lang]=\"c.code\"\n [value]=\"activeText()\"\n [maxlength]=\"maxLength()\"\n [required]=\"required() && c.code === defaultCode()\"\n [errors]=\"activeErrors()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"activeReadOnly()\"\n [placeholderKey]=\"placeholderKey()\"\n [hintKey]=\"activeReadOnly() ? 'ef_localized_input_read_only_hint' : hintKey()\"\n [describedByExtra]=\"counterLimit() ? counterId : ''\"\n (valueChangeEvent)=\"setText(c.code, $event)\"\n />\n }\n @case ('textarea') {\n <ef-textarea\n [inputId]=\"controlId(c.code)\"\n [name]=\"name() + '.' + c.code\"\n [dir]=\"c.dir\"\n [lang]=\"c.code\"\n [rows]=\"rows()\"\n [value]=\"activeText()\"\n [maxlength]=\"maxLength()\"\n [required]=\"required() && c.code === defaultCode()\"\n [errors]=\"activeErrors()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"activeReadOnly()\"\n [placeholderKey]=\"placeholderKey()\"\n [hintKey]=\"activeReadOnly() ? 'ef_localized_input_read_only_hint' : hintKey()\"\n [describedByExtra]=\"counterLimit() ? counterId : ''\"\n (valueChangeEvent)=\"setText(c.code, $event)\"\n />\n }\n @case ('rich') {\n <!-- ef-rich-text, created from EF_RICH_TEXT (see createRich). -->\n <ng-container #richHost />\n }\n @case ('html') {\n <!-- The source stays ltr for every culture: raw HTML in an rtl\n textarea reads reversed (ADR-036). The preview takes the\n culture's direction. -->\n <ef-textarea\n mono\n dir=\"ltr\"\n [inputId]=\"controlId(c.code)\"\n [name]=\"name() + '.' + c.code\"\n [lang]=\"c.code\"\n [rows]=\"rows()\"\n [value]=\"activeText()\"\n [maxlength]=\"maxLength()\"\n [required]=\"required() && c.code === defaultCode()\"\n [errors]=\"activeErrors()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"activeReadOnly()\"\n [placeholderKey]=\"placeholderKey()\"\n [hintKey]=\"activeReadOnly() ? 'ef_localized_input_read_only_hint' : hintKey()\"\n [describedByExtra]=\"counterLimit() ? counterId : ''\"\n (valueChangeEvent)=\"setText(c.code, $event)\"\n />\n }\n }\n }\n\n @if (counterLimit(); as limit) {\n <span class=\"eli-counter\" data-counter [id]=\"counterId\" [class.is-over]=\"activeLength() > limit\">\n <bdi dir=\"ltr\">{{ activeLength() }} / {{ limit }}</bdi>\n @if (counterHintKey()) {\n <span class=\"eli-counter__sr\">{{ counterHintKey() | translate: { max: limit } }}</span>\n }\n </span>\n @if (counterOverKey()) {\n <!-- Always present, so a screen reader hears the text appear. -->\n <span class=\"eli-counter__sr\" data-over-message aria-live=\"polite\">\n @if (activeLength() > limit) {\n {{ counterOverKey() | translate: { max: limit } }}\n }\n </span>\n }\n }\n\n @if (effectiveMode() === 'html' && activeText().trim()) {\n @if (active(); as c) {\n <!-- Angular's default [innerHTML] sanitising strips scripts and handlers. -->\n <div class=\"eli-preview\" data-preview [attr.dir]=\"c.dir\" [attr.lang]=\"c.code\" [innerHTML]=\"activeText()\"></div>\n }\n }\n\n @if (fieldErrors(); as messages) {\n <div class=\"eli-errors\" data-field-errors role=\"alert\" aria-live=\"polite\">\n @for (message of messages; track message) {\n <small class=\"text-danger block\">{{ message | translate }}</small>\n }\n </div>\n }\n\n @if (errorsElsewhere(); as others) {\n @if (others.length) {\n <small class=\"eli-elsewhere text-danger block\" data-errors-elsewhere role=\"status\">\n {{ 'ef_localized_input_errors_in_other' | translate }}\n @for (c of others; track c.code; let last = $last) {\n {{ c.labelKey | translate }}{{ last ? '' : ',' }}\n }\n </small>\n }\n }\n\n @if (activeReadOnly()) {\n <div class=\"eli-link\">\n <ng-content select=\"[read-only-link]\" />\n </div>\n }\n</div>\n", styles: [":host{position:relative;display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s-2);min-width:0}.eli-head{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--s-2);min-width:0}.eli-label{min-width:0}.eli-tabs{min-width:0;max-width:100%;overflow-x:auto}.eli-panel{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s-2);min-width:0}.eli-counter{position:relative;font-size:12px;color:var(--text-mute);text-align:end;font-variant-numeric:tabular-nums}.eli-counter.is-over{color:var(--st-pending-fg);font-weight:600}.eli-counter__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.eli-preview{min-width:0;overflow-x:auto;overflow-wrap:anywhere;padding:var(--s-3);border:1px solid var(--rule);border-radius:var(--r-sm);background:var(--paper-alt);color:var(--text);font-size:13.5px;line-height:1.55}.eli-link{font-size:12px}@media(max-width:767px){.eli-tabs{--hit: var(--hit-touch);flex:1 1 100%}}\n"] }]
|
|
13944
|
+
}], ctorParameters: () => [], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], advisedLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "advisedLength", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], readOnlyCultures: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnlyCultures", required: false }] }], labelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelKey", required: false }] }], placeholderKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholderKey", required: false }] }], hintKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintKey", required: false }] }], counterHintKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "counterHintKey", required: false }] }], counterOverKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "counterOverKey", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], cultures: [{ type: i0.Input, args: [{ isSignal: true, alias: "cultures", required: false }] }], valueChangeEvent: [{ type: i0.Output, args: ["valueChangeEvent"] }], richHost: [{ type: i0.ViewChild, args: ['richHost', { ...{ read: ViewContainerRef }, isSignal: true }] }] } });
|
|
13945
|
+
|
|
13946
|
+
/**
|
|
13947
|
+
* The content cultures the SEO fields start from, shared with `ef-localized-input`.
|
|
13948
|
+
* @deprecated use `EF_DEFAULT_CONTENT_CULTURES` from `@elasticias/core`.
|
|
13949
|
+
*/
|
|
13950
|
+
const EF_SEO_DEFAULT_LOCALES = EF_DEFAULT_CONTENT_CULTURES;
|
|
13951
|
+
/**
|
|
13952
|
+
* SEO title and description in every content language, each an
|
|
13953
|
+
* `ef-localized-input` with a counter against what a search engine shows
|
|
13954
|
+
* (60 / 160, read out to screen readers), capped at the server's limits
|
|
13955
|
+
* (256 / 512), and the shared `ef-seo-preview` of the default language's
|
|
13956
|
+
* result with the entity's own title and description as the fallback the shop
|
|
13957
|
+
* uses. Card-less: each screen wraps it in its own `ef-card`.
|
|
13958
|
+
*
|
|
13959
|
+
* The controls are named `seoTitle` and `seoDescription`, so under
|
|
13960
|
+
* `[efServerErrors]` a `seoTitle.ar` error lands on the Arabic tab and shows
|
|
13961
|
+
* under its field. Languages come from `EF_CONTENT_CULTURES` unless `cultures`
|
|
13962
|
+
* is set.
|
|
13963
|
+
*
|
|
13964
|
+
* i18n keys the app's bundles must define, besides those of
|
|
13965
|
+
* `ef-localized-input`:
|
|
13966
|
+
*
|
|
13967
|
+
* | key | fr | ar | en |
|
|
13968
|
+
* |---|---|---|---|
|
|
13969
|
+
* | `ef_seo_fields_title_label` | Titre SEO | عنوان SEO | SEO title |
|
|
13970
|
+
* | `ef_seo_fields_description_label` | Description SEO | وصف SEO | SEO description |
|
|
13971
|
+
* | `ef_seo_fields_counter_hint` | Au-delà de {{max}} caractères, Google coupe le texte. | بعد {{max}} حرفا، يقتطع Google النص. | Past {{max}} characters, Google cuts the text. |
|
|
13972
|
+
* | `ef_seo_fields_over_hint` | Le texte dépasse {{max}} caractères. | النص يتجاوز {{max}} حرفا. | The text is over {{max}} characters. |
|
|
13973
|
+
* | `ef_seo_fields_preview` | Aperçu dans Google | المعاينة في Google | Google preview |
|
|
13974
|
+
* | `ef_seo_fields_fallback_hint` | Vide : la boutique reprend le titre et la description. | فارغ: يستخدم المتجر العنوان والوصف. | Empty: the shop uses the title and description. |
|
|
13379
13975
|
*
|
|
13380
13976
|
* ```html
|
|
13381
|
-
* <ef-seo-fields [seoTitle]="p.seoTitle"
|
|
13977
|
+
* <ef-seo-fields [seoTitle]="p.seoTitle" [seoDescription]="p.seoDescription"
|
|
13978
|
+
* [fallbackTitle]="p.title" [fallbackDescription]="p.description"
|
|
13979
|
+
* [previewUrl]="url()" (seoChange)="patch($event)" />
|
|
13382
13980
|
* ```
|
|
13383
13981
|
*/
|
|
13384
13982
|
class EfSeoFieldsComponent {
|
|
13385
|
-
seoTitle = input(
|
|
13386
|
-
seoDescription = input(
|
|
13387
|
-
seoTitleTranslations = input({}, ...(ngDevMode ? [{ debugName: "seoTitleTranslations" }] : /* istanbul ignore next */ []));
|
|
13388
|
-
seoDescriptionTranslations = input({}, ...(ngDevMode ? [{ debugName: "seoDescriptionTranslations" }] : /* istanbul ignore next */ []));
|
|
13983
|
+
seoTitle = input(null, ...(ngDevMode ? [{ debugName: "seoTitle" }] : /* istanbul ignore next */ []));
|
|
13984
|
+
seoDescription = input(null, ...(ngDevMode ? [{ debugName: "seoDescription" }] : /* istanbul ignore next */ []));
|
|
13389
13985
|
/** The entity's own texts, which the shop uses when SEO is empty. */
|
|
13390
|
-
fallbackTitle = input(
|
|
13391
|
-
fallbackDescription = input(
|
|
13986
|
+
fallbackTitle = input(null, ...(ngDevMode ? [{ debugName: "fallbackTitle" }] : /* istanbul ignore next */ []));
|
|
13987
|
+
fallbackDescription = input(null, ...(ngDevMode ? [{ debugName: "fallbackDescription" }] : /* istanbul ignore next */ []));
|
|
13392
13988
|
/** The page address shown in the preview, computed by the app. */
|
|
13393
13989
|
previewUrl = input('', ...(ngDevMode ? [{ debugName: "previewUrl" }] : /* istanbul ignore next */ []));
|
|
13394
13990
|
titleMax = input(256, ...(ngDevMode ? [{ debugName: "titleMax" }] : /* istanbul ignore next */ []));
|
|
13395
13991
|
descriptionMax = input(512, ...(ngDevMode ? [{ debugName: "descriptionMax" }] : /* istanbul ignore next */ []));
|
|
13396
13992
|
titleAdvised = input(60, ...(ngDevMode ? [{ debugName: "titleAdvised" }] : /* istanbul ignore next */ []));
|
|
13397
13993
|
descriptionAdvised = input(160, ...(ngDevMode ? [{ debugName: "descriptionAdvised" }] : /* istanbul ignore next */ []));
|
|
13398
|
-
|
|
13994
|
+
/** Overrides the `EF_CONTENT_CULTURES` list for both fields. */
|
|
13995
|
+
cultures = input(undefined, ...(ngDevMode ? [{ debugName: "cultures" }] : /* istanbul ignore next */ []));
|
|
13399
13996
|
seoChange = output();
|
|
13400
|
-
|
|
13401
|
-
|
|
13402
|
-
|
|
13403
|
-
const
|
|
13404
|
-
|
|
13405
|
-
|
|
13406
|
-
|
|
13407
|
-
|
|
13408
|
-
|
|
13409
|
-
|
|
13410
|
-
|
|
13411
|
-
|
|
13412
|
-
name(locale, field) {
|
|
13413
|
-
const base = field === 'title' ? 'seoTitle' : 'seoDescription';
|
|
13414
|
-
return locale === 'fr' ? base : `${base}${locale.charAt(0).toUpperCase()}${locale.slice(1)}`;
|
|
13415
|
-
}
|
|
13416
|
-
setText(locale, field, value) {
|
|
13417
|
-
if (locale === 'fr') {
|
|
13418
|
-
this.seoChange.emit(field === 'title' ? { seoTitle: value } : { seoDescription: value });
|
|
13419
|
-
return;
|
|
13420
|
-
}
|
|
13421
|
-
const key = field === 'title' ? 'seoTitleTranslations' : 'seoDescriptionTranslations';
|
|
13422
|
-
const next = { ...((field === 'title' ? this.seoTitleTranslations() : this.seoDescriptionTranslations()) ?? {}) };
|
|
13423
|
-
// A cleared field removes the entry, so the shop falls back instead of showing nothing.
|
|
13424
|
-
if (value)
|
|
13425
|
-
next[locale] = value;
|
|
13426
|
-
else
|
|
13427
|
-
delete next[locale];
|
|
13428
|
-
this.seoChange.emit({ [key]: next });
|
|
13997
|
+
contentCultures = inject(EF_CONTENT_CULTURES);
|
|
13998
|
+
/** The language the preview shows: the tenant's default if listed, else the first listed. */
|
|
13999
|
+
previewCode = computed(() => {
|
|
14000
|
+
const list = this.cultures() ?? this.contentCultures().cultures;
|
|
14001
|
+
const preferred = this.contentCultures().defaultCulture;
|
|
14002
|
+
return list.some(c => c.code === preferred) ? preferred : (list[0]?.code ?? preferred);
|
|
14003
|
+
}, ...(ngDevMode ? [{ debugName: "previewCode" }] : /* istanbul ignore next */ []));
|
|
14004
|
+
previewTitle = computed(() => this.shown(this.seoTitle(), this.fallbackTitle()), ...(ngDevMode ? [{ debugName: "previewTitle" }] : /* istanbul ignore next */ []));
|
|
14005
|
+
previewDescription = computed(() => this.shown(this.seoDescription(), this.fallbackDescription()), ...(ngDevMode ? [{ debugName: "previewDescription" }] : /* istanbul ignore next */ []));
|
|
14006
|
+
shown(seo, fallback) {
|
|
14007
|
+
const code = this.previewCode();
|
|
14008
|
+
return (seo?.[code]?.trim() || fallback?.[code]?.trim() || '');
|
|
13429
14009
|
}
|
|
13430
|
-
previewTitle = computed(() => (this.seoTitle() || this.fallbackTitle() || '').trim(), ...(ngDevMode ? [{ debugName: "previewTitle" }] : /* istanbul ignore next */ []));
|
|
13431
|
-
previewDescription = computed(() => (this.seoDescription() || this.fallbackDescription() || '').trim(), ...(ngDevMode ? [{ debugName: "previewDescription" }] : /* istanbul ignore next */ []));
|
|
13432
14010
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfSeoFieldsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
13433
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.
|
|
14011
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.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 }, 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 }, cultures: { classPropertyName: "cultures", publicName: "cultures", 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 <ef-localized-input\n name=\"seoTitle\"\n labelKey=\"ef_seo_fields_title_label\"\n [value]=\"seoTitle()\"\n [cultures]=\"cultures()\"\n [maxLength]=\"titleMax()\"\n [advisedLength]=\"titleAdvised()\"\n counterHintKey=\"ef_seo_fields_counter_hint\"\n counterOverKey=\"ef_seo_fields_over_hint\"\n (valueChangeEvent)=\"seoChange.emit({ seoTitle: $event })\"\n />\n\n <ef-localized-input\n name=\"seoDescription\"\n mode=\"textarea\"\n [rows]=\"3\"\n labelKey=\"ef_seo_fields_description_label\"\n [value]=\"seoDescription()\"\n [cultures]=\"cultures()\"\n [maxLength]=\"descriptionMax()\"\n [advisedLength]=\"descriptionAdvised()\"\n counterHintKey=\"ef_seo_fields_counter_hint\"\n counterOverKey=\"ef_seo_fields_over_hint\"\n (valueChangeEvent)=\"seoChange.emit({ seoDescription: $event })\"\n />\n\n <ef-seo-preview\n labelKey=\"ef_seo_fields_preview\"\n [contentLang]=\"previewCode()\"\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}\n"], dependencies: [{ kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfLocalizedInputComponent, selector: "ef-localized-input", inputs: ["name", "value", "mode", "rows", "maxLength", "advisedLength", "required", "readOnlyCultures", "labelKey", "placeholderKey", "hintKey", "counterHintKey", "counterOverKey", "variant", "disabled", "errors", "cultures"], 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 });
|
|
13434
14012
|
}
|
|
13435
14013
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfSeoFieldsComponent, decorators: [{
|
|
13436
14014
|
type: Component,
|
|
13437
|
-
args: [{ selector: 'ef-seo-fields', standalone: true, imports: [TranslateModule,
|
|
13438
|
-
}], propDecorators: { seoTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "seoTitle", required: false }] }], seoDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "seoDescription", required: false }] }],
|
|
14015
|
+
args: [{ selector: 'ef-seo-fields', standalone: true, imports: [TranslateModule, EfLocalizedInputComponent, EfSeoPreviewComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"seo-body\">\n <p class=\"seo-note text-mute\">{{ 'ef_seo_fields_fallback_hint' | translate }}</p>\n\n <ef-localized-input\n name=\"seoTitle\"\n labelKey=\"ef_seo_fields_title_label\"\n [value]=\"seoTitle()\"\n [cultures]=\"cultures()\"\n [maxLength]=\"titleMax()\"\n [advisedLength]=\"titleAdvised()\"\n counterHintKey=\"ef_seo_fields_counter_hint\"\n counterOverKey=\"ef_seo_fields_over_hint\"\n (valueChangeEvent)=\"seoChange.emit({ seoTitle: $event })\"\n />\n\n <ef-localized-input\n name=\"seoDescription\"\n mode=\"textarea\"\n [rows]=\"3\"\n labelKey=\"ef_seo_fields_description_label\"\n [value]=\"seoDescription()\"\n [cultures]=\"cultures()\"\n [maxLength]=\"descriptionMax()\"\n [advisedLength]=\"descriptionAdvised()\"\n counterHintKey=\"ef_seo_fields_counter_hint\"\n counterOverKey=\"ef_seo_fields_over_hint\"\n (valueChangeEvent)=\"seoChange.emit({ seoDescription: $event })\"\n />\n\n <ef-seo-preview\n labelKey=\"ef_seo_fields_preview\"\n [contentLang]=\"previewCode()\"\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}\n"] }]
|
|
14016
|
+
}], propDecorators: { seoTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "seoTitle", required: false }] }], seoDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "seoDescription", required: false }] }], fallbackTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "fallbackTitle", required: false }] }], fallbackDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "fallbackDescription", required: false }] }], previewUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "previewUrl", required: false }] }], titleMax: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleMax", required: false }] }], descriptionMax: [{ type: i0.Input, args: [{ isSignal: true, alias: "descriptionMax", required: false }] }], titleAdvised: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleAdvised", required: false }] }], descriptionAdvised: [{ type: i0.Input, args: [{ isSignal: true, alias: "descriptionAdvised", required: false }] }], cultures: [{ type: i0.Input, args: [{ isSignal: true, alias: "cultures", required: false }] }], seoChange: [{ type: i0.Output, args: ["seoChange"] }] } });
|
|
13439
14017
|
|
|
13440
14018
|
/**
|
|
13441
14019
|
* A button that opens the native file picker and emits the chosen `File`.
|
|
@@ -14345,5 +14923,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
14345
14923
|
* Generated bundle index. Do not edit.
|
|
14346
14924
|
*/
|
|
14347
14925
|
|
|
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 };
|
|
14926
|
+
export { AbstractEfFormControl, 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_RICH_TEXT, 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, EfLocalizedInputComponent, 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, plainTextLength, resolveEfStatusColor };
|
|
14349
14927
|
//# sourceMappingURL=elasticias-ui.mjs.map
|