@elasticias/ui 0.0.16 → 1.0.1

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,17 +1,22 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { PipeTransform, OnInit, OnDestroy, OnChanges, TemplateRef, EventEmitter, SimpleChanges } from '@angular/core';
3
- import { AppUtils } from '@elasticias/utils';
2
+ import { PipeTransform, OnInit, OnChanges, TemplateRef, EventEmitter, AfterContentInit, OnDestroy, SimpleChanges, AfterViewInit } from '@angular/core';
4
3
  import { Menu } from 'primeng/menu';
5
- import { ScreenContext } from '@elasticias/screens';
4
+ import * as _elasticias_screens from '@elasticias/screens';
5
+ import { ScreenContext, EfDetailToolbarAction, EfSearchToolbarAction, EfDataCardColumn, EfDataCardSort, EfDateRange, EfDatePresetKey } from '@elasticias/screens';
6
+ export { EfDataCardColumn, EfDataCardColumnAlign, EfDataCardColumnType, EfDataCardSort, EfDataCardSortDirection, EfDatePresetKey, EfDateRange, EfDetailToolbarAction, EfSearchToolbarAction } from '@elasticias/screens';
7
+ import * as _elasticias_core from '@elasticias/core';
8
+ import { EfModule, EfModuleId, EfNavSection, EfNavItem, EfToast, EfToastAction, EfThemeConfigService } from '@elasticias/core';
6
9
  import { BlockableUI } from 'primeng/api';
10
+ import { SafeResourceUrl } from '@angular/platform-browser';
11
+ import { ChartConfiguration } from 'chart.js';
7
12
  import { ControlValueAccessor, NgControl } from '@angular/forms';
13
+ import { TranslateService } from '@ngx-translate/core';
8
14
  import { InputNumber, InputNumberInputEvent } from 'primeng/inputnumber';
9
15
  import { Nullable } from 'primeng/ts-helpers';
10
16
  import * as _elasticias_ui from '@elasticias/ui';
11
17
  import { Table, TableLazyLoadEvent } from 'primeng/table';
18
+ import { Permissions, EmailMetrics, EmailEventRecord, EmailEventKind } from '@elasticias/types';
12
19
  import { PrimeNG } from 'primeng/config';
13
- import { EfThemeConfigService } from '@elasticias/core';
14
- import { EmailMetrics, EmailEventRecord, EmailEventKind } from '@elasticias/types';
15
20
 
