@elasticias/ui 1.0.53 → 1.0.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,18 +1,18 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { PipeTransform, OnInit, EventEmitter, TemplateRef, OnChanges, AfterContentInit, OnDestroy, SimpleChanges, InjectionToken, AfterViewInit } from '@angular/core';
2
+ import { PipeTransform, OnInit, EventEmitter, TemplateRef, OnChanges, AfterContentInit, OnDestroy, SimpleChanges, InjectionToken, DoCheck, Type, AfterViewInit } from '@angular/core';
3
3
  import * as _elasticias_screens from '@elasticias/screens';
4
4
  import { EfDataCardColumn, ScreenContext, EfDetailToolbarAction, EfSearchToolbarAction, EfDataCardSort, EfDataCardExportRequest, EfDateRange, EfDatePresetKey } from '@elasticias/screens';
5
5
  export { EfBoundedDateRange, EfDataCardColumn, EfDataCardColumnAlign, EfDataCardColumnMobileRole, EfDataCardColumnType, EfDataCardSort, EfDataCardSortDirection, EfDatePresetKey, EfDateRange, EfDetailToolbarAction, EfSearchToolbarAction } from '@elasticias/screens';
6
6
  import { Observable } from 'rxjs';
7
- import { EfPage, Permissions, EmailMetrics, EmailEventRecord, EmailEventKind } from '@elasticias/types';
7
+ import { EfPage, Permissions, LocalizedText, EmailMetrics, EmailEventRecord, EmailEventKind } from '@elasticias/types';
8
8
  import { Menu } from 'primeng/menu';
9
9
  import * as _elasticias_core from '@elasticias/core';
10
- import { EfBuildPackage, EfSessionIdleService, EfModule, EfNavSection, EfNavItem, EfModuleId, EfToast, EfToastAction, EfThemeConfigService } from '@elasticias/core';
10
+ import { EfBuildPackage, EfSessionIdleService, EfModule, EfNavSection, EfNavItem, EfModuleId, EfToast, EfToastAction, EfCulture, EfThemeConfigService } from '@elasticias/core';
11
11
  import * as i1 from 'primeng/tooltip';
12
12
  import { BlockableUI } from 'primeng/api';
13
13
  import { SafeResourceUrl, SafeHtml } from '@angular/platform-browser';
14
14
  import { ChartConfiguration } from 'chart.js';
15
- import { ControlValueAccessor, NgControl } from '@angular/forms';
15
+ import { ControlValueAccessor, NgControl, AbstractControl, ValidationErrors } from '@angular/forms';
16
16
  import { TranslateService } from '@ngx-translate/core';
17
17
  import { InputNumber, InputNumberInputEvent } from 'primeng/inputnumber';
18
18
  import { Nullable } from 'primeng/ts-helpers';
@@ -2260,6 +2260,20 @@ interface EfTabItem<T = string> {
2260
2260
  count?: number;
2261
2261
  /** Show a dirty/unread dot after the label/count. */
2262
2262
  dirty?: boolean;
2263
+ /** Translation key of a short note after the label (`'missing'`). It is
2264
+ * part of the tab's accessible name, so it says what the dot cannot. */
2265
+ noteKey?: string;
2266
+ /** Translation key of a small chip after the note (`'customized'`): a
2267
+ * state of the panel worth seeing from the tab row. Part of the tab's
2268
+ * accessible name, like the note. */
2269
+ badgeKey?: string;
2270
+ /** `'danger'` draws the tab in the error status colour: a required panel
2271
+ * left empty, or a panel holding server errors. */
2272
+ tone?: 'danger';
2273
+ /** DOM id of the tab, for a panel's `aria-labelledby`. */
2274
+ id?: string;
2275
+ /** DOM id of the panel the consumer renders for this tab (`aria-controls`). */
2276
+ panelId?: string;
2263
2277
  disabled?: boolean;
2264
2278
  }
2265
2279
  /**
@@ -2327,6 +2341,7 @@ declare class EfTabsComponent<T = string> implements AfterContentInit {
2327
2341
  * `fr.json` / `ar.json`. */
2328
2342
  dirtyLabelKey: string;
2329
2343
  readonly valueChange: EventEmitter<T>;
2344
+ private readonly host;
2330
2345
  private tabEls?;
2331
2346
  private readonly panels;
2332
2347
  private readonly panelMap;