16
21
  /**
17
22
  * Merged currency pipe from ERP (AppCurrencyPipe) and Store (CurrencyFormatPipe).
@@ -30,6 +35,16 @@ declare class TruncatePipe implements PipeTransform {
30
35
  static ɵpipe: _angular_core.ɵɵPipeDeclaration<TruncatePipe, "truncate", true>;
31
36
  }
32
37
 
38
+ /**
39
+ * Compact human-readable rendering of a number for KPI displays:
40
+ * 1000 → "1 k", 2_500_000 → "2,5 M" (fr-MA locale, decimal comma).
41
+ */
42
+ declare class EfCompactPipe implements PipeTransform {
43
+ transform(value: number | null | undefined, maxDecimals?: number): string;
44
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfCompactPipe, never>;
45
+ static ɵpipe: _angular_core.ɵɵPipeDeclaration<EfCompactPipe, "efCompact", true>;
46
+ }
47
+
33
48
  declare class EfPriceDisplayComponent {
34
49
  readonly amount: _angular_core.InputSignal<number>;
35
50
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfPriceDisplayComponent, never>;
@@ -289,6 +304,50 @@ declare class EfOrderSummaryComponent {
289
304
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfOrderSummaryComponent, "ef-order-summary", never, { "orderLines": { "alias": "orderLines"; "required": true; "isSignal": true; }; "countGroupLabels": { "alias": "countGroupLabels"; "required": false; "isSignal": true; }; "legend": { "alias": "legend"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
290
305
  }
291
306
 
307
+ /**
308
+ * Presentational shapes for {@link EfChangeHistoryComponent}. Structurally
309
+ * compatible with the backend `ChangeHistoryEntryDto` / `FieldChangeDto` that
310
+ * the NSwag client returns, so a screen can pass them straight through without
311
+ * mapping. Kept lib-local so `@elasticias/ui` stays decoupled from any app's
312
+ * generated API client.
313
+ */
314
+ interface EfChangeHistoryFieldChange {
315
+ field?: string;
316
+ oldValue?: string | null;
317
+ newValue?: string | null;
318
+ }
319
+ interface EfChangeHistoryEntry {
320
+ /** Standardized operation code: created | updated | status_changed | deleted | note. */
321
+ operation?: string;
322
+ occurredAt?: string | Date | null;
323
+ userId?: string | null;
324
+ userName?: string | null;
325
+ changes?: EfChangeHistoryFieldChange[] | null;
326
+ fromStatus?: string | null;
327
+ toStatus?: string | null;
328
+ note?: string | null;
329
+ }
330
+
331
+ /** Emitted when the user picks a product (with its active variant) from the menu. */
332
+ interface ProductTypeaheadSelection {
333
+ product: Record<string, unknown>;
334
+ variant: Record<string, unknown> | null;
335
+ unitPrice: number;
336
+ }
337
+ /** A run of result-label text, flagged when it matches the current query. */
338
+ interface HighlightSegment {
339
+ text: string;
340
+ mark: boolean;
341
+ }
342
+ /** Internal view-model for one result row. */
343
+ interface ProductTypeaheadRow {
344
+ key: string;
345
+ product: Record<string, unknown>;
346
+ label: string;
347
+ variants: Record<string, unknown>[];
348
+ segments: HighlightSegment[];
349
+ }
350
+
292
351
  /**
293
352
  * Reusable order builder component for orders, invoices, and quotes
294
353
  * Manages complete order entity with automatic change tracking
@@ -303,9 +362,10 @@ declare class EfOrderSummaryComponent {
303
362
  * (orderChanged)="handleOrderChange($event)"
304
363
  * />
305
364
  */
306
- declare class EfOrderBuilderComponent implements OnInit, OnDestroy {
365
+ /** Numeric line columns that support inline click-to-edit. */
366
+ type EditableLineField = 'price' | 'qty' | 'discount' | 'tva';
367
+ declare class EfOrderBuilderComponent implements OnInit {
307
368
  rowTrackBy: (_: number, item: OrderLineItem) => string | number;
308
- protected readonly AppUtils: typeof AppUtils;
309
369
  config: _angular_core.InputSignal<DocumentConfig>;
310
370
  order: _angular_core.ModelSignal<OrderEntity>;
311
371
  products: _angular_core.InputSignal<Record<string, unknown>[]>;
@@ -317,25 +377,19 @@ declare class EfOrderBuilderComponent implements OnInit, OnDestroy {
317
377
  backendErrors: _angular_core.InputSignal<{
318
378
  [key: string]: string[];
319
379
  }>;
380
+ /** Optional change-history entries; when non-empty the Historique rail card renders. */
381
+ auditEntries: _angular_core.InputSignal<EfChangeHistoryEntry[]>;
320
382
  openCatalogue: _angular_core.OutputEmitterRef<void>;
321
383
  orderChanged: _angular_core.OutputEmitterRef<OrderChangeInfo>;
322
384
  orderValidated: _angular_core.OutputEmitterRef<ValidationResult>;
323
385
  productsAdded: _angular_core.OutputEmitterRef<OrderLineItem[]>;
324
386
  private originalOrder;
325
- private updateTimeout;
326
387
  private initialized;
327
- quickAddProduct: _angular_core.WritableSignal<Record<string, unknown>>;
388
+ quickAddSelection: _angular_core.WritableSignal<ProductTypeaheadSelection>;
328
389
  quickAddPrice: _angular_core.WritableSignal<number>;
329
390
  quickAddQuantity: _angular_core.WritableSignal<number>;
330
391
  quickAddDisabled: _angular_core.Signal<boolean>;
331
392
  orderLines: _angular_core.Signal<OrderLineItem[]>;
332
- /**
333
- * Flatten products → one option per active variant for the inline ef-select.
334
- * Simple products (no variants) get a single row with the product label.
335
- * Each option exposes `_displayLabel` for `optionLabel` and carries the
336
- * underlying product/variant references for `onProductSelect`.
337
- */
338
- flattenedProductOptions: _angular_core.Signal<Record<string, unknown>[]>;
339
393
  grossTotal: _angular_core.Signal<number>;
340
394
  discountTotal: _angular_core.Signal<number>;
341
395
  tvaTotal: _angular_core.Signal<number>;
@@ -344,7 +398,6 @@ declare class EfOrderBuilderComponent implements OnInit, OnDestroy {
344
398
  locale: _angular_core.Signal<string>;
345
399
  hasItems: _angular_core.Signal<boolean>;
346
400
  productsSummary: _angular_core.Signal<OrderProductsSummary>;
347
- hasEditingRow: _angular_core.Signal<boolean>;
348
401
  validationResult: _angular_core.Signal<ValidationResult>;
349
402
  combinedErrors: _angular_core.Signal<{
350
403
  [x: string]: string[];
@@ -353,19 +406,41 @@ declare class EfOrderBuilderComponent implements OnInit, OnDestroy {
353
406
  isValid: _angular_core.Signal<boolean>;
354
407
  changeInfo: _angular_core.Signal<OrderChangeInfo>;
355
408
  hasChanges: _angular_core.Signal<boolean>;
409
+ focusMode: _angular_core.ModelSignal<boolean>;
410
+ railOpen: _angular_core.WritableSignal<boolean>;
411
+ /**
412
+ * Inline cell editing — lines render their numeric values as read-only text
413
+ * until the user clicks a value, which swaps just that cell to an input
414
+ * (V1 behaviour). One cell is editable at a time; blur / Enter / Escape /
415
+ * Tab commits and returns the cell to display.
416
+ */
417
+ editingCell: _angular_core.WritableSignal<{
418
+ id: OrderLineItem["id"];
419
+ field: EditableLineField;
420
+ }>;
421
+ /** Number of table columns, used for the empty-state colspan. */
422
+ colCount: _angular_core.Signal<number>;
423
+ isEditingCell(item: OrderLineItem, field: EditableLineField): boolean;
424
+ startEditCell(item: OrderLineItem, field: EditableLineField): void;
425
+ stopEditCell(): void;
426
+ onEditCellKeydown(event: KeyboardEvent): void;
427
+ toggleFocus(): void;
428
+ toggleRail(open?: boolean): void;
429
+ hasAudit: _angular_core.Signal<boolean>;
430
+ /** Récap items grouped into columns (column index from reference data). */
431
+ recapColumns: _angular_core.Signal<OrderSummaryItem[][]>;
356
432
  constructor();
357
433
  ngOnInit(): void;
358
- ngOnDestroy(): void;
359
- addNewProductRow(): void;
360
- removeOrderLine(id: number | undefined): void;
361
- onProductSelect(item: OrderLineItem): void;
362
- startEditField(item: OrderLineItem, fieldName: string): void;
363
- stopEditField(item: OrderLineItem): void;
434
+ removeOrderLine(id: string | number | undefined): void;
364
435
  onFieldChange(item: OrderLineItem): void;
365
- saveEditingRow(item: OrderLineItem): void;
366
436
  calculateSubtotal(item: OrderLineItem): number;
367
437
  handleOpenCatalogue(): void;
368
- onQuickAddProductSelect(option: Record<string, unknown> | null): void;
438
+ private readonly typeahead;
439
+ /**
440
+ * Fill the quick-add bar from a typeahead pick. Does NOT add the line —
441
+ * the user reviews price/quantity then clicks "Ajouter" (addQuickProduct).
442
+ */
443
+ onTypeaheadSelect(selection: ProductTypeaheadSelection): void;
369
444
  addQuickProduct(): void;
370
445
  addProductsFromCatalogue(selectedProducts: Array<{
371
446
  product: Record<string, unknown>;
@@ -381,7 +456,6 @@ declare class EfOrderBuilderComponent implements OnInit, OnDestroy {
381
456
  validate(): ValidationResult;
382
457
  private updateOrderLines;
383
458
  private getCustomerName;
384
- private productKey;
385
459
  getProduct(productId: unknown): Record<string, unknown> | undefined;
386
460
  getCustomer(customerId: string): Record<string, unknown> | undefined;
387
461
  initializeOrderLines(): void;
@@ -393,7 +467,63 @@ declare class EfOrderBuilderComponent implements OnInit, OnDestroy {
393
467
  };
394
468
  exportOrder(): OrderEntity;
395
469
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfOrderBuilderComponent, never>;
396
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfOrderBuilderComponent, "ef-order-builder", never, { "config": { "alias": "config"; "required": false; "isSignal": true; }; "order": { "alias": "order"; "required": true; "isSignal": true; }; "products": { "alias": "products"; "required": true; "isSignal": true; }; "customers": { "alias": "customers"; "required": true; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "countGroupLabels": { "alias": "countGroupLabels"; "required": false; "isSignal": true; }; "productKeyField": { "alias": "productKeyField"; "required": false; "isSignal": true; }; "backendErrors": { "alias": "backendErrors"; "required": false; "isSignal": true; }; }, { "order": "orderChange"; "openCatalogue": "openCatalogue"; "orderChanged": "orderChanged"; "orderValidated": "orderValidated"; "productsAdded": "productsAdded"; }, never, never, true, never>;
470
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfOrderBuilderComponent, "ef-order-builder", never, { "config": { "alias": "config"; "required": false; "isSignal": true; }; "order": { "alias": "order"; "required": true; "isSignal": true; }; "products": { "alias": "products"; "required": true; "isSignal": true; }; "customers": { "alias": "customers"; "required": true; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "countGroupLabels": { "alias": "countGroupLabels"; "required": false; "isSignal": true; }; "productKeyField": { "alias": "productKeyField"; "required": false; "isSignal": true; }; "backendErrors": { "alias": "backendErrors"; "required": false; "isSignal": true; }; "auditEntries": { "alias": "auditEntries"; "required": false; "isSignal": true; }; "focusMode": { "alias": "focusMode"; "required": false; "isSignal": true; }; }, { "order": "orderChange"; "openCatalogue": "openCatalogue"; "orderChanged": "orderChanged"; "orderValidated": "orderValidated"; "productsAdded": "productsAdded"; "focusMode": "focusModeChange"; }, never, never, true, never>;
471
+ }
472
+
473
+ /**
474
+ * Keyboard-navigable product search. Filters products by display name,
475
+ * highlights the match, exposes per-product variant chips, and emits the
476
+ * chosen product + active variant + unit price. Pure input-driven — no API.
477
+ *
478
+ * @example
479
+ * <ef-product-typeahead
480
+ * [products]="products()"
481
+ * productKeyField="id"
482
+ * placeholderKey="label.search_product"
483
+ * (productSelect)="onPicked($event)" />
484
+ */
485
+ declare class EfProductTypeaheadComponent {
486
+ products: _angular_core.InputSignal<Record<string, unknown>[]>;
487
+ productKeyField: _angular_core.InputSignal<string>;
488
+ placeholderKey: _angular_core.InputSignal<string>;
489
+ disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
490
+ currency: _angular_core.InputSignal<string>;
491
+ locale: _angular_core.InputSignal<string>;
492
+ /**
493
+ * How variants are presented.
494
+ * - `true` (default): compact — one row per product with inline variant
495
+ * chips (e.g. `30ml` / `50ml`); clicking a chip switches the active
496
+ * variant + price.
497
+ * - `false`: V1 expanded — one row per active variant, with the variant
498
+ * title composed into the label (`Name - 30ml`) and its own price.
499
+ *
500
+ * Display-only: the emitted `ProductTypeaheadSelection` (product + chosen
501
+ * variant + unit price) is identical either way.
502
+ */
503
+ showVariants: _angular_core.InputSignalWithTransform<boolean, unknown>;
504
+ productSelect: _angular_core.OutputEmitterRef<ProductTypeaheadSelection>;
505
+ query: _angular_core.WritableSignal<string>;
506
+ open: _angular_core.WritableSignal<boolean>;
507
+ highlightedIndex: _angular_core.WritableSignal<number>;
508
+ /** Map of product key -> chosen variant index (defaults to 0). */
509
+ activeVariant: _angular_core.WritableSignal<Record<string, number>>;
510
+ private static norm;
511
+ rows: _angular_core.Signal<ProductTypeaheadRow[]>;
512
+ buildSegments(label: string, query: string): HighlightSegment[];
513
+ onInput(value: string): void;
514
+ moveHighlight(delta: number): void;
515
+ onKeydown(event: KeyboardEvent): void;
516
+ /** Active variant index for a row (defaults to 0). */
517
+ variantIndexOf(row: ProductTypeaheadRow): number;
518
+ variantOf(row: ProductTypeaheadRow): Record<string, unknown> | null;
519
+ priceOf(row: ProductTypeaheadRow): number;
520
+ setActiveVariant(row: ProductTypeaheadRow, index: number): void;
521
+ selectRow(row: ProductTypeaheadRow): void;
522
+ selectHighlighted(): void;
523
+ /** Clear the field after the consumer has consumed the selection. */
524
+ reset(): void;
525
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfProductTypeaheadComponent, never>;
526
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfProductTypeaheadComponent, "ef-product-typeahead", never, { "products": { "alias": "products"; "required": true; "isSignal": true; }; "productKeyField": { "alias": "productKeyField"; "required": false; "isSignal": true; }; "placeholderKey": { "alias": "placeholderKey"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "currency": { "alias": "currency"; "required": false; "isSignal": true; }; "locale": { "alias": "locale"; "required": false; "isSignal": true; }; "showVariants": { "alias": "showVariants"; "required": false; "isSignal": true; }; }, { "productSelect": "productSelect"; }, never, never, true, never>;
397
527
  }
398
528
 
399
529
  interface CategoryItem {
@@ -576,16 +706,106 @@ declare class EfProductCatalogueComponent implements OnChanges {
576
706
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfProductCatalogueComponent, "ef-product-catalogue", never, { "products": { "alias": "products"; "required": false; "isSignal": true; }; "categoryGroups": { "alias": "categoryGroups"; "required": false; "isSignal": true; }; "initialSelectedItems": { "alias": "initialSelectedItems"; "required": false; "isSignal": true; }; "defaultFilters": { "alias": "defaultFilters"; "required": false; "isSignal": true; }; "trackByField": { "alias": "trackByField"; "required": false; "isSignal": true; }; "currencyCode": { "alias": "currencyCode"; "required": false; "isSignal": true; }; "localeCode": { "alias": "localeCode"; "required": false; "isSignal": true; }; }, { "productsValidated": "productsValidated"; }, ["productThumbnailTpl"], never, true, never>;
577
707
  }
578
708
 
709
+ /**
710
+ * Dynamic action descriptor for `ef-dialog`. The dialog renders one
711
+ * `<button class="btn …">` per entry, in array order (left → right);
712
+ * the rightmost entry is the focused / "primary" by convention.
713
+ *
714
+ * When `command` returns a `Promise`, the button auto-toggles
715
+ * `.is-loading` for the duration and the dialog closes when it
716
+ * resolves (unless `dismiss === false`).
717
+ */
718
+ interface EfDialogAction {
719
+ /** Direct label text. Use `labelKey` for i18n. */
720
+ label?: string;
721
+ /** Translation key for the label — takes precedence over `label`. */
722
+ labelKey?: string;
723
+ /** Visual severity (controls the .btn class). Default: `'ghost'`. */
724
+ severity?: 'primary' | 'ghost' | 'tenant' | 'danger';
725
+ /** PrimeNG icon class (e.g. `'pi pi-trash'`). Optional. */
726
+ icon?: string;
727
+ /** Extra class(es) appended to the footer button — for brand-tinted
728
+ * actions (e.g. `'btn-whatsapp'`) or layout helpers (`'btn-dialog-leading'`)
729
+ * beyond the four standard severities. */
730
+ styleClass?: string;
731
+ /** Click handler. Return a `Promise` to auto-toggle the loading
732
+ * spinner overlay on the button until it settles. */
733
+ command?: () => void | Promise<void>;
734
+ /** Close the dialog after the command resolves. Default: `true`. */
735
+ dismiss?: boolean;
736
+ /** Render the button as disabled. */
737
+ disabled?: boolean;
738
+ /** Render the button as loading (in-flight). Mutated automatically
739
+ * by the dialog when the command returns a Promise. */
740
+ loading?: boolean;
741
+ }
742
+ type EfDialogSeverity = 'default' | 'danger' | 'warn' | 'info' | 'success' | 'tenant';
743
+ type EfDialogSize = 'sm' | 'md' | 'lg' | 'xl' | 'full';
744
+ /**
745
+ * Comptoir dialog — wraps PrimeNG `p-dialog` and skins it against the
746
+ * `.es-dialog` patterns lifted from the static prototype
747
+ * (`docs/ux-ui/erp/screens/components.html` § Section 05).
748
+ *
749
+ * **Card design with two rules** — the header has a `border-bottom`,
750
+ * the footer has a `border-top` (`1px var(--rule)`). Header layout:
751
+ * optional severity-tinted icon · title + subtitle · close ✕.
752
+ *
753
+ * **Dynamic actions** — pass an `EfDialogAction[]` via `[actions]`
754
+ * and the footer renders one button per entry, in order. The rightmost
755
+ * is conventionally the primary action; consumers control severity
756
+ * per-entry. Async commands flip the button to `.is-loading` until
757
+ * resolved, then auto-dismiss (unless opted out).
758
+ *
759
+ * ```html
760
+ * <ef-dialog
761
+ * [(visible)]="show"
762
+ * headerKey="orders.delete_confirm"
763
+ * subtitleKey="orders.delete_irreversible"
764
+ * severity="danger"
765
+ * size="md"
766
+ * [actions]="[
767
+ * { labelKey: 'common_cancel', severity: 'ghost' },
768
+ * { labelKey: 'common_delete', severity: 'danger',
769
+ * command: () => this.api.deleteOrder(this.id) }
770
+ * ]"
771
+ * >
772
+ * <div class="es-dialog__highlight">
773
+ * <span class="av tenant">L</span>
774
+ * <div class="body">
775
+ * <div class="nm">SO-2026-198 · Leila Bennani</div>
776
+ * <div class="meta">07 mai · 1 132,00 MAD</div>
777
+ * </div>
778
+ * </div>
779
+ * <p>{{ 'orders.delete_warning' | translate }}</p>
780
+ * </ef-dialog>
781
+ * ```
782
+ */
579
783
  declare class EfDialogComponent {
580
- /** Direct header text (not translated) */
784
+ private readonly translate;
785
+ /** Direct header text (not translated). */
581
786
  header?: string;
582
- /** Translation key — takes priority over header */
787
+ /** Translation key — takes precedence over `header`. */
583
788
  headerKey?: string;
789
+ /** Direct subtitle text. */
790
+ subtitle?: string;
791
+ /** Translation key — takes precedence over `subtitle`. */
792
+ subtitleKey?: string;
584
793
  visible: boolean;
585
794
  modal: boolean;
586
795
  closable: boolean;
587
796
  draggable: boolean;
588
797
  resizable: boolean;
798
+ /** Close the dialog when the user clicks the backdrop mask. */
799
+ dismissableMask: boolean;
800
+ /** Close the dialog on Escape (PrimeNG default = true). */
801
+ closeOnEscape: boolean;
802
+ /** Severity drives the header icon tint (panel itself stays neutral).
803
+ * Pair with `[icon]` to override the default icon-class per severity. */
804
+ severity: EfDialogSeverity;
805
+ /** Header icon class (PrimeNG `pi pi-*`). Overrides the severity default. */
806
+ icon?: string;
807
+ /** Width preset: sm 380 / md 480 (default) / lg 640 / xl 880 / full. */
808
+ size: EfDialogSize;
589
809
  style?: {
590
810
  [key: string]: string;
591
811
  };
@@ -593,9 +813,22 @@ declare class EfDialogComponent {
593
813
  position: 'center' | 'top' | 'bottom' | 'left' | 'right' | 'topleft' | 'topright' | 'bottomleft' | 'bottomright';
594
814
  appendTo?: string;
595
815
  /**
596
- * Render a standard cancel + save action footer. Caller wires (saveAction)
597
- * and (cancelAction). When false (default), the footer falls back to the
598
- * `[dialogFooter]` projection slot for fully custom footers.
816
+ * Render variant.
817
+ * - `'comptoir'` (default) applies the `.es-dialog` Comptoir skin,
818
+ * custom header (icon + title + subtitle + close), and dynamic-action
819
+ * footer.
820
+ * - `'primeng'` — bare `p-dialog` with its native header (just the
821
+ * `[header]` text + PrimeNG's default close). Use for legacy screens
822
+ * or to dodge the Comptoir skin one-off.
823
+ */
824
+ variant: 'primeng' | 'comptoir';
825
+ /** Footer actions, rendered left → right. When set (non-empty),
826
+ * takes precedence over `defaultActions`. */
827
+ actions?: EfDialogAction[];
828
+ /**
829
+ * Render a standard cancel + save action footer. Backward-compat
830
+ * shortcut — for richer footers prefer `[actions]`. When false (and
831
+ * no `[actions]` provided), the footer is omitted entirely.
599
832
  */
600
833
  defaultActions: boolean;
601
834
  saveLabelKey: string;
@@ -603,31 +836,55 @@ declare class EfDialogComponent {
603
836
  saveLabel?: string;
604
837
  cancelLabel?: string;
605
838
  saveIcon: string;
839
+ cancelIcon?: string;
606
840
  saveDisabled: boolean;
607
- saveStyleClass: string;
608
- cancelStyleClass: string;
841
+ saveStyleClass?: string;
842
+ cancelStyleClass?: string;
843
+ saveSeverity: 'primary' | 'tenant' | 'ghost' | 'danger';
844
+ cancelSeverity: 'primary' | 'tenant' | 'ghost' | 'danger';
609
845
  visibleChange: EventEmitter<boolean>;
610
846
  showEvent: EventEmitter<void>;
611
847
  hideEvent: EventEmitter<void>;
612
848
  saveAction: EventEmitter<void>;
613
849
  cancelAction: EventEmitter<void>;
850
+ /** Emits for every action in the dynamic `[actions]` footer. The
851
+ * payload includes the action object and its index. */
852
+ actionClick: EventEmitter<{
853
+ action: EfDialogAction;
854
+ index: number;
855
+ }>;
856
+ /** ViewChild ref to the default-action footer template — used in the
857
+ * `'primeng'` variant via [footerTemplate] input. */
614
858
  defaultFooterTpl?: TemplateRef<void>;
859
+ /** Severity → default header icon. Override per-instance with `[icon]`. */
860
+ private static readonly SEV_ICONS;
861
+ get effectiveStyleClass(): string;
862
+ get effectiveTitle(): string;
863
+ get effectiveSubtitle(): string;
864
+ get effectiveIcon(): string | null;
865
+ /** True when the dynamic `[actions]` path should render the footer. */
866
+ get useDynamicActions(): boolean;
867
+ /** Whether any kind of footer should render at all. */
868
+ get hasFooter(): boolean;
869
+ actionLabel(a: EfDialogAction): string;
870
+ actionClass(a: EfDialogAction): string;
871
+ handleActionClick(a: EfDialogAction, index: number): void;
615
872
  handleVisibleChange(value: boolean): void;
616
873
  handleShow(): void;
617
874
  handleHide(): void;
618
- /**
619
- * Default cancel handler — emits and also closes the dialog. Callers can
620
- * override behavior via (cancelAction) (close still happens).
621
- */
875
+ /** Default cancel — emits and closes. */
622
876
  onDefaultCancel(): void;
877
+ /** Default save — emits only (consumer closes after success). */
623
878
  onDefaultSave(): void;
624
879
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfDialogComponent, never>;
625
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfDialogComponent, "ef-dialog", never, { "header": { "alias": "header"; "required": false; }; "headerKey": { "alias": "headerKey"; "required": false; }; "visible": { "alias": "visible"; "required": false; }; "modal": { "alias": "modal"; "required": false; }; "closable": { "alias": "closable"; "required": false; }; "draggable": { "alias": "draggable"; "required": false; }; "resizable": { "alias": "resizable"; "required": false; }; "style": { "alias": "style"; "required": false; }; "styleClass": { "alias": "styleClass"; "required": false; }; "position": { "alias": "position"; "required": false; }; "appendTo": { "alias": "appendTo"; "required": false; }; "defaultActions": { "alias": "defaultActions"; "required": false; }; "saveLabelKey": { "alias": "saveLabelKey"; "required": false; }; "cancelLabelKey": { "alias": "cancelLabelKey"; "required": false; }; "saveLabel": { "alias": "saveLabel"; "required": false; }; "cancelLabel": { "alias": "cancelLabel"; "required": false; }; "saveIcon": { "alias": "saveIcon"; "required": false; }; "saveDisabled": { "alias": "saveDisabled"; "required": false; }; "saveStyleClass": { "alias": "saveStyleClass"; "required": false; }; "cancelStyleClass": { "alias": "cancelStyleClass"; "required": false; }; }, { "visibleChange": "visibleChange"; "showEvent": "showEvent"; "hideEvent": "hideEvent"; "saveAction": "saveAction"; "cancelAction": "cancelAction"; }, never, ["*"], true, never>;
880
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfDialogComponent, "ef-dialog", never, { "header": { "alias": "header"; "required": false; }; "headerKey": { "alias": "headerKey"; "required": false; }; "subtitle": { "alias": "subtitle"; "required": false; }; "subtitleKey": { "alias": "subtitleKey"; "required": false; }; "visible": { "alias": "visible"; "required": false; }; "modal": { "alias": "modal"; "required": false; }; "closable": { "alias": "closable"; "required": false; }; "draggable": { "alias": "draggable"; "required": false; }; "resizable": { "alias": "resizable"; "required": false; }; "dismissableMask": { "alias": "dismissableMask"; "required": false; }; "closeOnEscape": { "alias": "closeOnEscape"; "required": false; }; "severity": { "alias": "severity"; "required": false; }; "icon": { "alias": "icon"; "required": false; }; "size": { "alias": "size"; "required": false; }; "style": { "alias": "style"; "required": false; }; "styleClass": { "alias": "styleClass"; "required": false; }; "position": { "alias": "position"; "required": false; }; "appendTo": { "alias": "appendTo"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "actions": { "alias": "actions"; "required": false; }; "defaultActions": { "alias": "defaultActions"; "required": false; }; "saveLabelKey": { "alias": "saveLabelKey"; "required": false; }; "cancelLabelKey": { "alias": "cancelLabelKey"; "required": false; }; "saveLabel": { "alias": "saveLabel"; "required": false; }; "cancelLabel": { "alias": "cancelLabel"; "required": false; }; "saveIcon": { "alias": "saveIcon"; "required": false; }; "cancelIcon": { "alias": "cancelIcon"; "required": false; }; "saveDisabled": { "alias": "saveDisabled"; "required": false; }; "saveStyleClass": { "alias": "saveStyleClass"; "required": false; }; "cancelStyleClass": { "alias": "cancelStyleClass"; "required": false; }; "saveSeverity": { "alias": "saveSeverity"; "required": false; }; "cancelSeverity": { "alias": "cancelSeverity"; "required": false; }; }, { "visibleChange": "visibleChange"; "showEvent": "showEvent"; "hideEvent": "hideEvent"; "saveAction": "saveAction"; "cancelAction": "cancelAction"; "actionClick": "actionClick"; }, never, ["*"], true, never>;
626
881
  static ngAcceptInputType_visible: unknown;
627
882
  static ngAcceptInputType_modal: unknown;
628
883
  static ngAcceptInputType_closable: unknown;
629
884
  static ngAcceptInputType_draggable: unknown;
630
885
  static ngAcceptInputType_resizable: unknown;
886
+ static ngAcceptInputType_dismissableMask: unknown;
887
+ static ngAcceptInputType_closeOnEscape: unknown;
631
888
  static ngAcceptInputType_defaultActions: unknown;
632
889
  static ngAcceptInputType_saveDisabled: unknown;
633
890
  }
@@ -646,13 +903,44 @@ declare class EfFieldsetComponent {
646
903
  static ngAcceptInputType_collapsed: unknown;
647
904
  }
648
905
 
906
+ /** PrimeNG severity vocabulary (v1's default). */
907
+ type EfButtonPrimeNGSeverity = 'primary' | 'secondary' | 'success' | 'info' | 'warn' | 'danger' | 'help' | 'contrast';
908
+ /** Comptoir severity vocabulary — maps to `.btn-{name}` rules in
909
+ * `@elasticias/ui/styles/patterns`. `'danger'` renders as
910
+ * `.btn-ghost` with the cancelled-fg tint applied inline. */
911
+ type EfButtonComptoirSeverity = 'primary' | 'tenant' | 'ghost' | 'danger';
912
+ type EfButtonSeverity = EfButtonPrimeNGSeverity | EfButtonComptoirSeverity;
913
+ /**
914
+ * Wrapper button for both v1 (PrimeNG-themed) and V2 (Comptoir-themed)
915
+ * surfaces. `[variant]` picks the rendering path:
916
+ *
917
+ * - `'primeng'` (default) — wraps `<p-button>` with PrimeNG severities
918
+ * (`primary / success / danger / …`). Keeps v1 imports working
919
+ * unchanged.
920
+ *
921
+ * - `'comptoir'` — renders a native `<button class="btn btn-{severity}
922
+ * btn-sm">` against the Comptoir `.btn` pattern. Severities are
923
+ * `'primary' | 'tenant' | 'ghost' | 'danger'`; `'danger'` falls back
924
+ * to `.btn-ghost` with the `--st-cancelled-fg` tint applied inline,
925
+ * matching the prototype's Delete-button styling.
926
+ *
927
+ * Both variants honour `labelKey` (i18n preferred), `icon`,
928
+ * `disabled`, `loading`, `pTooltip` (via `tooltipKey`), and emit the
929
+ * same `clickEvent`.
930
+ */
649
931
  declare class EfButtonComponent {
650
- /** Direct label text (not translated) */
932
+ /** Direct label text (not translated). */
651
933
  label?: string;
652
- /** Translation key — takes priority over label */
934
+ /** Translation key — takes priority over `label`. */
653
935
  labelKey?: string;
654
936
  icon?: string;
655
- severity: 'primary' | 'secondary' | 'success' | 'info' | 'warn' | 'danger' | 'help' | 'contrast';
937
+ /**
938
+ * Which rendering surface to use. Default `'primeng'` keeps v1
939
+ * consumers unchanged. V2 detail/list screens pass `'comptoir'`
940
+ * to opt into the Comptoir `.btn` styling.
941
+ */
942
+ variant: 'primeng' | 'comptoir';
943
+ severity: EfButtonSeverity;
656
944
  size: 'small' | 'large';
657
945
  disabled: boolean;
658
946
  rounded: boolean;
@@ -664,15 +952,33 @@ declare class EfButtonComponent {
664
952
  type: 'button' | 'submit';
665
953
  badge?: string;
666
954
  badgeSeverity?: 'success' | 'info' | 'warn' | 'danger' | 'help' | 'primary' | 'secondary' | 'contrast';
667
- /** Direct tooltip text (not translated) */
955
+ /** Direct tooltip text (not translated). Falls back to `label`
956
+ * when neither `tooltip` nor `tooltipKey` is set. */
668
957
  tooltip?: string;
669
- /** Translation key for tooltip — takes priority over tooltip */
958
+ /** Translation key for tooltip — takes priority over `tooltip`.
959
+ * Falls back to `labelKey` when not provided, so buttons with
960
+ * visible labels still expose them to screen readers / mobile
961
+ * long-press once the label is hidden by responsive CSS. */
670
962
  tooltipKey?: string;
671
963
  tooltipPosition: 'top' | 'bottom' | 'left' | 'right';
964
+ /** Resolved tooltip translation key — `tooltipKey` if provided,
965
+ * otherwise `labelKey`. Lets responsive icon-only buttons reuse
966
+ * their label as the tooltip without restating it at every call
967
+ * site. */
968
+ get effectiveTooltipKey(): string | undefined;
969
+ /** Resolved plain tooltip text — `tooltip` if provided, otherwise
970
+ * `label`. Used only when neither `tooltipKey` nor `labelKey` is
971
+ * set. */
972
+ get effectiveTooltip(): string | undefined;
672
973
  clickEvent: EventEmitter<Event>;
974
+ /** Composed class string for the Comptoir variant —
975
+ * `btn btn-{severity} btn-sm` plus any caller-supplied `styleClass`. */
976
+ get comptoirClass(): string;
977
+ /** Inline danger tint for the `severity="danger"` Comptoir variant. */
978
+ get comptoirInlineColor(): string | null;
673
979
  handleClick(event: Event): void;
674
980
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfButtonComponent, never>;
675
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfButtonComponent, "ef-button", never, { "label": { "alias": "label"; "required": false; }; "labelKey": { "alias": "labelKey"; "required": false; }; "icon": { "alias": "icon"; "required": false; }; "severity": { "alias": "severity"; "required": false; }; "size": { "alias": "size"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "rounded": { "alias": "rounded"; "required": false; }; "outlined": { "alias": "outlined"; "required": false; }; "raised": { "alias": "raised"; "required": false; }; "text": { "alias": "text"; "required": false; }; "loading": { "alias": "loading"; "required": false; }; "styleClass": { "alias": "styleClass"; "required": false; }; "type": { "alias": "type"; "required": false; }; "badge": { "alias": "badge"; "required": false; }; "badgeSeverity": { "alias": "badgeSeverity"; "required": false; }; "tooltip": { "alias": "tooltip"; "required": false; }; "tooltipKey": { "alias": "tooltipKey"; "required": false; }; "tooltipPosition": { "alias": "tooltipPosition"; "required": false; }; }, { "clickEvent": "clickEvent"; }, never, never, true, never>;
981
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfButtonComponent, "ef-button", never, { "label": { "alias": "label"; "required": false; }; "labelKey": { "alias": "labelKey"; "required": false; }; "icon": { "alias": "icon"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "severity": { "alias": "severity"; "required": false; }; "size": { "alias": "size"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "rounded": { "alias": "rounded"; "required": false; }; "outlined": { "alias": "outlined"; "required": false; }; "raised": { "alias": "raised"; "required": false; }; "text": { "alias": "text"; "required": false; }; "loading": { "alias": "loading"; "required": false; }; "styleClass": { "alias": "styleClass"; "required": false; }; "type": { "alias": "type"; "required": false; }; "badge": { "alias": "badge"; "required": false; }; "badgeSeverity": { "alias": "badgeSeverity"; "required": false; }; "tooltip": { "alias": "tooltip"; "required": false; }; "tooltipKey": { "alias": "tooltipKey"; "required": false; }; "tooltipPosition": { "alias": "tooltipPosition"; "required": false; }; }, { "clickEvent": "clickEvent"; }, never, never, true, never>;
676
982
  static ngAcceptInputType_disabled: unknown;
677
983
  static ngAcceptInputType_rounded: unknown;
678
984
  static ngAcceptInputType_outlined: unknown;
@@ -681,17 +987,46 @@ declare class EfButtonComponent {
681
987
  static ngAcceptInputType_loading: unknown;
682
988
  }
683
989
 
990
+ declare class EfButtonGroupComponent {
991
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfButtonGroupComponent, never>;
992
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfButtonGroupComponent, "ef-button-group", never, {}, {}, never, ["*"], true, never>;
993
+ }
994
+
995
+ /**
996
+ * Comptoir form label — renders a `<label>` with the design-system
997
+ * typography (uppercase Manrope 11px / 700 / `--text-mute`,
998
+ * 0.06em letter-spacing) defined under `.ef-label` in
999
+ * `@elasticias/ui/styles/patterns`.
1000
+ *
1001
+ * - `[labelKey]` — translation key (preferred). Falls back to
1002
+ * `[label]` when missing. The `feedback_i18n_keys` memory says
1003
+ * always use `*Key` on shared `ef-*` components.
1004
+ * - `[for]` — DOM id of the input the label labels. Mapped through
1005
+ * `[attr.for]` so an empty value renders no attribute (the input
1006
+ * doesn't end up with a broken `for=""`).
1007
+ * - `[required]` — toggles a red asterisk via
1008
+ * `.ef-label.is-required::after`.
1009
+ * - `[styleClass]` — caller-supplied extra class. Defaults to
1010
+ * `'form-label'` for v1 back-compat. The `.ef-label` Comptoir
1011
+ * class is always applied on top.
1012
+ */
684
1013
  declare class EfLabelComponent {
685
- /** Direct label text (not translated) */
1014
+ /** Direct label text use `labelKey` for i18n. */
686
1015
  label?: string;
687
- /** Translation key — takes priority over label */
1016
+ /** Translation key — preferred. */
688
1017
  labelKey?: string;
689
- /** The for attribute linking to an input id */
1018
+ /** DOM id of the labelled input. Empty / unset → no `for` attr. */
690
1019
  for?: string;
691
- /** CSS classes for the label element */
1020
+ /** Caller-supplied extra class. The Comptoir `.ef-label` class is
1021
+ * always applied; this stacks on top. Defaults to `'form-label'`
1022
+ * for v1 back-compat. */
692
1023
  styleClass: string;
693
- /** Show required indicator */
1024
+ /** Show the required-asterisk modifier. */
694
1025
  required: boolean;
1026
+ /** Composes `'ef-label'` + `styleClass` + the `'is-required'`
1027
+ * modifier into a single class string. Angular's `[class]`
1028
+ * binding replaces (doesn't merge) so we do the joining here. */
1029
+ get resolvedClass(): string;
695
1030
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfLabelComponent, never>;
696
1031
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfLabelComponent, "ef-label", never, { "label": { "alias": "label"; "required": false; }; "labelKey": { "alias": "labelKey"; "required": false; }; "for": { "alias": "for"; "required": false; }; "styleClass": { "alias": "styleClass"; "required": false; }; "required": { "alias": "required"; "required": false; }; }, {}, never, never, true, never>;
697
1032
  static ngAcceptInputType_required: unknown;
@@ -872,7 +1207,7 @@ declare class EfToolbarComponent {
872
1207
  get isSearchState(): boolean;
873
1208
  get isDetailState(): boolean;
874
1209
  get hasReadPermission(): boolean;
875
- get hasWritePermission(): boolean;
1210
+ get hasCreatePermission(): boolean;
876
1211
  get hasEditPermission(): boolean;
877
1212
  get hasDeletePermission(): boolean;
878
1213
  get hasDuplicatePermission(): boolean;
@@ -892,6 +1227,459 @@ declare class EfToolbarComponent {
892
1227
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfToolbarComponent, "ef-toolbar", never, { "context": { "alias": "context"; "required": false; }; "state": { "alias": "state"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "backButton": { "alias": "backButton"; "required": false; }; "deleteButton": { "alias": "deleteButton"; "required": false; }; "duplicateButton": { "alias": "duplicateButton"; "required": false; }; "saveButton": { "alias": "saveButton"; "required": false; }; "exportButton": { "alias": "exportButton"; "required": false; }; "printButton": { "alias": "printButton"; "required": false; }; "addButton": { "alias": "addButton"; "required": false; }; "clearButton": { "alias": "clearButton"; "required": false; }; "searchButton": { "alias": "searchButton"; "required": false; }; "showFreeSearchInput": { "alias": "showFreeSearchInput"; "required": false; }; }, { "back": "back"; "save": "save"; "delete": "delete"; "duplicate": "duplicate"; "print": "print"; "triggerCustoAction": "triggerCustoAction"; "add": "add"; "search": "search"; "clear": "clear"; }, never, ["[toolbarStart]", "[toolbarEnd]"], true, never>;
893
1228
  }
894
1229
 
1230
+ /**
1231
+ * Top-level Comptoir shell.
1232
+ *
1233
+ * Composes the chrome an Elasticias app puts around its `<router-outlet />`.
1234
+ * Apps mount it at their root component:
1235
+ *
1236
+ * ```html
1237
+ * <ef-app-shell>
1238
+ * <router-outlet />
1239
+ * </ef-app-shell>
1240
+ * ```
1241
+ *
1242
+ * The shell reads:
1243
+ * - `EfViewportService` to switch between desktop and mobile branches
1244
+ * - `EfPermissionService` (via the rail / mobile tabs) for visible modules
1245
+ * - `EfActiveModuleService` for the active module accent
1246
+ *
1247
+ * **Desktop / tablet:** rail (64px) + side (240px) + (top + main).
1248
+ * **Mobile:** delegates to `<ef-app-shell-mobile>` — top bar + scrollable
1249
+ * body + 5-tab bottom bar + FAB slot.
1250
+ *
1251
+ * Top-bar slots are forwarded to `<ef-app-top>` on both layouts: project
1252
+ * with `breadcrumb`, `search`, `actions` attributes. The `side-footer`
1253
+ * slot is forwarded to the bottom of `<ef-module-side>` (desktop only —
1254
+ * mobile drops it; surface identity via `[actions]` instead). The `logo`
1255
+ * slot is forwarded to the top of `<ef-module-rail>` (desktop/tablet
1256
+ * only — mobile uses the bottom tab bar instead).
1257
+ */
1258
+ declare class EfAppShellComponent {
1259
+ private readonly vp;
1260
+ /** Show the 3px module-color stripe at the top of the content area. */
1261
+ stripe: boolean;
1262
+ readonly viewport: _angular_core.Signal<_elasticias_core.EfViewport>;
1263
+ readonly isMobile: _angular_core.Signal<boolean>;
1264
+ readonly showSide: _angular_core.Signal<boolean>;
1265
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfAppShellComponent, never>;
1266
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfAppShellComponent, "ef-app-shell", never, { "stripe": { "alias": "stripe"; "required": false; }; }, {}, never, ["[breadcrumb]", "[search]", "[actions]", "[fab]", "*", "[logo]", "[side-header-meta]", "[side-footer]", "[breadcrumb]", "[actions]", "*"], true, never>;
1267
+ }
1268
+
1269
+ /**
1270
+ * Mobile-only shell. Picks up when `ef-app-shell` detects a mobile
1271
+ * viewport. Composition:
1272
+ *
1273
+ * - Top bar (`ef-app-top`) with a menu trigger that opens the module
1274
+ * switcher in a bottom sheet.
1275
+ * - Scrollable content (`ef-app-main`) — projects `<router-outlet />`.
1276
+ * - 5-tab bottom bar — first 5 visible modules from
1277
+ * `EfPermissionService.visibleModules()`. Apps that want a usage-
1278
+ * ranked tab order can replace the registry via `EF_MODULES_TOKEN`.
1279
+ * - FAB slot (`[fab]`) — apps project a `<ef-fab />` here.
1280
+ *
1281
+ * The module switcher (bottom sheet) lists all visible modules so
1282
+ * users can reach modules outside the top-5 tabs.
1283
+ */
1284
+ declare class EfAppShellMobileComponent {
1285
+ private readonly perms;
1286
+ private readonly active;
1287
+ private readonly router;
1288
+ readonly activeId: _angular_core.Signal<_elasticias_core.EfModuleId>;
1289
+ readonly switcherOpen: _angular_core.WritableSignal<boolean>;
1290
+ readonly tabs: _angular_core.Signal<EfModule[]>;
1291
+ readonly allVisible: _angular_core.Signal<readonly EfModule[]>;
1292
+ openSwitcher(): void;
1293
+ onSwitcherChange(open: boolean): void;
1294
+ onSelect(module: EfModule): void;
1295
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfAppShellMobileComponent, never>;
1296
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfAppShellMobileComponent, "ef-app-shell-mobile", never, {}, {}, never, ["[breadcrumb]", "[search]", "[actions]", "*", "[fab]"], true, never>;
1297
+ }
1298
+
1299
+ /**
1300
+ * Content scroll container. Lives at the right of the rail+side
1301
+ * on desktop, full-width on mobile. Sets `data-module` so the
1302
+ * Comptoir SCSS layer's `[data-module="..."]` rules apply the
1303
+ * active module's `--module` accent inside this region.
1304
+ *
1305
+ * Usage:
1306
+ * ```html
1307
+ * <ef-app-main [stripe]="true">
1308
+ * <router-outlet />
1309
+ * </ef-app-main>
1310
+ * ```
1311
+ *
1312
+ * Reads the active module from `EfActiveModuleService` — no per-app
1313
+ * wiring required.
1314
+ */
1315
+ declare class EfAppMainComponent {
1316
+ private readonly activeModule;
1317
+ /** Render the 3px module-color stripe at the top of the content area. */
1318
+ stripe: boolean;
1319
+ /** `data-module` value bound to the host. Null when no module is active. */
1320
+ readonly moduleId: _angular_core.Signal<_elasticias_core.EfModuleId>;
1321
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfAppMainComponent, never>;
1322
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfAppMainComponent, "ef-app-main", never, { "stripe": { "alias": "stripe"; "required": false; }; }, {}, never, ["*"], true, never>;
1323
+ static ngAcceptInputType_stripe: unknown;
1324
+ }
1325
+
1326
+ /**
1327
+ * Top chrome bar for the desktop and mobile shells.
1328
+ *
1329
+ * Layout is three slots: `breadcrumb` (left), `search` (centre), and
1330
+ * `actions` (right). Apps project into `search` and `actions` for global
1331
+ * features (⌘K, notifications bell, profile chip).
1332
+ *
1333
+ * The breadcrumb slot fills automatically from `EfActiveModuleService`:
1334
+ * a leading dot in the active module's accent, the module label, then
1335
+ * the active nav-item label as the leaf. Apps that need a custom
1336
+ * breadcrumb can still project their own content into `[breadcrumb]`.
1337
+ */
1338
+ declare class EfAppTopComponent {
1339
+ private readonly active;
1340
+ readonly module: _angular_core.Signal<_elasticias_core.EfModule>;
1341
+ readonly navItem: _angular_core.Signal<_elasticias_core.EfNavItem>;
1342
+ readonly hasBreadcrumb: _angular_core.Signal<boolean>;
1343
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfAppTopComponent, never>;
1344
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfAppTopComponent, "ef-app-top", never, {}, {}, never, ["[breadcrumb]", "[actions]"], true, never>;
1345
+ }
1346
+
1347
+ /**
1348
+ * Rail row — either a module button (with the metadata needed by the
1349
+ * template) or a divider rendered between two modules whose `group`
1350
+ * differs.
1351
+ */
1352
+ type RailRow = {
1353
+ kind: 'module';
1354
+ module: EfModule;
1355
+ } | {
1356
+ kind: 'divider';
1357
+ };
1358
+ /**
1359
+ * 64px vertical module switcher rendered at the left of `ef-app-shell`
1360
+ * on desktop and tablet. Black (`--ink-active`) background with
1361
+ * white-alpha icons, 44×44px buttons, and a 3px module-color accent
1362
+ * strip on the inside edge of the active button.
1363
+ *
1364
+ * Reads from `EfPermissionService.visibleModules()` and
1365
+ * `EfActiveModuleService.activeModule()`. No per-app wiring needed.
1366
+ *
1367
+ * Apps can supply per-module badge content (e.g., notification counts)
1368
+ * via the `[badges]` input keyed by module id, and project a logo into
1369
+ * the `[logo]` content slot — typical content is the tenant initial,
1370
+ * an avatar, or a small SVG. When a logo is projected, a hairline
1371
+ * divider is rendered between the logo and the first module group.
1372
+ *
1373
+ * Dividers also appear automatically between consecutive modules whose
1374
+ * `EfModule.group` differs (e.g. Sales/operations followed by
1375
+ * Admin/admin).
1376
+ */
1377
+ declare class EfModuleRailComponent {
1378
+ private readonly perms;
1379
+ private readonly active;
1380
+ private readonly router;
1381
+ /** Optional notification badges per module id. */
1382
+ badges: Partial<Record<EfModuleId, string | number>>;
1383
+ readonly visibleModules: _angular_core.Signal<readonly EfModule[]>;
1384
+ readonly activeId: _angular_core.Signal<EfModuleId>;
1385
+ /** Visible modules interleaved with dividers at group boundaries. */
1386
+ readonly rows: _angular_core.Signal<RailRow[]>;
1387
+ readonly hasBadges: _angular_core.Signal<boolean>;
1388
+ onSelect(module: EfModule): void;
1389
+ badgeFor(id: EfModuleId): string | number | undefined;
1390
+ trackRow(index: number, row: RailRow): string;
1391
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfModuleRailComponent, never>;
1392
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfModuleRailComponent, "ef-module-rail", never, { "badges": { "alias": "badges"; "required": false; }; }, {}, never, ["[logo]"], true, never>;
1393
+ }
1394
+
1395
+ /**
1396
+ * 240px module-specific side panel. Renders the active module's
1397
+ * `navSections`, filtered by `EfPermissionService.can()` against
1398
+ * each item's `requiredAction` (defaults to `'read'`).
1399
+ *
1400
+ * When no module is active, or the active module has no sections,
1401
+ * the panel collapses (renders nothing) — apps using `ef-app-shell`
1402
+ * already handle the layout reflow.
1403
+ */
1404
+ declare class EfModuleSideComponent {
1405
+ private readonly active;
1406
+ private readonly perms;
1407
+ readonly module: _angular_core.Signal<_elasticias_core.EfModule>;
1408
+ readonly moduleId: _angular_core.Signal<_elasticias_core.EfModuleId>;
1409
+ readonly visibleSections: _angular_core.Signal<EfNavSection[]>;
1410
+ trackSection(_: number, s: EfNavSection): string;
1411
+ trackItem(_: number, i: EfNavItem): string;
1412
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfModuleSideComponent, never>;
1413
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfModuleSideComponent, "ef-module-side", never, {}, {}, never, ["[side-header-meta]", "*"], true, never>;
1414
+ }
1415
+
1416
+ /**
1417
+ * Slide-up overlay with a grabber. Replaces dropdowns and dialogs on
1418
+ * mobile per the Comptoir plan. Controlled component — apps drive
1419
+ * `[open]` and listen to `(openChange)`.
1420
+ *
1421
+ * Tapping the backdrop or pressing Escape emits `openChange.emit(false)`.
1422
+ *
1423
+ * Usage:
1424
+ * ```html
1425
+ * <ef-bottom-sheet [open]="filtersOpen()" (openChange)="filtersOpen.set($event)">
1426
+ * <ng-template>...filters body...</ng-template>
1427
+ * </ef-bottom-sheet>
1428
+ * ```
1429
+ */
1430
+ declare class EfBottomSheetComponent {
1431
+ open: boolean;
1432
+ dismissOnBackdrop: boolean;
1433
+ openChange: EventEmitter<boolean>;
1434
+ onBackdropClick(): void;
1435
+ onEscape(): void;
1436
+ close(): void;
1437
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfBottomSheetComponent, never>;
1438
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfBottomSheetComponent, "ef-bottom-sheet", never, { "open": { "alias": "open"; "required": false; }; "dismissOnBackdrop": { "alias": "dismissOnBackdrop"; "required": false; }; }, { "openChange": "openChange"; }, never, ["*"], true, never>;
1439
+ static ngAcceptInputType_open: unknown;
1440
+ static ngAcceptInputType_dismissOnBackdrop: unknown;
1441
+ }
1442
+
1443
+ type EfFabPosition = 'bottom-right' | 'bottom-center' | 'bottom-left';
1444
+ /**
1445
+ * Floating action button — mobile primary action surface.
1446
+ *
1447
+ * Lives at the bottom of the mobile viewport, carrying the screen's
1448
+ * principal action (e.g. Nouvelle commande). Reads `--tenant-500`
1449
+ * for its background; foreground is `--tenant-contrast` (defaults
1450
+ * to white). When `label` / `labelKey` is provided, renders as an
1451
+ * extended FAB with the label inline.
1452
+ *
1453
+ * Usage:
1454
+ * ```html
1455
+ * <ef-fab icon="pi pi-plus" labelKey="orders.new" (press)="onNew()" />
1456
+ * ```
1457
+ */
1458
+ declare class EfFabComponent {
1459
+ icon: string;
1460
+ label?: string;
1461
+ /** Translation key — takes priority over `label`. */
1462
+ labelKey?: string;
1463
+ position: EfFabPosition;
1464
+ disabled: boolean;
1465
+ press: EventEmitter<MouseEvent>;
1466
+ get isExtended(): boolean;
1467
+ onClick(e: MouseEvent): void;
1468
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfFabComponent, never>;
1469
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfFabComponent, "ef-fab", never, { "icon": { "alias": "icon"; "required": false; }; "label": { "alias": "label"; "required": false; }; "labelKey": { "alias": "labelKey"; "required": false; }; "position": { "alias": "position"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; }, { "press": "press"; }, never, never, true, never>;
1470
+ static ngAcceptInputType_disabled: unknown;
1471
+ }
1472
+
1473
+ interface EfPillItem<T = string> {
1474
+ value: T;
1475
+ /** Direct label (no translation). */
1476
+ label?: string;
1477
+ /** Translation key — takes priority over label. */
1478
+ labelKey?: string;
1479
+ /** Optional count rendered after a thin separator. */
1480
+ count?: number;
1481
+ }
1482
+ /**
1483
+ * Segmented pill filter. Used for tab-like selectors over a small
1484
+ * set of options — "Toutes · 87 / À approuver · 14 / En retard · 3".
1485
+ *
1486
+ * ```html
1487
+ * <ef-pill-group
1488
+ * [items]="filters"
1489
+ * [(value)]="activeFilter"
1490
+ * (valueChange)="onFilterChange($event)"
1491
+ * />
1492
+ * ```
1493
+ *
1494
+ * The active pill picks up a contrast fill (`--ink-active`); inactive
1495
+ * pills sit on transparent ground with muted text. Heights map to
1496
+ * `--hit` (32px) so it stacks naturally next to compact action buttons.
1497
+ */
1498
+ declare class EfPillGroupComponent<T = string> {
1499
+ items: ReadonlyArray<EfPillItem<T>>;
1500
+ value: T | null;
1501
+ ariaLabel: string;
1502
+ /** Hide counts even when items provide them. */
1503
+ hideCounts: boolean;
1504
+ readonly valueChange: EventEmitter<T>;
1505
+ select(item: EfPillItem<T>): void;
1506
+ trackValue(_: number, item: EfPillItem<T>): T;
1507
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfPillGroupComponent<any>, never>;
1508
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfPillGroupComponent<any>, "ef-pill-group", never, { "items": { "alias": "items"; "required": true; }; "value": { "alias": "value"; "required": false; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; }; "hideCounts": { "alias": "hideCounts"; "required": false; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
1509
+ }
1510
+
1511
+ /** Visual treatment of the tabset. `'pill'` is the default. */
1512
+ type EfTabsVariant = 'pill' | 'underline';
1513
+ /** Known module slugs whose accent (`var(--m-{slug})`) drives the
1514
+ * active indicator and count tint when `variant === 'underline'`. */
1515
+ type EfTabsModule = 'sales' | 'purchase' | 'stock' | 'pos' | 'marketing' | 'store' | 'finance' | 'admin';
1516
+ interface EfTabItem<T = string> {
1517
+ value: T;
1518
+ /** Direct label (not translated). */
1519
+ label?: string;
1520
+ /** Translation key — takes priority over `label`. */
1521
+ labelKey?: string;
1522
+ /** PrimeIcons class string, e.g. `'pi pi-th-large'`. Rendered
1523
+ * before the label, muted when idle. */
1524
+ icon?: string;
1525
+ /** Optional count rendered after the label in a mono badge. */
1526
+ count?: number;
1527
+ /** Show a dirty/unread dot after the label/count. */
1528
+ dirty?: boolean;
1529
+ disabled?: boolean;
1530
+ }
1531
+ /**
1532
+ * Comptoir tabset. Pilots the visible *view* on a detail screen
1533
+ * (Identité / Médias / Variantes / SEO / Activité…). Defaults to the
1534
+ * pill variant — chunky toggle suited for sub-modes (Editor / Preview,
1535
+ * Day / Week) — with `'underline'` available for fiche-wide navigation
1536
+ * where the active tab adopts the module accent.
1537
+ *
1538
+ * Two consumer surfaces, mirroring `<ef-data-card>`:
1539
+ *
1540
+ * 1. **Tablist only** — no panels projected. The component renders
1541
+ * the tab strip and emits `valueChange` ; the consumer owns panel
1542
+ * rendering via `@switch` / `@if`.
1543
+ *
1544
+ * ```html
1545
+ * <ef-tabs [items]="sections" [(value)]="active" />
1546
+ * @switch (active) {
1547
+ * @case ('identity') { <product-identity /> }
1548
+ * @case ('media') { <product-media /> }
1549
+ * }
1550
+ * ```
1551
+ *
1552
+ * 2. **Tabs + projected panels** — `<ng-template efTabPanel="…">`
1553
+ * blocks declare the body of each tab. The component shows the one
1554
+ * whose `efTabPanel` matches the active value.
1555
+ *
1556
+ * ```html
1557
+ * <ef-tabs [items]="sections" [(value)]="active" variant="underline" module="store">
1558
+ * <ng-template efTabPanel="identity"><product-identity /></ng-template>
1559
+ * <ng-template efTabPanel="media"><product-media /></ng-template>
1560
+ * </ef-tabs>
1561
+ * ```
1562
+ *
1563
+ * For filter toggles over result sets (Toutes / Pending / Shipped),
1564
+ * prefer `<ef-pill-group>` — it shares the pill look but stays scoped
1565
+ * to filter selection rather than view switching.
1566
+ */
1567
+ declare class EfTabsComponent<T = string> implements AfterContentInit {
1568
+ items: ReadonlyArray<EfTabItem<T>>;
1569
+ /** Currently-selected tab value. When no `*efTabPanel` templates
1570
+ * are projected, panel rendering is the consumer's job (typically
1571
+ * via `@switch (active)` in the template). */
1572
+ value: T | null;
1573
+ /** Visual variant. `'pill'` (default) is the segmented toggle ;
1574
+ * `'underline'` draws a 2px indicator under the active tab. */
1575
+ variant: EfTabsVariant;
1576
+ /** Forces the module accent on the active indicator + count tint
1577
+ * (only meaningful for `variant='underline'`). When omitted, the
1578
+ * underline inherits `--module` from the closest `[data-module]`
1579
+ * ancestor (and falls back to `--ink-active`). */
1580
+ module?: EfTabsModule;
1581
+ /** Show edge fades when the tablist overflows horizontally. The
1582
+ * list itself always scrolls — tabs are never collapsed into a
1583
+ * menu. */
1584
+ overflow: boolean;
1585
+ /** Hide count badges even when items provide them. */
1586
+ hideCounts: boolean;
1587
+ ariaLabel: string;
1588
+ /** Translation key used as the `aria-label` of the dirty dot.
1589
+ * Defaults to `'common.dirty'` — apps add the matching entry to
1590
+ * `fr.json` / `ar.json`. */
1591
+ dirtyLabelKey: string;
1592
+ readonly valueChange: EventEmitter<T>;
1593
+ private tabEls?;
1594
+ private readonly panels;
1595
+ private readonly panelMap;
1596
+ /** Active panel template for the current `value`, or `null` when
1597
+ * no template matches (incl. tablist-only mode). Read by the
1598
+ * template via `ngTemplateOutlet`. */
1599
+ activePanel(): TemplateRef<unknown> | null;
1600
+ /** `true` when at least one `*efTabPanel` template has been
1601
+ * projected — toggles the panel rendering slot in the template. */
1602
+ hasPanels(): boolean;
1603
+ ngAfterContentInit(): void;
1604
+ select(item: EfTabItem<T>): void;
1605
+ onKeydown(event: KeyboardEvent, index: number): void;
1606
+ trackValue(_: number, item: EfTabItem<T>): T;
1607
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfTabsComponent<any>, never>;
1608
+ 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; }; "dirtyLabelKey": { "alias": "dirtyLabelKey"; "required": false; }; }, { "valueChange": "valueChange"; }, ["panels"], never, true, never>;
1609
+ static ngAcceptInputType_overflow: unknown;
1610
+ static ngAcceptInputType_hideCounts: unknown;
1611
+ }
1612
+
1613
+ /**
1614
+ * Marks an `<ng-template>` as the panel renderer for a tab value on
1615
+ * `<ef-tabs>`. When at least one panel template is projected,
1616
+ * `ef-tabs` renders the matching one below the tablist. When none is
1617
+ * projected, the component stays a tablist only — the consumer owns
1618
+ * panel rendering via `@switch` / `@if` on the active value.
1619
+ *
1620
+ * ```html
1621
+ * <ef-tabs [items]="sections" [(value)]="active">
1622
+ * <ng-template efTabPanel="identity">
1623
+ * <product-identity />
1624
+ * </ng-template>
1625
+ * <ng-template efTabPanel="media">
1626
+ * <product-media />
1627
+ * </ng-template>
1628
+ * </ef-tabs>
1629
+ * ```
1630
+ *
1631
+ * The input value must match one of the `EfTabItem.value` entries the
1632
+ * tabset is driven by.
1633
+ */
1634
+ declare class EfTabPanelDirective {
1635
+ readonly templateRef: TemplateRef<unknown>;
1636
+ value: string;
1637
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfTabPanelDirective, never>;
1638
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<EfTabPanelDirective, "[efTabPanel]", never, { "value": { "alias": "efTabPanel"; "required": true; }; }, {}, never, never, true, never>;
1639
+ }
1640
+
1641
+ /**
1642
+ * Compact identity chip — avatar + name + role — designed to sit at the
1643
+ * bottom of `ef-module-side` (project via the `side-footer` slot of
1644
+ * `ef-app-shell`). On mobile, apps usually surface the same identity in
1645
+ * the top bar `[actions]` slot instead.
1646
+ *
1647
+ * ```html
1648
+ * <ef-app-shell>
1649
+ * <ef-profile-chip
1650
+ * side-footer
1651
+ * [name]="user.name"
1652
+ * [role]="user.roleLabel"
1653
+ * (clickEvent)="openUserMenu()"
1654
+ * />
1655
+ * <router-outlet />
1656
+ * </ef-app-shell>
1657
+ * ```
1658
+ *
1659
+ * The component is intentionally dumb: apps wire their own identity
1660
+ * source. Initials are derived from `name` unless explicitly overridden.
1661
+ */
1662
+ declare class EfProfileChipComponent {
1663
+ /** Direct name text (not translated). */
1664
+ name?: string;
1665
+ /** Direct role text (not translated). */
1666
+ role?: string;
1667
+ /** Translation key for the role line — takes priority over `role`. */
1668
+ roleKey?: string;
1669
+ /** Override the avatar glyph. Defaults to the first letter of `name`. */
1670
+ initial?: string;
1671
+ /** Tenant tone uses `--tenant-500`; neutral uses `--ink-active`. */
1672
+ tone: 'tenant' | 'neutral';
1673
+ /** Render as a non-interactive container (no hover/focus, no click). */
1674
+ readonly: boolean;
1675
+ readonly clickEvent: EventEmitter<Event>;
1676
+ get computedInitial(): string;
1677
+ handleClick(event: Event): void;
1678
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfProfileChipComponent, never>;
1679
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfProfileChipComponent, "ef-profile-chip", never, { "name": { "alias": "name"; "required": false; }; "role": { "alias": "role"; "required": false; }; "roleKey": { "alias": "roleKey"; "required": false; }; "initial": { "alias": "initial"; "required": false; }; "tone": { "alias": "tone"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; }, { "clickEvent": "clickEvent"; }, never, never, true, never>;
1680
+ static ngAcceptInputType_readonly: unknown;
1681
+ }
1682
+
895
1683
  declare class EfBlockUiComponent {
896
1684
  isLoading: boolean;
897
1685
  contentPanel: any;
@@ -914,75 +1702,1193 @@ declare class EfBlockableDivComponent implements BlockableUI {
914
1702
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfBlockableDivComponent, "ef-blockable-div", never, { "style": { "alias": "style"; "required": false; }; "cssClass": { "alias": "cssClass"; "required": false; }; }, {}, never, ["*"], true, never>;
915
1703
  }
916
1704
 
917
- declare class EfInputTextComponent implements ControlValueAccessor {
1705
+ /**
1706
+ * Live-indicator dot with a soft breath animation.
1707
+ *
1708
+ * Used for "Live", presence indicators, anything that says "this is
1709
+ * happening right now." Defaults to the Comptoir `--st-delivered-fg`
1710
+ * green; pass `[severity]="'warn' | 'danger' | 'mute'"` for amber, red,
1711
+ * or grey variants.
1712
+ *
1713
+ * ```html
1714
+ * <ef-pulse labelKey="status.live" />
1715
+ * <ef-pulse label="Offline" severity="mute" />
1716
+ * ```
1717
+ */
1718
+ declare class EfPulseComponent {
1719
+ /** Direct label text (no translation). */
918
1720
  label?: string;
1721
+ /** Translation key — takes priority over label. */
919
1722
  labelKey?: string;
920
- inputId?: string;
921
- name?: string;
922
- placeholder?: string;
923
- placeholderKey?: string;
924
- size: 'small' | 'large';
925
- type: string;
926
- maxlength?: number;
927
- readonly: boolean;
928
- required: boolean;
929
- fluid: boolean;
930
- inline: boolean;
931
- autocomplete?: string;
932
- get isInline(): boolean;
933
- valueChangeEvent: EventEmitter<string>;
934
- value: string;
935
- disabled: boolean;
936
- private onChange;
937
- private onTouched;
938
- readonly ngControl: NgControl;
939
- private readonly translateService;
1723
+ /** Visual tone of the dot. */
1724
+ severity: 'success' | 'warn' | 'danger' | 'mute';
1725
+ /** Set false to freeze the breath animation. */
1726
+ active: boolean;
1727
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfPulseComponent, never>;
1728
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfPulseComponent, "ef-pulse", never, { "label": { "alias": "label"; "required": false; }; "labelKey": { "alias": "labelKey"; "required": false; }; "severity": { "alias": "severity"; "required": false; }; "active": { "alias": "active"; "required": false; }; }, {}, never, never, true, never>;
1729
+ }
1730
+
1731
+ /**
1732
+ * Reusable PDF preview overlay — an `ef-dialog` hosting the browser's native
1733
+ * PDF viewer (an `<iframe>` fed a sanitized object URL) plus an optional action
1734
+ * bar (WhatsApp share · download · open in new tab · print · close).
1735
+ *
1736
+ * Self-contained: pass a `Blob` and a `fileName`, toggle which actions show.
1737
+ * The object URL is created/revoked automatically with the blob's lifetime.
1738
+ *
1739
+ * @example
1740
+ * <ef-pdf-preview
1741
+ * [(visible)]="pdfVisible"
1742
+ * [blob]="pdfBlob()"
1743
+ * [fileName]="pdfName()"
1744
+ * [showWhatsappShare]="true"
1745
+ * [whatsappPhone]="customerPhone()" />
1746
+ */
1747
+ declare class EfPdfPreviewComponent {
1748
+ private readonly sanitizer;
1749
+ /** The PDF blob to preview. Setting it (re)creates the object URL. */
1750
+ blob: _angular_core.InputSignal<Blob>;
1751
+ /** File name shown in the header + used for download / share. */
1752
+ fileName: _angular_core.InputSignal<string>;
1753
+ /** Two-way dialog visibility. */
1754
+ visible: _angular_core.ModelSignal<boolean>;
1755
+ /** Header title translation key. */
1756
+ titleKey: _angular_core.InputSignal<string>;
1757
+ showDownload: _angular_core.InputSignalWithTransform<boolean, unknown>;
1758
+ showPrint: _angular_core.InputSignalWithTransform<boolean, unknown>;
1759
+ showOpenInNewTab: _angular_core.InputSignalWithTransform<boolean, unknown>;
1760
+ /** Show the WhatsApp share button (Web Share API → wa.me fallback). */
1761
+ showWhatsappShare: _angular_core.InputSignalWithTransform<boolean, unknown>;
1762
+ /** Optional recipient phone for the wa.me fallback (digits / +). */
1763
+ whatsappPhone: _angular_core.InputSignal<string>;
1764
+ private readonly objectUrl;
1765
+ private currentUrl;
1766
+ readonly safeUrl: _angular_core.Signal<SafeResourceUrl>;
1767
+ /** Footer buttons, rendered by `ef-dialog`'s `[actions]`. `dismiss:false`
1768
+ * keeps the preview open after download/print/share/open. */
1769
+ readonly actions: _angular_core.Signal<EfDialogAction[]>;
940
1770
  constructor();
941
- get effectivePlaceholder(): string;
942
- get effectiveId(): string;
943
- get showRequired(): boolean;
944
- get serverErrors(): string[] | null;
945
- get isInvalid(): boolean;
946
- writeValue(value: string): void;
947
- registerOnChange(fn: (value: string) => void): void;
948
- registerOnTouched(fn: () => void): void;
949
- setDisabledState(isDisabled: boolean): void;
950
- handleInput(value: string): void;
951
- handleBlur(): void;
952
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfInputTextComponent, never>;
953
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfInputTextComponent, "ef-input-text", never, { "label": { "alias": "label"; "required": false; }; "labelKey": { "alias": "labelKey"; "required": false; }; "inputId": { "alias": "inputId"; "required": false; }; "name": { "alias": "name"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "placeholderKey": { "alias": "placeholderKey"; "required": false; }; "size": { "alias": "size"; "required": false; }; "type": { "alias": "type"; "required": false; }; "maxlength": { "alias": "maxlength"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; "required": { "alias": "required"; "required": false; }; "fluid": { "alias": "fluid"; "required": false; }; "inline": { "alias": "inline"; "required": false; }; "autocomplete": { "alias": "autocomplete"; "required": false; }; }, { "valueChangeEvent": "valueChangeEvent"; }, never, never, true, never>;
954
- static ngAcceptInputType_readonly: unknown;
955
- static ngAcceptInputType_required: unknown;
956
- static ngAcceptInputType_fluid: unknown;
957
- static ngAcceptInputType_inline: unknown;
1771
+ private revoke;
1772
+ close(): void;
1773
+ download(): void;
1774
+ openInNewTab(): void;
1775
+ print(): void;
1776
+ shareWhatsapp(): void;
1777
+ private openWhatsappWeb;
1778
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfPdfPreviewComponent, never>;
1779
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfPdfPreviewComponent, "ef-pdf-preview", never, { "blob": { "alias": "blob"; "required": false; "isSignal": true; }; "fileName": { "alias": "fileName"; "required": false; "isSignal": true; }; "visible": { "alias": "visible"; "required": false; "isSignal": true; }; "titleKey": { "alias": "titleKey"; "required": false; "isSignal": true; }; "showDownload": { "alias": "showDownload"; "required": false; "isSignal": true; }; "showPrint": { "alias": "showPrint"; "required": false; "isSignal": true; }; "showOpenInNewTab": { "alias": "showOpenInNewTab"; "required": false; "isSignal": true; }; "showWhatsappShare": { "alias": "showWhatsappShare"; "required": false; "isSignal": true; }; "whatsappPhone": { "alias": "whatsappPhone"; "required": false; "isSignal": true; }; }, { "visible": "visibleChange"; }, never, never, true, never>;
958
1780
  }
959
1781
 
960
- declare class EfSelectComponent implements ControlValueAccessor, OnInit, OnChanges {
1782
+ /**
1783
+ * Comptoir status spectrum — the canonical set of chip tones every
1784
+ * status reference can map to via `metadata.color`.
1785
+ *
1786
+ * Tenants customize their status palettes by writing one of these
1787
+ * values into `reference_data.metadata.color` for each entry. The
1788
+ * elastic-portal admin (when ready) picks from this same enum, so
1789
+ * the framework stays tenant-agnostic and the data layer is the
1790
+ * only knob that needs to change per tenant.
1791
+ */
1792
+ type EfStatusColor = 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled' | 'refunded' | 'onhold' | 'tenant' | 'module' | 'neutral';
1793
+ /**
1794
+ * Shape `ef-status-chip` expects when reading from a reference
1795
+ * dictionary. Mirrors the `reference_data` MongoDB collection in
1796
+ * ElasticERP — apps can pass any object with these fields.
1797
+ */
1798
+ interface EfStatusReferenceItem {
1799
+ code: string;
961
1800
  label?: string;
962
1801
  labelKey?: string;
963
- inputId?: string;
964
- name?: string;
965
- options: any[];
966
- optionLabel: string;
967
- optionValue?: string;
968
- placeholder: string;
969
- placeholderKey?: string;
970
- size: 'small' | 'large';
971
- required: boolean;
972
- fluid: boolean;
973
- inline: boolean;
974
- filter: boolean;
975
- showClear: boolean;
976
- disabled: boolean;
977
- appendTo?: string;
978
- /** Filter options by matching key-value pairs */
1802
+ metadata?: {
1803
+ color?: EfStatusColor | string;
1804
+ icon?: string;
1805
+ [key: string]: unknown;
1806
+ };
1807
+ }
1808
+ /** Canonical tone list — used for membership checks by the resolver. */
1809
+ declare const EF_STATUS_COLORS: ReadonlyArray<EfStatusColor>;
1810
+ /**
1811
+ * Generic color-name aliases → Comptoir tones. Seeded `reference_data`
1812
+ * commonly stores plain color names in `metadata.color` (e.g.
1813
+ * `sales_order_status`: Draft=gray, Completed=green, Cancelled=red).
1814
+ * Resolving them here keeps chips AND status-keyed charts on the same
1815
+ * `--st-*` tokens without migrating tenant data.
1816
+ */
1817
+ declare const EF_STATUS_COLOR_ALIASES: Readonly<Record<string, EfStatusColor>>;
1818
+ /**
1819
+ * Resolve a `metadata.color` value — a canonical tone OR a generic
1820
+ * color-name alias — to its Comptoir tone. `undefined` when unknown,
1821
+ * so callers can chain fallbacks (e.g. the status code itself, then
1822
+ * `'neutral'`). Case-insensitive.
1823
+ */
1824
+ declare function resolveEfStatusColor(value: string | null | undefined): EfStatusColor | undefined;
1825
+
1826
+ /**
1827
+ * Comptoir status chip — single source of truth for any kind of
1828
+ * status / pill across the app.
1829
+ *
1830
+ * Resolves the chip's tone + label from the `reference_data`
1831
+ * collection so each tenant can re-skin its status palette without
1832
+ * touching consumer code:
1833
+ *
1834
+ * 1. Look up `referenceKey` in `SCREEN_REF_DATA_SERVICE`.
1835
+ * 2. Find the entry whose `code` matches the `code` input.
1836
+ * 3. Use `metadata.color` for the chip class and `label` /
1837
+ * `labelKey` for the text.
1838
+ * 4. Fall back to the `code` itself (lowercased) when no reference
1839
+ * data is registered — this lets screens render meaningfully even
1840
+ * before the real `ReferenceDataService` is wired.
1841
+ *
1842
+ * ```html
1843
+ * <ef-status-chip
1844
+ * referenceKey="sales_order_status"
1845
+ * [code]="row.status"
1846
+ * />
1847
+ * ```
1848
+ *
1849
+ * Override knobs (escape hatches):
1850
+ * - `[color]` forces a specific Comptoir tone, ignoring metadata.
1851
+ * - `[label]` / `[labelKey]` force the text, ignoring reference data.
1852
+ * - `[hideDot]` removes the leading ● glyph.
1853
+ * - `[showCode]` displays the raw code (useful for admin screens).
1854
+ */
1855
+ declare class EfStatusChipComponent {
1856
+ private readonly refDataService;
1857
+ /** Reference key (e.g. `'sales_order_status'`). */
1858
+ readonly referenceKey: _angular_core.InputSignal<string>;
1859
+ /** Entry code to look up (e.g. `'Pending'`). */
1860
+ readonly code: _angular_core.InputSignal<string>;
1861
+ /** Force a specific Comptoir tone (overrides metadata.color). */
1862
+ readonly color: _angular_core.InputSignal<"" | EfStatusColor>;
1863
+ /** Force the displayed label (skips reference lookup). */
1864
+ readonly label: _angular_core.InputSignal<string>;
1865
+ /** Translation key — preferred when forcing a label. */
1866
+ readonly labelKey: _angular_core.InputSignal<string>;
1867
+ /** Hide the leading ● dot. */
1868
+ readonly hideDot: _angular_core.InputSignalWithTransform<boolean, unknown>;
1869
+ /** Render the raw `code` after the label (admin-screen helper). */
1870
+ readonly showCode: _angular_core.InputSignalWithTransform<boolean, unknown>;
1871
+ /** The reference entry matching `code`, or `undefined`. */
1872
+ private readonly entry;
1873
+ /** Chip tone — explicit override → metadata (canonical tone or
1874
+ * generic color-name alias, e.g. `green` → `delivered`) → code
1875
+ * fallback → `neutral`. */
1876
+ readonly resolvedColor: _angular_core.Signal<EfStatusColor>;
1877
+ /** Resolved label — labelKey input → label input → reference label → code. */
1878
+ readonly resolvedLabel: _angular_core.Signal<string>;
1879
+ /** Translation key returned for the template's translate pipe. */
1880
+ readonly resolvedLabelKey: _angular_core.Signal<string>;
1881
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfStatusChipComponent, never>;
1882
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfStatusChipComponent, "ef-status-chip", never, { "referenceKey": { "alias": "referenceKey"; "required": false; "isSignal": true; }; "code": { "alias": "code"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "hideDot": { "alias": "hideDot"; "required": false; "isSignal": true; }; "showCode": { "alias": "showCode"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1883
+ }
1884
+
1885
+ /**
1886
+ * Available anchor positions for the toast region.
1887
+ */
1888
+ type EfToastRegionPosition = 'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center';
1889
+ /**
1890
+ * Comptoir toast region — V2 successor to PrimeNG `<p-toast>`.
1891
+ *
1892
+ * Mounts once at the app root (or any layout shell) and renders the
1893
+ * live `EfToastService.toasts` queue. Auto-dismiss timers + manual
1894
+ * close button + optional inline action buttons. Severity styling
1895
+ * comes from the `.toast.<severity>` rules in `_patterns.scss`,
1896
+ * mapped 1:1 to the Comptoir status spectrum.
1897
+ *
1898
+ * ```html
1899
+ * <!-- App root -->
1900
+ * <ef-toast-region position="bottom-right" />
1901
+ * ```
1902
+ */
1903
+ declare class EfToastRegionComponent {
1904
+ private readonly toastService;
1905
+ private readonly destroyRef;
1906
+ /** Anchor position. Default: bottom-right. */
1907
+ readonly position: _angular_core.InputSignal<EfToastRegionPosition>;
1908
+ /** Cap the visible stack — older toasts get auto-dismissed. */
1909
+ readonly maxStack: _angular_core.InputSignal<number>;
1910
+ /** Hide the bottom progress bar across all toasts. */
1911
+ readonly hideProgress: _angular_core.InputSignalWithTransform<boolean, unknown>;
1912
+ /** Toasts currently mid-exit-animation (kept around for the
1913
+ * duration of the CSS transition before being removed from DOM). */
1914
+ readonly exiting: _angular_core.WritableSignal<ReadonlySet<number>>;
1915
+ /** Live queue from the service — direct read so the template
1916
+ * re-renders when a toast is pushed or dismissed. */
1917
+ readonly toasts: _angular_core.WritableSignal<readonly EfToast[]>;
1918
+ /** Per-toast lifespan timers (manual setTimeout — robust against
1919
+ * Angular zoneless mode and visible across HMR reloads). */
1920
+ private readonly timers;
1921
+ constructor();
1922
+ severityClass(t: EfToast): string;
1923
+ iconClass(t: EfToast): string;
1924
+ progressStyle(t: EfToast): {
1925
+ [k: string]: string;
1926
+ } | null;
1927
+ onClose(t: EfToast): void;
1928
+ onActionClick(t: EfToast, action: EfToastAction): void;
1929
+ trackById: (_: number, t: EfToast) => number;
1930
+ private syncTimers;
1931
+ private enforceMaxStack;
1932
+ /** Mark the toast as exiting → wait for the CSS animation → tell
1933
+ * the service to remove it. Keeps the DOM around for the
1934
+ * out-animation duration. */
1935
+ private beginExit;
1936
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfToastRegionComponent, never>;
1937
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfToastRegionComponent, "ef-toast-region", never, { "position": { "alias": "position"; "required": false; "isSignal": true; }; "maxStack": { "alias": "maxStack"; "required": false; "isSignal": true; }; "hideProgress": { "alias": "hideProgress"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1938
+ }
1939
+
1940
+ /** Shape of the placeholder rendered while a widget's data is in flight. */
1941
+ type EfSkeletonVariant = 'text' | 'kpi' | 'chart' | 'table';
1942
+ /**
1943
+ * Comptoir loading skeleton — the one reusable "waiting for results"
1944
+ * placeholder for async card/widget bodies (dashboards, reports,
1945
+ * detail sidebars). Pure CSS shimmer on the design-system tokens;
1946
+ * decorative only (`aria-hidden`), so hosts flag busyness themselves
1947
+ * (`ef-card` sets `aria-busy` on its body when `[loading]` is true).
1948
+ *
1949
+ * Prefer injecting it through `ef-card`'s `[loading]` + `skeleton`
1950
+ * inputs — the card swaps its body for the skeleton with a single
1951
+ * binding. Use the component directly only for non-card shells
1952
+ * (e.g. a KPI tile row):
1953
+ *
1954
+ * ```html
1955
+ * <ef-card titleKey="…" [loading]="state() === 'loading'" skeleton="chart">…</ef-card>
1956
+ * <ef-skeleton class="kpi" variant="kpi" />
1957
+ * ```
1958
+ *
1959
+ * Variants: `text` (default, `rows` lines), `kpi` (label / value /
1960
+ * delta bars), `chart` (one block, `height` px), `table` (header bar
1961
+ * + `rows` row bars).
1962
+ */
1963
+ declare class EfSkeletonComponent {
1964
+ /** Placeholder shape. */
1965
+ readonly variant: _angular_core.InputSignal<EfSkeletonVariant>;
1966
+ /** Line/row count for the `text` and `table` variants. */
1967
+ readonly rows: _angular_core.InputSignal<number>;
1968
+ /** Block height (px) for the `chart` variant. */
1969
+ readonly height: _angular_core.InputSignal<number>;
1970
+ readonly lines: _angular_core.Signal<number[]>;
1971
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfSkeletonComponent, never>;
1972
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfSkeletonComponent, "ef-skeleton", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "rows": { "alias": "rows"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1973
+ }
1974
+
1975
+ /**
1976
+ * Comptoir card primitive — `.card` shell with optional `.card-head`,
1977
+ * `.card-body`, `.card-foot`. Phase 7's most reused container.
1978
+ *
1979
+ * Three projection slots:
1980
+ * `head-extra` — projected on the right of the head row, after the
1981
+ * built-in title/meta. Use for a status chip,
1982
+ * action button, kbd hint, etc.
1983
+ * default — body content (replaced by the reusable
1984
+ * `ef-skeleton` while `[loading]` is true).
1985
+ * `foot` — bottom strip with rule-soft separator + paper bg.
1986
+ *
1987
+ * ```html
1988
+ * <ef-card titleKey="orders_status" [tone]="'module'">
1989
+ * <ng-container head-extra>
1990
+ * <ef-status-chip referenceKey="sales_order_status" [code]="status" />
1991
+ * </ng-container>
1992
+ * <div>… body content …</div>
1993
+ * <div foot>
1994
+ * <span class="small text-mute">Modifié il y a 2 min</span>
1995
+ * </div>
1996
+ * </ef-card>
1997
+ * ```
1998
+ */
1999
+ declare class EfCardComponent {
2000
+ /** Translation key for the head title — preferred. */
2001
+ readonly titleKey: _angular_core.InputSignal<string>;
2002
+ /** Direct title fallback when `titleKey` is empty. */
2003
+ readonly title: _angular_core.InputSignal<string>;
2004
+ /** Translation key for the head's small meta line. */
2005
+ readonly metaKey: _angular_core.InputSignal<string>;
2006
+ readonly meta: _angular_core.InputSignal<string>;
2007
+ /** Module-tinted head — uses `var(--module)` for the head bg. */
2008
+ readonly tone: _angular_core.InputSignal<"default" | "module">;
2009
+ /** Force the head to render even when no title/meta (e.g. when
2010
+ * using only the head-extra slot). Defaults to auto-detect. */
2011
+ readonly forceHead: _angular_core.InputSignalWithTransform<boolean, unknown>;
2012
+ /** Render the foot strip — set when projecting `[foot]` content. */
2013
+ readonly hasFoot: _angular_core.InputSignalWithTransform<boolean, unknown>;
2014
+ /** When true, the head shows a chevron toggle and the body/foot collapse. */
2015
+ readonly collapsible: _angular_core.InputSignalWithTransform<boolean, unknown>;
2016
+ /** Let overlay content (custom dropdowns/menus rendered inside the body)
2017
+ * escape the card's `overflow: hidden`. PrimeNG overlays should prefer
2018
+ * `appendTo="body"`; use this for non-PrimeNG absolute-positioned menus. */
2019
+ readonly overflowVisible: _angular_core.InputSignalWithTransform<boolean, unknown>;
2020
+ /** While true, the body renders the reusable `ef-skeleton` instead of
2021
+ * the projected content (and flags `aria-busy`). Bind each card's own
2022
+ * async state so widgets shimmer independently:
2023
+ * `[loading]="state().series === 'loading'"`. */
2024
+ readonly loading: _angular_core.InputSignalWithTransform<boolean, unknown>;
2025
+ /** Skeleton shape shown while `loading` — match the body content. */
2026
+ readonly skeleton: _angular_core.InputSignal<EfSkeletonVariant>;
2027
+ /** Line/row count for the `text` / `table` skeleton variants. */
2028
+ readonly skeletonRows: _angular_core.InputSignal<number>;
2029
+ /** Block height (px) for the `chart` skeleton variant. */
2030
+ readonly skeletonHeight: _angular_core.InputSignal<number>;
2031
+ /** Collapsed state (two-way). Bind `[collapsed]="true"` to start collapsed. */
2032
+ readonly collapsed: _angular_core.ModelSignal<boolean>;
2033
+ readonly showHead: _angular_core.Signal<boolean>;
2034
+ readonly showFoot: _angular_core.Signal<boolean>;
2035
+ /** Toggle collapsed state (no-op when not collapsible). */
2036
+ toggle(): void;
2037
+ /** Clicking anywhere on a collapsible head (except the actions area, which
2038
+ * stops propagation) toggles the card. */
2039
+ onHeadClick(): void;
2040
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfCardComponent, never>;
2041
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfCardComponent, "ef-card", never, { "titleKey": { "alias": "titleKey"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "metaKey": { "alias": "metaKey"; "required": false; "isSignal": true; }; "meta": { "alias": "meta"; "required": false; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "forceHead": { "alias": "forceHead"; "required": false; "isSignal": true; }; "hasFoot": { "alias": "hasFoot"; "required": false; "isSignal": true; }; "collapsible": { "alias": "collapsible"; "required": false; "isSignal": true; }; "overflowVisible": { "alias": "overflowVisible"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "skeleton": { "alias": "skeleton"; "required": false; "isSignal": true; }; "skeletonRows": { "alias": "skeletonRows"; "required": false; "isSignal": true; }; "skeletonHeight": { "alias": "skeletonHeight"; "required": false; "isSignal": true; }; "collapsed": { "alias": "collapsed"; "required": false; "isSignal": true; }; }, { "collapsed": "collapsedChange"; }, never, ["[head-extra]", "*", "[foot]"], true, never>;
2042
+ }
2043
+
2044
+ /**
2045
+ * 12-column form-grid primitive. Children use `.col-2/3/4/6/8/9/12`
2046
+ * to claim spans; on viewports < 720px every span collapses to 12.
2047
+ *
2048
+ * ```html
2049
+ * <ef-form-grid>
2050
+ * <div class="col-6 ef-field">
2051
+ * <label>Client</label>
2052
+ * <ef-select … />
2053
+ * </div>
2054
+ * <div class="col-3 ef-field">
2055
+ * <label>Date</label>
2056
+ * <ef-datepicker … />
2057
+ * </div>
2058
+ * <div class="col-3 ef-field">
2059
+ * <label>Total</label>
2060
+ * <span class="read strong">12 350,00 MAD</span>
2061
+ * </div>
2062
+ * </ef-form-grid>
2063
+ * ```
2064
+ */
2065
+ declare class EfFormGridComponent {
2066
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfFormGridComponent, never>;
2067
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfFormGridComponent, "ef-form-grid", never, {}, {}, never, ["*"], true, never>;
2068
+ }
2069
+
2070
+ /**
2071
+ * Sticky detail-screen toolbar. Two slots and an optional declarative
2072
+ * action API:
2073
+ *
2074
+ * - default — entity identity (ref / name / status chip / meta).
2075
+ * - `[customActions]` input — declarative array of EfDetailToolbarAction;
2076
+ * rendered at the start of the right group,
2077
+ * before the standard actions.
2078
+ * - `[actions]` slot — full-template escape hatch (rendered after
2079
+ * `[customActions]`, before the separator).
2080
+ * - Standard actions — Print, Duplicate, Delete, Save (in that order),
2081
+ * shown via `[showPrintAction]` / `[showDuplicateAction]`
2082
+ * / `[showDeleteAction]` / `[showSaveAction]` and
2083
+ * gated by `[context]`'s permissions.
2084
+ *
2085
+ * Layout: `…identity (left) | …customActions [actions] | print duplicate delete save…`
2086
+ *
2087
+ * ```html
2088
+ * <ef-detail-toolbar
2089
+ * [customActions]="screenActions()"
2090
+ * [context]="context"
2091
+ * [showPrintAction]="editionState()"
2092
+ * [showDuplicateAction]="editionState()"
2093
+ * [showDeleteAction]="editionState()"
2094
+ * [saving]="loading()"
2095
+ * [saveLabelKey]="editionState() ? 'common_save' : 'common_create'"
2096
+ * (print)="print()"
2097
+ * (duplicate)="duplicate()"
2098
+ * (delete)="delete()"
2099
+ * (save)="save()"
2100
+ * >
2101
+ * <span class="ref">{{ ref() }}</span>
2102
+ * <ef-status-chip referenceKey="…" [code]="status()" />
2103
+ * </ef-detail-toolbar>
2104
+ * ```
2105
+ *
2106
+ * Custom actions can declare `permission` and `visible` like
2107
+ * `EfRowAction`. With `[context]` bound, the toolbar filters them
2108
+ * against `ScreenContext.isGranted()` automatically.
2109
+ */
2110
+ declare class EfDetailToolbarComponent {
2111
+ /** Custom screen-specific actions, rendered before the standard
2112
+ * set in the right group. Each one gets the same permission /
2113
+ * visibility filtering used by EfRowAction. */
2114
+ readonly customActions: _angular_core.InputSignal<readonly EfDetailToolbarAction[]>;
2115
+ /** Optional ScreenContext — when present, custom actions with a
2116
+ * `permission` field and standard actions Print / Duplicate /
2117
+ * Delete are filtered against `context.isGranted(...)`. */
2118
+ readonly context: _angular_core.InputSignal<ScreenContext>;
2119
+ /** Render the standard Print action (left of the standard group). */
2120
+ readonly showPrintAction: _angular_core.InputSignalWithTransform<boolean, unknown>;
2121
+ /** Render the standard Duplicate action. */
2122
+ readonly showDuplicateAction: _angular_core.InputSignalWithTransform<boolean, unknown>;
2123
+ /** Render the standard Delete action (danger-tinted). */
2124
+ readonly showDeleteAction: _angular_core.InputSignalWithTransform<boolean, unknown>;
2125
+ /** Render the standard Save action (rightmost, tenant-styled). */
2126
+ readonly showSaveAction: _angular_core.InputSignalWithTransform<boolean, unknown>;
2127
+ /** Disable Save while a save round-trip is in flight. */
2128
+ readonly saving: _angular_core.InputSignalWithTransform<boolean, unknown>;
2129
+ /** i18n key for the Save button label. Screens typically pass
2130
+ * `'common_create'` in create mode and `'common_save'` in edit. */
2131
+ readonly saveLabelKey: _angular_core.InputSignal<string>;
2132
+ /** Fired when a `customActions` button is clicked. The toast
2133
+ * service or screen handler should consume `id`. The `command`
2134
+ * callback on the action is also invoked. */
2135
+ readonly customAction: _angular_core.OutputEmitterRef<EfDetailToolbarAction>;
2136
+ readonly print: _angular_core.OutputEmitterRef<void>;
2137
+ readonly duplicate: _angular_core.OutputEmitterRef<void>;
2138
+ readonly delete: _angular_core.OutputEmitterRef<void>;
2139
+ readonly save: _angular_core.OutputEmitterRef<void>;
2140
+ /** Custom actions after permission + `visible` filtering. */
2141
+ readonly visibleCustomActions: _angular_core.Signal<readonly EfDetailToolbarAction[]>;
2142
+ readonly canPrint: _angular_core.Signal<boolean>;
2143
+ readonly canDuplicate: _angular_core.Signal<boolean>;
2144
+ readonly canDelete: _angular_core.Signal<boolean>;
2145
+ readonly canSave: _angular_core.Signal<boolean>;
2146
+ readonly hasStandardActions: _angular_core.Signal<boolean>;
2147
+ readonly hasCustomActions: _angular_core.Signal<boolean>;
2148
+ /** Whether the `|` separator should render — both groups present. */
2149
+ readonly showSeparator: _angular_core.Signal<boolean>;
2150
+ onCustomClick(action: EfDetailToolbarAction): void;
2151
+ private isCustomVisible;
2152
+ /** Standard action visibility: `show*Action` flag AND permission
2153
+ * granted by `context` (or no context bound). */
2154
+ private checkStandard;
2155
+ /** Stable trackBy across renders. */
2156
+ trackByAction: (i: number, a: EfDetailToolbarAction) => unknown;
2157
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfDetailToolbarComponent, never>;
2158
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfDetailToolbarComponent, "ef-detail-toolbar", never, { "customActions": { "alias": "customActions"; "required": false; "isSignal": true; }; "context": { "alias": "context"; "required": false; "isSignal": true; }; "showPrintAction": { "alias": "showPrintAction"; "required": false; "isSignal": true; }; "showDuplicateAction": { "alias": "showDuplicateAction"; "required": false; "isSignal": true; }; "showDeleteAction": { "alias": "showDeleteAction"; "required": false; "isSignal": true; }; "showSaveAction": { "alias": "showSaveAction"; "required": false; "isSignal": true; }; "saving": { "alias": "saving"; "required": false; "isSignal": true; }; "saveLabelKey": { "alias": "saveLabelKey"; "required": false; "isSignal": true; }; }, { "customAction": "customAction"; "print": "print"; "duplicate": "duplicate"; "delete": "delete"; "save": "save"; }, never, ["*", "[actions]"], true, never>;
2159
+ }
2160
+
2161
+ /**
2162
+ * Page-level toolbar for list / search screens. Mirrors the
2163
+ * {@link EfDetailToolbarComponent} concept — two slots and a
2164
+ * declarative action API — but ships with the search-screen
2165
+ * default actions instead of the detail-screen ones.
2166
+ *
2167
+ * - default — page identity (title / meta / status pills).
2168
+ * - `[customActions]` input — declarative array of
2169
+ * EfSearchToolbarAction; rendered at the
2170
+ * start of the right group, before the
2171
+ * standard actions.
2172
+ * - `[actions]` slot — full-template escape hatch (rendered after
2173
+ * `[customActions]`, before the separator).
2174
+ * - Standard actions — Export, Add (in that order), shown via
2175
+ * `[showExportAction]` / `[showAddAction]` and
2176
+ * gated by `[context]`'s permissions.
2177
+ *
2178
+ * Search / Clear deliberately do **not** live here — those belong
2179
+ * to `ef-smart-bar` (its built-in `(search)` / `(clear)` outputs).
2180
+ * This toolbar owns the page-level actions, not the filter actions.
2181
+ *
2182
+ * Layout: `…title (left) | …customActions [actions] | export add`
2183
+ *
2184
+ * ```html
2185
+ * <ef-search-toolbar
2186
+ * [context]="context"
2187
+ * [customActions]="screenActions()"
2188
+ * showExportAction
2189
+ * (export)="exportItems()"
2190
+ * (add)="add()"
2191
+ * >
2192
+ * <div class="title-meta">
2193
+ * <h1>{{ 'catalog_products_title' | translate }}</h1>
2194
+ * <p class="meta">{{ totalCount() }} items</p>
2195
+ * </div>
2196
+ * </ef-search-toolbar>
2197
+ * ```
2198
+ *
2199
+ * Custom actions can declare `permission` and `visible` like
2200
+ * `EfRowAction`. With `[context]` bound, the toolbar filters them
2201
+ * against `ScreenContext.isGranted()` automatically.
2202
+ */
2203
+ declare class EfSearchToolbarComponent {
2204
+ /** Custom screen-specific actions, rendered before the standard
2205
+ * set in the right group. Each one gets the same permission /
2206
+ * visibility filtering used by EfRowAction. */
2207
+ readonly customActions: _angular_core.InputSignal<readonly _elasticias_screens.EfDetailToolbarAction[]>;
2208
+ /** Optional ScreenContext — when present, custom actions with a
2209
+ * `permission` field and standard actions Export / Add are
2210
+ * filtered against `context.isGranted(...)`. */
2211
+ readonly context: _angular_core.InputSignal<ScreenContext>;
2212
+ /** Render the standard Export action (left of the standard group). */
2213
+ readonly showExportAction: _angular_core.InputSignalWithTransform<boolean, unknown>;
2214
+ /** Render the standard Add action (rightmost, tenant-styled). */
2215
+ readonly showAddAction: _angular_core.InputSignalWithTransform<boolean, unknown>;
2216
+ /** Disable Add while a navigation / fetch is in flight. */
2217
+ readonly busy: _angular_core.InputSignalWithTransform<boolean, unknown>;
2218
+ /** i18n key for the Add button label. Screens typically pass
2219
+ * `'common_new'` (default) or a screen-specific
2220
+ * `'catalog_products_new'`. */
2221
+ readonly addLabelKey: _angular_core.InputSignal<string>;
2222
+ /** i18n key for the Export button label. */
2223
+ readonly exportLabelKey: _angular_core.InputSignal<string>;
2224
+ /** Fired when a `customActions` button is clicked. The toast
2225
+ * service or screen handler should consume `id`. The `command`
2226
+ * callback on the action is also invoked. */
2227
+ readonly customAction: _angular_core.OutputEmitterRef<_elasticias_screens.EfDetailToolbarAction>;
2228
+ readonly export: _angular_core.OutputEmitterRef<void>;
2229
+ readonly add: _angular_core.OutputEmitterRef<void>;
2230
+ /** Custom actions after permission + `visible` filtering. */
2231
+ readonly visibleCustomActions: _angular_core.Signal<readonly _elasticias_screens.EfDetailToolbarAction[]>;
2232
+ readonly canExport: _angular_core.Signal<boolean>;
2233
+ readonly canAdd: _angular_core.Signal<boolean>;
2234
+ readonly hasStandardActions: _angular_core.Signal<boolean>;
2235
+ readonly hasCustomActions: _angular_core.Signal<boolean>;
2236
+ /** Whether the `|` separator should render — both groups present. */
2237
+ readonly showSeparator: _angular_core.Signal<boolean>;
2238
+ onCustomClick(action: EfSearchToolbarAction): void;
2239
+ private isCustomVisible;
2240
+ /** Standard action visibility: `show*Action` flag AND permission
2241
+ * granted by `context` (or no context bound). */
2242
+ private checkStandard;
2243
+ /** Stable trackBy across renders. */
2244
+ trackByAction: (i: number, a: EfSearchToolbarAction) => unknown;
2245
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfSearchToolbarComponent, never>;
2246
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfSearchToolbarComponent, "ef-search-toolbar", never, { "customActions": { "alias": "customActions"; "required": false; "isSignal": true; }; "context": { "alias": "context"; "required": false; "isSignal": true; }; "showExportAction": { "alias": "showExportAction"; "required": false; "isSignal": true; }; "showAddAction": { "alias": "showAddAction"; "required": false; "isSignal": true; }; "busy": { "alias": "busy"; "required": false; "isSignal": true; }; "addLabelKey": { "alias": "addLabelKey"; "required": false; "isSignal": true; }; "exportLabelKey": { "alias": "exportLabelKey"; "required": false; "isSignal": true; }; }, { "customAction": "customAction"; "export": "export"; "add": "add"; }, never, ["*", "[actions]"], true, never>;
2247
+ }
2248
+
2249
+ /**
2250
+ * One key-value row in the totals stack.
2251
+ */
2252
+ interface EfTotalsRow {
2253
+ /** Translation key for the row label — preferred. */
2254
+ labelKey?: string;
2255
+ /** Direct label fallback when `labelKey` is empty. */
2256
+ label?: string;
2257
+ /** Pre-formatted value text (e.g. `'12 350,00'`). */
2258
+ value: string | number;
2259
+ /** Optional small unit text right of the value (e.g. `'MAD'`). */
2260
+ unit?: string;
2261
+ /** `'discount'` paints the value in `--st-pending-fg`. */
2262
+ tone?: 'default' | 'discount';
2263
+ }
2264
+ /**
2265
+ * Display-size grand row (Bricolage 28px) shown after a divider.
2266
+ * Same shape as a regular row but rendered with the `.grand-row` class.
2267
+ */
2268
+ interface EfTotalsGrand {
2269
+ labelKey?: string;
2270
+ label?: string;
2271
+ value: string | number;
2272
+ /** Currency / unit suffix shown smaller after the big number. */
2273
+ currency?: string;
2274
+ }
2275
+
2276
+ /**
2277
+ * Sidebar totals stack — key-value rows with optional display-size
2278
+ * grand-row (Bricolage Grotesque, ~28px). Lives inside an `<ef-card>`
2279
+ * on order / invoice / quote detail screens.
2280
+ *
2281
+ * ```html
2282
+ * <ef-card titleKey="orders_totals">
2283
+ * <ef-totals
2284
+ * [rows]="[
2285
+ * { labelKey: 'totals_subtotal', value: '12 350,00', unit: 'MAD' },
2286
+ * { labelKey: 'totals_discount', value: '-450,00', unit: 'MAD', tone: 'discount' },
2287
+ * { labelKey: 'totals_tax', value: '2 372,40', unit: 'MAD' },
2288
+ * ]"
2289
+ * [grand]="{ labelKey: 'totals_grand', value: '14 272,40', currency: 'MAD' }"
2290
+ * />
2291
+ * </ef-card>
2292
+ * ```
2293
+ */
2294
+ declare class EfTotalsComponent {
2295
+ readonly rows: _angular_core.InputSignal<readonly EfTotalsRow[]>;
2296
+ readonly grand: _angular_core.InputSignal<EfTotalsGrand>;
2297
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfTotalsComponent, never>;
2298
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfTotalsComponent, "ef-totals", never, { "rows": { "alias": "rows"; "required": false; "isSignal": true; }; "grand": { "alias": "grand"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
2299
+ }
2300
+
2301
+ /**
2302
+ * One row in the stats stack — a label/value pair with optional unit
2303
+ * and an optional `big` flag that swaps the value to a Bricolage
2304
+ * display number (~22px).
2305
+ */
2306
+ interface EfStatsRow {
2307
+ labelKey?: string;
2308
+ label?: string;
2309
+ value: string | number;
2310
+ /** Small grey unit shown right of the value (e.g. `'MAD'`, `'cmds'`). */
2311
+ unit?: string;
2312
+ /** Render the value as a Bricolage display number. Used for the
2313
+ * one or two headline metrics in a sidebar stats card. */
2314
+ big?: boolean;
2315
+ }
2316
+
2317
+ /**
2318
+ * Sidebar mini-KPI stack. Lives inside an `<ef-card>` on detail
2319
+ * screens; rule-soft separators between rows, optional `.big` row
2320
+ * swaps the value to a Bricolage display number.
2321
+ *
2322
+ * ```html
2323
+ * <ef-card titleKey="clients_stats">
2324
+ * <ef-stats
2325
+ * [rows]="[
2326
+ * { labelKey: 'stats_revenue_ytd', value: '128 450', unit: 'MAD', big: true },
2327
+ * { labelKey: 'stats_orders_count', value: 42 },
2328
+ * { labelKey: 'stats_last_order', value: 'Il y a 3 jours' },
2329
+ * ]"
2330
+ * />
2331
+ * </ef-card>
2332
+ * ```
2333
+ *
2334
+ * Stats are sidebar-only — never on list/search screens (KPIs there
2335
+ * are forbidden, see CLAUDE.md memory).
2336
+ */
2337
+ declare class EfStatsComponent {
2338
+ readonly rows: _angular_core.InputSignal<readonly EfStatsRow[]>;
2339
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfStatsComponent, never>;
2340
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfStatsComponent, "ef-stats", never, { "rows": { "alias": "rows"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
2341
+ }
2342
+
2343
+ type EfTimelineState = 'done' | 'now' | 'upcoming';
2344
+ /**
2345
+ * One step in a vertical timeline.
2346
+ */
2347
+ interface EfTimelineItem {
2348
+ /** Stable id for trackBy / template references. */
2349
+ id?: string | number;
2350
+ labelKey?: string;
2351
+ label?: string;
2352
+ /** Small grey meta line below the label (e.g. `'Karim · 12 mai 14:30'`). */
2353
+ metaKey?: string;
2354
+ meta?: string;
2355
+ /**
2356
+ * Step state — drives the dot styling:
2357
+ * - `'done'` → filled with `--st-delivered-fg`
2358
+ * - `'now'` → filled with `--module` + halo
2359
+ * - `'upcoming'` → outlined `--ink-300` (default)
2360
+ */
2361
+ state?: EfTimelineState;
2362
+ }
2363
+
2364
+ /**
2365
+ * Vertical timeline — used on order / user / client detail screens
2366
+ * to surface the entity's history. Three dot states (`done` / `now`
2367
+ * / `upcoming`) line up with the prototype.
2368
+ *
2369
+ * ```html
2370
+ * <ef-card titleKey="orders_history">
2371
+ * <ef-timeline
2372
+ * [items]="[
2373
+ * { state: 'done', label: 'Commande créée', meta: 'Karim · 12 mai 14:30' },
2374
+ * { state: 'done', label: 'Paiement reçu', meta: 'Stripe · 12 mai 14:32' },
2375
+ * { state: 'now', label: 'Préparation', meta: 'En cours' },
2376
+ * { state: 'upcoming', label: 'Expédition' },
2377
+ * ]"
2378
+ * />
2379
+ * </ef-card>
2380
+ * ```
2381
+ *
2382
+ * The `'now'` dot picks up `var(--module)` so each module's accent
2383
+ * tints its own active step.
2384
+ */
2385
+ declare class EfTimelineComponent {
2386
+ readonly items: _angular_core.InputSignal<readonly EfTimelineItem[]>;
2387
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfTimelineComponent, never>;
2388
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfTimelineComponent, "ef-timeline", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
2389
+ }
2390
+
2391
+ interface OpMeta {
2392
+ cls: string;
2393
+ }
2394
+ /**
2395
+ * Standardized change-history box. Drop it on any detail screen and feed it the
2396
+ * record's audit entries (from `GET /api/audit/{entityType}/{id}` via the NSwag
2397
+ * `AuditClient`). Renders a vertical timeline inside an {@link EfCardComponent},
2398
+ * resolving operation labels through `audit_op_*` i18n keys and field labels
2399
+ * through `audit_field_*` (falling back to the raw field key when unmapped).
2400
+ *
2401
+ * ```html
2402
+ * <ef-change-history [entries]="auditEntries()" />
2403
+ * ```
2404
+ */
2405
+ declare class EfChangeHistoryComponent {
2406
+ private readonly translate;
2407
+ readonly entries: _angular_core.InputSignal<readonly EfChangeHistoryEntry[]>;
2408
+ readonly titleKey: _angular_core.InputSignal<string>;
2409
+ readonly emptyKey: _angular_core.InputSignal<string>;
2410
+ /** Cap the total rows considered (newest first). 0 = no cap. */
2411
+ readonly limit: _angular_core.InputSignal<number>;
2412
+ /** How many recent entries to show inline in the card. 0 = all. The rest
2413
+ * are reachable via the "view details" dialog. */
2414
+ readonly previewLimit: _angular_core.InputSignal<number>;
2415
+ /** Render inside a collapsible card. */
2416
+ readonly collapsible: _angular_core.InputSignal<boolean>;
2417
+ /** Initial collapsed state — expanded by default. */
2418
+ readonly startCollapsed: _angular_core.InputSignal<boolean>;
2419
+ /** "Show more" dialog visibility. */
2420
+ readonly dialogVisible: _angular_core.WritableSignal<boolean>;
2421
+ /** First `previewLimit` rows shown inline in the card. */
2422
+ readonly previewRows: _angular_core.Signal<{
2423
+ entry: EfChangeHistoryEntry;
2424
+ meta: OpMeta;
2425
+ operationLabel: string;
2426
+ }[]>;
2427
+ /** True when the full list has more entries than the inline preview. */
2428
+ readonly hasMore: _angular_core.Signal<boolean>;
2429
+ openDialog(event?: Event): void;
2430
+ readonly rows: _angular_core.Signal<{
2431
+ entry: EfChangeHistoryEntry;
2432
+ meta: OpMeta;
2433
+ operationLabel: string;
2434
+ }[]>;
2435
+ /** Resolve a field key to its label. Adding an `audit_field_<field>` key is
2436
+ * OPTIONAL — when it's missing the raw field name is humanized
2437
+ * (`clientCode` → "Client code"), so new entities/fields work with zero
2438
+ * i18n maintenance. Add a key only for a properly localized (fr/ar) term. */
2439
+ fieldLabel(field: string | undefined): string;
2440
+ /** Returns the translation when present, otherwise a humanized fallback of
2441
+ * the source token (ngx-translate echoes the key on a miss). */
2442
+ private resolveLabel;
2443
+ /** `clientCode` / `status_changed` → "Client code" / "Status changed". */
2444
+ private humanize;
2445
+ /** Locale-stable dd/MM/yyyy HH:mm — avoids depending on Angular locale data
2446
+ * registration (the app's default locale is fr-MA). */
2447
+ formatWhen(value: string | Date | null | undefined): string;
2448
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfChangeHistoryComponent, never>;
2449
+ 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; }; }, {}, never, never, true, never>;
2450
+ }
2451
+
2452
+ /**
2453
+ * Sparkline series — array of numeric values, normalized to the
2454
+ * card's viewBox at render time. Pass an empty array to hide the
2455
+ * sparkline.
2456
+ */
2457
+ type EfKpiSparkline = ReadonlyArray<number>;
2458
+ /**
2459
+ * Trend tone — drives the delta arrow color.
2460
+ * - `'up'` → success green (`--st-delivered-fg`)
2461
+ * - `'down'` → danger red (`--st-cancelled-fg`)
2462
+ * - `'flat'` → muted grey
2463
+ */
2464
+ type EfKpiDeltaTone = 'up' | 'down' | 'flat';
2465
+
2466
+ /**
2467
+ * Dashboard headline KPI tile — the one place KPIs are allowed in
2468
+ * the design system. List/search screens stay focused on results;
2469
+ * KPIs live on per-module dashboards (and detail-screen sidebars
2470
+ * via `ef-stats`).
2471
+ *
2472
+ * ```html
2473
+ * <ef-kpi-card
2474
+ * labelKey="kpi_revenue_7d"
2475
+ * value="52 480"
2476
+ * unit="MAD"
2477
+ * delta="▲ 12%"
2478
+ * deltaTone="up"
2479
+ * [spark]="[22, 20, 17, 18, 12, 14, 9, 11, 7, 10, 5, 8, 4, 3, 2]"
2480
+ * />
2481
+ * ```
2482
+ *
2483
+ * Sparkline values are normalized to a fixed 200×28 viewBox; the
2484
+ * stroke colour follows the active module's color (`--module`, e.g.
2485
+ * Sales blue) by default — the convention for report/dashboard
2486
+ * accents — override via `[sparkColor]="'var(--st-pending-fg)'"` for
2487
+ * variants. Pass an empty array to hide the sparkline.
2488
+ */
2489
+ declare class EfKpiCardComponent {
2490
+ /** Translation key for the small label above the headline number. */
2491
+ readonly labelKey: _angular_core.InputSignal<string>;
2492
+ /** Direct label fallback. */
2493
+ readonly label: _angular_core.InputSignal<string>;
2494
+ /** Pre-formatted headline value (e.g. `'52 480'`, `'603'`). */
2495
+ readonly value: _angular_core.InputSignal<string | number>;
2496
+ /** Optional small unit suffix (e.g. `'MAD'`, `'%'`). */
2497
+ readonly unit: _angular_core.InputSignal<string>;
2498
+ /** Pre-formatted delta text including the arrow glyph (`'▲ 12%'`). */
2499
+ readonly delta: _angular_core.InputSignal<string>;
2500
+ /** Delta tone — drives the arrow color. */
2501
+ readonly deltaTone: _angular_core.InputSignal<EfKpiDeltaTone>;
2502
+ /** Raw series for the sparkline. */
2503
+ readonly spark: _angular_core.InputSignal<EfKpiSparkline>;
2504
+ /** Stroke color for the sparkline polyline. Defaults to the active
2505
+ * module's color (report-accent convention); SVG resolves the CSS
2506
+ * custom property natively. */
2507
+ readonly sparkColor: _angular_core.InputSignal<string>;
2508
+ /** Computed `points="x,y x,y …"` attribute, normalized into the
2509
+ * 200×28 viewBox so the polyline always fits. Returns null
2510
+ * when the series is empty. */
2511
+ readonly sparkPoints: _angular_core.Signal<string>;
2512
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfKpiCardComponent, never>;
2513
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfKpiCardComponent, "ef-kpi-card", never, { "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "unit": { "alias": "unit"; "required": false; "isSignal": true; }; "delta": { "alias": "delta"; "required": false; "isSignal": true; }; "deltaTone": { "alias": "deltaTone"; "required": false; "isSignal": true; }; "spark": { "alias": "spark"; "required": false; "isSignal": true; }; "sparkColor": { "alias": "sparkColor"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
2514
+ }
2515
+
2516
+ type EfChartType = 'line' | 'bar' | 'donut';
2517
+ interface EfChartSeries {
2518
+ label?: string;
2519
+ data: ReadonlyArray<number>;
2520
+ color?: string;
2521
+ /** Per-point colors (donut slices / bars). Sparse entries fall back to
2522
+ * `color`, then the palette — use for status-keyed charts so slices
2523
+ * reuse the exact status-chip tokens (`--st-*-fg`) instead of accents. */
2524
+ colors?: ReadonlyArray<string | undefined>;
2525
+ }
2526
+ /** Concrete hexes (canvas can't resolve CSS var() strings). Order matches the tenant accents.
2527
+ *
2528
+ * Convention: the DOMINANT color of report/dashboard visuals is the **active
2529
+ * module's color** (`--module`, e.g. Sales blue `--m-sales`). `ef-chart`
2530
+ * resolves the token at render time and passes it as `moduleColor`, which
2531
+ * takes the palette's first slot — these accents only color secondary
2532
+ * series/slices. A series' explicit `color` always wins. */
2533
+ declare const EF_CHART_PALETTE: ReadonlyArray<string>;
2534
+ declare function buildChartConfig(type: EfChartType, labels: ReadonlyArray<string>, series: ReadonlyArray<EfChartSeries>,
2535
+ /** Resolved `--module` color — becomes the palette's first (dominant) slot. */
2536
+ moduleColor?: string): ChartConfiguration;
2537
+
2538
+ /**
2539
+ * Thin chart.js wrapper for report/dashboard visuals (line, bar, donut).
2540
+ * Labels/series are data — i18n happens in the parent. Zoneless-safe: the
2541
+ * effect below re-renders whenever inputs change; instance destroyed on cleanup.
2542
+ *
2543
+ * Color convention: the dominant series/slice color defaults to the **active
2544
+ * module's color** — the component resolves `--module` (set by `ef-app-main`'s
2545
+ * `[data-module]`, e.g. Sales blue) from the canvas at render time, since the
2546
+ * chart.js canvas can't consume CSS `var()` strings. Secondary series fall back
2547
+ * to `EF_CHART_PALETTE`; a series' explicit `color` always wins.
2548
+ */
2549
+ declare class EfChartComponent implements OnDestroy {
2550
+ readonly type: _angular_core.InputSignal<EfChartType>;
2551
+ readonly labels: _angular_core.InputSignal<readonly string[]>;
2552
+ readonly series: _angular_core.InputSignal<readonly EfChartSeries[]>;
2553
+ readonly height: _angular_core.InputSignal<number>;
2554
+ private readonly canvas;
2555
+ private chart;
2556
+ constructor();
2557
+ ngOnDestroy(): void;
2558
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfChartComponent, never>;
2559
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfChartComponent, "ef-chart", never, { "type": { "alias": "type"; "required": true; "isSignal": true; }; "labels": { "alias": "labels"; "required": false; "isSignal": true; }; "series": { "alias": "series"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
2560
+ }
2561
+
2562
+ interface EfRankRow {
2563
+ label: string;
2564
+ value: string;
2565
+ secondary?: string;
2566
+ pct: number;
2567
+ }
2568
+ /** Top-N ranked rows (label · proportional bar · value). Lives inside an ef-card. */
2569
+ declare class EfRankListComponent {
2570
+ readonly rows: _angular_core.InputSignal<readonly EfRankRow[]>;
2571
+ clampPct(pct: number): number;
2572
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfRankListComponent, never>;
2573
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfRankListComponent, "ef-rank-list", never, { "rows": { "alias": "rows"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
2574
+ }
2575
+
2576
+ /**
2577
+ * Shared base for every Comptoir form control (`ef-input-text`,
2578
+ * `ef-textarea`, `ef-select`, `ef-input-number`, `ef-checkbox`, …).
2579
+ *
2580
+ * Hoists the cross-cutting concerns that every form control wants
2581
+ * to handle the same way:
2582
+ *
2583
+ * - **Identity** — `inputId` / `name` plus an auto-generated id
2584
+ * (`ef-ctrl-<n>`) so the `<label for>` ↔ `<input id>` binding
2585
+ * always works for screen readers + click-to-focus, even when
2586
+ * the consumer didn't pass anything.
2587
+ * - **Label / placeholder i18n** — `labelKey` / `label` and
2588
+ * `placeholderKey` / `placeholder` with the same precedence
2589
+ * convention (`*Key` wins, falls back to literal). All ef-*
2590
+ * form controls follow the
2591
+ * [feedback_i18n_keys](memory) convention by surfacing both
2592
+ * inputs.
2593
+ * - **ARIA** — `ariaLabelKey` / `ariaLabel` for screen-reader-only
2594
+ * labels; `effectiveAriaLabel` returns `null` when a visible
2595
+ * label is rendered (which already serves as the ARIA name).
2596
+ * `errorsId` provides a stable id for the error/hint container
2597
+ * so subclasses can wire `aria-describedby`.
2598
+ * - **State** — `required` / `readonly` / `disabled` shared inputs,
2599
+ * used directly in subclass templates and toggled by CVA's
2600
+ * `setDisabledState` in subclasses.
2601
+ *
2602
+ * Subclasses still own their own `value` field, ControlValueAccessor
2603
+ * implementation, and any control-specific inputs (e.g. textarea's
2604
+ * `rows`, select's `options`).
2605
+ *
2606
+ * ```ts
2607
+ * export class EfInputTextComponent extends AbstractEfFormControl
2608
+ * implements ControlValueAccessor {
2609
+ * @Input() type = 'text';
2610
+ * @Input() value = '';
2611
+ * // ... CVA + value-specific logic
2612
+ * }
2613
+ * ```
2614
+ */
2615
+ declare abstract class AbstractEfFormControl {
2616
+ /** Explicit DOM `id` for the inner control. Falls back to
2617
+ * `name`, then to the auto-generated `_autoId`. */
2618
+ readonly inputId: _angular_core.InputSignal<string>;
2619
+ /** Form `name` attribute — also used as the id fallback. */
2620
+ readonly name: _angular_core.InputSignal<string>;
2621
+ /** Per-instance auto id used when neither `inputId` nor `name`
2622
+ * is supplied. The static counter is shared across all
2623
+ * AbstractEfFormControl subclasses, which is fine — uniqueness
2624
+ * per-page is the only requirement. */
2625
+ private static _nextId;
2626
+ protected readonly _autoId: string;
2627
+ /** Direct label text. Use `labelKey` for i18n. */
2628
+ readonly label: _angular_core.InputSignal<string>;
2629
+ /** Translation key for the label — preferred (memory:
2630
+ * feedback_i18n_keys says always use `*Key`). */
2631
+ readonly labelKey: _angular_core.InputSignal<string>;
2632
+ readonly placeholder: _angular_core.InputSignal<string>;
2633
+ readonly placeholderKey: _angular_core.InputSignal<string>;
2634
+ readonly ariaLabel: _angular_core.InputSignal<string>;
2635
+ readonly ariaLabelKey: _angular_core.InputSignal<string>;
2636
+ readonly required: _angular_core.InputSignalWithTransform<boolean, unknown>;
2637
+ readonly readonly: _angular_core.InputSignalWithTransform<boolean, unknown>;
2638
+ /** The `[disabled]` input. The **effective** disabled (input OR
2639
+ * form-driven via CVA) is {@link isDisabled} — bind that in templates. */
2640
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
2641
+ /** Form-driven disabled set by CVA `setDisabledState`, merged with the
2642
+ * `[disabled]` input. Writable signal (the input itself is read-only). */
2643
+ private readonly _cvaDisabled;
2644
+ /** Effective disabled state — the `[disabled]` input OR a reactive-form
2645
+ * `disable()`. Templates/logic should use this, not `disabled()`. */
2646
+ readonly isDisabled: _angular_core.Signal<boolean>;
2647
+ /** CVA hook for subclasses: `setDisabledState(d) { this.updateDisabledState(d); }`. */
2648
+ protected updateDisabledState(disabled: boolean): void;
2649
+ /**
2650
+ * Show the inline clear ✕ (the shared `ef-clear-button`). On by default —
2651
+ * every control is clearable; the ✕ only appears when there's a value and
2652
+ * the field is editable (see {@link canClear}). Set `[showClear]="false"`
2653
+ * to opt a field out.
2654
+ */
2655
+ readonly showClear: _angular_core.InputSignalWithTransform<boolean, unknown>;
2656
+ /** Whether the clear ✕ should currently render: opted in, the control
2657
+ * holds a value ({@link hasValue}), and the field is editable. */
2658
+ get canClear(): boolean;
2659
+ /** Subclasses report whether they currently hold a clearable value.
2660
+ * Default `false` (controls that don't override never show a clear). */
2661
+ protected get hasValue(): boolean;
2662
+ /** Reset the control's value (and notify forms / `valueChangeEvent`).
2663
+ * Overridden by subclasses that support clearing. */
2664
+ clearValue(): void;
2665
+ /**
2666
+ * Forms-independent server/validation errors for this field.
2667
+ *
2668
+ * The signal-based V2 detail screens bind values with `[value]` +
2669
+ * `(valueChangeEvent)` and never register an `NgControl`, so the
2670
+ * `ngControl`-based `serverError` path can't surface backend
2671
+ * validation messages. Two ways to feed them in:
2672
+ *
2673
+ * - Explicit, per-field: `[errors]="serverErrors()['clientType']"`.
2674
+ * - Automatic, by `name`: wrap the fields in `[efServerErrors]="serverErrors()"`
2675
+ * and the control links itself via {@link EfServerErrorsDirective}
2676
+ * (no per-field binding). See {@link resolvedExternalErrors}.
2677
+ *
2678
+ * Either way the control renders the messages and the invalid state.
2679
+ * Falls back to the `NgControl` `serverError` path when neither is
2680
+ * present, so `ngModel`-based (v1) screens keep working unchanged.
2681
+ */
2682
+ readonly errors: _angular_core.InputSignal<string[]>;
2683
+ /** Optional enclosing `[efServerErrors]` scope (auto-link by `name`). */
2684
+ private readonly serverErrorsScope;
2685
+ /**
2686
+ * External (server) errors for this control, resolved reactively from
2687
+ * either the explicit `[errors]` input (wins) or the enclosing
2688
+ * `[efServerErrors]` scope keyed by `name`. A `computed` so reads inside
2689
+ * the subclass `serverErrors` / `isInvalid` getters track the source
2690
+ * signals under zoneless change detection.
2691
+ */
2692
+ readonly resolvedExternalErrors: _angular_core.Signal<string[]>;
2693
+ protected readonly translateService: TranslateService;
2694
+ /** Resolved DOM id. Always non-empty so `<label for>` and
2695
+ * `<input id>` can match unambiguously. */
2696
+ get effectiveId(): string;
2697
+ /** Translated placeholder (or empty string). */
2698
+ get effectivePlaceholder(): string;
2699
+ /**
2700
+ * Resolved `aria-label` value — used when no visible label is
2701
+ * rendered. Returns `null` (no attribute) when a visible label
2702
+ * IS shown, since the visible label already serves as the
2703
+ * accessible name and duplicate `aria-label` would override it.
2704
+ */
2705
+ get effectiveAriaLabel(): string | null;
2706
+ /** Stable id for the error / hint container — wired through
2707
+ * `aria-describedby` on the input so screen readers announce
2708
+ * the validation message together with the field name. */
2709
+ get errorsId(): string;
2710
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<AbstractEfFormControl, never>;
2711
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AbstractEfFormControl, "ng-component", never, { "inputId": { "alias": "inputId"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "placeholderKey": { "alias": "placeholderKey"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "ariaLabelKey": { "alias": "ariaLabelKey"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "showClear": { "alias": "showClear"; "required": false; "isSignal": true; }; "errors": { "alias": "errors"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
2712
+ }
2713
+
2714
+ declare class EfInputTextComponent extends AbstractEfFormControl implements ControlValueAccessor {
2715
+ size: 'small' | 'large';
2716
+ type: string;
2717
+ maxlength?: number;
2718
+ fluid: boolean;
2719
+ inline: boolean;
2720
+ autocomplete?: string;
2721
+ /** Clearable (showClear / canClear) is inherited from AbstractEfFormControl;
2722
+ * we only supply the value predicate + the reset. */
2723
+ protected get hasValue(): boolean;
2724
+ clearValue(): void;
2725
+ /**
2726
+ * Rendering variant — same dual-mode pattern as `ef-button`.
2727
+ * - `'comptoir'` (default) — native `<input class="ef-input">` against
2728
+ * the Comptoir pattern styles. Renders at the
2729
+ * canonical `--hit-base` (40px) control height.
2730
+ * Required for `prefix` / `suffix`.
2731
+ * - `'primeng'` — wraps `<input pInputText>` (v1). Opt in when
2732
+ * you specifically need PrimeNG input behaviour.
2733
+ * See ADR-009 for why comptoir is the height-consistent default.
2734
+ */
2735
+ variant: 'primeng' | 'comptoir';
2736
+ /** Inline prefix addon — e.g. `'/p/'` for slugs, `'$'` for prices.
2737
+ * Renders only in the Comptoir variant; ignored otherwise. */
2738
+ prefix?: string;
2739
+ /** Inline suffix addon — e.g. `'MAD'`, `'cm'`. Renders only in the
2740
+ * Comptoir variant; ignored otherwise. */
2741
+ suffix?: string;
2742
+ get isInline(): boolean;
2743
+ valueChangeEvent: EventEmitter<string>;
2744
+ /** Two-way-bindable as `[value]` (one-way) or
2745
+ * `[(value)]="signal()"` (set + listen via `valueChangeEvent`). */
2746
+ value: string;
2747
+ private onChange;
2748
+ private onTouched;
2749
+ readonly ngControl: NgControl;
2750
+ constructor();
2751
+ get showRequired(): boolean;
2752
+ get serverErrors(): string[] | null;
2753
+ get isInvalid(): boolean;
2754
+ writeValue(value: string): void;
2755
+ registerOnChange(fn: (value: string) => void): void;
2756
+ registerOnTouched(fn: () => void): void;
2757
+ setDisabledState(isDisabled: boolean): void;
2758
+ handleInput(value: string): void;
2759
+ handleBlur(): void;
2760
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfInputTextComponent, never>;
2761
+ 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; }; "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>;
2762
+ static ngAcceptInputType_fluid: unknown;
2763
+ static ngAcceptInputType_inline: unknown;
2764
+ }
2765
+
2766
+ /**
2767
+ * Comptoir select — wraps PrimeNG `p-select` (single) or `p-multiselect`
2768
+ * (multi) and skins it against the `.es` / `.es__panel` patterns lifted
2769
+ * from the static prototype (`docs/ux-ui/erp/screens/components.html`,
2770
+ * Section 04). All the heavy machinery (filter, virtual scroll, group
2771
+ * support, keyboard nav, ARIA listbox semantics, RTL, focus trapping,
2772
+ * lazy loading) comes from PrimeNG; we own only the visuals + the
2773
+ * Elasticias API surface (i18n `*Key` inputs, `selectionChangeEvent`,
2774
+ * `filterByKeys`, content-projected templates).
2775
+ *
2776
+ * The `'comptoir'` variant remains as a lite, no-deps escape hatch
2777
+ * (native `<select>` styled via `.ef-select-native`); use the default
2778
+ * `'primeng'` variant for any screen that needs filter / multi /
2779
+ * group / templating.
2780
+ *
2781
+ * ```html
2782
+ * <ef-select
2783
+ * labelKey="orders.status"
2784
+ * placeholderKey="orders.status_placeholder"
2785
+ * [(value)]="status"
2786
+ * [options]="statuses"
2787
+ * optionLabel="label" optionValue="code"
2788
+ * [filter]="true" [showClear]="true">
2789
+ * </ef-select>
2790
+ *
2791
+ * <ef-select
2792
+ * labelKey="users.assigned"
2793
+ * [options]="users" optionLabel="name" optionValue="id"
2794
+ * [multiple]="true" [filter]="true">
2795
+ * <ng-template #item let-u>
2796
+ * <span class="av">{{ u.initials }}</span>
2797
+ * <span class="body">
2798
+ * <span class="nm">{{ u.name }}</span>
2799
+ * <span class="meta">{{ u.role }}</span>
2800
+ * </span>
2801
+ * </ng-template>
2802
+ * </ef-select>
2803
+ * ```
2804
+ */
2805
+ declare class EfSelectComponent extends AbstractEfFormControl implements ControlValueAccessor, OnInit, OnChanges {
2806
+ options: any[];
2807
+ optionLabel: string;
2808
+ optionValue?: string;
2809
+ optionDisabled?: string;
2810
+ /** Stable identity key — required for groups and virtual scroll. */
2811
+ dataKey?: string;
2812
+ /** Group mode — pair with `optionGroupLabel` + `optionGroupChildren`. */
2813
+ group: boolean;
2814
+ optionGroupLabel: string;
2815
+ optionGroupChildren: string;
2816
+ /** Control height. Default (undefined) = the canonical `--hit-base` (40px),
2817
+ * matching native inputs (ADR-009). `'small'` opts into the dense 32px
2818
+ * variant for compact contexts (e.g. table-row editors). */
2819
+ size?: 'small' | 'large';
2820
+ fluid: boolean;
2821
+ inline: boolean;
2822
+ appendTo?: string;
2823
+ /** Multi-selection — internally swaps `<p-select>` for `<p-multiselect>`. */
2824
+ multiple: boolean;
2825
+ filter: boolean;
2826
+ /** Filter the list against these option fields (string or csv).
2827
+ * PrimeNG default: `optionLabel`. */
2828
+ filterBy?: string;
2829
+ filterPlaceholder?: string;
2830
+ filterPlaceholderKey?: string;
2831
+ /** Auto-focus the filter input when the panel opens (default: true). */
2832
+ autofocusFilter: boolean;
2833
+ editable: boolean;
2834
+ loading: boolean;
2835
+ /** Virtual-scroll for long lists (>100 items). Pair with
2836
+ * `virtualScrollItemSize` (default 38px). */
2837
+ virtualScroll: boolean;
2838
+ virtualScrollItemSize: number;
2839
+ scrollHeight: string;
2840
+ /** Multi-select trigger display: `'chip'` (default — chips visible
2841
+ * in the trigger, individually removable) or `'comma'`. */
2842
+ multiDisplay: 'chip' | 'comma';
2843
+ /** Multi-select header "select all" toggle. Off by default — the
2844
+ * Comptoir multiselect leads with the filter; opt in when bulk
2845
+ * select-all is genuinely useful. */
2846
+ showToggleAll: boolean;
2847
+ emptyMessage?: string;
2848
+ emptyMessageKey?: string;
2849
+ emptyFilterMessage?: string;
2850
+ emptyFilterMessageKey?: string;
2851
+ styleClass?: string;
2852
+ panelStyleClass?: string;
2853
+ /** Filter options by matching key-value pairs (legacy Elasticias
2854
+ * helper — pre-filters `options` before they reach PrimeNG). */
979
2855
  filterByKeys?: {
980
2856
  [key: string]: any;
981
2857
  };
2858
+ /**
2859
+ * Rendering variant.
2860
+ * - `'primeng'` (default) — wraps `p-select` / `p-multiselect`, skinned with
2861
+ * the `.es` / `.es__panel` patterns (the custom
2862
+ * dropdown: filter, styled options, animated panel
2863
+ * — see `components.html` Section 04). Also pinned
2864
+ * to the canonical `--hit-base` (40px) trigger
2865
+ * height (`.es.p-select { min-height: --hit-base }`),
2866
+ * so it aligns with inputs/buttons in a row.
2867
+ * - `'comptoir'` — lite native `<select class="ef-select-native">`.
2868
+ * No filter / panel / multi / templates; use it for
2869
+ * inline / dense contexts that want the plain native
2870
+ * control (e.g. table-row editors).
2871
+ * The `.es` dropdown is the default per the design system; opt into
2872
+ * `comptoir` only when a native control is specifically wanted. See ADR-009.
2873
+ */
2874
+ variant: 'primeng' | 'comptoir';
982
2875
  get isInline(): boolean;
2876
+ readonly hostClass = true;
983
2877
  itemTemplate: TemplateRef<any> | null;
984
2878
  selectedItemTemplate: TemplateRef<any> | null;
2879
+ groupTemplate: TemplateRef<any> | null;
2880
+ headerTemplate: TemplateRef<any> | null;
2881
+ footerTemplate: TemplateRef<any> | null;
2882
+ emptyTemplate: TemplateRef<any> | null;
2883
+ emptyFilterTemplate: TemplateRef<any> | null;
2884
+ filterTemplate: TemplateRef<any> | null;
2885
+ loaderTemplate: TemplateRef<any> | null;
985
2886
  selectionChangeEvent: EventEmitter<any>;
2887
+ filterChangeEvent: EventEmitter<string>;
2888
+ panelShowEvent: EventEmitter<void>;
2889
+ panelHideEvent: EventEmitter<void>;
2890
+ /** Two-way-bindable as `[value]` (one-way) or
2891
+ * `[(value)]` (set + listen via `selectionChangeEvent`). */
986
2892
  value: any;
987
2893
  filteredOptions: any[];
988
2894
  private onChange;
@@ -990,10 +2896,17 @@ declare class EfSelectComponent implements ControlValueAccessor, OnInit, OnChang
990
2896
  /** Resolved lazily in ngOnInit to avoid circular DI with NG_VALUE_ACCESSOR */
991
2897
  ngControl: NgControl | null;
992
2898
  private readonly injector;
993
- private readonly translateService;
994
2899
  ngOnInit(): void;
995
- get effectivePlaceholder(): string;
996
- get effectiveId(): string;
2900
+ /** Translated filter placeholder. Falls back to a generic key, then
2901
+ * to "Search…" so it always renders something readable. */
2902
+ get effectiveFilterPlaceholder(): string;
2903
+ get effectiveEmptyMessage(): string;
2904
+ get effectiveEmptyFilterMessage(): string;
2905
+ /** Always include `.es` so the global Comptoir skin in
2906
+ * `_patterns.scss` always finds a hook, regardless of what the
2907
+ * consumer passes via `styleClass`. */
2908
+ get effectiveStyleClass(): string;
2909
+ get effectivePanelStyleClass(): string;
997
2910
  get showRequired(): boolean;
998
2911
  get serverErrors(): string[] | null;
999
2912
  get isInvalid(): boolean;
@@ -1007,49 +2920,134 @@ declare class EfSelectComponent implements ControlValueAccessor, OnInit, OnChang
1007
2920
  value: any;
1008
2921
  }): void;
1009
2922
  handleClear(): void;
2923
+ handleFilter(event: {
2924
+ originalEvent?: Event;
2925
+ filter: string;
2926
+ }): void;
2927
+ handlePanelShow(): void;
2928
+ handlePanelHide(): void;
2929
+ /** Comptoir variant — native `<select>` change handler. The empty
2930
+ * string from the placeholder option becomes `null`; otherwise we
2931
+ * resolve the value through `optionValue` if provided. */
2932
+ handleNativeSelectChange(rawValue: string): void;
1010
2933
  private applyFilters;
1011
2934
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfSelectComponent, never>;
1012
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfSelectComponent, "ef-select", never, { "label": { "alias": "label"; "required": false; }; "labelKey": { "alias": "labelKey"; "required": false; }; "inputId": { "alias": "inputId"; "required": false; }; "name": { "alias": "name"; "required": false; }; "options": { "alias": "options"; "required": false; }; "optionLabel": { "alias": "optionLabel"; "required": false; }; "optionValue": { "alias": "optionValue"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "placeholderKey": { "alias": "placeholderKey"; "required": false; }; "size": { "alias": "size"; "required": false; }; "required": { "alias": "required"; "required": false; }; "fluid": { "alias": "fluid"; "required": false; }; "inline": { "alias": "inline"; "required": false; }; "filter": { "alias": "filter"; "required": false; }; "showClear": { "alias": "showClear"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "appendTo": { "alias": "appendTo"; "required": false; }; "filterByKeys": { "alias": "filterByKeys"; "required": false; }; }, { "selectionChangeEvent": "selectionChangeEvent"; }, ["itemTemplate", "selectedItemTemplate"], never, true, never>;
1013
- static ngAcceptInputType_required: unknown;
2935
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfSelectComponent, "ef-select", never, { "options": { "alias": "options"; "required": false; }; "optionLabel": { "alias": "optionLabel"; "required": false; }; "optionValue": { "alias": "optionValue"; "required": false; }; "optionDisabled": { "alias": "optionDisabled"; "required": false; }; "dataKey": { "alias": "dataKey"; "required": false; }; "group": { "alias": "group"; "required": false; }; "optionGroupLabel": { "alias": "optionGroupLabel"; "required": false; }; "optionGroupChildren": { "alias": "optionGroupChildren"; "required": false; }; "size": { "alias": "size"; "required": false; }; "fluid": { "alias": "fluid"; "required": false; }; "inline": { "alias": "inline"; "required": false; }; "appendTo": { "alias": "appendTo"; "required": false; }; "multiple": { "alias": "multiple"; "required": false; }; "filter": { "alias": "filter"; "required": false; }; "filterBy": { "alias": "filterBy"; "required": false; }; "filterPlaceholder": { "alias": "filterPlaceholder"; "required": false; }; "filterPlaceholderKey": { "alias": "filterPlaceholderKey"; "required": false; }; "autofocusFilter": { "alias": "autofocusFilter"; "required": false; }; "editable": { "alias": "editable"; "required": false; }; "loading": { "alias": "loading"; "required": false; }; "virtualScroll": { "alias": "virtualScroll"; "required": false; }; "virtualScrollItemSize": { "alias": "virtualScrollItemSize"; "required": false; }; "scrollHeight": { "alias": "scrollHeight"; "required": false; }; "multiDisplay": { "alias": "multiDisplay"; "required": false; }; "showToggleAll": { "alias": "showToggleAll"; "required": false; }; "emptyMessage": { "alias": "emptyMessage"; "required": false; }; "emptyMessageKey": { "alias": "emptyMessageKey"; "required": false; }; "emptyFilterMessage": { "alias": "emptyFilterMessage"; "required": false; }; "emptyFilterMessageKey": { "alias": "emptyFilterMessageKey"; "required": false; }; "styleClass": { "alias": "styleClass"; "required": false; }; "panelStyleClass": { "alias": "panelStyleClass"; "required": false; }; "filterByKeys": { "alias": "filterByKeys"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "value": { "alias": "value"; "required": false; }; }, { "selectionChangeEvent": "selectionChangeEvent"; "filterChangeEvent": "filterChangeEvent"; "panelShowEvent": "panelShowEvent"; "panelHideEvent": "panelHideEvent"; }, ["itemTemplate", "selectedItemTemplate", "groupTemplate", "headerTemplate", "footerTemplate", "emptyTemplate", "emptyFilterTemplate", "filterTemplate", "loaderTemplate"], never, true, never>;
2936
+ static ngAcceptInputType_group: unknown;
1014
2937
  static ngAcceptInputType_fluid: unknown;
1015
2938
  static ngAcceptInputType_inline: unknown;
2939
+ static ngAcceptInputType_multiple: unknown;
1016
2940
  static ngAcceptInputType_filter: unknown;
1017
- static ngAcceptInputType_showClear: unknown;
1018
- static ngAcceptInputType_disabled: unknown;
2941
+ static ngAcceptInputType_autofocusFilter: unknown;
2942
+ static ngAcceptInputType_editable: unknown;
2943
+ static ngAcceptInputType_loading: unknown;
2944
+ static ngAcceptInputType_virtualScroll: unknown;
2945
+ static ngAcceptInputType_showToggleAll: unknown;
1019
2946
  }
1020
2947
 
1021
- declare class EfCheckboxComponent implements ControlValueAccessor {
1022
- /** Direct label text (not translated) */
1023
- label?: string;
1024
- /** Translation key takes priority over label */
1025
- labelKey?: string;
1026
- inputId?: string;
1027
- name?: string;
2948
+ /**
2949
+ * Comptoir checkbox.
2950
+ *
2951
+ * Two render variants share the same API:
2952
+ * - `'primeng'` (default) — wraps `p-checkbox`. Stock PrimeNG visuals.
2953
+ * - `'comptoir'` — native `<input type="checkbox">` styled
2954
+ * against `.ef-checkbox-native`. Lean, no animation, fits dense
2955
+ * grids (variants table, role / permission lists, etc.).
2956
+ *
2957
+ * Identity / label / aria / required / readonly / disabled inherited
2958
+ * from `AbstractEfFormControl`. `binary` flips between boolean mode
2959
+ * (`checked: true/false`) and value-mode (`checked` is the bound
2960
+ * value when ticked, `null` when unticked) — the latter pairs with
2961
+ * a parent NgModel array for multi-select scenarios.
2962
+ *
2963
+ * ```html
2964
+ * <ef-checkbox
2965
+ * labelKey="catalog.is_active"
2966
+ * variant="comptoir"
2967
+ * [(checked)]="variant.isActive"
2968
+ * binary />
2969
+ * ```
2970
+ */
2971
+ declare class EfCheckboxComponent extends AbstractEfFormControl implements ControlValueAccessor {
2972
+ /** Underlying checkbox value when ticked (non-binary mode). */
1028
2973
  value?: any;
2974
+ /** Binary mode — checked becomes a boolean rather than the
2975
+ * `value` input. Default for new code. */
1029
2976
  binary: boolean;
1030
- required: boolean;
1031
- disabled: boolean;
2977
+ /**
2978
+ * Render variant.
2979
+ * - `'primeng'` (default) — wraps `p-checkbox`.
2980
+ * - `'comptoir'` — native `<input type="checkbox">`
2981
+ * styled against `.ef-checkbox-native`.
2982
+ */
2983
+ variant: 'primeng' | 'comptoir';
2984
+ /** Two-way bindable `[(checked)]`. */
1032
2985
  checked: any;
2986
+ checkedChange: EventEmitter<any>;
1033
2987
  private onChange;
1034
2988
  private onTouched;
1035
2989
  readonly ngControl: NgControl;
1036
2990
  constructor();
1037
- get effectiveId(): string;
1038
- get effectiveLabel(): string | undefined;
1039
2991
  get showRequired(): boolean;
1040
2992
  get serverErrors(): string[] | null;
2993
+ get isInvalid(): boolean;
1041
2994
  writeValue(value: any): void;
1042
2995
  registerOnChange(fn: (value: any) => void): void;
1043
2996
  registerOnTouched(fn: () => void): void;
1044
2997
  setDisabledState(isDisabled: boolean): void;
2998
+ /** Handler for PrimeNG's onChange event. */
1045
2999
  handleChange(event: any): void;
3000
+ /** Handler for the native checkbox change event (Comptoir variant). */
3001
+ handleNativeChange(rawChecked: boolean): void;
1046
3002
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfCheckboxComponent, never>;
1047
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfCheckboxComponent, "ef-checkbox", never, { "label": { "alias": "label"; "required": false; }; "labelKey": { "alias": "labelKey"; "required": false; }; "inputId": { "alias": "inputId"; "required": false; }; "name": { "alias": "name"; "required": false; }; "value": { "alias": "value"; "required": false; }; "binary": { "alias": "binary"; "required": false; }; "required": { "alias": "required"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; }, {}, never, never, true, never>;
3003
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfCheckboxComponent, "ef-checkbox", never, { "value": { "alias": "value"; "required": false; }; "binary": { "alias": "binary"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "checked": { "alias": "checked"; "required": false; }; }, { "checkedChange": "checkedChange"; }, never, never, true, never>;
1048
3004
  static ngAcceptInputType_binary: unknown;
1049
- static ngAcceptInputType_required: unknown;
1050
- static ngAcceptInputType_disabled: unknown;
1051
3005
  }
1052
3006
 
3007
+ /**
3008
+ * Comptoir toggle switch — wraps `p-toggleswitch`, boolean-only.
3009
+ *
3010
+ * Identity / label / aria / required / readonly / disabled inherited
3011
+ * from `AbstractEfFormControl`. Use for on/off state that applies
3012
+ * immediately (row activation toggles, settings), `ef-checkbox` for
3013
+ * form fields that are saved with the enclosing form.
3014
+ *
3015
+ * ```html
3016
+ * <ef-toggleswitch
3017
+ * [checked]="row.isActive"
3018
+ * [disabled]="!context.hasEditPermission"
3019
+ * ariaLabelKey="sales_clients_toggle_active_aria"
3020
+ * (checkedChange)="setActive(row, $event)" />
3021
+ * ```
3022
+ */
3023
+ declare class EfToggleSwitchComponent extends AbstractEfFormControl implements ControlValueAccessor {
3024
+ /** Two-way bindable `[(checked)]` — boolean only. */
3025
+ checked: boolean;
3026
+ checkedChange: EventEmitter<boolean>;
3027
+ private onChange;
3028
+ private onTouched;
3029
+ readonly ngControl: NgControl;
3030
+ constructor();
3031
+ writeValue(value: unknown): void;
3032
+ registerOnChange(fn: (value: boolean) => void): void;
3033
+ registerOnTouched(fn: () => void): void;
3034
+ setDisabledState(isDisabled: boolean): void;
3035
+ handleChange(event: {
3036
+ checked: boolean;
3037
+ }): void;
3038
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfToggleSwitchComponent, never>;
3039
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfToggleSwitchComponent, "ef-toggleswitch", never, { "checked": { "alias": "checked"; "required": false; }; }, { "checkedChange": "checkedChange"; }, never, never, true, never>;
3040
+ }
3041
+
3042
+ /**
3043
+ * @deprecated Wraps PrimeNG `p-datepicker`, which does not follow the
3044
+ * Comptoir design system. Prefer `ef-datepicker-advanced` in **field
3045
+ * mode** (`mode="field"`, `selectionMode="single" | "range"`) — it is a
3046
+ * Comptoir-native form control (ControlValueAccessor) with the same
3047
+ * `[(ngModel)]` / `formControlName` binding and a `Date` / `[start, end]`
3048
+ * value shape. This wrapper is kept only for legacy V1 screens and will
3049
+ * be removed once they are migrated.
3050
+ */
1053
3051
  declare class EfDatepickerComponent implements ControlValueAccessor {
1054
3052
  label?: string;
1055
3053
  labelKey?: string;
@@ -1101,15 +3099,42 @@ declare class EfDatepickerComponent implements ControlValueAccessor {
1101
3099
  static ngAcceptInputType_showButtonBar: unknown;
1102
3100
  }
1103
3101
 
1104
- declare class EfInputNumberComponent implements ControlValueAccessor {
3102
+ /**
3103
+ * Comptoir number input.
3104
+ *
3105
+ * Two render variants share the same API:
3106
+ * - `'comptoir'` (default) — native `<input type="text" inputmode="decimal">`
3107
+ * styled against `.ef-input` / `.ef-input-group`. Lean, no locale
3108
+ * formatting on display, but data entry accepts both `,` and `.` as
3109
+ * the decimal separator (fr-MA users type commas) and ignores
3110
+ * space group separators; pair with `suffix="MAD"` for unit display
3111
+ * in dense grids (variants table, inventory adjustments, etc.).
3112
+ * See ADR-009.
3113
+ * - `'primeng'` — wraps `p-inputnumber`. Full locale-aware
3114
+ * currency / decimal formatting, stepper buttons, prefix / suffix.
3115
+ *
3116
+ * Identity / label / placeholder / aria / required / readonly /
3117
+ * disabled are inherited from `AbstractEfFormControl`.
3118
+ *
3119
+ * ```html
3120
+ * <ef-inputnumber
3121
+ * labelKey="catalog.price"
3122
+ * variant="primeng"
3123
+ * mode="currency" currency="MAD" locale="fr-MA"
3124
+ * [(value)]="variant.price" />
3125
+ *
3126
+ * <ef-inputnumber
3127
+ * labelKey="catalog.inventory"
3128
+ * variant="comptoir"
3129
+ * [showButtons]="true" buttonLayout="horizontal"
3130
+ * [min]="0" [step]="1"
3131
+ * [(value)]="variant.inventoryQuantity" />
3132
+ * ```
3133
+ */
3134
+ declare class EfInputNumberComponent extends AbstractEfFormControl implements ControlValueAccessor {
1105
3135
  inputNumber?: InputNumber;
1106
- label?: string;
1107
- labelKey?: string;
1108
- required: boolean;
1109
3136
  value?: Nullable<number>;
1110
3137
  invalid: boolean;
1111
- disabled?: boolean;
1112
- readonly: boolean;
1113
3138
  format: boolean;
1114
3139
  showButtons: boolean;
1115
3140
  buttonLayout: 'stacked' | 'horizontal' | 'vertical';
@@ -1130,49 +3155,56 @@ declare class EfInputNumberComponent implements ControlValueAccessor {
1130
3155
  min?: number;
1131
3156
  max?: number;
1132
3157
  step: number;
1133
- allowEmpty: boolean;
1134
- inputId?: string;
1135
- name?: string;
1136
- placeholder?: string;
1137
- placeholderKey?: string;
3158
+ allowEmpty: boolean;
1138
3159
  inputSize?: number;
3160
+ /** Default (undefined) = canonical `--hit-base` (40px); `'small'` = dense 32px. */
1139
3161
  size?: 'small' | 'large';
1140
3162
  maxlength?: number;
1141
3163
  minlength?: number;
1142
3164
  pattern?: string;
1143
3165
  tabindex?: number;
1144
3166
  title?: string;
1145
- ariaLabel?: string;
1146
3167
  ariaLabelledBy?: string;
1147
- ariaDescribedBy?: string;
1148
- ariaRequired?: boolean;
1149
3168
  inputStyle?: any;
1150
3169
  inputStyleClass?: string;
1151
3170
  style?: any;
1152
3171
  styleClass?: string;
1153
- showClear: boolean;
1154
- variant?: 'filled' | 'outlined';
1155
- autofocus?: boolean;
3172
+ /** PrimeNG visual variant (`'filled'` | `'outlined'`) — only
3173
+ * honored when {@link variant} === 'primeng'. */
3174
+ primeNgVariant?: 'filled' | 'outlined';
3175
+ autofocus: boolean;
1156
3176
  autocomplete?: string;
1157
- fluid?: boolean;
3177
+ fluid: boolean;
1158
3178
  inline: boolean;
3179
+ /**
3180
+ * Render variant.
3181
+ * - `'comptoir'` (default) — native `<input type="text" inputmode="decimal">`
3182
+ * styled against `.ef-input` / `.ef-input-group`,
3183
+ * at the canonical `--hit-base` (40px) height.
3184
+ * Accepts `,` and `.` as decimal separator.
3185
+ * - `'primeng'` — wraps `p-inputnumber`. Opt in for PrimeNG's
3186
+ * formatted stepper behaviour.
3187
+ * See ADR-009 for why comptoir is the height-consistent default.
3188
+ */
3189
+ variant: 'primeng' | 'comptoir';
1159
3190
  get isInline(): boolean;
1160
3191
  pt?: any;
1161
3192
  ptOptions?: any;
1162
3193
  dt?: any;
1163
3194
  unstyled?: boolean;
1164
3195
  inputEvent: EventEmitter<InputNumberInputEvent>;
3196
+ valueChangeEvent: EventEmitter<number | null>;
1165
3197
  focusEvent: EventEmitter<Event>;
1166
3198
  blurEvent: EventEmitter<Event>;
1167
3199
  keyDownEvent: EventEmitter<KeyboardEvent>;
1168
3200
  clearEvent: EventEmitter<void>;
3201
+ /** A number field is clearable when it holds any value (including 0). */
3202
+ protected get hasValue(): boolean;
3203
+ clearValue(): void;
1169
3204
  private onChange;
1170
3205
  private onTouched;
1171
3206
  readonly ngControl: NgControl;
1172
- private readonly translateService;
1173
3207
  constructor();
1174
- get effectivePlaceholder(): string;
1175
- get effectiveId(): string;
1176
3208
  get showRequired(): boolean;
1177
3209
  get serverErrors(): string[] | null;
1178
3210
  get isInvalid(): boolean;
@@ -1181,6 +3213,30 @@ declare class EfInputNumberComponent implements ControlValueAccessor {
1181
3213
  registerOnTouched(fn: any): void;
1182
3214
  setDisabledState(isDisabled: boolean): void;
1183
3215
  handleInput(event: InputNumberInputEvent): void;
3216
+ /** Comptoir native input handler — the text input emits a raw
3217
+ * string; normalize + coerce to number or null (empty / invalid). */
3218
+ handleNativeInput(raw: string): void;
3219
+ /**
3220
+ * Parses comptoir free-text numeric entry.
3221
+ *
3222
+ * fr-MA users type commas as the decimal separator (`23,50`), while
3223
+ * `Number()` only understands `.` — so a comma is normalized to a
3224
+ * dot before parsing, and spaces (JS `\s` covers NBSP / narrow-NBSP
3225
+ * group separators) are stripped. Both `23,50` and `23.50` parse to
3226
+ * 23.5; unparseable text yields `null` (existing empty semantics).
3227
+ */
3228
+ private parseComptoirValue;
3229
+ /**
3230
+ * Blur-time clamp for the comptoir variant. The native input no
3231
+ * longer carries `min` / `max` attributes (it's `type="text"`), so
3232
+ * range enforcement — previously only advisory on `type="number"`
3233
+ * anyway — happens here, and the field text is re-synced to the
3234
+ * canonical parsed value (e.g. a dangling `23,` becomes `23`).
3235
+ */
3236
+ private clampAndReflect;
3237
+ /** Comptoir stepper buttons handler — bumps `value` by `step`,
3238
+ * clamped to `min` / `max`. */
3239
+ step$(direction: 1 | -1): void;
1184
3240
  handleFocus(event: Event): void;
1185
3241
  handleBlur(event: Event): void;
1186
3242
  handleKeyDown(event: KeyboardEvent): void;
@@ -1188,20 +3244,14 @@ declare class EfInputNumberComponent implements ControlValueAccessor {
1188
3244
  focus(): void;
1189
3245
  clear(): void;
1190
3246
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfInputNumberComponent, never>;
1191
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfInputNumberComponent, "ef-inputnumber", never, { "label": { "alias": "label"; "required": false; }; "labelKey": { "alias": "labelKey"; "required": false; }; "required": { "alias": "required"; "required": false; }; "value": { "alias": "value"; "required": false; }; "invalid": { "alias": "invalid"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; "format": { "alias": "format"; "required": false; }; "showButtons": { "alias": "showButtons"; "required": false; }; "buttonLayout": { "alias": "buttonLayout"; "required": false; }; "incrementButtonClass": { "alias": "incrementButtonClass"; "required": false; }; "decrementButtonClass": { "alias": "decrementButtonClass"; "required": false; }; "incrementButtonIcon": { "alias": "incrementButtonIcon"; "required": false; }; "decrementButtonIcon": { "alias": "decrementButtonIcon"; "required": false; }; "prefix": { "alias": "prefix"; "required": false; }; "suffix": { "alias": "suffix"; "required": false; }; "currency": { "alias": "currency"; "required": false; }; "currencyDisplay": { "alias": "currencyDisplay"; "required": false; }; "locale": { "alias": "locale"; "required": false; }; "localeMatcher": { "alias": "localeMatcher"; "required": false; }; "mode": { "alias": "mode"; "required": false; }; "useGrouping": { "alias": "useGrouping"; "required": false; }; "minFractionDigits": { "alias": "minFractionDigits"; "required": false; }; "maxFractionDigits": { "alias": "maxFractionDigits"; "required": false; }; "min": { "alias": "min"; "required": false; }; "max": { "alias": "max"; "required": false; }; "step": { "alias": "step"; "required": false; }; "allowEmpty": { "alias": "allowEmpty"; "required": false; }; "inputId": { "alias": "inputId"; "required": false; }; "name": { "alias": "name"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "placeholderKey": { "alias": "placeholderKey"; "required": false; }; "inputSize": { "alias": "inputSize"; "required": false; }; "size": { "alias": "size"; "required": false; }; "maxlength": { "alias": "maxlength"; "required": false; }; "minlength": { "alias": "minlength"; "required": false; }; "pattern": { "alias": "pattern"; "required": false; }; "tabindex": { "alias": "tabindex"; "required": false; }; "title": { "alias": "title"; "required": false; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; }; "ariaLabelledBy": { "alias": "ariaLabelledBy"; "required": false; }; "ariaDescribedBy": { "alias": "ariaDescribedBy"; "required": false; }; "ariaRequired": { "alias": "ariaRequired"; "required": false; }; "inputStyle": { "alias": "inputStyle"; "required": false; }; "inputStyleClass": { "alias": "inputStyleClass"; "required": false; }; "style": { "alias": "style"; "required": false; }; "styleClass": { "alias": "styleClass"; "required": false; }; "showClear": { "alias": "showClear"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "autofocus": { "alias": "autofocus"; "required": false; }; "autocomplete": { "alias": "autocomplete"; "required": false; }; "fluid": { "alias": "fluid"; "required": false; }; "inline": { "alias": "inline"; "required": false; }; "pt": { "alias": "pt"; "required": false; }; "ptOptions": { "alias": "ptOptions"; "required": false; }; "dt": { "alias": "dt"; "required": false; }; "unstyled": { "alias": "unstyled"; "required": false; }; }, { "inputEvent": "inputEvent"; "focusEvent": "focusEvent"; "blurEvent": "blurEvent"; "keyDownEvent": "keyDownEvent"; "clearEvent": "clearEvent"; }, never, never, true, never>;
1192
- static ngAcceptInputType_required: unknown;
3247
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfInputNumberComponent, "ef-inputnumber", never, { "value": { "alias": "value"; "required": false; }; "invalid": { "alias": "invalid"; "required": false; }; "format": { "alias": "format"; "required": false; }; "showButtons": { "alias": "showButtons"; "required": false; }; "buttonLayout": { "alias": "buttonLayout"; "required": false; }; "incrementButtonClass": { "alias": "incrementButtonClass"; "required": false; }; "decrementButtonClass": { "alias": "decrementButtonClass"; "required": false; }; "incrementButtonIcon": { "alias": "incrementButtonIcon"; "required": false; }; "decrementButtonIcon": { "alias": "decrementButtonIcon"; "required": false; }; "prefix": { "alias": "prefix"; "required": false; }; "suffix": { "alias": "suffix"; "required": false; }; "currency": { "alias": "currency"; "required": false; }; "currencyDisplay": { "alias": "currencyDisplay"; "required": false; }; "locale": { "alias": "locale"; "required": false; }; "localeMatcher": { "alias": "localeMatcher"; "required": false; }; "mode": { "alias": "mode"; "required": false; }; "useGrouping": { "alias": "useGrouping"; "required": false; }; "minFractionDigits": { "alias": "minFractionDigits"; "required": false; }; "maxFractionDigits": { "alias": "maxFractionDigits"; "required": false; }; "min": { "alias": "min"; "required": false; }; "max": { "alias": "max"; "required": false; }; "step": { "alias": "step"; "required": false; }; "allowEmpty": { "alias": "allowEmpty"; "required": false; }; "inputSize": { "alias": "inputSize"; "required": false; }; "size": { "alias": "size"; "required": false; }; "maxlength": { "alias": "maxlength"; "required": false; }; "minlength": { "alias": "minlength"; "required": false; }; "pattern": { "alias": "pattern"; "required": false; }; "tabindex": { "alias": "tabindex"; "required": false; }; "title": { "alias": "title"; "required": false; }; "ariaLabelledBy": { "alias": "ariaLabelledBy"; "required": false; }; "inputStyle": { "alias": "inputStyle"; "required": false; }; "inputStyleClass": { "alias": "inputStyleClass"; "required": false; }; "style": { "alias": "style"; "required": false; }; "styleClass": { "alias": "styleClass"; "required": false; }; "primeNgVariant": { "alias": "primeNgVariant"; "required": false; }; "autofocus": { "alias": "autofocus"; "required": false; }; "autocomplete": { "alias": "autocomplete"; "required": false; }; "fluid": { "alias": "fluid"; "required": false; }; "inline": { "alias": "inline"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "pt": { "alias": "pt"; "required": false; }; "ptOptions": { "alias": "ptOptions"; "required": false; }; "dt": { "alias": "dt"; "required": false; }; "unstyled": { "alias": "unstyled"; "required": false; }; }, { "inputEvent": "inputEvent"; "valueChangeEvent": "valueChangeEvent"; "focusEvent": "focusEvent"; "blurEvent": "blurEvent"; "keyDownEvent": "keyDownEvent"; "clearEvent": "clearEvent"; }, never, never, true, never>;
3248
+ static ngAcceptInputType_showButtons: unknown;
3249
+ static ngAcceptInputType_autofocus: unknown;
1193
3250
  static ngAcceptInputType_inline: unknown;
1194
3251
  }
1195
3252
 
1196
- declare class EfPasswordComponent implements ControlValueAccessor {
1197
- label?: string;
1198
- labelKey?: string;
1199
- inputId?: string;
1200
- name?: string;
1201
- placeholder?: string;
1202
- placeholderKey?: string;
1203
- size: 'small' | 'large';
1204
- required: boolean;
3253
+ declare class EfPasswordComponent extends AbstractEfFormControl implements ControlValueAccessor {
3254
+ size?: 'small' | 'large';
1205
3255
  fluid: boolean;
1206
3256
  inline: boolean;
1207
3257
  toggleMask: boolean;
@@ -1210,17 +3260,16 @@ declare class EfPasswordComponent implements ControlValueAccessor {
1210
3260
  get isInline(): boolean;
1211
3261
  valueChangeEvent: EventEmitter<string>;
1212
3262
  value: string;
1213
- disabled: boolean;
1214
3263
  private onChange;
1215
3264
  private onTouched;
1216
3265
  readonly ngControl: NgControl;
1217
- private readonly translateService;
1218
3266
  constructor();
1219
- get effectivePlaceholder(): string;
1220
- get effectiveId(): string;
1221
3267
  get showRequired(): boolean;
1222
3268
  get serverErrors(): string[] | null;
1223
3269
  get isInvalid(): boolean;
3270
+ /** Clearable hooks (showClear / canClear inherited from the base). */
3271
+ protected get hasValue(): boolean;
3272
+ clearValue(): void;
1224
3273
  writeValue(value: string): void;
1225
3274
  registerOnChange(fn: (value: string) => void): void;
1226
3275
  registerOnTouched(fn: () => void): void;
@@ -1228,34 +3277,24 @@ declare class EfPasswordComponent implements ControlValueAccessor {
1228
3277
  handleChange(value: string): void;
1229
3278
  handleBlur(): void;
1230
3279
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfPasswordComponent, never>;
1231
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfPasswordComponent, "ef-password", never, { "label": { "alias": "label"; "required": false; }; "labelKey": { "alias": "labelKey"; "required": false; }; "inputId": { "alias": "inputId"; "required": false; }; "name": { "alias": "name"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "placeholderKey": { "alias": "placeholderKey"; "required": false; }; "size": { "alias": "size"; "required": false; }; "required": { "alias": "required"; "required": false; }; "fluid": { "alias": "fluid"; "required": false; }; "inline": { "alias": "inline"; "required": false; }; "toggleMask": { "alias": "toggleMask"; "required": false; }; "feedback": { "alias": "feedback"; "required": false; }; "autocomplete": { "alias": "autocomplete"; "required": false; }; }, { "valueChangeEvent": "valueChangeEvent"; }, never, never, true, never>;
1232
- static ngAcceptInputType_required: unknown;
3280
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfPasswordComponent, "ef-password", never, { "size": { "alias": "size"; "required": false; }; "fluid": { "alias": "fluid"; "required": false; }; "inline": { "alias": "inline"; "required": false; }; "toggleMask": { "alias": "toggleMask"; "required": false; }; "feedback": { "alias": "feedback"; "required": false; }; "autocomplete": { "alias": "autocomplete"; "required": false; }; }, { "valueChangeEvent": "valueChangeEvent"; }, never, never, true, never>;
1233
3281
  static ngAcceptInputType_fluid: unknown;
1234
3282
  static ngAcceptInputType_inline: unknown;
1235
3283
  static ngAcceptInputType_toggleMask: unknown;
1236
3284
  static ngAcceptInputType_feedback: unknown;
1237
3285
  }
1238
3286
 
1239
- declare class EfMultiSelectComponent implements ControlValueAccessor, OnChanges {
3287
+ declare class EfMultiSelectComponent extends AbstractEfFormControl implements ControlValueAccessor, OnChanges {
1240
3288
  changeEvent: EventEmitter<string[]>;
1241
- label?: string;
1242
- labelKey?: string;
1243
- id: string;
1244
- name: string;
1245
3289
  optionLabel: string;
1246
3290
  optionValue: string;
1247
3291
  options: any;
1248
3292
  filteredOptions: any;
1249
3293
  maxSelectedLabels: number;
1250
- disabled: boolean;
1251
3294
  display: 'comma' | 'chip';
1252
3295
  size: 'large' | 'small';
1253
3296
  selectionLimit: any;
1254
3297
  filters: any;
1255
- placeholder: string;
1256
- placeholderKey?: string;
1257
- showClear: boolean;
1258
- required: boolean;
1259
3298
  inline: boolean;
1260
3299
  get isInline(): boolean;
1261
3300
  get isRequired(): boolean;
@@ -1263,9 +3302,7 @@ declare class EfMultiSelectComponent implements ControlValueAccessor, OnChanges
1263
3302
  private propagateChange;
1264
3303
  private propagateTouched;
1265
3304
  readonly ngControl: NgControl;
1266
- private readonly translateService;
1267
3305
  constructor();
1268
- get effectivePlaceholder(): string;
1269
3306
  get showRequired(): boolean;
1270
3307
  get serverErrors(): string[] | null;
1271
3308
  writeValue(value: string[]): void;
@@ -1277,22 +3314,16 @@ declare class EfMultiSelectComponent implements ControlValueAccessor, OnChanges
1277
3314
  ngOnChanges(): void;
1278
3315
  private applyFilters;
1279
3316
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfMultiSelectComponent, never>;
1280
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfMultiSelectComponent, "ef-multi-select", never, { "label": { "alias": "label"; "required": false; }; "labelKey": { "alias": "labelKey"; "required": false; }; "id": { "alias": "id"; "required": false; }; "name": { "alias": "name"; "required": false; }; "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; }; "disabled": { "alias": "disabled"; "required": false; }; "display": { "alias": "display"; "required": false; }; "size": { "alias": "size"; "required": false; }; "selectionLimit": { "alias": "selectionLimit"; "required": false; }; "filters": { "alias": "filters"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "placeholderKey": { "alias": "placeholderKey"; "required": false; }; "showClear": { "alias": "showClear"; "required": false; }; "required": { "alias": "required"; "required": false; }; "inline": { "alias": "inline"; "required": false; }; }, { "changeEvent": "changeEvent"; }, never, never, true, never>;
1281
- static ngAcceptInputType_showClear: unknown;
3317
+ 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>;
1282
3318
  static ngAcceptInputType_inline: unknown;
1283
3319
  }
1284
3320
 
1285
- declare class EfSelectButtonComponent implements ControlValueAccessor {
1286
- /** Direct label text above the selectbutton (not translated) */
1287
- label?: string;
1288
- /** Translation key for label — takes priority over label */
1289
- labelKey?: string;
3321
+ declare class EfSelectButtonComponent extends AbstractEfFormControl implements ControlValueAccessor {
1290
3322
  options: any[];
1291
3323
  optionLabel: string;
1292
3324
  optionValue: string;
1293
3325
  multiple: boolean;
1294
3326
  allowEmpty: boolean;
1295
- disabled: boolean;
1296
3327
  size: 'small' | 'large';
1297
3328
  styleClass?: string;
1298
3329
  itemTemplate?: TemplateRef<any>;
@@ -1307,10 +3338,9 @@ declare class EfSelectButtonComponent implements ControlValueAccessor {
1307
3338
  setDisabledState(isDisabled: boolean): void;
1308
3339
  handleChange(event: any): void;
1309
3340
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfSelectButtonComponent, never>;
1310
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfSelectButtonComponent, "ef-selectbutton", never, { "label": { "alias": "label"; "required": false; }; "labelKey": { "alias": "labelKey"; "required": false; }; "options": { "alias": "options"; "required": false; }; "optionLabel": { "alias": "optionLabel"; "required": false; }; "optionValue": { "alias": "optionValue"; "required": false; }; "multiple": { "alias": "multiple"; "required": false; }; "allowEmpty": { "alias": "allowEmpty"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "size": { "alias": "size"; "required": false; }; "styleClass": { "alias": "styleClass"; "required": false; }; }, {}, ["itemTemplate"], never, true, never>;
3341
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfSelectButtonComponent, "ef-selectbutton", never, { "options": { "alias": "options"; "required": false; }; "optionLabel": { "alias": "optionLabel"; "required": false; }; "optionValue": { "alias": "optionValue"; "required": false; }; "multiple": { "alias": "multiple"; "required": false; }; "allowEmpty": { "alias": "allowEmpty"; "required": false; }; "size": { "alias": "size"; "required": false; }; "styleClass": { "alias": "styleClass"; "required": false; }; }, {}, ["itemTemplate"], never, true, never>;
1311
3342
  static ngAcceptInputType_multiple: unknown;
1312
3343
  static ngAcceptInputType_allowEmpty: unknown;
1313
- static ngAcceptInputType_disabled: unknown;
1314
3344
  }
1315
3345
 
1316
3346
  /**
@@ -1749,6 +3779,1033 @@ declare class EfDatatableActionBarComponent {
1749
3779
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfDatatableActionBarComponent, "ef-datatable-actionbar", never, { "context": { "alias": "context"; "required": false; }; "state": { "alias": "state"; "required": false; }; "duplicateButton": { "alias": "duplicateButton"; "required": false; }; "editButton": { "alias": "editButton"; "required": false; }; "deleteButton": { "alias": "deleteButton"; "required": false; }; }, { "duplicate": "duplicate"; "edit": "edit"; "delete": "delete"; }, never, never, true, never>;
1750
3780
  }
1751
3781
 
3782
+ /**
3783
+ * Declarative row-action item consumed by `ef-row-actions`.
3784
+ *
3785
+ * The component renders a native Comptoir-styled popup (no PrimeNG
3786
+ * Menu) so the design — kbd hint, danger tone, divider, ink-active
3787
+ * trigger when open — matches the prototype 1:1.
3788
+ *
3789
+ * Use `command` for actions, `separator: true` for a divider line.
3790
+ */
3791
+ interface EfRowAction {
3792
+ /** Stable identifier for trackBy / tests. */
3793
+ id?: string;
3794
+ /** Translation key — preferred. */
3795
+ labelKey?: string;
3796
+ /** Direct label — used only when `labelKey` is empty. */
3797
+ label?: string;
3798
+ /** PrimeIcons class (e.g. `'pi pi-pencil'`). */
3799
+ icon?: string;
3800
+ /** Optional keyboard-shortcut hint shown right-aligned (e.g. `'↵'`, `'E'`, `'⌘D'`). */
3801
+ kbd?: string;
3802
+ /** Tone: `'danger'` styles destructive items in the cancelled palette. */
3803
+ severity?: 'default' | 'danger';
3804
+ /** Render a divider in this slot — all other fields ignored. */
3805
+ separator?: boolean;
3806
+ /** Disable without removing. */
3807
+ disabled?: boolean;
3808
+ /**
3809
+ * Hide the item explicitly. Combined with `permission` and any
3810
+ * caller-supplied filter; defaults to `true` (visible).
3811
+ */
3812
+ visible?: boolean;
3813
+ /**
3814
+ * If set, the item is hidden unless the bound `ScreenContext`
3815
+ * grants this permission. Items with no `permission` always show.
3816
+ */
3817
+ permission?: Permissions;
3818
+ /** Click handler. The container handles closing the popup. */
3819
+ command?: () => void;
3820
+ }
3821
+
3822
+ /** Action identifier emitted by `ef-data-card`'s auto row-actions cell. */
3823
+ type EfDataCardRowAction = 'view' | 'edit' | 'duplicate' | 'delete';
3824
+ /**
3825
+ * Comptoir data card — column-driven `.tbl-wrap` with header (count
3826
+ * info + actions) and integrated pager.
3827
+ *
3828
+ * Two consumer surfaces:
3829
+ * 1. **Columns + rows + templates** (preferred): declare columns in
3830
+ * TS as `EfDataCardColumn[]`, project body / header templates via
3831
+ * `*efColumnTemplate` / `*efColumnHeaderTemplate` for cells that
3832
+ * need rich HTML. Built-in cell renderers handle text / number /
3833
+ * money / date / datetime / boolean / mono / chip / reference.
3834
+ * 2. **`tbl-head-info` / `tbl-head-actions` slots**: still projected
3835
+ * so the consumer can render the count text + density / columns /
3836
+ * view-toggle buttons next to the table header.
3837
+ *
3838
+ * ```html
3839
+ * <ef-data-card
3840
+ * [columns]="cols"
3841
+ * [rows]="orders()"
3842
+ * [pageNumber]="criteria().pagination.pageNumber"
3843
+ * [pageSize]="criteria().pagination.pageSize"
3844
+ * [totalCount]="totalCount()"
3845
+ * [loading]="loading()"
3846
+ * [sort]="currentSort()"
3847
+ * (pageChange)="setPage($event)"
3848
+ * (sortChange)="setSort($event.field, $event.direction)"
3849
+ * trackByField="id"
3850
+ * >
3851
+ * <span tbl-head-info>…count…</span>
3852
+ *
3853
+ * <ng-template efColumnTemplate="status" let-row>
3854
+ * <span class="chip" [class]="'chip chip-' + row.status">…</span>
3855
+ * </ng-template>
3856
+ * </ef-data-card>
3857
+ * ```
3858
+ */
3859
+ declare class EfDataCardComponent<TRow = any> implements AfterContentInit {
3860
+ private readonly refDataService;
3861
+ readonly columns: _angular_core.InputSignal<readonly EfDataCardColumn[]>;
3862
+ readonly rows: _angular_core.InputSignal<readonly TRow[]>;
3863
+ /** Field path on each row used as the trackBy key. */
3864
+ readonly trackByField: _angular_core.InputSignal<string>;
3865
+ readonly pageNumber: _angular_core.InputSignal<number>;
3866
+ readonly pageSize: _angular_core.InputSignal<number>;
3867
+ readonly totalCount: _angular_core.InputSignal<number>;
3868
+ readonly pageSizeOptions: _angular_core.InputSignal<readonly number[]>;
3869
+ readonly hidePager: _angular_core.InputSignalWithTransform<boolean, unknown>;
3870
+ /** Currently active sort. Header click toggles asc / desc. */
3871
+ readonly sort: _angular_core.InputSignal<EfDataCardSort>;
3872
+ readonly loading: _angular_core.InputSignalWithTransform<boolean, unknown>;
3873
+ /** Empty string = no error. Non-empty triggers an error chip in tbl-head. */
3874
+ readonly errorMsg: _angular_core.InputSignal<string>;
3875
+ readonly loadingKey: _angular_core.InputSignal<string>;
3876
+ /**
3877
+ * When `true`, double-clicking anywhere on a `<tr>` emits the row
3878
+ * via `rowDoubleClick` and the body rows render with
3879
+ * `cursor: pointer`. Consumers typically wire the output to
3880
+ * `navigateToDetails(row.id)` from {@link AbstractSearchScreenV2}.
3881
+ *
3882
+ * Double-clicks that originate inside an interactive child
3883
+ * (checkbox, button, link, form control) are ignored — those keep
3884
+ * their native behaviour (toggle selection, run an action, …).
3885
+ */
3886
+ readonly rowDoubleClickable: _angular_core.InputSignalWithTransform<boolean, unknown>;
3887
+ readonly hasActionsColumn: _angular_core.InputSignalWithTransform<boolean, unknown>;
3888
+ readonly rowActionsContext: _angular_core.InputSignal<ScreenContext>;
3889
+ readonly showViewAction: _angular_core.InputSignalWithTransform<boolean, unknown>;
3890
+ readonly showEditAction: _angular_core.InputSignalWithTransform<boolean, unknown>;
3891
+ readonly showDuplicateAction: _angular_core.InputSignalWithTransform<boolean, unknown>;
3892
+ readonly showDeleteAction: _angular_core.InputSignalWithTransform<boolean, unknown>;
3893
+ readonly pageChange: _angular_core.OutputEmitterRef<number>;
3894
+ readonly pageSizeChange: _angular_core.OutputEmitterRef<number>;
3895
+ readonly sortChange: _angular_core.OutputEmitterRef<EfDataCardSort>;
3896
+ readonly rowDoubleClick: _angular_core.OutputEmitterRef<TRow>;
3897
+ /** Emitted by the auto row-actions cell — `{ action, row }`. */
3898
+ readonly rowAction: _angular_core.OutputEmitterRef<{
3899
+ action: EfDataCardRowAction;
3900
+ row: TRow;
3901
+ }>;
3902
+ private readonly bodyTemplates;
3903
+ private readonly headerTemplates;
3904
+ private readonly bodyTemplateMap;
3905
+ private readonly headerTemplateMap;
3906
+ ngAfterContentInit(): void;
3907
+ /** Columns enriched with effective alignment / sortField defaults
3908
+ * + the auto `'actions'` column when `[hasActionsColumn]` is on
3909
+ * and the consumer hasn't declared one already. */
3910
+ readonly effectiveColumns: _angular_core.Signal<readonly EfDataCardColumn[]>;
3911
+ /** Items array for the auto row-actions cell. Rebuilt per call so
3912
+ * the ef-row-actions component receives a fresh closure per row.
3913
+ * Visibility is filtered later by ef-row-actions against
3914
+ * `rowActionsContext` (ScreenContext.isGranted). */
3915
+ defaultRowActions(row: TRow): ReadonlyArray<EfRowAction>;
3916
+ bodyTemplate(columnId: string): TemplateRef<unknown> | undefined;
3917
+ headerTemplate(columnId: string): TemplateRef<unknown> | undefined;
3918
+ /** Pluck `column.field` (or `column.id`) from a row, dotted-path safe. */
3919
+ cellValue(row: TRow, col: EfDataCardColumn): unknown;
3920
+ headerAlignClass(col: EfDataCardColumn): string;
3921
+ cellAlignClass(col: EfDataCardColumn): string;
3922
+ /** Sort indicator: '↑' / '↓' / '' for the given column. */
3923
+ sortIndicator(col: EfDataCardColumn): string;
3924
+ /**
3925
+ * Resolve a reference cell — looks up the row's value in
3926
+ * `referenceKey`, finds the entry where
3927
+ * `entry[referenceValueField] === value`, returns
3928
+ * `entry[referenceLabelField]`. Falls back to the raw value when
3929
+ * no match is found.
3930
+ */
3931
+ resolveReference(value: unknown, col: EfDataCardColumn): string;
3932
+ /** Build an Angular DigitInfo string from min/max fraction-digit hints. */
3933
+ numberFormat(col: EfDataCardColumn): string;
3934
+ /** Tracks how many `ef-row-actions` are currently open inside this
3935
+ * card. When > 0, `.tbl-wrap` lifts its `overflow: hidden` so the
3936
+ * popup can escape its rounded edges. */
3937
+ readonly openMenuCount: _angular_core.WritableSignal<number>;
3938
+ onRowActionsToggle(event: Event): void;
3939
+ /** Fires `rowDoubleClick` unless the dblclick originated on an
3940
+ * interactive child (checkbox, button, link, form control). */
3941
+ onRowDoubleClick(row: TRow, event: MouseEvent): void;
3942
+ onHeaderClick(col: EfDataCardColumn): void;
3943
+ trackByRow: (_: number, row: TRow) => unknown;
3944
+ trackByColumn: (_: number, col: EfDataCardColumn) => string;
3945
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfDataCardComponent<any>, never>;
3946
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfDataCardComponent<any>, "ef-data-card", never, { "columns": { "alias": "columns"; "required": false; "isSignal": true; }; "rows": { "alias": "rows"; "required": false; "isSignal": true; }; "trackByField": { "alias": "trackByField"; "required": false; "isSignal": true; }; "pageNumber": { "alias": "pageNumber"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "totalCount": { "alias": "totalCount"; "required": false; "isSignal": true; }; "pageSizeOptions": { "alias": "pageSizeOptions"; "required": false; "isSignal": true; }; "hidePager": { "alias": "hidePager"; "required": false; "isSignal": true; }; "sort": { "alias": "sort"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "errorMsg": { "alias": "errorMsg"; "required": false; "isSignal": true; }; "loadingKey": { "alias": "loadingKey"; "required": false; "isSignal": true; }; "rowDoubleClickable": { "alias": "rowDoubleClickable"; "required": false; "isSignal": true; }; "hasActionsColumn": { "alias": "hasActionsColumn"; "required": false; "isSignal": true; }; "rowActionsContext": { "alias": "rowActionsContext"; "required": false; "isSignal": true; }; "showViewAction": { "alias": "showViewAction"; "required": false; "isSignal": true; }; "showEditAction": { "alias": "showEditAction"; "required": false; "isSignal": true; }; "showDuplicateAction": { "alias": "showDuplicateAction"; "required": false; "isSignal": true; }; "showDeleteAction": { "alias": "showDeleteAction"; "required": false; "isSignal": true; }; }, { "pageChange": "pageChange"; "pageSizeChange": "pageSizeChange"; "sortChange": "sortChange"; "rowDoubleClick": "rowDoubleClick"; "rowAction": "rowAction"; }, ["bodyTemplates", "headerTemplates"], ["[tbl-head-info]", "[tbl-head-actions]", "[empty-state]"], true, never>;
3947
+ }
3948
+
3949
+ /**
3950
+ * Marks an `<ng-template>` as the body cell renderer for a given
3951
+ * column id on `<ef-data-card>`.
3952
+ *
3953
+ * ```html
3954
+ * <ng-template efColumnTemplate="status" let-row let-col="col">
3955
+ * <span class="chip" [class]="'chip chip-' + row.status">…</span>
3956
+ * </ng-template>
3957
+ * ```
3958
+ *
3959
+ * Context:
3960
+ * - `$implicit` — the row data
3961
+ * - `col` — the column config (`EfDataCardColumn`)
3962
+ * - `index` — the row index in the current page
3963
+ */
3964
+ declare class EfColumnTemplateDirective {
3965
+ readonly templateRef: TemplateRef<unknown>;
3966
+ columnId: string;
3967
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfColumnTemplateDirective, never>;
3968
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<EfColumnTemplateDirective, "[efColumnTemplate]", never, { "columnId": { "alias": "efColumnTemplate"; "required": true; }; }, {}, never, never, true, never>;
3969
+ }
3970
+ /**
3971
+ * Marks an `<ng-template>` as the **header** cell renderer for a
3972
+ * given column id. Use this when the header needs richer markup
3973
+ * than a translated string (e.g. a select-all checkbox).
3974
+ *
3975
+ * ```html
3976
+ * <ng-template efColumnHeaderTemplate="select">
3977
+ * <input type="checkbox" class="cb"
3978
+ * [checked]="allSelected()" (change)="toggleAll()" />
3979
+ * </ng-template>
3980
+ * ```
3981
+ */
3982
+ declare class EfColumnHeaderTemplateDirective {
3983
+ readonly templateRef: TemplateRef<unknown>;
3984
+ columnId: string;
3985
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfColumnHeaderTemplateDirective, never>;
3986
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<EfColumnHeaderTemplateDirective, "[efColumnHeaderTemplate]", never, { "columnId": { "alias": "efColumnHeaderTemplate"; "required": true; }; }, {}, never, never, true, never>;
3987
+ }
3988
+
3989
+ /**
3990
+ * Row-actions dropdown — V2 successor to `ef-datatable-actionbar`.
3991
+ *
3992
+ * Renders a discreet trailing-cell ⋯ trigger (34×28 pill) that opens
3993
+ * a small Comptoir-styled menu with the items declared in TS:
3994
+ *
3995
+ * ```ts
3996
+ * actions: ReadonlyArray<EfRowAction> = [
3997
+ * { id: 'view', labelKey: 'common_view', icon: 'pi pi-eye', kbd: '↵',
3998
+ * permission: Permissions.Read, command: () => view(row) },
3999
+ * { id: 'edit', labelKey: 'common_edit', icon: 'pi pi-pencil', kbd: 'E',
4000
+ * permission: Permissions.Edit, command: () => edit(row) },
4001
+ * { id: 'duplicate', labelKey: 'common_duplicate', icon: 'pi pi-copy',
4002
+ * command: () => duplicate(row) },
4003
+ * { separator: true },
4004
+ * { id: 'delete', labelKey: 'common_delete', icon: 'pi pi-trash', severity: 'danger',
4005
+ * permission: Permissions.Delete, command: () => delete(row) },
4006
+ * ];
4007
+ * ```
4008
+ *
4009
+ * ```html
4010
+ * <ng-template efColumnTemplate="actions" let-row>
4011
+ * <ef-row-actions [items]="rowActions(row)" [context]="context" />
4012
+ * </ng-template>
4013
+ * ```
4014
+ *
4015
+ * Behaviour:
4016
+ * - Click the trigger to toggle. Trigger gets `.open` (ink-active) while the menu is open.
4017
+ * - Click outside or press `Escape` to close.
4018
+ * - Clicking an item runs its `command` then closes the menu.
4019
+ * - dblclick on the trigger or menu does NOT propagate, so the data-card's
4020
+ * `rowDoubleClickable` won't fire from this column.
4021
+ *
4022
+ * Permission filtering: pass `[context]="..."` (a `ScreenContext`); items
4023
+ * declaring a `permission` are hidden unless that permission is granted.
4024
+ * Items with no `permission` always show.
4025
+ */
4026
+ declare class EfRowActionsComponent {
4027
+ private readonly host;
4028
+ /** Action descriptors. */
4029
+ readonly items: _angular_core.InputSignal<readonly EfRowAction[]>;
4030
+ /** Optional context — when present, items with a `permission` are
4031
+ * filtered against `context.isGranted(permission)`. */
4032
+ readonly context: _angular_core.InputSignal<ScreenContext>;
4033
+ /** Override the trigger icon (default ellipsis). */
4034
+ readonly triggerIcon: _angular_core.InputSignal<string>;
4035
+ /** Override the trigger aria-label translation key. */
4036
+ readonly triggerAriaKey: _angular_core.InputSignal<string>;
4037
+ /** Disable the entire trigger (e.g., row not actionable). */
4038
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
4039
+ /**
4040
+ * Pin the menu to the inline-start edge of the trigger instead of
4041
+ * inline-end. Use when the actions cell sits at the start of the row.
4042
+ */
4043
+ readonly menuLeft: _angular_core.InputSignalWithTransform<boolean, unknown>;
4044
+ /** Forwarded when the menu opens (e.g., for a data-card to lift overflow). */
4045
+ readonly opened: _angular_core.OutputEmitterRef<void>;
4046
+ /** Forwarded when the menu closes. */
4047
+ readonly closed: _angular_core.OutputEmitterRef<void>;
4048
+ /** Visible items (after permission + visible filtering). */
4049
+ readonly visibleItems: _angular_core.Signal<readonly EfRowAction[]>;
4050
+ /** Open state. */
4051
+ readonly open: _angular_core.WritableSignal<boolean>;
4052
+ toggle(event: MouseEvent): void;
4053
+ runCommand(action: EfRowAction, event: MouseEvent): void;
4054
+ close(): void;
4055
+ /** Bubbling DOM event so ancestors (e.g. ef-data-card) can react —
4056
+ * see `.tbl-wrap.has-open-menu { overflow: visible }`. */
4057
+ private dispatchToggle;
4058
+ /** Outside click — closes the menu. */
4059
+ onDocumentClick(event: MouseEvent): void;
4060
+ /** Escape closes the menu. */
4061
+ onEscape(): void;
4062
+ /** Swallow dblclick so row-double-click doesn't fire from this column. */
4063
+ onDblClick(event: MouseEvent): void;
4064
+ private isVisible;
4065
+ /** Stable trackBy across renders. */
4066
+ trackByItem: (i: number, action: EfRowAction) => unknown;
4067
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfRowActionsComponent, never>;
4068
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfRowActionsComponent, "ef-row-actions", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "context": { "alias": "context"; "required": false; "isSignal": true; }; "triggerIcon": { "alias": "triggerIcon"; "required": false; "isSignal": true; }; "triggerAriaKey": { "alias": "triggerAriaKey"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "menuLeft": { "alias": "menuLeft"; "required": false; "isSignal": true; }; }, { "opened": "opened"; "closed": "closed"; }, never, never, true, never>;
4069
+ }
4070
+
4071
+ interface PageBtn {
4072
+ kind: 'page' | 'ellipsis';
4073
+ value: number;
4074
+ }
4075
+ /**
4076
+ * Pagination control matching the Comptoir prototype's `.pager`
4077
+ * shape (lines-per-page selector · range · page buttons).
4078
+ *
4079
+ * ```html
4080
+ * <ef-pager
4081
+ * [pageNumber]="criteria().pagination.pageNumber"
4082
+ * [pageSize]="criteria().pagination.pageSize"
4083
+ * [totalCount]="totalCount()"
4084
+ * (pageChange)="setPage($event)"
4085
+ * (pageSizeChange)="setPage(1, $event)"
4086
+ * />
4087
+ * ```
4088
+ *
4089
+ * Renders a windowed page list (first / current ±1 / last with
4090
+ * ellipsis) for total > 7 pages. Below 7 pages all buttons show.
4091
+ */
4092
+ declare class EfPagerComponent {
4093
+ readonly pageNumber: _angular_core.InputSignal<number>;
4094
+ readonly pageSize: _angular_core.InputSignal<number>;
4095
+ readonly totalCount: _angular_core.InputSignal<number>;
4096
+ readonly pageSizeOptions: _angular_core.InputSignal<readonly number[]>;
4097
+ /** Translation keys (preferred — consumer-side i18n). */
4098
+ readonly linesPerPageLabelKey: _angular_core.InputSignal<string>;
4099
+ readonly ofLabelKey: _angular_core.InputSignal<string>;
4100
+ readonly prevLabelKey: _angular_core.InputSignal<string>;
4101
+ readonly nextLabelKey: _angular_core.InputSignal<string>;
4102
+ /** Hide the lines-per-page selector + range when only paging is wanted. */
4103
+ readonly hideSize: _angular_core.InputSignal<boolean>;
4104
+ readonly pageChange: _angular_core.OutputEmitterRef<number>;
4105
+ readonly pageSizeChange: _angular_core.OutputEmitterRef<number>;
4106
+ readonly totalPages: _angular_core.Signal<number>;
4107
+ readonly range: _angular_core.Signal<{
4108
+ start: number;
4109
+ end: number;
4110
+ }>;
4111
+ /** First page · current ±1 · last page, with ellipsis fillers. */
4112
+ readonly pageButtons: _angular_core.Signal<readonly PageBtn[]>;
4113
+ goToPage(page: number): void;
4114
+ onSizeChange(value: string): void;
4115
+ trackBtn(index: number, btn: PageBtn): string;
4116
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfPagerComponent, never>;
4117
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfPagerComponent, "ef-pager", never, { "pageNumber": { "alias": "pageNumber"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "totalCount": { "alias": "totalCount"; "required": false; "isSignal": true; }; "pageSizeOptions": { "alias": "pageSizeOptions"; "required": false; "isSignal": true; }; "linesPerPageLabelKey": { "alias": "linesPerPageLabelKey"; "required": false; "isSignal": true; }; "ofLabelKey": { "alias": "ofLabelKey"; "required": false; "isSignal": true; }; "prevLabelKey": { "alias": "prevLabelKey"; "required": false; "isSignal": true; }; "nextLabelKey": { "alias": "nextLabelKey"; "required": false; "isSignal": true; }; "hideSize": { "alias": "hideSize"; "required": false; "isSignal": true; }; }, { "pageChange": "pageChange"; "pageSizeChange": "pageSizeChange"; }, never, never, true, never>;
4118
+ }
4119
+
4120
+ /**
4121
+ * Comptoir bulk-action bar — ink-active pill that shows a selection
4122
+ * count + projected action buttons. Auto-hides when count is 0.
4123
+ *
4124
+ * ```html
4125
+ * <ef-bulk-bar [count]="selectionCount()">
4126
+ * <button class="btn">{{ 'common_export' | translate }}</button>
4127
+ * <button class="btn">{{ 'sales_orders_bulk_approve' | translate }}</button>
4128
+ * </ef-bulk-bar>
4129
+ * ```
4130
+ */
4131
+ declare class EfBulkBarComponent {
4132
+ readonly count: _angular_core.InputSignal<number>;
4133
+ /** Translation key for the suffix word ("selected"). */
4134
+ readonly suffixKey: _angular_core.InputSignal<string>;
4135
+ /** Direct suffix text (used only when `suffixKey` is empty). */
4136
+ readonly suffix: _angular_core.InputSignal<string>;
4137
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfBulkBarComponent, never>;
4138
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfBulkBarComponent, "ef-bulk-bar", never, { "count": { "alias": "count"; "required": false; "isSignal": true; }; "suffixKey": { "alias": "suffixKey"; "required": false; "isSignal": true; }; "suffix": { "alias": "suffix"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
4139
+ }
4140
+
4141
+ /**
4142
+ * Comptoir empty-state — circular dashed icon + title + optional
4143
+ * message + projected actions slot. Drop into a data-card body when
4144
+ * results are zero.
4145
+ *
4146
+ * ```html
4147
+ * <ef-empty-state
4148
+ * icon="pi pi-inbox"
4149
+ * titleKey="sales_orders_empty_title"
4150
+ * messageKey="sales_orders_empty_msg">
4151
+ * <button class="btn btn-tenant btn-sm">{{ 'sales_orders_new' | translate }}</button>
4152
+ * </ef-empty-state>
4153
+ * ```
4154
+ */
4155
+ declare class EfEmptyStateComponent {
4156
+ readonly icon: _angular_core.InputSignal<string>;
4157
+ readonly titleKey: _angular_core.InputSignal<string>;
4158
+ readonly title: _angular_core.InputSignal<string>;
4159
+ readonly messageKey: _angular_core.InputSignal<string>;
4160
+ readonly message: _angular_core.InputSignal<string>;
4161
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfEmptyStateComponent, never>;
4162
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfEmptyStateComponent, "ef-empty-state", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "titleKey": { "alias": "titleKey"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "messageKey": { "alias": "messageKey"; "required": false; "isSignal": true; }; "message": { "alias": "message"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
4163
+ }
4164
+
4165
+ /**
4166
+ * Comptoir smart bar — the search/filter card every list screen
4167
+ * starts with. Composes:
4168
+ * 1. Search input (with `⌘K` kbd hint) + Effacer / Rechercher
4169
+ * action buttons.
4170
+ * 2. A filter-row slot for `[filters]` (pill-group + filter-pill +
4171
+ * preset-pill + "+ Plus de filtres").
4172
+ * 3. A drawer slot for `[drawer]` (advanced filters via
4173
+ * `ef-filter-drawer`).
4174
+ *
4175
+ * The `searchText` is a `model()` so consumers can `[(searchText)]`
4176
+ * for two-way binding, or watch the (search) output for explicit
4177
+ * Rechercher clicks.
4178
+ *
4179
+ * ```html
4180
+ * <ef-smart-bar
4181
+ * [(searchText)]="searchQuery"
4182
+ * placeholderKey="sales_orders_search_placeholder"
4183
+ * (search)="setSearchText($event)"
4184
+ * (clear)="clearAll()"
4185
+ * >
4186
+ * <ng-container filters>
4187
+ * <ef-pill-group …/>
4188
+ * <ef-filter-pill …/>
4189
+ * <ef-preset-pill …/>
4190
+ * </ng-container>
4191
+ * <ng-container drawer>
4192
+ * <ef-filter-drawer …/>
4193
+ * </ng-container>
4194
+ * </ef-smart-bar>
4195
+ * ```
4196
+ */
4197
+ declare class EfSmartBarComponent {
4198
+ /** Two-way bound search input. */
4199
+ readonly searchText: _angular_core.ModelSignal<string>;
4200
+ readonly placeholderKey: _angular_core.InputSignal<string>;
4201
+ readonly placeholder: _angular_core.InputSignal<string>;
4202
+ readonly clearLabelKey: _angular_core.InputSignal<string>;
4203
+ readonly clearTitleKey: _angular_core.InputSignal<string>;
4204
+ readonly searchLabelKey: _angular_core.InputSignal<string>;
4205
+ readonly showKbdHint: _angular_core.InputSignalWithTransform<boolean, unknown>;
4206
+ readonly hideClear: _angular_core.InputSignalWithTransform<boolean, unknown>;
4207
+ readonly hideSearch: _angular_core.InputSignalWithTransform<boolean, unknown>;
4208
+ readonly loading: _angular_core.InputSignalWithTransform<boolean, unknown>;
4209
+ readonly search: _angular_core.OutputEmitterRef<string>;
4210
+ readonly clear: _angular_core.OutputEmitterRef<void>;
4211
+ onSubmit(): void;
4212
+ onClear(): void;
4213
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfSmartBarComponent, never>;
4214
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfSmartBarComponent, "ef-smart-bar", never, { "searchText": { "alias": "searchText"; "required": false; "isSignal": true; }; "placeholderKey": { "alias": "placeholderKey"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "clearLabelKey": { "alias": "clearLabelKey"; "required": false; "isSignal": true; }; "clearTitleKey": { "alias": "clearTitleKey"; "required": false; "isSignal": true; }; "searchLabelKey": { "alias": "searchLabelKey"; "required": false; "isSignal": true; }; "showKbdHint": { "alias": "showKbdHint"; "required": false; "isSignal": true; }; "hideClear": { "alias": "hideClear"; "required": false; "isSignal": true; }; "hideSearch": { "alias": "hideSearch"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; }, { "searchText": "searchTextChange"; "search": "search"; "clear": "clear"; }, never, ["[filters]", "[drawer]"], true, never>;
4215
+ }
4216
+
4217
+ /**
4218
+ * Comptoir filter pill — value-bearing button used in the smart-bar's
4219
+ * filter row. Three tones (`neutral` / `active` / `tenant`), optional
4220
+ * caret (when the pill toggles a popover), optional ✕ clear button.
4221
+ *
4222
+ * The popover content is consumer-projected via the `[popover]` slot
4223
+ * — the pill positions it absolutely below itself per the prototype's
4224
+ * `.popover` class. Visibility is driven by the `popoverOpen` input.
4225
+ *
4226
+ * ```html
4227
+ * <!-- value-bearing, removable -->
4228
+ * <ef-filter-pill
4229
+ * icon="pi pi-user"
4230
+ * labelKey="filter_client"
4231
+ * value="Leila Bennani"
4232
+ * tone="active"
4233
+ * hasClear
4234
+ * (clearClick)="removeFilter('client')"
4235
+ * />
4236
+ *
4237
+ * <!-- with popover (consumer renders preset list inside) -->
4238
+ * <ef-filter-pill
4239
+ * icon="pi pi-calendar"
4240
+ * labelKey="filter_date"
4241
+ * [value]="activeDatePreset().labelKey | translate"
4242
+ * hasPopover
4243
+ * [popoverOpen]="datePopoverOpen()"
4244
+ * (pillClick)="toggleDatePopover()"
4245
+ * >
4246
+ * <ng-container popover>
4247
+ * @for (p of presets; track p.key) {
4248
+ * <div class="preset" [class.active]="p === active">…</div>
4249
+ * }
4250
+ * </ng-container>
4251
+ * </ef-filter-pill>
4252
+ * ```
4253
+ */
4254
+ declare class EfFilterPillComponent {
4255
+ /** PrimeNG icon class (e.g. `pi pi-calendar`). */
4256
+ readonly icon: _angular_core.InputSignal<string>;
4257
+ /** Translation key — preferred. */
4258
+ readonly labelKey: _angular_core.InputSignal<string>;
4259
+ /** Direct label text (used only when `labelKey` is empty). */
4260
+ readonly label: _angular_core.InputSignal<string>;
4261
+ /** Current value displayed after the label. */
4262
+ readonly value: _angular_core.InputSignal<string>;
4263
+ /** `'active'` paints ink-active; `'tenant'` paints the tenant-50 surface. */
4264
+ readonly tone: _angular_core.InputSignal<"active" | "tenant" | "neutral">;
4265
+ /** Show the ✕ clear button at the end of the pill. */
4266
+ readonly hasClear: _angular_core.InputSignalWithTransform<boolean, unknown>;
4267
+ /** Show a chevron + position the projected `[popover]` slot. */
4268
+ readonly hasPopover: _angular_core.InputSignalWithTransform<boolean, unknown>;
4269
+ /** Whether the popover is currently open (consumer-controlled). */
4270
+ readonly popoverOpen: _angular_core.InputSignalWithTransform<boolean, unknown>;
4271
+ /** Translation key for the ✕ clear button's aria-label. */
4272
+ readonly clearAriaKey: _angular_core.InputSignal<string>;
4273
+ /** Emitted when the pill body is clicked (open popover, etc.). */
4274
+ readonly pillClick: _angular_core.OutputEmitterRef<void>;
4275
+ /** Emitted when the ✕ clear button is clicked (stops propagation). */
4276
+ readonly clearClick: _angular_core.OutputEmitterRef<void>;
4277
+ onPillClick(): void;
4278
+ onClearClick(event: Event): void;
4279
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfFilterPillComponent, never>;
4280
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfFilterPillComponent, "ef-filter-pill", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "hasClear": { "alias": "hasClear"; "required": false; "isSignal": true; }; "hasPopover": { "alias": "hasPopover"; "required": false; "isSignal": true; }; "popoverOpen": { "alias": "popoverOpen"; "required": false; "isSignal": true; }; "clearAriaKey": { "alias": "clearAriaKey"; "required": false; "isSignal": true; }; }, { "pillClick": "pillClick"; "clearClick": "clearClick"; }, never, ["[popover]"], true, never>;
4281
+ }
4282
+
4283
+ /**
4284
+ * Scopes a map of server/validation errors to its descendant `ef-*` form
4285
+ * controls, which auto-link by their `name` — no per-field `[errors]`
4286
+ * binding needed.
4287
+ *
4288
+ * Drop it on any container that wraps the form fields and bind the screen's
4289
+ * `serverErrors()` signal once:
4290
+ *
4291
+ * ```html
4292
+ * <div class="detail-grid" [efServerErrors]="serverErrors()">
4293
+ * <ef-input-text name="companyName" ... /> <!-- picks up errors['companyName'] -->
4294
+ * <ef-select name="clientType" ... /> <!-- picks up errors['clientType'] -->
4295
+ * </div>
4296
+ * ```
4297
+ *
4298
+ * Each control resolves its messages via `AbstractEfFormControl`
4299
+ * (`resolvedExternalErrors`): an explicit `[errors]` input still wins when
4300
+ * present, otherwise the control falls back to this scope keyed by `name`.
4301
+ * Keys must match the control `name` (camelCase) — `AbstractScreenComponent.setServerErrors`
4302
+ * already normalizes the backend's PascalCase keys, so the map is camelCase.
4303
+ *
4304
+ * The input is a signal, so updates propagate to controls under zoneless
4305
+ * change detection (controls read it through a `computed`).
4306
+ */
4307
+ declare class EfServerErrorsDirective {
4308
+ /** Field-name → messages map (camelCase keys, matching control `name`). */
4309
+ readonly errors: _angular_core.InputSignal<{
4310
+ [key: string]: string[];
4311
+ }>;
4312
+ /** Messages for a control by its `name`, or `null` when none. */
4313
+ errorsFor(name: string | undefined | null): string[] | null;
4314
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfServerErrorsDirective, never>;
4315
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<EfServerErrorsDirective, "[efServerErrors]", never, { "errors": { "alias": "efServerErrors"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
4316
+ }
4317
+
4318
+ /**
4319
+ * The shared inline "clear" affordance — a circular button with the canonical
4320
+ * stroke-✕ glyph. One component so every clearable control (ef-input-text,
4321
+ * ef-inputnumber, ef-select via its `#clearicon` template, ef-product-typeahead,
4322
+ * …) shows the exact same clear icon, hover circle, and hit area.
4323
+ *
4324
+ * Styling lives in `_patterns.scss` under `.ef-clear` (global, so the same
4325
+ * glyph is reachable from PrimeNG templates that can't import this component).
4326
+ *
4327
+ * `mousedown` is prevented so clicking the clear doesn't blur/commit the field
4328
+ * before the `clear` handler runs.
4329
+ */
4330
+ declare class EfClearButtonComponent {
4331
+ ariaLabel: string;
4332
+ disabled: boolean;
4333
+ clear: EventEmitter<Event>;
4334
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfClearButtonComponent, never>;
4335
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfClearButtonComponent, "ef-clear-button", never, { "ariaLabel": { "alias": "ariaLabel"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; }, { "clear": "clear"; }, never, never, true, never>;
4336
+ }
4337
+
4338
+ /**
4339
+ * Native Comptoir textarea — no PrimeNG wrapper. Pairs with the
4340
+ * `.ef-textarea` rule in `@elasticias/ui/styles/patterns` (focus
4341
+ * ring, paper-alt bg, rule border).
4342
+ *
4343
+ * Identity / label / placeholder / aria / required / readonly /
4344
+ * disabled inherited from `AbstractEfFormControl`. Implements
4345
+ * ControlValueAccessor so it slots into reactive forms AND works
4346
+ * with `[(value)]` two-way binding via `(valueChangeEvent)`.
4347
+ *
4348
+ * ```html
4349
+ * <ef-textarea
4350
+ * labelKey="products_details_description_label"
4351
+ * placeholderKey="products_details_description_placeholder"
4352
+ * [rows]="4"
4353
+ * [(value)]="description"
4354
+ * />
4355
+ * ```
4356
+ */
4357
+ declare class EfTextareaComponent extends AbstractEfFormControl implements ControlValueAccessor {
4358
+ rows: number;
4359
+ maxlength?: number;
4360
+ autofocus: boolean;
4361
+ valueChangeEvent: EventEmitter<string>;
4362
+ /** Two-way-bindable as `[value]` (one-way) or
4363
+ * `[(value)]="signal()"` (set + listen via `valueChangeEvent`). */
4364
+ value: string;
4365
+ /** Clearable (showClear / canClear) inherited from AbstractEfFormControl. */
4366
+ protected get hasValue(): boolean;
4367
+ clearValue(): void;
4368
+ private onChange;
4369
+ private onTouched;
4370
+ readonly ngControl: NgControl;
4371
+ constructor();
4372
+ get isInvalid(): boolean;
4373
+ get serverErrors(): string[] | null;
4374
+ get showRequired(): boolean;
4375
+ writeValue(value: string): void;
4376
+ registerOnChange(fn: (value: string) => void): void;
4377
+ registerOnTouched(fn: () => void): void;
4378
+ setDisabledState(isDisabled: boolean): void;
4379
+ handleInput(value: string): void;
4380
+ handleBlur(): void;
4381
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfTextareaComponent, never>;
4382
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfTextareaComponent, "ef-textarea", never, { "rows": { "alias": "rows"; "required": false; }; "maxlength": { "alias": "maxlength"; "required": false; }; "autofocus": { "alias": "autofocus"; "required": false; }; "value": { "alias": "value"; "required": false; }; }, { "valueChangeEvent": "valueChangeEvent"; }, never, never, true, never>;
4383
+ static ngAcceptInputType_autofocus: unknown;
4384
+ }
4385
+
4386
+ interface EfPresetItem<T = string> {
4387
+ /** Stable identifier (used as the `value`). */
4388
+ key: T;
4389
+ /** Translation key for the row label (preferred). */
4390
+ labelKey?: string;
4391
+ /** Direct label (used only when `labelKey` is empty). */
4392
+ label?: string;
4393
+ /** Mono hint to the right (date range, count, …). */
4394
+ hint?: string;
4395
+ }
4396
+ /**
4397
+ * Filter pill that opens a list of presets on click. Consumer-side
4398
+ * use cases: date ranges (`Aujourd'hui · 30 derniers jours · …`),
4399
+ * non-date enums (`Jamais connecté`), any short canned list.
4400
+ *
4401
+ * State model: `open` is a `model()` so callers can let the pill
4402
+ * manage its own visibility (default) or hoist it into a parent
4403
+ * signal for fully controlled use (`[(open)]`).
4404
+ *
4405
+ * ```html
4406
+ * <ef-preset-pill
4407
+ * icon="pi pi-calendar"
4408
+ * labelKey="filter_date"
4409
+ * [presets]="datePresets"
4410
+ * [(activeKey)]="activeDateKey"
4411
+ * (presetChange)="onDateRangeChange($event)"
4412
+ * />
4413
+ * ```
4414
+ */
4415
+ declare class EfPresetPillComponent<T = string> {
4416
+ private readonly translate;
4417
+ readonly icon: _angular_core.InputSignal<string>;
4418
+ readonly labelKey: _angular_core.InputSignal<string>;
4419
+ readonly label: _angular_core.InputSignal<string>;
4420
+ readonly tone: _angular_core.InputSignal<"active" | "tenant" | "neutral">;
4421
+ readonly presets: _angular_core.InputSignal<readonly EfPresetItem<T>[]>;
4422
+ readonly activeKey: _angular_core.ModelSignal<T>;
4423
+ /** Two-way; consumer can hoist for controlled mode. */
4424
+ readonly open: _angular_core.ModelSignal<boolean>;
4425
+ /** Auto-close the popover after selection (default on). */
4426
+ readonly closeOnSelect: _angular_core.InputSignalWithTransform<boolean, unknown>;
4427
+ readonly presetChange: _angular_core.OutputEmitterRef<EfPresetItem<T>>;
4428
+ /** Displayed value — translates `labelKey` of the active preset if present. */
4429
+ readonly activeLabel: _angular_core.Signal<any>;
4430
+ toggle(): void;
4431
+ select(preset: EfPresetItem<T>): void;
4432
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfPresetPillComponent<any>, never>;
4433
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfPresetPillComponent<any>, "ef-preset-pill", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "presets": { "alias": "presets"; "required": true; "isSignal": true; }; "activeKey": { "alias": "activeKey"; "required": false; "isSignal": true; }; "open": { "alias": "open"; "required": false; "isSignal": true; }; "closeOnSelect": { "alias": "closeOnSelect"; "required": false; "isSignal": true; }; }, { "activeKey": "activeKeyChange"; "open": "openChange"; "presetChange": "presetChange"; }, never, never, true, never>;
4434
+ }
4435
+
4436
+ /**
4437
+ * One row in the picker's presets list. UI-specific — carries the
4438
+ * optional `compute` and `hintFn` callbacks that the picker invokes
4439
+ * at render time. Stays in @elasticias/ui because it's a render-time
4440
+ * descriptor, not a state shape.
4441
+ */
4442
+ interface EfDatePreset {
4443
+ /** Stable key. Built-in keys (`today`, `this_week`, …) are computed
4444
+ * automatically; custom keys must come with a date computer. */
4445
+ key: _elasticias_screens.EfDatePresetKey;
4446
+ /** Translation key for the row label. */
4447
+ labelKey?: string;
4448
+ /** Direct label fallback when `labelKey` is empty. */
4449
+ label?: string;
4450
+ /**
4451
+ * Computer for non-built-in presets — given today, return start/end
4452
+ * (inclusive). For built-in keys this is provided by the component.
4453
+ */
4454
+ compute?: (today: Date) => {
4455
+ start: Date;
4456
+ end: Date;
4457
+ };
4458
+ /** Optional secondary text right-aligned (e.g. `'5 j'`, `'T2'`). */
4459
+ hintFn?: (today: Date) => string;
4460
+ }
4461
+
4462
+ /** Value shape emitted / accepted by the field-mode ControlValueAccessor.
4463
+ * Mirrors PrimeNG's `p-datepicker`: a lone `Date` for single selection,
4464
+ * a `[start, end]` tuple for a range — so `ef-datepicker-advanced` is a
4465
+ * drop-in replacement for the legacy `ef-datepicker` in forms. */
4466
+ type EfDateFieldValue = Date | [Date, Date] | null;
4467
+ /**
4468
+ * Comptoir period selector for smart-bars — a pill trigger ("Date — 30
4469
+ * derniers jours v") that opens a popover with a preset list and an
4470
+ * optional custom-range view (full month calendar with range band).
4471
+ *
4472
+ * Replaces the legacy `ef-preset-pill` for date filters and the
4473
+ * date-range mini-popover that screens used to roll inline.
4474
+ *
4475
+ * ```html
4476
+ * <ef-datepicker-advanced
4477
+ * [activePreset]="dateFilter().presetKey"
4478
+ * [start]="dateFilter().start"
4479
+ * [end]="dateFilter().end"
4480
+ * triggerLabelKey="filter_date"
4481
+ * (rangeChange)="onDateChange($event)"
4482
+ * />
4483
+ * ```
4484
+ *
4485
+ * Built-in presets (computed from `today` at open time):
4486
+ * `today`, `this_week`, `this_month`, `last_30_days`, `last_90_days`,
4487
+ * `this_quarter`, `this_year`. Override with `[presets]="customList"`
4488
+ * if you need a different set.
4489
+ */
4490
+ declare class EfDatepickerAdvancedComponent extends AbstractEfFormControl implements ControlValueAccessor, AfterViewInit {
4491
+ private readonly host;
4492
+ private readonly locale;
4493
+ private readonly vcr;
4494
+ private readonly destroyRef;
4495
+ private readonly cdr;
4496
+ /**
4497
+ * Self-injected form control (present only when the consumer binds
4498
+ * `[(ngModel)]` / `formControlName` in field mode). We register as its
4499
+ * `valueAccessor` manually — same pattern as `ef-datepicker` — so no
4500
+ * `NG_VALUE_ACCESSOR` provider is needed and there's no circular DI.
4501
+ * `null` in filter mode (the output-only smart-bar usage).
4502
+ */
4503
+ readonly ngControl: NgControl;
4504
+ /** Always-present wrapper — the anchor for portal positioning (the
4505
+ * trigger itself lives behind an `@if`, so we anchor to the host box). */
4506
+ private anchorEl;
4507
+ /** Panel template — instantiated and attached to <body> on open. */
4508
+ private panelTpl;
4509
+ /** Active embedded view + its root node, while the panel is open. */
4510
+ private panelView;
4511
+ private panelRoot;
4512
+ private resizeHandler;
4513
+ constructor();
4514
+ ngAfterViewInit(): void;
4515
+ /** Trigger left-side label (translation key). Default: `'filter_date'`. */
4516
+ readonly triggerLabelKey: _angular_core.InputSignal<string>;
4517
+ /** Direct label fallback when `triggerLabelKey` is empty. */
4518
+ readonly triggerLabel: _angular_core.InputSignal<string>;
4519
+ /**
4520
+ * Override the default preset list. The component ships sensible
4521
+ * defaults for `today`, `this_week`, `this_month`, `last_30_days`,
4522
+ * `last_90_days`, `this_quarter`, `this_year` — pass anything to
4523
+ * replace them outright (e.g. include a `last_year` preset).
4524
+ */
4525
+ readonly presets: _angular_core.InputSignal<readonly EfDatePreset[]>;
4526
+ /** Currently active preset key (controlled). */
4527
+ readonly activePreset: _angular_core.InputSignal<string>;
4528
+ /** Currently selected start (controlled — pairs with `activePreset`). */
4529
+ readonly start: _angular_core.InputSignal<Date>;
4530
+ /** Currently selected end. */
4531
+ readonly end: _angular_core.InputSignal<Date>;
4532
+ /** Show a `'Personnalisé…'` row that opens the calendar view. */
4533
+ readonly allowCustomRange: _angular_core.InputSignalWithTransform<boolean, unknown>;
4534
+ /** Component behaviour: smart-bar filter vs. form field. */
4535
+ readonly mode: _angular_core.InputSignal<"filter" | "field">;
4536
+ /** Field mode only — pick a single date or a `[start, end]` range. */
4537
+ readonly selectionMode: _angular_core.InputSignal<"single" | "range">;
4538
+ /** Field mode trigger appearance: a labelled field box (default) or
4539
+ * the rounded pill used by filter mode. */
4540
+ readonly variant: _angular_core.InputSignal<"field" | "pill">;
4541
+ /**
4542
+ * Field mode only — also pick a time (24h `HH:mm`, free minutes).
4543
+ * Single: the calendar no longer commits on day-click; a time field +
4544
+ * Apply appear. Range: an `HH:mm` field sits under each of Du / Au.
4545
+ * The committed value is a `Date` with hours/minutes set (not
4546
+ * start-of-day) and the trigger renders `DD/MM/YYYY HH:mm`.
4547
+ */
4548
+ readonly showTime: _angular_core.InputSignalWithTransform<boolean, unknown>;
4549
+ /** Emitted whenever a preset or applied custom range changes. */
4550
+ readonly rangeChange: _angular_core.OutputEmitterRef<EfDateRange>;
4551
+ /** Forwarded when the popover opens / closes (mirrors ef-row-actions). */
4552
+ readonly opened: _angular_core.OutputEmitterRef<void>;
4553
+ readonly closed: _angular_core.OutputEmitterRef<void>;
4554
+ readonly open: _angular_core.WritableSignal<boolean>;
4555
+ readonly view: _angular_core.WritableSignal<"custom" | "presets">;
4556
+ /**
4557
+ * Custom view sub-mode: the day grid, the month picker, or the year
4558
+ * picker. Clicking the month / year labels in the calendar header
4559
+ * drills into the corresponding picker; selecting a cell returns to
4560
+ * the day grid.
4561
+ */
4562
+ readonly calMode: _angular_core.WritableSignal<"days" | "months" | "years">;
4563
+ /** First year shown in the 12-cell year picker grid. */
4564
+ readonly yearWindowStart: _angular_core.WritableSignal<number>;
4565
+ /** Custom view: month being displayed (1st of the month, 00:00). */
4566
+ readonly visibleMonth: _angular_core.WritableSignal<Date>;
4567
+ /** Custom view: working start/end, before the user clicks Apply. */
4568
+ readonly draftStart: _angular_core.WritableSignal<Date>;
4569
+ readonly draftEnd: _angular_core.WritableSignal<Date>;
4570
+ /** Effective preset list — defaults if none provided. */
4571
+ readonly effectivePresets: _angular_core.Signal<readonly EfDatePreset[]>;
4572
+ /** Current value resolved against today's date. */
4573
+ readonly resolvedRange: _angular_core.Signal<EfDateRange>;
4574
+ /** 7×6 grid for the visible month, prev/next-month overflow days included. */
4575
+ readonly calendarGrid: _angular_core.Signal<readonly {
4576
+ date: Date;
4577
+ day: number;
4578
+ out: boolean;
4579
+ today: boolean;
4580
+ inRange: boolean;
4581
+ rangeStart: boolean;
4582
+ rangeEnd: boolean;
4583
+ }[]>;
4584
+ /** Day-of-week labels from CLDR (Mon-first). */
4585
+ readonly dowLabels: _angular_core.Signal<readonly string[]>;
4586
+ /** Month + year title for the calendar header. */
4587
+ readonly monthLabel: _angular_core.Signal<string>;
4588
+ readonly yearLabel: _angular_core.Signal<string>;
4589
+ /** Range label for the year-picker header, e.g. `2024 – 2035`. */
4590
+ readonly yearWindowLabel: _angular_core.Signal<string>;
4591
+ /** 12 abbreviated months for the month picker (Jan…Déc), CLDR-localised. */
4592
+ readonly monthCells: _angular_core.Signal<{
4593
+ month: number;
4594
+ label: string;
4595
+ active: boolean;
4596
+ today: boolean;
4597
+ }[]>;
4598
+ /** 12 consecutive years for the year picker, starting at `yearWindowStart`. */
4599
+ readonly yearCells: _angular_core.Signal<{
4600
+ year: number;
4601
+ active: boolean;
4602
+ today: boolean;
4603
+ }[]>;
4604
+ /** Live duration in days for the draft range. Mirrors `applyCustomRange`:
4605
+ * a lone start is treated as a single-day range (→ "1 jour"). */
4606
+ readonly draftDurationDays: _angular_core.Signal<number>;
4607
+ /** Du / Au inputs as DD/MM/YYYY strings (kept in sync with drafts). */
4608
+ readonly fromText: _angular_core.Signal<string>;
4609
+ readonly toText: _angular_core.Signal<string>;
4610
+ /** Committed value pushed in via `writeValue` / day-pick / Apply. */
4611
+ private readonly fieldValue;
4612
+ /** Committed start / end derived from `fieldValue`. */
4613
+ readonly fieldStart: _angular_core.Signal<Date>;
4614
+ readonly fieldEnd: _angular_core.Signal<Date>;
4615
+ /** Whether the field currently holds a value (vs. placeholder). */
4616
+ readonly hasFieldValue: _angular_core.Signal<boolean>;
4617
+ /** Text rendered in the field-mode trigger (date, +time when enabled). */
4618
+ readonly fieldDisplay: _angular_core.Signal<string>;
4619
+ readonly draftStartTime: _angular_core.WritableSignal<{
4620
+ h: number;
4621
+ m: number;
4622
+ }>;
4623
+ readonly draftEndTime: _angular_core.WritableSignal<{
4624
+ h: number;
4625
+ m: number;
4626
+ }>;
4627
+ /** Du / Au time inputs as `HH:mm` strings (kept in sync with drafts). */
4628
+ readonly fromTimeText: _angular_core.Signal<string>;
4629
+ readonly toTimeText: _angular_core.Signal<string>;
4630
+ private onChange;
4631
+ private onTouched;
4632
+ /** Required error is shown once the control is touched. */
4633
+ get showRequired(): boolean;
4634
+ /** Backend validation messages stashed under `serverError`. */
4635
+ get serverErrors(): string[] | null;
4636
+ /** Invalid + interacted — drives the red border on the field box. */
4637
+ get isInvalid(): boolean;
4638
+ writeValue(value: Date | Date[] | string | number | null | undefined): void;
4639
+ registerOnChange(fn: (value: EfDateFieldValue) => void): void;
4640
+ registerOnTouched(fn: () => void): void;
4641
+ setDisabledState(isDisabled: boolean): void;
4642
+ protected get hasValue(): boolean;
4643
+ clearValue(): void;
4644
+ /** Normalise whatever a form pushes in to our internal value shape.
4645
+ * Tolerates `null`, `[start, null]`, strings/epochs, and stray
4646
+ * multi-element arrays (keeps the first two). */
4647
+ private coerceValue;
4648
+ private toDate;
4649
+ /** Commit a field-mode value: update internal state, notify the form,
4650
+ * and close the popover. `single` → `Date`, `range` → `[start, end]`. */
4651
+ private commitField;
4652
+ /** Merge a day with a draft time. Returns start-of-day when `showTime`
4653
+ * is off, so date-only commits stay time-free. */
4654
+ private composeDateTime;
4655
+ /** Format a draft time as zero-padded `HH:mm`. */
4656
+ private formatTime;
4657
+ /** Parse `H:mm` … `HH:mm` (24h) → `{h, m}`, or null when out of range. */
4658
+ private parseTimeInput;
4659
+ /** Allow only digits and `:` while typing a time; Enter commits via blur. */
4660
+ onTimeKeydown(event: KeyboardEvent, el: HTMLInputElement): void;
4661
+ /** Commit a typed `HH:mm` value to the start or end draft time. */
4662
+ commitTimeInput(which: 'start' | 'end', el: HTMLInputElement): void;
4663
+ toggle(event: MouseEvent): void;
4664
+ close(): void;
4665
+ onDocumentClick(event: MouseEvent): void;
4666
+ onEscape(): void;
4667
+ selectPreset(preset: EfDatePreset): void;
4668
+ isActivePreset(key: EfDatePresetKey): boolean;
4669
+ presetHint(preset: EfDatePreset): string;
4670
+ openCustomView(): void;
4671
+ backToPresets(): void;
4672
+ headerPrev(): void;
4673
+ headerNext(): void;
4674
+ private stepHeader;
4675
+ /** Open the month picker (triggered by clicking the month label). */
4676
+ openMonthPicker(): void;
4677
+ /** Open the year picker (triggered by clicking the year label). */
4678
+ openYearPicker(): void;
4679
+ /** Pick a month → jump the day grid to it. */
4680
+ selectMonth(month: number): void;
4681
+ /** Pick a year → keep the current month, return to the day grid. */
4682
+ selectYear(year: number): void;
4683
+ /** Allow only digits and `/` for printable keys; Enter commits via blur. */
4684
+ onDateKeydown(event: KeyboardEvent, el: HTMLInputElement): void;
4685
+ /** Commit a typed `JJ/MM/AAAA` value to the start or end draft. */
4686
+ commitDateInput(which: 'start' | 'end', el: HTMLInputElement): void;
4687
+ private attachPortal;
4688
+ private detachPortal;
4689
+ /** Anchor the panel's top-left to the trigger's bottom-left, using
4690
+ * page coords so the panel scrolls with the trigger naturally.
4691
+ * Flips to right-aligned (and clamps to the viewport) when the
4692
+ * default left-anchor would overflow the right edge — otherwise the
4693
+ * panel is clipped for triggers that sit near the right of the page. */
4694
+ private positionPanel;
4695
+ /** Click handling: 1st click sets start, 2nd click sets end (or swap).
4696
+ * Field single-date mode short-circuits — one click commits & closes. */
4697
+ selectDay(day: Date): void;
4698
+ applyCustomRange(): void;
4699
+ /** Field single-date mode: commit a typed `JJ/MM/AAAA` value & close.
4700
+ * With `showTime`, only update the draft — Apply commits date + time. */
4701
+ commitSingleInput(el: HTMLInputElement): void;
4702
+ /** Default preset list — built-in semantics, all keys recognised. */
4703
+ private readonly defaultPresets;
4704
+ private seedDraftFromValue;
4705
+ /** Compute the date range for a preset key. */
4706
+ private computePresetRange;
4707
+ /** Assemble an EfDateRange — exposes `labelKey` (for the trigger
4708
+ * to render via `| translate`) and a literal `label` fallback. */
4709
+ private buildRange;
4710
+ private builtInRange;
4711
+ private builtInHint;
4712
+ /** 6 weeks × 7 days. Each cell carries its date + flags. */
4713
+ private buildMonthGrid;
4714
+ private buildDowLabels;
4715
+ private startOfDay;
4716
+ private startOfMonth;
4717
+ private addDays;
4718
+ private addMonths;
4719
+ private daysBetween;
4720
+ private formatDayInput;
4721
+ /** Parse a `J/M/AAAA`…`JJ/MM/AAAA` string to a start-of-day Date, or
4722
+ * null when malformed or out of range (e.g. `31/02/2026`). */
4723
+ private parseDayInput;
4724
+ /** First year of the 12-cell window containing `year` (e.g. 2026 → 2016). */
4725
+ private startOfYearWindow;
4726
+ /** Stable trackBy across renders. */
4727
+ trackByPreset: (_: number, p: EfDatePreset) => string;
4728
+ trackByDay: (_: number, day: {
4729
+ date: Date;
4730
+ }) => number;
4731
+ trackByDow: (_: number, label: string) => string;
4732
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfDatepickerAdvancedComponent, never>;
4733
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfDatepickerAdvancedComponent, "ef-datepicker-advanced", never, { "triggerLabelKey": { "alias": "triggerLabelKey"; "required": false; "isSignal": true; }; "triggerLabel": { "alias": "triggerLabel"; "required": false; "isSignal": true; }; "presets": { "alias": "presets"; "required": false; "isSignal": true; }; "activePreset": { "alias": "activePreset"; "required": false; "isSignal": true; }; "start": { "alias": "start"; "required": false; "isSignal": true; }; "end": { "alias": "end"; "required": false; "isSignal": true; }; "allowCustomRange": { "alias": "allowCustomRange"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "selectionMode": { "alias": "selectionMode"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "showTime": { "alias": "showTime"; "required": false; "isSignal": true; }; }, { "rangeChange": "rangeChange"; "opened": "opened"; "closed": "closed"; }, never, never, true, never>;
4734
+ }
4735
+
4736
+ /**
4737
+ * Comptoir advanced-filter drawer — collapsible card that lives at
4738
+ * the bottom of the smart-bar. Consumer projects their grid of
4739
+ * `<div class="field">` controls; the component handles the open
4740
+ * animation, the footer Reset / Apply buttons, and the optional hint
4741
+ * line on the left of the footer.
4742
+ *
4743
+ * ```html
4744
+ * <ef-filter-drawer
4745
+ * [open]="drawerOpen()"
4746
+ * hintKey="sales_orders_adv_hint"
4747
+ * (apply)="runSearch()"
4748
+ * (reset)="clearAll()"
4749
+ * >
4750
+ * <div class="field">
4751
+ * <label for="adv-employee">{{ 'sales_orders_adv_employee' | translate }}</label>
4752
+ * <select id="adv-employee">…</select>
4753
+ * </div>
4754
+ * …
4755
+ * </ef-filter-drawer>
4756
+ * ```
4757
+ */
4758
+ declare class EfFilterDrawerComponent {
4759
+ readonly open: _angular_core.InputSignalWithTransform<boolean, unknown>;
4760
+ readonly hintKey: _angular_core.InputSignal<string>;
4761
+ readonly hint: _angular_core.InputSignal<string>;
4762
+ readonly resetLabelKey: _angular_core.InputSignal<string>;
4763
+ readonly applyLabelKey: _angular_core.InputSignal<string>;
4764
+ readonly hideApply: _angular_core.InputSignalWithTransform<boolean, unknown>;
4765
+ readonly apply: _angular_core.OutputEmitterRef<void>;
4766
+ readonly reset: _angular_core.OutputEmitterRef<void>;
4767
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfFilterDrawerComponent, never>;
4768
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfFilterDrawerComponent, "ef-filter-drawer", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "hintKey": { "alias": "hintKey"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "resetLabelKey": { "alias": "resetLabelKey"; "required": false; "isSignal": true; }; "applyLabelKey": { "alias": "applyLabelKey"; "required": false; "isSignal": true; }; "hideApply": { "alias": "hideApply"; "required": false; "isSignal": true; }; }, { "apply": "apply"; "reset": "reset"; }, never, ["*"], true, never>;
4769
+ }
4770
+
4771
+ /**
4772
+ * Comptoir activation tri-state filter — a `.field`-anatomy pill group
4773
+ * (all / active / inactive) for boolean activation criteria
4774
+ * (`IsActive: bool?` pattern).
4775
+ *
4776
+ * `value` is the criteria value itself: `true`, `false`, or
4777
+ * `undefined`/`null` for "all". `valueChange` emits the same shape, so
4778
+ * the component binds straight onto a search screen's advanced-filter
4779
+ * state; pair with `AbstractSearchScreenV2.activationFilterKey` for
4780
+ * apply / baseline-clear / cache-restore.
4781
+ *
4782
+ * ```html
4783
+ * <ef-activation-filter
4784
+ * [value]="advancedValues()['isActive']"
4785
+ * (valueChange)="setAdvancedValue('isActive', $event)"
4786
+ * />
4787
+ * ```
4788
+ */
4789
+ declare class EfActivationFilterComponent {
4790
+ private static nextId;
4791
+ readonly labelId: string;
4792
+ /** Current criteria value: `true` / `false` / `undefined` (= all). */
4793
+ readonly value: _angular_core.InputSignal<unknown>;
4794
+ readonly labelKey: _angular_core.InputSignal<string>;
4795
+ readonly allLabelKey: _angular_core.InputSignal<string>;
4796
+ readonly activeLabelKey: _angular_core.InputSignal<string>;
4797
+ readonly inactiveLabelKey: _angular_core.InputSignal<string>;
4798
+ readonly valueChange: _angular_core.OutputEmitterRef<boolean>;
4799
+ readonly selected: _angular_core.Signal<"active" | "inactive" | "all">;
4800
+ readonly buckets: _angular_core.Signal<{
4801
+ key: string;
4802
+ labelKey: string;
4803
+ value: boolean;
4804
+ }[]>;
4805
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfActivationFilterComponent, never>;
4806
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfActivationFilterComponent, "ef-activation-filter", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "allLabelKey": { "alias": "allLabelKey"; "required": false; "isSignal": true; }; "activeLabelKey": { "alias": "activeLabelKey"; "required": false; "isSignal": true; }; "inactiveLabelKey": { "alias": "inactiveLabelKey"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
4807
+ }
4808
+
1752
4809
  /**
1753
4810
  * Theme configurator panel for switching PrimeNG presets, primary colors,
1754
4811
  * surface palettes, ripple effect, and RTL direction.
@@ -1864,5 +4921,5 @@ declare class EfEmailMetricsCardComponent {
1864
4921
  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>;
1865
4922
  }
1866
4923
 
1867
- export { EF_BADGE_BG_COLORS, EF_BADGE_BG_LIGHT_COLORS, EF_BADGE_BORDER_COLORS, EF_BADGE_DEFAULTS, EF_BADGE_TEXT_COLORS, EF_DATATABLE_ACTIONS_DEFAULTS, EF_DATATABLE_COLUMN_DEFAULTS, EF_DATATABLE_DEFAULTS, EfBadgeComponent, EfBlockUiComponent, EfBlockableDivComponent, EfButtonComponent, EfCheckboxComponent, EfCurrencyPipe, EfDatatableActionBarComponent, EfDatatableComponent, EfDatepickerComponent, EfDialogComponent, EfEmailMetricsCardComponent, EfFieldsetComponent, EfInputNumberComponent, EfInputTextComponent, EfLabelComponent, EfMultiSelectComponent, EfOrderBuilderComponent, EfOrderSummaryComponent, EfPasswordComponent, EfPriceDisplayComponent, EfProductCatalogueComponent, EfProductCatalogueFilterComponent, EfQuantitySelectorComponent, EfQuantityStepperComponent, EfSelectButtonComponent, EfSelectComponent, EfThemeConfiguratorComponent, EfToolbarComponent, OrderEntityHelper, OrderLineItemHelper, TruncatePipe, getColumnAlignment, mergeColumnDefaults };
1868
- export type { CatalogueProduct, CatalogueProductVariant, CategoryAssignment, CategoryGroup, CategoryItem, DatatableSearchEntity, DocumentConfig, EfBadgeColor, EfBadgeConfig, EfBadgeSize, EfBadgeVariant, EfDatatableActions, EfDatatableColumn, EfDatatableColumnAlign, EfDatatableColumnType, EfDatatableConfig, OrderChangeInfo, OrderEntity, OrderLineItem, OrderProductsSummary, OrderSummaryItem, ProductCountGroup, ProductCountGroupLabel, ProductWithCountGroup, SelectedFilter, ValidationResult };
4924
+ 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_STATUS_COLORS, EF_STATUS_COLOR_ALIASES, EfActivationFilterComponent, EfAppMainComponent, EfAppShellComponent, EfAppShellMobileComponent, EfAppTopComponent, EfBadgeComponent, EfBlockUiComponent, EfBlockableDivComponent, EfBottomSheetComponent, EfBulkBarComponent, EfButtonComponent, EfButtonGroupComponent, EfCardComponent, EfChangeHistoryComponent, EfChartComponent, EfCheckboxComponent, EfClearButtonComponent, EfColumnHeaderTemplateDirective, EfColumnTemplateDirective, EfCompactPipe, EfCurrencyPipe, EfDataCardComponent, EfDatatableActionBarComponent, EfDatatableComponent, EfDatepickerAdvancedComponent, EfDatepickerComponent, EfDetailToolbarComponent, EfDialogComponent, EfEmailMetricsCardComponent, EfEmptyStateComponent, EfFabComponent, EfFieldsetComponent, EfFilterDrawerComponent, EfFilterPillComponent, EfFormGridComponent, EfInputNumberComponent, EfInputTextComponent, EfKpiCardComponent, EfLabelComponent, EfModuleRailComponent, EfModuleSideComponent, EfMultiSelectComponent, EfOrderBuilderComponent, EfOrderSummaryComponent, EfPagerComponent, EfPasswordComponent, EfPdfPreviewComponent, EfPillGroupComponent, EfPresetPillComponent, EfPriceDisplayComponent, EfProductCatalogueComponent, EfProductCatalogueFilterComponent, EfProductTypeaheadComponent, EfProfileChipComponent, EfPulseComponent, EfQuantitySelectorComponent, EfQuantityStepperComponent, EfRankListComponent, EfRowActionsComponent, EfSearchToolbarComponent, EfSelectButtonComponent, EfSelectComponent, EfServerErrorsDirective, EfSkeletonComponent, EfSmartBarComponent, EfStatsComponent, EfStatusChipComponent, EfTabPanelDirective, EfTabsComponent, EfTextareaComponent, EfThemeConfiguratorComponent, EfTimelineComponent, EfToastRegionComponent, EfToggleSwitchComponent, EfToolbarComponent, EfTotalsComponent, OrderEntityHelper, OrderLineItemHelper, TruncatePipe, buildChartConfig, getColumnAlignment, mergeColumnDefaults, resolveEfStatusColor };
4925
+ export type { CatalogueProduct, CatalogueProductVariant, CategoryAssignment, CategoryGroup, CategoryItem, DatatableSearchEntity, DocumentConfig, EfBadgeColor, EfBadgeConfig, EfBadgeSize, EfBadgeVariant, EfButtonComptoirSeverity, EfButtonPrimeNGSeverity, EfButtonSeverity, EfChangeHistoryEntry, EfChangeHistoryFieldChange, EfChartSeries, EfChartType, EfDataCardRowAction, EfDatatableActions, EfDatatableColumn, EfDatatableColumnAlign, EfDatatableColumnType, EfDatatableConfig, EfDateFieldValue, EfDatePreset, EfDialogAction, EfDialogSeverity, EfDialogSize, EfFabPosition, EfKpiDeltaTone, EfKpiSparkline, EfPillItem, EfPresetItem, EfRankRow, EfRowAction, 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 };