@@ -2340,6 +2355,7 @@ declare class EfTabsComponent<T = string> implements AfterContentInit {
2340
2355
  ngAfterContentInit(): void;
2341
2356
  select(item: EfTabItem<T>): void;
2342
2357
  onKeydown(event: KeyboardEvent, index: number): void;
2358
+ private isRtl;
2343
2359
  trackValue(_: number, item: EfTabItem<T>): T;
2344
2360
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfTabsComponent<any>, never>;
2345
2361
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfTabsComponent<any>, "ef-tabs", never, { "items": { "alias": "items"; "required": true; }; "value": { "alias": "value"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "module": { "alias": "module"; "required": false; }; "overflow": { "alias": "overflow"; "required": false; }; "hideCounts": { "alias": "hideCounts"; "required": false; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; }; "ariaLabelKey": { "alias": "ariaLabelKey"; "required": false; }; "dirtyLabelKey": { "alias": "dirtyLabelKey"; "required": false; }; }, { "valueChange": "valueChange"; }, ["panels"], never, true, never>;
@@ -3368,12 +3384,45 @@ declare class EfTimelineComponent {
3368
3384
  interface OpMeta {
3369
3385
  cls: string;
3370
3386
  }
3387
+ /**
3388
+ * The i18n key segment of a backend entity type: PascalCase to snake_case,
3389
+ * an acronym kept as one word. `Product` gives `product`, `EmailTemplate`
3390
+ * gives `email_template`, `POSSession` gives `pos_session`; a segment
3391
+ * already in snake_case is kept.
3392
+ */
3393
+ declare function auditEntityKeySegment(entityType: string): string;
3371
3394
  /**
3372
3395
  * Standardized change-history box. Drop it on any detail screen and feed it the
3373
3396
  * record's audit entries (from `GET /api/audit/{entityType}/{id}` via the NSwag
3374
3397
  * `AuditClient`). Renders a vertical timeline inside an {@link EfCardComponent},
3375
3398
  * resolving operation labels through `audit_op_*` i18n keys and field labels
3376
- * through `audit_field_*` (falling back to the raw field key when unmapped).
3399
+ * through `audit_field_*` (falling back to the humanized field path when
3400
+ * unmapped).
3401
+ *
3402
+ * **Field labels.** For a field path (dots become `_` in the key) the box
3403
+ * tries, in order:
3404
+ *
3405
+ * 1. `audit_field_<entity>_<field>`, when `entityType` is set, with the type
3406
+ * converted by {@link auditEntityKeySegment} (`EmailTemplate` gives
3407
+ * `email_template`); the field keeps its camelCase (`htmlBody`), as
3408
+ * `ChangeDiff` records it;
3409
+ * 2. `audit_field_<field>`;
3410
+ * 3. when the last segment (`notes.ar`, as ChangeDiff writes a
3411
+ * LocalizedText) or, for older entries, the first segment (`fr.subject`)
3412
+ * is a known culture: the label of the rest of the path, found by steps 1
3413
+ * and 2 or humanized, then the culture's name in brackets: "Notes
3414
+ * olfactives (Arabe)". A culture is known when the app's bundle has
3415
+ * `ef_localized_input_culture_<xx>`, the key that also names it. The
3416
+ * bundle, not `EF_CONTENT_CULTURES`, decides, because a history outlives
3417
+ * the tenant's current culture list: a language the tenant dropped still
3418
+ * labels its old entries. Any other two-letter segment (`dimensions.cm`)
3419
+ * is part of the path;
3420
+ * 4. the humanized path (`clientCode` gives "Client code").
3421
+ *
3422
+ * So one `audit_field_product_notes` key labels every language of
3423
+ * `Product.Notes`, and an entity can name a field differently from the
3424
+ * global key used by others. On a V2 detail screen bind
3425
+ * `[entityType]="auditEntityType()"`.
3377
3426
  *
3378
3427
  * ```html
3379
3428
  * <ef-change-history [entries]="auditEntries()" />
@@ -3414,6 +3463,10 @@ declare class EfChangeHistoryComponent {
3414
3463
  * or null to keep the raw note. Rendered as plain text (never HTML). A
3415
3464
  * screen uses it to translate coded notes such as `order_email_resent:order_confirmed`. */
3416
3465
  readonly noteFormatter: _angular_core.InputSignal<((note: string) => string | null) | null>;
3466
+ /** The backend entity type (`ScreenConfig.AUDIT_ENTITY_TYPE`, e.g.
3467
+ * `EmailTemplate`), for the entity's own `audit_field_<entity>_<field>`
3468
+ * labels. Unset uses the global keys only. */
3469
+ readonly entityType: _angular_core.InputSignal<string | null | undefined>;
3417
3470
  /** "Show more" dialog visibility. */
3418
3471
  readonly dialogVisible: _angular_core.WritableSignal<boolean>;
3419
3472
  /** First `previewLimit` rows shown inline in the card. */
@@ -3430,11 +3483,15 @@ declare class EfChangeHistoryComponent {
3430
3483
  meta: OpMeta;
3431
3484
  operationLabel: string;
3432
3485
  }[]>;
3433
- /** Resolve a field key to its label. Adding an `audit_field_<field>` key is
3434
- * OPTIONAL — when it's missing the raw field name is humanized
3435
- * (`clientCode` → "Client code"), so new entities/fields work with zero
3436
- * i18n maintenance. Add a key only for a properly localized (fr/ar) term. */
3486
+ /** Resolve a field path to its label (order in the class docs). Keys are
3487
+ * OPTIONAL: without one the path is humanized (`clientCode` gives
3488
+ * "Client code"), so new fields work with zero i18n maintenance. Add a
3489
+ * key only for a properly localized (fr/ar) term. */
3437
3490
  fieldLabel(field: string | undefined): string;
3491
+ /** The culture's name from `ef_localized_input_culture_<code>`, or null when the bundle does not know it. */
3492
+ private cultureName;
3493
+ /** The entity key, then the global key, for a path; null when neither exists. */
3494
+ private lookupField;
3438
3495
  /** The note as shown: the formatter's text, else the raw note. */
3439
3496
  noteText(note: string | null | undefined): string;
3440
3497
  /** Display value for a field's raw value: the matching list item's label
@@ -3449,7 +3506,7 @@ declare class EfChangeHistoryComponent {
3449
3506
  * registration (the app's default locale is fr-MA). */
3450
3507
  formatWhen(value: string | Date | null | undefined): string;
3451
3508
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfChangeHistoryComponent, never>;
3452
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfChangeHistoryComponent, "ef-change-history", never, { "entries": { "alias": "entries"; "required": false; "isSignal": true; }; "titleKey": { "alias": "titleKey"; "required": false; "isSignal": true; }; "emptyKey": { "alias": "emptyKey"; "required": false; "isSignal": true; }; "limit": { "alias": "limit"; "required": false; "isSignal": true; }; "previewLimit": { "alias": "previewLimit"; "required": false; "isSignal": true; }; "collapsible": { "alias": "collapsible"; "required": false; "isSignal": true; }; "startCollapsed": { "alias": "startCollapsed"; "required": false; "isSignal": true; }; "valueLists": { "alias": "valueLists"; "required": false; "isSignal": true; }; "noteFormatter": { "alias": "noteFormatter"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3509
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfChangeHistoryComponent, "ef-change-history", never, { "entries": { "alias": "entries"; "required": false; "isSignal": true; }; "titleKey": { "alias": "titleKey"; "required": false; "isSignal": true; }; "emptyKey": { "alias": "emptyKey"; "required": false; "isSignal": true; }; "limit": { "alias": "limit"; "required": false; "isSignal": true; }; "previewLimit": { "alias": "previewLimit"; "required": false; "isSignal": true; }; "collapsible": { "alias": "collapsible"; "required": false; "isSignal": true; }; "startCollapsed": { "alias": "startCollapsed"; "required": false; "isSignal": true; }; "valueLists": { "alias": "valueLists"; "required": false; "isSignal": true; }; "noteFormatter": { "alias": "noteFormatter"; "required": false; "isSignal": true; }; "entityType": { "alias": "entityType"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3453
3510
  }
3454
3511
 
3455
3512
  /**
@@ -3653,6 +3710,9 @@ declare class EfSeoPreviewComponent {
3653
3710
  }
3654
3711
 
3655
3712
  /**
3713
+ * Exported for `@elasticias/ui/rich-text` only, not a supported extension
3714
+ * point: it may change without a major version.
3715
+ *
3656
3716
  * Shared base for every Comptoir form control (`ef-input-text`,
3657
3717
  * `ef-textarea`, `ef-select`, `ef-input-number`, `ef-checkbox`, …).
3658
3718
  *
@@ -3833,6 +3893,10 @@ declare class EfInputTextComponent extends AbstractEfFormControl implements Cont
3833
3893
  * follow the value's first strong character. The label and messages keep
3834
3894
  * the page direction. Null or absent leaves the input unchanged. */
3835
3895
  dir: 'ltr' | 'rtl' | 'auto' | null;
3896
+ /** Language of the typed value (`'ar'` for an Arabic title on a French
3897
+ * screen), applied to the native input only: the label and hint are page
3898
+ * text and keep the page language. Null or absent renders no attribute. */
3899
+ lang: string | null;
3836
3900
  /** Clearable (showClear / canClear) is inherited from AbstractEfFormControl;
3837
3901
  * we only supply the value predicate + the reset. */
3838
3902
  protected get hasValue(): boolean;
@@ -3890,7 +3954,7 @@ declare class EfInputTextComponent extends AbstractEfFormControl implements Cont
3890
3954
  private nativeField;
3891
3955
  handleBlur(): void;
3892
3956
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfInputTextComponent, never>;
3893
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfInputTextComponent, "ef-input-text", never, { "size": { "alias": "size"; "required": false; }; "type": { "alias": "type"; "required": false; }; "maxlength": { "alias": "maxlength"; "required": false; }; "fluid": { "alias": "fluid"; "required": false; }; "inline": { "alias": "inline"; "required": false; }; "autocomplete": { "alias": "autocomplete"; "required": false; }; "dir": { "alias": "dir"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "prefix": { "alias": "prefix"; "required": false; }; "suffix": { "alias": "suffix"; "required": false; }; "value": { "alias": "value"; "required": false; }; }, { "valueChangeEvent": "valueChangeEvent"; }, never, never, true, never>;
3957
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfInputTextComponent, "ef-input-text", never, { "size": { "alias": "size"; "required": false; }; "type": { "alias": "type"; "required": false; }; "maxlength": { "alias": "maxlength"; "required": false; }; "fluid": { "alias": "fluid"; "required": false; }; "inline": { "alias": "inline"; "required": false; }; "autocomplete": { "alias": "autocomplete"; "required": false; }; "dir": { "alias": "dir"; "required": false; }; "lang": { "alias": "lang"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "prefix": { "alias": "prefix"; "required": false; }; "suffix": { "alias": "suffix"; "required": false; }; "value": { "alias": "value"; "required": false; }; }, { "valueChangeEvent": "valueChangeEvent"; }, never, never, true, never>;
3894
3958
  static ngAcceptInputType_fluid: unknown;
3895
3959
  static ngAcceptInputType_inline: unknown;
3896
3960
  }
@@ -4580,6 +4644,17 @@ declare class EfMultiSelectComponent extends AbstractEfFormControl implements Co
4580
4644
  selectionLimit: any;
4581
4645
  filters: any;
4582
4646
  inline: boolean;
4647
+ /**
4648
+ * Rendering variant, as on `ef-select`.
4649
+ * - `'primeng'` (default): PrimeNG's own multiselect skin, unchanged.
4650
+ * - `'comptoir'`: the shared `.es` skin with `.es--comptoir`, the same hook
4651
+ * `ef-select variant="comptoir"` uses: a trigger on
4652
+ * `--hit-field` (`--hit-base` 40px, `--hit-touch` 48px on a
4653
+ * coarse pointer) and no minimum width, so a select and a
4654
+ * multi-select in one row align and both shrink with it.
4655
+ */
4656
+ variant: 'primeng' | 'comptoir';
4657
+ get effectiveStyleClass(): string | undefined;
4583
4658
  get isInline(): boolean;
4584
4659
  get isRequired(): boolean;
4585
4660
  selectedValues: any[];
@@ -4598,7 +4673,7 @@ declare class EfMultiSelectComponent extends AbstractEfFormControl implements Co
4598
4673
  ngOnChanges(): void;
4599
4674
  private applyFilters;
4600
4675
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfMultiSelectComponent, never>;
4601
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfMultiSelectComponent, "ef-multi-select", never, { "optionLabel": { "alias": "optionLabel"; "required": false; }; "optionValue": { "alias": "optionValue"; "required": false; }; "options": { "alias": "options"; "required": false; }; "filteredOptions": { "alias": "filteredOptions"; "required": false; }; "maxSelectedLabels": { "alias": "maxSelectedLabels"; "required": false; }; "display": { "alias": "display"; "required": false; }; "size": { "alias": "size"; "required": false; }; "selectionLimit": { "alias": "selectionLimit"; "required": false; }; "filters": { "alias": "filters"; "required": false; }; "inline": { "alias": "inline"; "required": false; }; }, { "changeEvent": "changeEvent"; }, never, never, true, never>;
4676
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfMultiSelectComponent, "ef-multi-select", never, { "optionLabel": { "alias": "optionLabel"; "required": false; }; "optionValue": { "alias": "optionValue"; "required": false; }; "options": { "alias": "options"; "required": false; }; "filteredOptions": { "alias": "filteredOptions"; "required": false; }; "maxSelectedLabels": { "alias": "maxSelectedLabels"; "required": false; }; "display": { "alias": "display"; "required": false; }; "size": { "alias": "size"; "required": false; }; "selectionLimit": { "alias": "selectionLimit"; "required": false; }; "filters": { "alias": "filters"; "required": false; }; "inline": { "alias": "inline"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; }, { "changeEvent": "changeEvent"; }, never, never, true, never>;
4602
4677
  static ngAcceptInputType_inline: unknown;
4603
4678
  }
4604
4679
 
@@ -6189,6 +6264,15 @@ declare class EfServerErrorsDirective {
6189
6264
  } | null>;
6190
6265
  /** Messages for a control by its `name`, or `null` when none. */
6191
6266
  errorsFor(name: string | undefined | null): string[] | null;
6267
+ /**
6268
+ * Messages for every key that starts with `prefix`, keyed by what follows
6269
+ * it: `errorsWithPrefix('title.')` turns `title.ar` into `ar`. For a control
6270
+ * that owns several backend keys, such as a text per language. Empty lists
6271
+ * are left out.
6272
+ */
6273
+ errorsWithPrefix(prefix: string): {
6274
+ [suffix: string]: string[];
6275
+ };
6192
6276
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfServerErrorsDirective, never>;
6193
6277
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<EfServerErrorsDirective, "[efServerErrors]", never, { "errors": { "alias": "efServerErrors"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
6194
6278
  }
@@ -6356,6 +6440,9 @@ declare class EfRadioComponent extends AbstractEfFormControl implements ControlV
6356
6440
  declare class EfTextareaComponent extends AbstractEfFormControl implements ControlValueAccessor {
6357
6441
  rows: number;
6358
6442
  maxlength?: number;
6443
+ /** Language of the typed value, applied to the native textarea only (the
6444
+ * label and hint keep the page language). Null or absent renders none. */
6445
+ lang: string | null;
6359
6446
  autofocus: boolean;
6360
6447
  /** Monospace face for source text (an HTML body, a JSON snippet), where
6361
6448
  * tags and indentation must stay readable. */
@@ -6398,70 +6485,415 @@ declare class EfTextareaComponent extends AbstractEfFormControl implements Contr
6398
6485
  private nativeField;
6399
6486
  handleBlur(): void;
6400
6487
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfTextareaComponent, never>;
6401
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfTextareaComponent, "ef-textarea", never, { "rows": { "alias": "rows"; "required": false; }; "maxlength": { "alias": "maxlength"; "required": false; }; "autofocus": { "alias": "autofocus"; "required": false; }; "mono": { "alias": "mono"; "required": false; }; "value": { "alias": "value"; "required": false; }; }, { "valueChangeEvent": "valueChangeEvent"; }, never, never, true, never>;
6488
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfTextareaComponent, "ef-textarea", never, { "rows": { "alias": "rows"; "required": false; }; "maxlength": { "alias": "maxlength"; "required": false; }; "lang": { "alias": "lang"; "required": false; }; "autofocus": { "alias": "autofocus"; "required": false; }; "mono": { "alias": "mono"; "required": false; }; "value": { "alias": "value"; "required": false; }; }, { "valueChangeEvent": "valueChangeEvent"; }, never, never, true, never>;
6402
6489
  static ngAcceptInputType_autofocus: unknown;
6403
6490
  static ngAcceptInputType_mono: unknown;
6404
6491
  }
6405
6492
 
6406
- /** A language the SEO texts are written in. */
6407
- interface EfSeoLocale {
6408
- code: string;
6409
- labelKey: string;
6410
- dir: 'auto' | 'rtl' | 'ltr';
6411
- }
6412
- /** What the fields write back: French is the scalar, every other language a dictionary entry. */
6493
+ /** A language the SEO texts are written in: the same shape as every content culture. */
6494
+ type EfSeoLocale = EfCulture;
6495
+ /**
6496
+ * What the fields write back: the whole object of the field that changed,
6497
+ * blank languages removed. Only the field that changed is present.
6498
+ */
6413
6499
  interface EfSeoPatch {
6414
- seoTitle?: string;
6415
- seoDescription?: string;
6416
- seoTitleTranslations?: Record<string, string>;
6417
- seoDescriptionTranslations?: Record<string, string>;
6500
+ seoTitle?: LocalizedText;
6501
+ seoDescription?: LocalizedText;
6418
6502
  }
6419
- type Field = 'title' | 'description';
6503
+ /**
6504
+ * The content cultures the SEO fields start from, shared with `ef-localized-input`.
6505
+ * @deprecated use `EF_DEFAULT_CONTENT_CULTURES` from `@elasticias/core`.
6506
+ */
6420
6507
  declare const EF_SEO_DEFAULT_LOCALES: ReadonlyArray<EfSeoLocale>;
6421
6508
  /**
6422
- * SEO title and description per language, each with a counter against what a
6423
- * search engine shows (60 / 160), capped at the server's limits (256 / 512),
6424
- * and the shared `ef-seo-preview` of the French result with the entity's own
6425
- * title and description as the fallback the shop uses. Card-less: each screen
6426
- * wraps it in its own `ef-card`.
6427
- *
6428
- * The French controls carry the backend names (`seoTitle`, `seoDescription`)
6429
- * so `[efServerErrors]` links their errors; the others (`seoTitleAr`, ...) are
6430
- * dictionary entries whose errors are toasted (ADR-010).
6509
+ * SEO title and description in every content language, each an
6510
+ * `ef-localized-input` with a counter against what a search engine shows
6511
+ * (60 / 160, read out to screen readers), capped at the server's limits
6512
+ * (256 / 512), and the shared `ef-seo-preview` of the default language's
6513
+ * result with the entity's own title and description as the fallback the shop
6514
+ * uses. Card-less: each screen wraps it in its own `ef-card`.
6515
+ *
6516
+ * The controls are named `seoTitle` and `seoDescription`, so under
6517
+ * `[efServerErrors]` a `seoTitle.ar` error lands on the Arabic tab and shows
6518
+ * under its field. Languages come from `EF_CONTENT_CULTURES` unless `cultures`
6519
+ * is set.
6520
+ *
6521
+ * i18n keys the app's bundles must define, besides those of
6522
+ * `ef-localized-input`:
6523
+ *
6524
+ * | key | fr | ar | en |
6525
+ * |---|---|---|---|
6526
+ * | `ef_seo_fields_title_label` | Titre SEO | عنوان SEO | SEO title |
6527
+ * | `ef_seo_fields_description_label` | Description SEO | وصف SEO | SEO description |
6528
+ * | `ef_seo_fields_counter_hint` | Au-delà de {{max}} caractères, Google coupe le texte. | بعد {{max}} حرفا، يقتطع Google النص. | Past {{max}} characters, Google cuts the text. |
6529
+ * | `ef_seo_fields_over_hint` | Le texte dépasse {{max}} caractères. | النص يتجاوز {{max}} حرفا. | The text is over {{max}} characters. |
6530
+ * | `ef_seo_fields_preview` | Aperçu dans Google | المعاينة في Google | Google preview |
6531
+ * | `ef_seo_fields_fallback_hint` | Vide : la boutique reprend le titre et la description. | فارغ: يستخدم المتجر العنوان والوصف. | Empty: the shop uses the title and description. |
6431
6532
  *
6432
6533
  * ```html
6433
- * <ef-seo-fields [seoTitle]="p.seoTitle" ... [previewUrl]="url()" (seoChange)="patch($event)" />
6534
+ * <ef-seo-fields [seoTitle]="p.seoTitle" [seoDescription]="p.seoDescription"
6535
+ * [fallbackTitle]="p.title" [fallbackDescription]="p.description"
6536
+ * [previewUrl]="url()" (seoChange)="patch($event)" />
6434
6537
  * ```
6435
6538
  */
6436
6539
  declare class EfSeoFieldsComponent {
6437
- readonly seoTitle: _angular_core.InputSignal<string | null | undefined>;
6438
- readonly seoDescription: _angular_core.InputSignal<string | null | undefined>;
6439
- readonly seoTitleTranslations: _angular_core.InputSignal<Record<string, string> | null | undefined>;
6440
- readonly seoDescriptionTranslations: _angular_core.InputSignal<Record<string, string> | null | undefined>;
6540
+ readonly seoTitle: _angular_core.InputSignal<Partial<Record<string, string>> | null | undefined>;
6541
+ readonly seoDescription: _angular_core.InputSignal<Partial<Record<string, string>> | null | undefined>;
6441
6542
  /** The entity's own texts, which the shop uses when SEO is empty. */
6442
- readonly fallbackTitle: _angular_core.InputSignal<string | null | undefined>;
6443
- readonly fallbackDescription: _angular_core.InputSignal<string | null | undefined>;
6543
+ readonly fallbackTitle: _angular_core.InputSignal<Partial<Record<string, string>> | null | undefined>;
6544
+ readonly fallbackDescription: _angular_core.InputSignal<Partial<Record<string, string>> | null | undefined>;
6444
6545
  /** The page address shown in the preview, computed by the app. */
6445
6546
  readonly previewUrl: _angular_core.InputSignal<string>;
6446
6547
  readonly titleMax: _angular_core.InputSignal<number>;
6447
6548
  readonly descriptionMax: _angular_core.InputSignal<number>;
6448
6549
  readonly titleAdvised: _angular_core.InputSignal<number>;
6449
6550
  readonly descriptionAdvised: _angular_core.InputSignal<number>;
6450
- readonly locales: _angular_core.InputSignal<readonly EfSeoLocale[]>;
6551
+ /** Overrides the `EF_CONTENT_CULTURES` list for both fields. */
6552
+ readonly cultures: _angular_core.InputSignal<readonly EfCulture[] | undefined>;
6451
6553
  readonly seoChange: _angular_core.OutputEmitterRef<EfSeoPatch>;
6452
- text(locale: string, field: Field): string;
6453
- /** Id of a counter, which the field it counts points at with `aria-describedby`. */
6454
- counterId(locale: string, field: Field): string;
6455
- private readonly uid;
6456
- /** French matches the backend property; the others are unique for the label link. */
6457
- name(locale: string, field: Field): string;
6458
- setText(locale: string, field: Field, value: string): void;
6554
+ private readonly contentCultures;
6555
+ /** The language the preview shows: the tenant's default if listed, else the first listed. */
6556
+ protected readonly previewCode: _angular_core.Signal<string>;
6459
6557
  protected readonly previewTitle: _angular_core.Signal<string>;
6460
6558
  protected readonly previewDescription: _angular_core.Signal<string>;
6559
+ private shown;
6461
6560
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfSeoFieldsComponent, never>;
6462
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfSeoFieldsComponent, "ef-seo-fields", never, { "seoTitle": { "alias": "seoTitle"; "required": false; "isSignal": true; }; "seoDescription": { "alias": "seoDescription"; "required": false; "isSignal": true; }; "seoTitleTranslations": { "alias": "seoTitleTranslations"; "required": false; "isSignal": true; }; "seoDescriptionTranslations": { "alias": "seoDescriptionTranslations"; "required": false; "isSignal": true; }; "fallbackTitle": { "alias": "fallbackTitle"; "required": false; "isSignal": true; }; "fallbackDescription": { "alias": "fallbackDescription"; "required": false; "isSignal": true; }; "previewUrl": { "alias": "previewUrl"; "required": false; "isSignal": true; }; "titleMax": { "alias": "titleMax"; "required": false; "isSignal": true; }; "descriptionMax": { "alias": "descriptionMax"; "required": false; "isSignal": true; }; "titleAdvised": { "alias": "titleAdvised"; "required": false; "isSignal": true; }; "descriptionAdvised": { "alias": "descriptionAdvised"; "required": false; "isSignal": true; }; "locales": { "alias": "locales"; "required": false; "isSignal": true; }; }, { "seoChange": "seoChange"; }, never, never, true, never>;
6561
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfSeoFieldsComponent, "ef-seo-fields", never, { "seoTitle": { "alias": "seoTitle"; "required": false; "isSignal": true; }; "seoDescription": { "alias": "seoDescription"; "required": false; "isSignal": true; }; "fallbackTitle": { "alias": "fallbackTitle"; "required": false; "isSignal": true; }; "fallbackDescription": { "alias": "fallbackDescription"; "required": false; "isSignal": true; }; "previewUrl": { "alias": "previewUrl"; "required": false; "isSignal": true; }; "titleMax": { "alias": "titleMax"; "required": false; "isSignal": true; }; "descriptionMax": { "alias": "descriptionMax"; "required": false; "isSignal": true; }; "titleAdvised": { "alias": "titleAdvised"; "required": false; "isSignal": true; }; "descriptionAdvised": { "alias": "descriptionAdvised"; "required": false; "isSignal": true; }; "cultures": { "alias": "cultures"; "required": false; "isSignal": true; }; }, { "seoChange": "seoChange"; }, never, never, true, never>;
6562
+ }
6563
+
6564
+ /** What one input's tab for a culture would show; read by the input that draws a shared row. */
6565
+ interface EfLocalizedCultureFlags {
6566
+ /** Danger tone: a server error, or the required default culture left empty once touched. */
6567
+ readonly error: boolean;
6568
+ /** A server error under `<name>.<culture>` (what moves the shared culture). */
6569
+ readonly serverError: boolean;
6570
+ /** Edited since the value arrived. */
6571
+ readonly dirty: boolean;
6572
+ }
6573
+ /**
6574
+ * One owner-written text (a title, a description, an HTML body) in every
6575
+ * content language: a pill tab per culture, the active culture's field below
6576
+ * it, and the whole object out on every edit with blank cultures removed
6577
+ * (ADR-050 section 7).
6578
+ *
6579
+ * - **Cultures** come from `EF_CONTENT_CULTURES` (the ERP provides them from
6580
+ * the tenant profile) unless `cultures` is set. The active culture starts
6581
+ * on the default and is two-way through `[(activeCulture)]`.
6582
+ * - **One tab row for several texts** (an email's subject and body): bind the
6583
+ * same signal to `[(activeCulture)]` on each and set `[showTabs]="false"`
6584
+ * on all but one, and list those in the row owner's `[linkedInputs]` so
6585
+ * the row also shows their error tone, required tone and dirty dot. A new
6586
+ * set of server errors moves the shared culture to the first culture in
6587
+ * error in the row's order.
6588
+ * - **Tabs** show the language name only (no "missing" note: an empty culture
6589
+ * is visible from its empty field), the error tone on the default culture of a `required` field left
6590
+ * empty once the field is touched, changed or its form submitted, and the
6591
+ * dirty dot on cultures edited since the value arrived. Dirty detection
6592
+ * relies on the parent storing the emitted object by reference (the V2
6593
+ * signal pattern): a parent that clones the value resets the dots on every
6594
+ * edit, though it never loses text.
6595
+ * - **Direction.** Each field gets its culture's `dir` and `lang`. In `html`
6596
+ * mode the source stays `ltr` for every culture (raw HTML in an `rtl`
6597
+ * textarea reads reversed) and the sanitised preview takes the culture's
6598
+ * direction.
6599
+ * - **Rich mode** (`mode="rich"`) edits each culture with `ef-rich-text`, the
6600
+ * shared editor in `@elasticias/ui/rich-text`. The app turns it on once with
6601
+ * `provideEfRichText()` (and installs `quill`); without it the field falls
6602
+ * back to the `html` source and logs why in development, as it does when
6603
+ * the editor's chunk fails to load. With `advisedLength` the counter
6604
+ * counts the text a reader sees; with only `maxLength` it counts the
6605
+ * stored HTML, which is what the server measures. Neither caps typing in
6606
+ * this mode.
6607
+ * - **Read-only cultures** are shown read-only (still focusable, so the hint
6608
+ * is read and the text can be copied) with
6609
+ * `ef_localized_input_read_only_hint` and the projected `[read-only-link]`;
6610
+ * their text still travels in the emitted object. `[efReadOnly]`, `[disabled]`
6611
+ * and a form's `disable()` disable every culture; the tabs stay usable so the
6612
+ * other languages can still be read.
6613
+ * - **Forms.** A ControlValueAccessor (`[formControl]`, `formControlName`,
6614
+ * `[(ngModel)]`) as well as `[value]` + `(valueChangeEvent)`. With
6615
+ * `required`, the form control reports `required` while the default culture
6616
+ * is empty, and once the field is touched, changed or submitted the default
6617
+ * culture's field shows `validation.required` and is `aria-invalid`.
6618
+ * - **Server errors** (ADR-010) come from the enclosing `[efServerErrors]`
6619
+ * scope. `<name>.<culture>` errors tint that culture's tab, show under its
6620
+ * field while it is active (the field links itself by name) and, from another
6621
+ * tab, show as a hint naming the languages in error; a new set of errors
6622
+ * moves to the first of them. `<name>` errors, or an explicit `[errors]`,
6623
+ * show under the field on every tab, as do errors for a language the field
6624
+ * does not list (`title.xx`), prefixed with that language's name (or its
6625
+ * code) through `ef_localized_input_error_in_culture`. Messages are
6626
+ * translated as keys. Keys: `ef_localized_input_errors_in_other`,
6627
+ * `ef_localized_input_error_in_culture`.
6628
+ * - **Counter.** With `advisedLength` it reads as advice through
6629
+ * `ef_localized_input_counter_advised` (`{{count}}`, `{{max}}`), so 5123 of
6630
+ * 5000 does not look like a cap; with `maxLength` alone it stays `n / max`.
6631
+ * - **Spoken limit.** `counterHintKey` is read with the counter (through
6632
+ * `aria-describedby`) and `counterOverKey` is announced politely when the
6633
+ * text passes the limit; both take `{{max}}`. On a field with several
6634
+ * cultures the announcement is prefixed with the culture's name through
6635
+ * `ef_localized_input_error_in_culture`, so `counterOverKey` should not
6636
+ * name it.
6637
+ * - **Names.** Each culture's field is named `<name>.<culture>` (`title.ar`),
6638
+ * the key the backend reports a per-culture error under.
6639
+ *
6640
+ * i18n keys the app's bundles must define (the libs ship no JSON):
6641
+ *
6642
+ * | key | fr | ar | en |
6643
+ * |---|---|---|---|
6644
+ * | `ef_localized_input_culture_fr` | Français | الفرنسية | French |
6645
+ * | `ef_localized_input_culture_ar` | Arabe | العربية | Arabic |
6646
+ * | `ef_localized_input_culture_en` | Anglais | الإنجليزية | English |
6647
+ * | `ef_localized_input_cultures` | Langues du texte | لغات النص | Text languages |
6648
+ * | `ef_localized_input_errors_in_other` | Erreur dans : | خطأ في: | Error in: |
6649
+ * | `ef_localized_input_counter_advised` | {{count}} / {{max}} conseillés | {{count}} / {{max}} موصى بها | {{count}} / {{max}} advised |
6650
+ * | `ef_localized_input_error_in_culture` | {{culture}} : {{message}} | {{culture}}: {{message}} | {{culture}}: {{message}} |
6651
+ * | `ef_localized_input_read_only_hint` | Ce texte se modifie ailleurs. | يُعدَّل هذا النص في مكان آخر. | This text is edited elsewhere. |
6652
+ *
6653
+ * ```html
6654
+ * <ef-localized-input name="title" labelKey="product_title" required
6655
+ * [value]="product().title" (valueChangeEvent)="patch({ title: $event })"
6656
+ * [advisedLength]="60" [maxLength]="256" />
6657
+ *
6658
+ * <ef-localized-input name="subject" labelKey="email_subject" [maxLength]="200" [linkedInputs]="[body]" [(activeCulture)]="culture" ... />
6659
+ * <ef-localized-input #body name="htmlBody" labelKey="email_body" mode="html" [showTabs]="false" [(activeCulture)]="culture" ... />
6660
+ *
6661
+ * <ef-localized-input name="title" [readOnlyCultures]="['fr']" ...>
6662
+ * <a read-only-link [routerLink]="catalogLink()">{{ 'store_open_catalog' | translate }}</a>
6663
+ * </ef-localized-input>
6664
+ * ```
6665
+ */
6666
+ declare class EfLocalizedInputComponent implements ControlValueAccessor, DoCheck {
6667
+ /** The camelCase backend property (`title`); each field is `<name>.<culture>`. */
6668
+ readonly name: _angular_core.InputSignal<string>;
6669
+ readonly value: _angular_core.InputSignal<Partial<Record<string, string>> | null | undefined>;
6670
+ readonly mode: _angular_core.InputSignal<"html" | "textarea" | "text" | "rich">;
6671
+ readonly rows: _angular_core.InputSignal<number>;
6672
+ /** Hard cap, the server's limit. */
6673
+ readonly maxLength: _angular_core.InputSignal<number | undefined>;
6674
+ /** What the counter measures against (what a search engine shows, say). */
6675
+ readonly advisedLength: _angular_core.InputSignal<number | undefined>;
6676
+ /** The default culture must be filled. */
6677
+ readonly required: _angular_core.InputSignalWithTransform<boolean, unknown>;
6678
+ /** Cultures shown but not editable here, with the `[read-only-link]` slot. */
6679
+ readonly readOnlyCultures: _angular_core.InputSignal<readonly string[]>;
6680
+ /** Labels the field and names its tablist; without it the tablist is
6681
+ * named `ef_localized_input_cultures`. */
6682
+ readonly labelKey: _angular_core.InputSignal<string>;
6683
+ readonly placeholderKey: _angular_core.InputSignal<string>;
6684
+ readonly hintKey: _angular_core.InputSignal<string>;
6685
+ /** i18n key (with `{{max}}`) read out with the counter, for a limit that is advice rather than a cap. */
6686
+ readonly counterHintKey: _angular_core.InputSignal<string>;
6687
+ /** i18n key (with `{{max}}`) announced politely when the text goes past the limit. */
6688
+ readonly counterOverKey: _angular_core.InputSignal<string>;
6689
+ /** Field style in `text` mode only, named like ef-input-text's (`primeng`
6690
+ * or `comptoir`); the textarea, html and rich modes have one style.
6691
+ * `default` is the former name of `primeng`, kept for one release. */
6692
+ readonly variant: _angular_core.InputSignal<"primeng" | "comptoir" | "default">;
6693
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
6694
+ /**
6695
+ * Messages for the whole field. Without it they come from the enclosing
6696
+ * `[efServerErrors]` scope under `name`; an explicit list wins. The
6697
+ * per-language messages (`<name>.<culture>`) always come from the scope.
6698
+ */
6699
+ readonly errors: _angular_core.InputSignal<string[] | null | undefined>;
6700
+ /** Overrides the `EF_CONTENT_CULTURES` list for this field. */
6701
+ readonly cultures: _angular_core.InputSignal<readonly EfCulture[] | undefined>;
6702
+ /**
6703
+ * The culture on show, two-way (`[(activeCulture)]`). `null` or a culture
6704
+ * the field does not list means the default; the field then writes back
6705
+ * the culture it settled on, so the parent always reads a listed code.
6706
+ * Bind one signal to several inputs to give them one tab row.
6707
+ */
6708
+ readonly activeCulture: _angular_core.ModelSignal<string | null>;
6709
+ /**
6710
+ * Draws the culture tab row. Off for a field that follows another
6711
+ * field's row through a shared `activeCulture`: its culture is then
6712
+ * named to assistive tech through `aria-describedby` and the hint still
6713
+ * names the cultures in error. List it in the row owner's
6714
+ * `linkedInputs` so the row draws its error tone, required tone and
6715
+ * dirty dot too. Without tabs it never rewrites `activeCulture` to fit
6716
+ * its own culture list (it shows its own fallback instead), and it moves
6717
+ * the shared culture to its first culture in error only when no row
6718
+ * owner links it.
6719
+ */
6720
+ readonly showTabs: _angular_core.InputSignalWithTransform<boolean, unknown>;
6721
+ /**
6722
+ * On the input that draws the shared tab row: the inputs that follow it
6723
+ * (`[showTabs]="false"`), whose `cultureFlags` the row merges into its
6724
+ * tones and dots. A new set of server errors on any of them moves the
6725
+ * shared culture to the first culture in error in this row's order.
6726
+ * Badges stay this input's own. A template reference works forward:
6727
+ * `<ef-localized-input [linkedInputs]="[body]" ... /> <ef-localized-input #body [showTabs]="false" ... />`.
6728
+ */
6729
+ readonly linkedInputs: _angular_core.InputSignal<readonly EfLocalizedInputComponent[]>;
6730
+ /** `html` mode only: the sanitised preview under the source. Off when
6731
+ * the screen renders its own preview (an email rendered by the server,
6732
+ * with its tokens filled in). */
6733
+ readonly preview: _angular_core.InputSignalWithTransform<boolean, unknown>;
6734
+ /** A badge per culture tab, culture code to i18n key (`{ ar:
6735
+ * 'email_templates_customized' }`): a state of that language worth
6736
+ * seeing from the tab row. Drawn by the input that shows the tabs. */
6737
+ readonly tabBadges: _angular_core.InputSignal<Readonly<Record<string, string>>>;
6738
+ /** The whole new object, trimmed, with blank cultures removed. */
6739
+ readonly valueChangeEvent: _angular_core.OutputEmitterRef<Partial<Record<string, string>>>;
6740
+ private readonly contentCultures;
6741
+ private readonly readOnlyScope;
6742
+ readonly ngControl: NgControl | null;
6743
+ private readonly serverErrorsScope;
6744
+ private readonly richLoader;
6745
+ private readonly richType;
6746
+ /** The editor's chunk failed to load (offline, a stale deploy). */
6747
+ private readonly richFailed;
6748
+ private readonly richHost;
6749
+ private richRef;
6750
+ private readonly textField;
6751
+ private readonly areaField;
6752
+ /** `rich` without a provided editor renders the html source instead. */
6753
+ protected readonly effectiveMode: _angular_core.Signal<"html" | "textarea" | "text" | "rich">;
6754
+ private readonly parentForm;
6755
+ private readonly host;
6756
+ private readonly translateService;
6757
+ /** The user typed in, or left, the field. */
6758
+ private readonly interacted;
6759
+ /** The form control is touched or dirty, or its form was submitted; read on every check. */
6760
+ private readonly formTouched;
6761
+ /** Errors wait for touch, a change or a submit, like every ef-* control. */
6762
+ private readonly showErrors;
6763
+ protected readonly uid: string;
6764
+ /** The object this component last emitted; an echo of it is not new data. */
6765
+ private lastEmitted;
6766
+ /** The text as typed, blanks and spaces kept, so an echo never rewrites a field mid-word. */
6767
+ private readonly raw;
6768
+ /** What the dirty dots compare against: the last value that came from outside. */
6769
+ private readonly baseline;
6770
+ private readonly cleaned;
6771
+ protected readonly list: _angular_core.Signal<readonly EfCulture[]>;
6772
+ protected readonly defaultCode: _angular_core.Signal<string>;
6773
+ protected readonly active: _angular_core.Signal<EfCulture | undefined>;
6774
+ /** The active culture's code: unlike active(), equal when an equal culture list arrives. */
6775
+ private readonly activeCode;
6776
+ /** Server messages for the whole field (`title`), and those of unlisted
6777
+ * cultures, translated here (never through the pipe a second time). */
6778
+ protected readonly fieldErrors: _angular_core.Signal<any[] | null>;
6779
+ /** Every `<name>.<culture>` message the scope holds. */
6780
+ private readonly allCultureErrors;
6781
+ /** Server messages per language (`title.ar` gives `ar`), only for listed cultures. */
6782
+ private readonly cultureErrors;
6783
+ /** Messages for a language this field does not list (`title.xx`): shown
6784
+ * with the whole field, never dropped, prefixed with the culture
6785
+ * (`xx : message`) since no tab says which language they concern. */
6786
+ private readonly unlistedErrors;
6787
+ /** Bumps when texts change, so translated messages follow the language. */
6788
+ private readonly textsVersion;
6789
+ /**
6790
+ * The active language's messages, translated once here for the field
6791
+ * below. Without one, a required default culture left empty says so
6792
+ * once the field is touched, changed or submitted (`validation.required`,
6793
+ * the family's message), which also sets `aria-invalid` on it.
6794
+ */
6795
+ protected readonly activeErrors: _angular_core.Signal<string[] | null>;
6796
+ /** Cultures with a server error, in tab order. */
6797
+ private readonly culturesInError;
6798
+ /** Cultures in error other than the one on show, for the hint under the tabs. */
6799
+ protected readonly errorsElsewhere: _angular_core.Signal<EfCulture[]>;
6800
+ /** Changes only when the set of errors does, so a tab the user picked stays picked. */
6801
+ private readonly errorSignature;
6802
+ /** How many row owners list this input in their `linkedInputs`. */
6803
+ private readonly linkedBy;
6804
+ /** Danger tone on the default culture: required, empty, and touched, changed or submitted. */
6805
+ private requiredTone;
6806
+ /**
6807
+ * What this input's tab would show for each culture it lists, read by
6808
+ * the input that draws a shared row (`linkedInputs`). Read-only.
6809
+ */
6810
+ readonly cultureFlags: _angular_core.Signal<Readonly<Record<string, EfLocalizedCultureFlags>>>;
6811
+ protected readonly tabItems: _angular_core.Signal<EfTabItem<string>[]>;
6812
+ private readonly cvaDisabled;
6813
+ /** Every culture is disabled: `[disabled]`, a form's `disable()`, or `[efReadOnly]`. */
6814
+ readonly isDisabled: _angular_core.Signal<boolean>;
6815
+ protected readonly activeReadOnly: _angular_core.Signal<boolean>;
6816
+ protected readonly activeText: _angular_core.Signal<string>;
6817
+ /**
6818
+ * What the counter counts. Rich text against `advisedLength` counts the
6819
+ * characters a reader sees; against `maxLength` alone it counts the stored
6820
+ * HTML, because that is what the server's limit measures.
6821
+ */
6822
+ protected readonly activeLength: _angular_core.Signal<number>;
6823
+ protected readonly labelId: string;
6824
+ protected readonly counterLimit: _angular_core.Signal<number | undefined>;
6825
+ /**
6826
+ * The polite over-limit announcement: empty under the limit, else the
6827
+ * `counterOverKey` text, prefixed with the culture's name when the field
6828
+ * has more than one. Its text changes only with the (culture, over)
6829
+ * pair, so typing past the limit is announced once, and a switch to
6830
+ * another language that is also over is announced and names it.
6831
+ */
6832
+ protected readonly overMessage: _angular_core.Signal<any>;
6833
+ protected readonly panelId: string;
6834
+ protected readonly counterId: string;
6835
+ /** The culture's name, read with a field that has no tab row. */
6836
+ protected readonly cultureNameId: string;
6837
+ /** Extra `aria-describedby` ids for the active field: the counter, and
6838
+ * the culture's name when no tab says it. */
6839
+ protected readonly describedByExtra: _angular_core.Signal<string>;
6840
+ private onChange;
6841
+ private touched;
6842
+ constructor();
6843
+ private createRich;
6844
+ /**
6845
+ * Inserts `text` (a `{{token}}`) into the culture on show, at the caret,
6846
+ * replacing a selection, or at the end when its field was never entered
6847
+ * or was just recreated by a culture switch; then emits the whole object
6848
+ * once, as typing would. Returns false, and changes nothing, when the
6849
+ * field is disabled, the culture is read-only, the result would pass
6850
+ * `maxLength`, there is no active field (no culture listed), or the
6851
+ * field is the rich editor (which has no insert yet). The field says
6852
+ * nothing itself on a refusal, so the caller should tell the user (a
6853
+ * toast such as "the variable would pass the length limit"); otherwise
6854
+ * a click on a variable silently does nothing.
6855
+ *
6856
+ * On a phone, the focus that follows the insert opens the keyboard.
6857
+ *
6858
+ * A caller tracking "the field last focused" should ignore focus on the
6859
+ * culture tabs (`[role="tab"]`): picking a language is not choosing where
6860
+ * the token goes.
6861
+ */
6862
+ insertAtCaret(text: string): boolean;
6863
+ protected tabId(code: string): string;
6864
+ protected controlId(code: string): string;
6865
+ protected select(code: string): void;
6866
+ protected setText(code: string, text: string): void;
6867
+ /** Touched when focus leaves the whole field, not when it moves from a tab to the text. */
6868
+ protected onFocusOut(event: FocusEvent): void;
6869
+ ngDoCheck(): void;
6870
+ /** `{ required: true }` while a required field's default culture has no text. */
6871
+ validateRequired(control: AbstractControl): ValidationErrors | null;
6872
+ writeValue(v: LocalizedText | null): void;
6873
+ registerOnChange(fn: (v: LocalizedText) => void): void;
6874
+ registerOnTouched(fn: () => void): void;
6875
+ setDisabledState(disabled: boolean): void;
6876
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfLocalizedInputComponent, never>;
6877
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfLocalizedInputComponent, "ef-localized-input", never, { "name": { "alias": "name"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "rows": { "alias": "rows"; "required": false; "isSignal": true; }; "maxLength": { "alias": "maxLength"; "required": false; "isSignal": true; }; "advisedLength": { "alias": "advisedLength"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "readOnlyCultures": { "alias": "readOnlyCultures"; "required": false; "isSignal": true; }; "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "placeholderKey": { "alias": "placeholderKey"; "required": false; "isSignal": true; }; "hintKey": { "alias": "hintKey"; "required": false; "isSignal": true; }; "counterHintKey": { "alias": "counterHintKey"; "required": false; "isSignal": true; }; "counterOverKey": { "alias": "counterOverKey"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "errors": { "alias": "errors"; "required": false; "isSignal": true; }; "cultures": { "alias": "cultures"; "required": false; "isSignal": true; }; "activeCulture": { "alias": "activeCulture"; "required": false; "isSignal": true; }; "showTabs": { "alias": "showTabs"; "required": false; "isSignal": true; }; "linkedInputs": { "alias": "linkedInputs"; "required": false; "isSignal": true; }; "preview": { "alias": "preview"; "required": false; "isSignal": true; }; "tabBadges": { "alias": "tabBadges"; "required": false; "isSignal": true; }; }, { "activeCulture": "activeCultureChange"; "valueChangeEvent": "valueChangeEvent"; }, never, ["[read-only-link]"], true, never>;
6463
6878
  }
6464
6879
 
6880
+ /**
6881
+ * Loads the rich text editor component that `ef-localized-input`'s `rich`
6882
+ * mode renders. The editor lives in the `@elasticias/ui/rich-text` entry
6883
+ * point so an app that never edits rich text neither bundles PrimeNG's Editor
6884
+ * nor needs `quill` installed. Provide it with `provideEfRichText()` from that
6885
+ * entry (the editor code is in the app's main bundle, Quill itself still loads
6886
+ * lazily the first time an editor renders), or with your own loader to defer
6887
+ * the editor code too:
6888
+ *
6889
+ * ```ts
6890
+ * { provide: EF_RICH_TEXT, useValue: () => import('@elasticias/ui/rich-text').then(m => m.EfRichTextComponent) }
6891
+ * ```
6892
+ */
6893
+ declare const EF_RICH_TEXT: InjectionToken<() => Promise<Type<unknown>>>;
6894
+ /** Length of the text a reader sees in an HTML fragment, tags not counted. Browser only: it parses with `document`. */
6895
+ declare function plainTextLength(html: string | null | undefined): number;
6896
+
6465
6897
  type EfFileRejectReason = 'size' | 'type';
6466
6898
  interface EfFileRejection {
6467
6899
  file: File;
@@ -7151,5 +7583,5 @@ declare class EfEmailMetricsCardComponent {
7151
7583
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfEmailMetricsCardComponent, "ef-email-metrics-card", never, { "metrics": { "alias": "metrics"; "required": false; "isSignal": true; }; "recentEvents": { "alias": "recentEvents"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; }, { "rangeChange": "rangeChange"; }, never, never, true, never>;
7152
7584
  }
7153
7585
 
7154
- 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 };
7155
- export type { CatalogueProduct, CatalogueProductVariant, CategoryAssignment, CategoryGroup, CategoryItem, DatatableSearchEntity, DocumentConfig, EfAboutServer, EfBadgeColor, EfBadgeConfig, EfBadgeSize, EfBadgeVariant, EfButtonComptoirSeverity, EfButtonPrimeNGSeverity, EfButtonSeverity, EfChangeHistoryEntry, EfChangeHistoryFieldChange, EfChangeHistoryValueItem, EfChangePasswordSubmit, EfChartSeries, EfChartType, EfDataCardMobileLayout, EfDataCardRowAction, EfDataCardRowActionState, EfDatatableActions, EfDatatableColumn, EfDatatableColumnAlign, EfDatatableColumnType, EfDatatableConfig, EfDateFieldOutput, EfDateFieldValue, EfDatePreset, EfDialogAction, EfDialogSeverity, EfDialogSize, EfFabPosition, EfFileRejectReason, EfFileRejection, EfIdentityThumbSize, EfKpiDeltaTone, EfKpiSparkline, EfOrderPriceList, EfOrderRecipient, EfPillItem, EfPresetItem, EfProductPickerItem, EfProductPickerQuery, EfProfileMenuItem, EfRankRow, EfRowAction, EfSeoLocale, EfSeoPatch, EfSkeletonVariant, EfStatsRow, EfStatusColor, EfStatusReferenceItem, EfTabItem, EfTabsModule, EfTabsVariant, EfTimelineItem, EfTimelineState, EfToastRegionPosition, EfTotalsGrand, EfTotalsRow, HighlightSegment, OrderChangeInfo, OrderEntity, OrderLineItem, OrderProductsSummary, OrderSummaryItem, ProductCountGroup, ProductCountGroupLabel, ProductTypeaheadRow, ProductTypeaheadSelection, ProductWithCountGroup, SelectedFilter, ValidationResult };
7586
+ 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, auditEntityKeySegment, buildChartConfig, defaultUnitPrice, getColumnAlignment, mergeColumnDefaults, plainTextLength, resolveEfStatusColor };
7587
+ export type { CatalogueProduct, CatalogueProductVariant, CategoryAssignment, CategoryGroup, CategoryItem, DatatableSearchEntity, DocumentConfig, EfAboutServer, EfBadgeColor, EfBadgeConfig, EfBadgeSize, EfBadgeVariant, EfButtonComptoirSeverity, EfButtonPrimeNGSeverity, EfButtonSeverity, EfChangeHistoryEntry, EfChangeHistoryFieldChange, EfChangeHistoryValueItem, EfChangePasswordSubmit, EfChartSeries, EfChartType, EfDataCardMobileLayout, EfDataCardRowAction, EfDataCardRowActionState, EfDatatableActions, EfDatatableColumn, EfDatatableColumnAlign, EfDatatableColumnType, EfDatatableConfig, EfDateFieldOutput, EfDateFieldValue, EfDatePreset, EfDialogAction, EfDialogSeverity, EfDialogSize, EfFabPosition, EfFileRejectReason, EfFileRejection, EfIdentityThumbSize, EfKpiDeltaTone, EfKpiSparkline, EfLocalizedCultureFlags, EfOrderPriceList, EfOrderRecipient, EfPillItem, EfPresetItem, EfProductPickerItem, EfProductPickerQuery, EfProfileMenuItem, EfRankRow, EfRowAction, EfSeoLocale, EfSeoPatch, EfSkeletonVariant, EfStatsRow, EfStatusColor, EfStatusReferenceItem, EfTabItem, EfTabsModule, EfTabsVariant, EfTimelineItem, EfTimelineState, EfToastRegionPosition, EfTotalsGrand, EfTotalsRow, HighlightSegment, OrderChangeInfo, OrderEntity, OrderLineItem, OrderProductsSummary, OrderSummaryItem, ProductCountGroup, ProductCountGroupLabel, ProductTypeaheadRow, ProductTypeaheadSelection, ProductWithCountGroup, SelectedFilter, ValidationResult };