@elasticias/ui 1.0.48 → 1.0.50

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,8 +1,10 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { PipeTransform, OnInit, OnChanges, TemplateRef, EventEmitter, AfterContentInit, OnDestroy, SimpleChanges, InjectionToken, AfterViewInit } from '@angular/core';
2
+ import { PipeTransform, OnInit, EventEmitter, TemplateRef, OnChanges, AfterContentInit, OnDestroy, SimpleChanges, InjectionToken, AfterViewInit } from '@angular/core';
3
3
  import * as _elasticias_screens from '@elasticias/screens';
4
4
  import { EfDataCardColumn, ScreenContext, EfDetailToolbarAction, EfSearchToolbarAction, EfDataCardSort, EfDataCardExportRequest, EfDateRange, EfDatePresetKey } from '@elasticias/screens';
5
5
  export { EfBoundedDateRange, EfDataCardColumn, EfDataCardColumnAlign, EfDataCardColumnMobileRole, EfDataCardColumnType, EfDataCardSort, EfDataCardSortDirection, EfDatePresetKey, EfDateRange, EfDetailToolbarAction, EfSearchToolbarAction } from '@elasticias/screens';
6
+ import { Observable } from 'rxjs';
7
+ import { EfPage, Permissions, EmailMetrics, EmailEventRecord, EmailEventKind } from '@elasticias/types';
6
8
  import { Menu } from 'primeng/menu';
7
9
  import * as _elasticias_core from '@elasticias/core';
8
10
  import { EfBuildPackage, EfSessionIdleService, EfModule, EfNavSection, EfNavItem, EfModuleId, EfToast, EfToastAction, EfThemeConfigService } from '@elasticias/core';
@@ -16,7 +18,6 @@ import { InputNumber, InputNumberInputEvent } from 'primeng/inputnumber';
16
18
  import { Nullable } from 'primeng/ts-helpers';
17
19
  import * as _elasticias_ui from '@elasticias/ui';
18
20
  import { Table, TableLazyLoadEvent } from 'primeng/table';
19
- import { Permissions, EmailMetrics, EmailEventRecord, EmailEventKind } from '@elasticias/types';
20
21
  import { PrimeNG } from 'primeng/config';
21
22
 
22
23
  /**
@@ -69,6 +70,20 @@ declare class EfQuantitySelectorComponent {
69
70
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfQuantitySelectorComponent, "ef-quantity-selector", never, { "quantity": { "alias": "quantity"; "required": false; "isSignal": true; }; }, { "quantityChange": "quantityChange"; }, never, never, true, never>;
70
71
  }
71
72
 
73
+ /**
74
+ * Which catalogue price a new order line starts from.
75
+ * - `wholesale`: the ERP channel price (`variant.price`, then `product.unitPrice`,
76
+ * which mirrors the default variant's, then `product.salePrice`). B2B sales orders.
77
+ * - `retail`: the shop price (`variant.salePrice`, then `product.salePrice`). A Web
78
+ * order must cost what the shop charges, and the shop sells a variant at its
79
+ * `salePrice` only: an unpriced variant is not sold, so retail never falls back to
80
+ * the wholesale price, and `compareAtPrice` (the "was" price) is never charged.
81
+ * With no shop price the line starts at 0 for the staff to set.
82
+ */
83
+ type EfOrderPriceList = 'wholesale' | 'retail';
84
+ /** The unit price a new line for `product` (and its chosen `variant`) starts at under `priceList`. */
85
+ declare function defaultUnitPrice(product: Record<string, unknown>, variant: Record<string, unknown> | null | undefined, priceList?: EfOrderPriceList): number;
86
+
72
87
  /**
73
88
  * Summary item for product count groups
74
89
  */
@@ -90,7 +105,7 @@ interface OrderProductsSummary {
90
105
  */
91
106
  interface OrderLineItem {
92
107
  id?: string | number;
93
- product?: Record<string, unknown>;
108
+ product?: Record<string, unknown> | null;
94
109
  productId?: unknown;
95
110
  variantId?: string | null;
96
111
  countGroup?: string;
@@ -124,6 +139,8 @@ interface OrderEntity {
124
139
  grossTotal: number;
125
140
  discountTotal: number;
126
141
  taxTotal: number;
142
+ /** Delivery amount (TTC). Absent or 0 for orders without one (B2B, POS). */
143
+ shippingTotal?: number;
127
144
  finalTotal: number;
128
145
  notes?: string;
129
146
  internalNotes?: string;
@@ -132,6 +149,17 @@ interface OrderEntity {
132
149
  billingAddress?: string;
133
150
  productsSummary?: OrderProductsSummary;
134
151
  }
152
+ /**
153
+ * The person an order goes to when there is no client to pick (a storefront
154
+ * order placed by a shopper). The builder shows it read-only in place of the
155
+ * customer select.
156
+ */
157
+ interface EfOrderRecipient {
158
+ name: string;
159
+ phone?: string | null;
160
+ city?: string | null;
161
+ address?: string | null;
162
+ }
135
163
  /**
136
164
  * Configuration for document types (order, invoice, quote)
137
165
  */
@@ -198,8 +226,9 @@ declare class OrderLineItemHelper {
198
226
  /**
199
227
  * Create a line item from product selection
200
228
  * @param keyField - Field name used to identify the product (default: 'id')
229
+ * @param priceList - Which catalogue price the line starts at (see `EfOrderPriceList`)
201
230
  */
202
- static createFromProduct(product: Record<string, unknown>, quantity?: number, keyField?: string, variantId?: string): OrderLineItem;
231
+ static createFromProduct(product: Record<string, unknown>, quantity?: number, keyField?: string, variantId?: string, priceList?: EfOrderPriceList): OrderLineItem;
203
232
  /**
204
233
  * Update calculations for an existing line item
205
234
  */
@@ -221,6 +250,13 @@ declare class OrderEntityHelper {
221
250
  * Create a new empty order entity
222
251
  */
223
252
  static createEmpty(documentType?: 'order' | 'invoice' | 'quote'): OrderEntity;
253
+ /**
254
+ * Round to the centime, half away from zero, as the backend does
255
+ * (`Math.Round(x, 2, MidpointRounding.AwayFromZero)` on a decimal). The
256
+ * product goes through 15 significant digits first so a binary value such
257
+ * as 1.00499999... rounds as the decimal 1.005 would.
258
+ */
259
+ static roundMoney(value: number): number;
224
260
  /**
225
261
  * Calculate all totals for an order
226
262
  */
@@ -387,19 +423,57 @@ interface ProductTypeaheadRow {
387
423
  /** Numeric line columns that support inline click-to-edit. */
388
424
  type EditableLineField = 'price' | 'qty' | 'discount' | 'tva';
389
425
  declare class EfOrderBuilderComponent implements OnInit {
390
- rowTrackBy: (_: number, item: OrderLineItem) => string | number;
426
+ rowTrackBy: (_: number, item: OrderLineItem) => string | number | undefined;
391
427
  config: _angular_core.InputSignal<DocumentConfig>;
392
428
  order: _angular_core.ModelSignal<OrderEntity>;
393
429
  products: _angular_core.InputSignal<Record<string, unknown>[]>;
394
430
  customers: _angular_core.InputSignal<Record<string, unknown>[]>;
431
+ /**
432
+ * Who the order goes to when it has no client to pick (a storefront order).
433
+ * When set, the header shows it read-only instead of the customer select,
434
+ * `customers` may be `[]` and the order validates without a `customerId`.
435
+ * Null keeps the customer select and its required rule.
436
+ */
437
+ recipient: _angular_core.InputSignal<EfOrderRecipient | null>;
438
+ /**
439
+ * Calling code (`'212'` or `'+212'`) for a national recipient phone, so the
440
+ * `tel:` link dials `+212612345678` for `0612345678` from anywhere.
441
+ */
442
+ defaultCallingCode: _angular_core.InputSignal<string>;
443
+ /**
444
+ * `tel:` target for the recipient's phone: E.164 when it can be made, else
445
+ * the digits as typed (a national number still dials inside the country).
446
+ */
447
+ recipientTel: _angular_core.Signal<string | null>;
395
448
  readonly: _angular_core.InputSignal<boolean>;
396
449
  countGroupLabels: _angular_core.InputSignal<ProductCountGroupLabel[]>;
397
450
  /** Field name used to identify products (must match catalogue's trackByField) */
398
451
  productKeyField: _angular_core.InputSignal<string>;
452
+ /**
453
+ * Which catalogue price a new line starts at, in quick-add, its typeahead
454
+ * and the catalogue: `'wholesale'` (default, B2B sales) or `'retail'`, the
455
+ * shop price a Web order is charged. Lines already on the order keep theirs.
456
+ */
457
+ priceList: _angular_core.InputSignal<EfOrderPriceList>;
399
458
  backendErrors: _angular_core.InputSignal<{
400
459
  [key: string]: string[];
401
460
  }>;
402
- /** Optional change-history entries; when non-empty the Historique rail card renders. */
461
+ /**
462
+ * Delivery amount (TTC) the host owns. It is the builder's only fee source:
463
+ * the rail, the bound order and `exportOrder()` all total it, so the saved
464
+ * total is the one shown. A host without a fee (B2B) leaves it at 0, and
465
+ * the bound order then carries no `shippingTotal` at all. A negative or
466
+ * non-numeric fee is treated as 0.
467
+ */
468
+ shippingTotal: _angular_core.InputSignalWithTransform<number, number | null | undefined>;
469
+ /**
470
+ * Optional change-history entries; when non-empty the Historique rail card renders.
471
+ *
472
+ * Host cards that belong in the rail (a delivery card, internal notes) are
473
+ * projected with the `obRail` attribute: `<ef-card obRail ...>`. They sit
474
+ * after the totals and the recap and before the history, as a column at
475
+ * desktop width and inside the drawer below that or in focus mode.
476
+ */
403
477
  auditEntries: _angular_core.InputSignal<EfChangeHistoryEntry[]>;
404
478
  openCatalogue: _angular_core.OutputEmitterRef<void>;
405
479
  orderChanged: _angular_core.OutputEmitterRef<OrderChangeInfo>;
@@ -407,7 +481,7 @@ declare class EfOrderBuilderComponent implements OnInit {
407
481
  productsAdded: _angular_core.OutputEmitterRef<OrderLineItem[]>;
408
482
  private originalOrder;
409
483
  private initialized;
410
- quickAddSelection: _angular_core.WritableSignal<ProductTypeaheadSelection>;
484
+ quickAddSelection: _angular_core.WritableSignal<ProductTypeaheadSelection | null>;
411
485
  quickAddPrice: _angular_core.WritableSignal<number>;
412
486
  quickAddQuantity: _angular_core.WritableSignal<number>;
413
487
  quickAddDisabled: _angular_core.Signal<boolean>;
@@ -426,7 +500,7 @@ declare class EfOrderBuilderComponent implements OnInit {
426
500
  }>;
427
501
  hasErrors: _angular_core.Signal<boolean>;
428
502
  isValid: _angular_core.Signal<boolean>;
429
- changeInfo: _angular_core.Signal<OrderChangeInfo>;
503
+ changeInfo: _angular_core.Signal<OrderChangeInfo | null>;
430
504
  hasChanges: _angular_core.Signal<boolean>;
431
505
  focusMode: _angular_core.ModelSignal<boolean>;
432
506
  railOpen: _angular_core.WritableSignal<boolean>;
@@ -439,7 +513,7 @@ declare class EfOrderBuilderComponent implements OnInit {
439
513
  editingCell: _angular_core.WritableSignal<{
440
514
  id: OrderLineItem["id"];
441
515
  field: EditableLineField;
442
- }>;
516
+ } | null>;
443
517
  /**
444
518
  * Columns for the lines table.
445
519
  *
@@ -506,11 +580,18 @@ declare class EfOrderBuilderComponent implements OnInit {
506
580
  grossTotal: number;
507
581
  discountTotal: number;
508
582
  tvaTotal: number;
583
+ shippingTotal: number;
509
584
  finalTotal: number;
510
585
  };
511
586
  exportOrder(): OrderEntity;
587
+ /**
588
+ * The bound order with its totals, the fee taken from the `shippingTotal`
589
+ * input. Without a fee the order carries no `shippingTotal`, so a B2B order
590
+ * stays exactly as it came in.
591
+ */
592
+ private withTotals;
512
593
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfOrderBuilderComponent, never>;
513
- 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>;
594
+ 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; }; "recipient": { "alias": "recipient"; "required": false; "isSignal": true; }; "defaultCallingCode": { "alias": "defaultCallingCode"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "countGroupLabels": { "alias": "countGroupLabels"; "required": false; "isSignal": true; }; "productKeyField": { "alias": "productKeyField"; "required": false; "isSignal": true; }; "priceList": { "alias": "priceList"; "required": false; "isSignal": true; }; "backendErrors": { "alias": "backendErrors"; "required": false; "isSignal": true; }; "shippingTotal": { "alias": "shippingTotal"; "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, ["[obRail]"], true, never>;
514
595
  }
515
596
 
516
597
  /**
@@ -544,6 +625,11 @@ declare class EfProductTypeaheadComponent {
544
625
  * variant + unit price) is identical either way.
545
626
  */
546
627
  showVariants: _angular_core.InputSignalWithTransform<boolean, unknown>;
628
+ /**
629
+ * Which catalogue price each row shows and its selection carries: the
630
+ * wholesale price (default) or the shop price for a Web order.
631
+ */
632
+ priceList: _angular_core.InputSignal<EfOrderPriceList>;
547
633
  productSelect: _angular_core.OutputEmitterRef<ProductTypeaheadSelection>;
548
634
  query: _angular_core.WritableSignal<string>;
549
635
  open: _angular_core.WritableSignal<boolean>;
@@ -566,195 +652,7 @@ declare class EfProductTypeaheadComponent {
566
652
  /** Clear the field after the consumer has consumed the selection. */
567
653
  reset(): void;
568
654
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfProductTypeaheadComponent, never>;
569
- 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>;
570
- }
571
-
572
- interface CategoryItem {
573
- code: string;
574
- label: string;
575
- description?: string | null;
576
- isActive: boolean;
577
- sortOrder: number;
578
- metadata?: Record<string, unknown> | null;
579
- children?: CategoryItem[];
580
- }
581
- interface CategoryGroup {
582
- code: string;
583
- label: string;
584
- description?: string | null;
585
- isActive: boolean;
586
- sortOrder: number;
587
- metadata?: Record<string, unknown> | null;
588
- children?: CategoryItem[];
589
- }
590
- interface CategoryAssignment {
591
- type: string;
592
- code: string;
593
- }
594
- interface SelectedFilter {
595
- type: string;
596
- code: string;
597
- label: string;
598
- }
599
- declare class EfProductCatalogueFilterComponent {
600
- /**
601
- * PrimeNG's Button reads `autofocus || buttonProps?.autofocus`, so a bare
602
- * `false` collapses to `undefined` and its AutoFocus directive writes the
603
- * attribute anyway. Passing the flag here too makes it resolve to `false`.
604
- */
605
- protected readonly noAutofocus: {
606
- autofocus: boolean;
607
- };
608
- /** Category groups from reference_data (product_categories items with children) */
609
- categoryGroups: _angular_core.InputSignal<CategoryGroup[]>;
610
- /** Products to calculate counts */
611
- products: _angular_core.InputSignal<{
612
- categoryAssignments?: CategoryAssignment[];
613
- }[]>;
614
- /** Selected filters (two-way binding) */
615
- selectedFilters: _angular_core.ModelSignal<SelectedFilter[]>;
616
- /** Emitted when filters change */
617
- filtersChanged: _angular_core.OutputEmitterRef<SelectedFilter[]>;
618
- /** Search terms for each category group */
619
- searchTerms: _angular_core.WritableSignal<Record<string, string>>;
620
- /** Accordion panel values (expanded panels) */
621
- expandedPanels: _angular_core.Signal<string[]>;
622
- /** Get filtered children for a category group based on search */
623
- getFilteredChildren(group: CategoryGroup): CategoryItem[];
624
- /** Get product count for a specific category item */
625
- getItemCount(groupCode: string, itemCode: string): number;
626
- /** Check if a filter is selected */
627
- isFilterSelected(groupCode: string, itemCode: string): boolean;
628
- /** Toggle a filter selection */
629
- toggleFilter(group: CategoryGroup, item: CategoryItem): void;
630
- /** Remove a specific filter */
631
- removeFilter(filter: SelectedFilter): void;
632
- /** Clear all filters */
633
- clearAllFilters(): void;
634
- /** Update search term for a category group */
635
- updateSearchTerm(groupCode: string, term: string): void;
636
- /** Get color class from metadata if available */
637
- getColorClass(item: CategoryItem): string | null;
638
- /** Check if group should show search input (more than 5 items) */
639
- shouldShowSearch(group: CategoryGroup): boolean;
640
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfProductCatalogueFilterComponent, never>;
641
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfProductCatalogueFilterComponent, "ef-product-catalogue-filter", never, { "categoryGroups": { "alias": "categoryGroups"; "required": true; "isSignal": true; }; "products": { "alias": "products"; "required": true; "isSignal": true; }; "selectedFilters": { "alias": "selectedFilters"; "required": false; "isSignal": true; }; }, { "selectedFilters": "selectedFiltersChange"; "filtersChanged": "filtersChanged"; }, never, never, true, never>;
642
- }
643
-
644
- interface CatalogueProductVariant {
645
- variantId: string;
646
- title: string;
647
- sku?: string | null;
648
- price: number;
649
- compareAtPrice?: number | null;
650
- optionValues?: Record<string, string>;
651
- isActive?: boolean;
652
- countGroup?: string | null;
653
- [key: string]: unknown;
654
- }
655
- interface CatalogueProduct {
656
- id: number;
657
- productCode: string;
658
- name: string;
659
- displayName: string;
660
- unitPrice?: number | null;
661
- packagingVolume?: number | null;
662
- categoryAssignments?: CategoryAssignment[];
663
- productType?: string | null;
664
- variants?: CatalogueProductVariant[];
665
- defaultVariantId?: string | null;
666
- [key: string]: unknown;
667
- }
668
- declare class EfProductCatalogueComponent implements OnChanges {
669
- private readonly locale;
670
- private readonly currencyPipe;
671
- /** Products to display */
672
- products: _angular_core.InputSignal<CatalogueProduct[]>;
673
- /** Category groups for filtering */
674
- categoryGroups: _angular_core.InputSignal<CategoryGroup[]>;
675
- /** Initial selected items (for editing existing orders) */
676
- initialSelectedItems: _angular_core.InputSignal<{
677
- productId?: unknown;
678
- quantity: number;
679
- variantId?: string | null;
680
- }[]>;
681
- /** Default filters to apply on init */
682
- defaultFilters: _angular_core.InputSignal<SelectedFilter[]>;
683
- /** Property name used as unique key for tracking products in the list */
684
- trackByField: _angular_core.InputSignal<string>;
685
- /** Currency code for price formatting */
686
- currencyCode: _angular_core.InputSignal<string>;
687
- /** Locale used for number/currency formatting and the quantity stepper */
688
- localeCode: _angular_core.InputSignal<string>;
689
- /**
690
- * Custom product thumbnail template.
691
- * Context: { $implicit: CatalogueProduct }
692
- *
693
- * @example
694
- * <ef-product-catalogue [products]="products()">
695
- * <ng-template #productThumbnail let-product>
696
- * <my-custom-thumbnail [product]="product" />
697
- * </ng-template>
698
- * </ef-product-catalogue>
699
- */
700
- productThumbnailTpl: _angular_core.Signal<TemplateRef<unknown>>;
701
- sortMenu: Menu;
702
- /** Emitted when products are validated */
703
- productsValidated: _angular_core.OutputEmitterRef<{
704
- product: CatalogueProduct;
705
- quantity: number;
706
- variantId?: string;
707
- }[]>;
708
- productSearch: _angular_core.WritableSignal<string>;
709
- selectedFilters: _angular_core.WritableSignal<SelectedFilter[]>;
710
- selectedSort: _angular_core.WritableSignal<string>;
711
- selectedProducts: _angular_core.WritableSignal<{
712
- product: CatalogueProduct;
713
- quantity: number;
714
- variantId?: string;
715
- }[]>;
716
- /** Tracks the currently selected variantId per product key */
717
- selectedVariants: _angular_core.WritableSignal<Record<string, string>>;
718
- filteredProducts: _angular_core.Signal<CatalogueProduct[]>;
719
- sortOptions: {
720
- label: string;
721
- icon: string;
722
- command: () => void;
723
- }[];
724
- onFiltersChanged(filters: SelectedFilter[]): void;
725
- ngOnChanges(): void;
726
- productKey(product: CatalogueProduct): unknown;
727
- private initializeSelectedProducts;
728
- private initializeDefaultFilters;
729
- toggleSortMenu(event: Event): void;
730
- /** Composite key for tracking product+variant selections */
731
- private selectionKey;
732
- /** Get the selected variantId for a product, or its defaultVariantId */
733
- getSelectedVariantId(product: CatalogueProduct): string | undefined;
734
- /** Set the selected variant for a product */
735
- setSelectedVariant(product: CatalogueProduct, variantId: string): void;
736
- /** Check if product is a VARIABLE product with variants */
737
- isVariableProduct(product: CatalogueProduct): boolean;
738
- /** Get active variants for a product */
739
- getActiveVariants(product: CatalogueProduct): CatalogueProductVariant[];
740
- /** Get total quantity for a product across all its variants */
741
- getProductQuantity(product: CatalogueProduct): number;
742
- /** Get quantity for a specific product+variant combination */
743
- getVariantQuantity(product: CatalogueProduct, variantId: string): number;
744
- addProduct(product: CatalogueProduct): void;
745
- removeProduct(product: CatalogueProduct): void;
746
- removeVariantSelection(product: CatalogueProduct, variantId: string): void;
747
- updateProductQuantity(product: CatalogueProduct, quantity: number): void;
748
- updateVariantQuantity(product: CatalogueProduct, variantId: string, quantity: number): void;
749
- validateSelection(): void;
750
- getPackagingLabel(product: CatalogueProduct): string;
751
- getVariantTitle(product: CatalogueProduct, variantId: string): string;
752
- trackKey(product: CatalogueProduct): unknown;
753
- formatPrice(product: CatalogueProduct): string;
754
- formatVariantPrice(variant: CatalogueProductVariant): string;
755
- private formatCurrency;
756
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfProductCatalogueComponent, never>;
757
- 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>;
655
+ 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; }; "priceList": { "alias": "priceList"; "required": false; "isSignal": true; }; }, { "productSelect": "productSelect"; }, never, never, true, never>;
758
656
  }
759
657
 
760
658
  /**
@@ -952,6 +850,305 @@ declare class EfDialogComponent {
952
850
  static ngAcceptInputType_saveDisabled: unknown;
953
851
  }
954
852
 
853
+ /** A product as the picker shows it. The caller maps its own DTO to this. */
854
+ interface EfProductPickerItem {
855
+ id: string;
856
+ /** Internal reference, shown in a mono column. */
857
+ reference?: string;
858
+ title: string;
859
+ /** Public image url, displayed as given (it comes from the backend). */
860
+ thumbnailUrl?: string;
861
+ /** One muted line of context ("Femme - publie"), already translated. */
862
+ meta?: string;
863
+ /** Shown but not pickable, for a product that is already in. */
864
+ disabled?: boolean;
865
+ }
866
+ /** What the picker asks the caller's search function for. */
867
+ interface EfProductPickerQuery {
868
+ text: string;
869
+ pageNumber: number;
870
+ pageSize: number;
871
+ }
872
+
873
+ /**
874
+ * Multi-select product picker: a dialog that searches the caller's catalogue
875
+ * page by page and hands back the ids the operator ticked. Data-agnostic: it
876
+ * knows nothing of collections or promotions, only a search function.
877
+ *
878
+ * Built from `ef-dialog` + `ef-smart-bar` + `ef-data-card` (select column,
879
+ * image column). The selection survives paging and new searches. The whole
880
+ * row is the toggle, 48px tall. On a phone (`EfViewportService`, <= 767px)
881
+ * the dialog is full screen and the table becomes a list whose row head is
882
+ * the toggle.
883
+ *
884
+ * `[(open)]` is two-way. The picker closes itself on X, Escape, Cancel and
885
+ * after a confirm, sets `open` back to false and emits `(closed)` each time.
886
+ * Each open starts over: empty selection, empty search, page one.
887
+ *
888
+ * Rows are toggle buttons (`ef-data-card` `rowPressed`): one tab stop each,
889
+ * Enter and Space toggle, `aria-pressed` says the state. The tick in the
890
+ * row is a picture of that state, not a second control. A `disabled` item is
891
+ * dimmed, `aria-disabled`, and cannot be picked.
892
+ *
893
+ * ```html
894
+ * <ef-product-picker
895
+ * [(open)]="pickerOpen"
896
+ * [search]="searchCandidates"
897
+ * titleKey="collections_products_picker_title"
898
+ * (confirm)="addProducts($event)"
899
+ * />
900
+ * ```
901
+ */
902
+ declare class EfProductPickerComponent {
903
+ private readonly viewport;
904
+ private readonly destroyRef;
905
+ /** Two-way: `[(open)]="pickerOpen"`. The picker sets it back to false
906
+ * on every close path, so the next open always opens. */
907
+ readonly open: _angular_core.ModelSignal<boolean>;
908
+ /** Called for every page and search. Map the caller's DTO to items. */
909
+ readonly search: _angular_core.InputSignal<(q: EfProductPickerQuery) => Observable<EfPage<EfProductPickerItem>>>;
910
+ readonly titleKey: _angular_core.InputSignal<string>;
911
+ readonly confirmLabelKey: _angular_core.InputSignal<string>;
912
+ readonly searchPlaceholderKey: _angular_core.InputSignal<string>;
913
+ readonly pageSize: _angular_core.InputSignal<number>;
914
+ /** The ticked ids, in the order they were ticked. */
915
+ readonly confirm: _angular_core.OutputEmitterRef<string[]>;
916
+ readonly closed: _angular_core.OutputEmitterRef<void>;
917
+ /** The list screen behind the dialog already owns the search shortcut. */
918
+ readonly searchShortcut: null;
919
+ protected readonly isMobile: _angular_core.Signal<boolean>;
920
+ private readonly selected;
921
+ readonly selectedIds: _angular_core.Signal<string[]>;
922
+ protected readonly selectedCount: _angular_core.Signal<number>;
923
+ protected readonly searchText: _angular_core.WritableSignal<string>;
924
+ protected readonly appliedText: _angular_core.WritableSignal<string>;
925
+ protected readonly pageNumber: _angular_core.WritableSignal<number>;
926
+ protected readonly rows: _angular_core.WritableSignal<readonly EfProductPickerItem[]>;
927
+ protected readonly totalCount: _angular_core.WritableSignal<number>;
928
+ protected readonly loading: _angular_core.WritableSignal<boolean>;
929
+ protected readonly errorMsg: _angular_core.WritableSignal<string>;
930
+ private readonly queries;
931
+ private lastQuery;
932
+ protected readonly pageSizeOptions: _angular_core.Signal<number[]>;
933
+ protected readonly dialogSize: _angular_core.Signal<"lg" | "full">;
934
+ protected readonly dialogClass: _angular_core.Signal<"ef-product-picker-dialog ef-product-picker-dialog--fullscreen" | "ef-product-picker-dialog">;
935
+ /** Desktop: a table. Phone: one composed cell, so the list row carries
936
+ * the tick, the thumb, the reference and the title without an expander. */
937
+ protected readonly columns: _angular_core.Signal<EfDataCardColumn[]>;
938
+ protected readonly actions: _angular_core.Signal<EfDialogAction[]>;
939
+ constructor();
940
+ onSearch(text: string): void;
941
+ onClear(): void;
942
+ onPageChange(page: number): void;
943
+ retry(): void;
944
+ isSelected(id: string): boolean;
945
+ /** Row state for ef-data-card. Arrow functions: data-card calls them. */
946
+ protected readonly rowPressed: (row: EfProductPickerItem) => boolean;
947
+ protected readonly rowDisabled: (row: EfProductPickerItem) => boolean;
948
+ toggle(item: EfProductPickerItem | null | undefined): void;
949
+ onVisibleChange(visible: boolean): void;
950
+ private startOver;
951
+ private run;
952
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfProductPickerComponent, never>;
953
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfProductPickerComponent, "ef-product-picker", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "search": { "alias": "search"; "required": true; "isSignal": true; }; "titleKey": { "alias": "titleKey"; "required": false; "isSignal": true; }; "confirmLabelKey": { "alias": "confirmLabelKey"; "required": false; "isSignal": true; }; "searchPlaceholderKey": { "alias": "searchPlaceholderKey"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; }, { "open": "openChange"; "confirm": "confirm"; "closed": "closed"; }, never, never, true, never>;
954
+ }
955
+
956
+ interface CategoryItem {
957
+ code: string;
958
+ label: string;
959
+ description?: string | null;
960
+ isActive: boolean;
961
+ sortOrder: number;
962
+ metadata?: Record<string, unknown> | null;
963
+ children?: CategoryItem[];
964
+ }
965
+ interface CategoryGroup {
966
+ code: string;
967
+ label: string;
968
+ description?: string | null;
969
+ isActive: boolean;
970
+ sortOrder: number;
971
+ metadata?: Record<string, unknown> | null;
972
+ children?: CategoryItem[];
973
+ }
974
+ interface CategoryAssignment {
975
+ type: string;
976
+ code: string;
977
+ }
978
+ interface SelectedFilter {
979
+ type: string;
980
+ code: string;
981
+ label: string;
982
+ }
983
+ declare class EfProductCatalogueFilterComponent {
984
+ /**
985
+ * PrimeNG's Button reads `autofocus || buttonProps?.autofocus`, so a bare
986
+ * `false` collapses to `undefined` and its AutoFocus directive writes the
987
+ * attribute anyway. Passing the flag here too makes it resolve to `false`.
988
+ */
989
+ protected readonly noAutofocus: {
990
+ autofocus: boolean;
991
+ };
992
+ /** Category groups from reference_data (product_categories items with children) */
993
+ categoryGroups: _angular_core.InputSignal<CategoryGroup[]>;
994
+ /** Products to calculate counts */
995
+ products: _angular_core.InputSignal<{
996
+ categoryAssignments?: CategoryAssignment[];
997
+ }[]>;
998
+ /** Selected filters (two-way binding) */
999
+ selectedFilters: _angular_core.ModelSignal<SelectedFilter[]>;
1000
+ /** Emitted when filters change */
1001
+ filtersChanged: _angular_core.OutputEmitterRef<SelectedFilter[]>;
1002
+ /** Search terms for each category group */
1003
+ searchTerms: _angular_core.WritableSignal<Record<string, string>>;
1004
+ /** Accordion panel values (expanded panels) */
1005
+ expandedPanels: _angular_core.Signal<string[]>;
1006
+ /** Get filtered children for a category group based on search */
1007
+ getFilteredChildren(group: CategoryGroup): CategoryItem[];
1008
+ /** Get product count for a specific category item */
1009
+ getItemCount(groupCode: string, itemCode: string): number;
1010
+ /** Check if a filter is selected */
1011
+ isFilterSelected(groupCode: string, itemCode: string): boolean;
1012
+ /** Toggle a filter selection */
1013
+ toggleFilter(group: CategoryGroup, item: CategoryItem): void;
1014
+ /** Remove a specific filter */
1015
+ removeFilter(filter: SelectedFilter): void;
1016
+ /** Clear all filters */
1017
+ clearAllFilters(): void;
1018
+ /** Update search term for a category group */
1019
+ updateSearchTerm(groupCode: string, term: string): void;
1020
+ /** Get color class from metadata if available */
1021
+ getColorClass(item: CategoryItem): string | null;
1022
+ /** Check if group should show search input (more than 5 items) */
1023
+ shouldShowSearch(group: CategoryGroup): boolean;
1024
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfProductCatalogueFilterComponent, never>;
1025
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfProductCatalogueFilterComponent, "ef-product-catalogue-filter", never, { "categoryGroups": { "alias": "categoryGroups"; "required": true; "isSignal": true; }; "products": { "alias": "products"; "required": true; "isSignal": true; }; "selectedFilters": { "alias": "selectedFilters"; "required": false; "isSignal": true; }; }, { "selectedFilters": "selectedFiltersChange"; "filtersChanged": "filtersChanged"; }, never, never, true, never>;
1026
+ }
1027
+
1028
+ interface CatalogueProductVariant {
1029
+ variantId: string;
1030
+ title: string;
1031
+ sku?: string | null;
1032
+ price: number;
1033
+ compareAtPrice?: number | null;
1034
+ optionValues?: Record<string, string>;
1035
+ isActive?: boolean;
1036
+ countGroup?: string | null;
1037
+ [key: string]: unknown;
1038
+ }
1039
+ interface CatalogueProduct {
1040
+ id: number;
1041
+ productCode: string;
1042
+ name: string;
1043
+ displayName: string;
1044
+ unitPrice?: number | null;
1045
+ packagingVolume?: number | null;
1046
+ categoryAssignments?: CategoryAssignment[];
1047
+ productType?: string | null;
1048
+ variants?: CatalogueProductVariant[];
1049
+ defaultVariantId?: string | null;
1050
+ [key: string]: unknown;
1051
+ }
1052
+ declare class EfProductCatalogueComponent implements OnChanges {
1053
+ private readonly locale;
1054
+ private readonly currencyPipe;
1055
+ /** Products to display */
1056
+ products: _angular_core.InputSignal<CatalogueProduct[]>;
1057
+ /** Category groups for filtering */
1058
+ categoryGroups: _angular_core.InputSignal<CategoryGroup[]>;
1059
+ /** Initial selected items (for editing existing orders) */
1060
+ initialSelectedItems: _angular_core.InputSignal<{
1061
+ productId?: unknown;
1062
+ quantity: number;
1063
+ variantId?: string | null;
1064
+ }[]>;
1065
+ /** Default filters to apply on init */
1066
+ defaultFilters: _angular_core.InputSignal<SelectedFilter[]>;
1067
+ /** Property name used as unique key for tracking products in the list */
1068
+ trackByField: _angular_core.InputSignal<string>;
1069
+ /** Currency code for price formatting */
1070
+ currencyCode: _angular_core.InputSignal<string>;
1071
+ /**
1072
+ * Which price the cards show: `'wholesale'` (default) or `'retail'`, the shop price.
1073
+ * Bind the same value as the order builder's `priceList`, so a card shows the price
1074
+ * of the line it adds (the rule lives in `defaultUnitPrice`).
1075
+ */
1076
+ priceList: _angular_core.InputSignal<EfOrderPriceList>;
1077
+ /** Locale used for number/currency formatting and the quantity stepper */
1078
+ localeCode: _angular_core.InputSignal<string>;
1079
+ /**
1080
+ * Custom product thumbnail template.
1081
+ * Context: { $implicit: CatalogueProduct }
1082
+ *
1083
+ * @example
1084
+ * <ef-product-catalogue [products]="products()">
1085
+ * <ng-template #productThumbnail let-product>
1086
+ * <my-custom-thumbnail [product]="product" />
1087
+ * </ng-template>
1088
+ * </ef-product-catalogue>
1089
+ */
1090
+ productThumbnailTpl: _angular_core.Signal<TemplateRef<unknown> | undefined>;
1091
+ sortMenu: Menu;
1092
+ /** Emitted when products are validated */
1093
+ productsValidated: _angular_core.OutputEmitterRef<{
1094
+ product: CatalogueProduct;
1095
+ quantity: number;
1096
+ variantId?: string;
1097
+ }[]>;
1098
+ productSearch: _angular_core.WritableSignal<string>;
1099
+ selectedFilters: _angular_core.WritableSignal<SelectedFilter[]>;
1100
+ selectedSort: _angular_core.WritableSignal<string>;
1101
+ selectedProducts: _angular_core.WritableSignal<{
1102
+ product: CatalogueProduct;
1103
+ quantity: number;
1104
+ variantId?: string;
1105
+ }[]>;
1106
+ /** Tracks the currently selected variantId per product key */
1107
+ selectedVariants: _angular_core.WritableSignal<Record<string, string>>;
1108
+ filteredProducts: _angular_core.Signal<CatalogueProduct[]>;
1109
+ sortOptions: {
1110
+ label: string;
1111
+ icon: string;
1112
+ command: () => void;
1113
+ }[];
1114
+ onFiltersChanged(filters: SelectedFilter[]): void;
1115
+ ngOnChanges(): void;
1116
+ productKey(product: CatalogueProduct): unknown;
1117
+ private initializeSelectedProducts;
1118
+ private initializeDefaultFilters;
1119
+ toggleSortMenu(event: Event): void;
1120
+ /** Composite key for tracking product+variant selections */
1121
+ private selectionKey;
1122
+ /** Get the selected variantId for a product, or its defaultVariantId */
1123
+ getSelectedVariantId(product: CatalogueProduct): string | undefined;
1124
+ /** Set the selected variant for a product */
1125
+ setSelectedVariant(product: CatalogueProduct, variantId: string): void;
1126
+ /** Check if product is a VARIABLE product with variants */
1127
+ isVariableProduct(product: CatalogueProduct): boolean;
1128
+ /** Get active variants for a product */
1129
+ getActiveVariants(product: CatalogueProduct): CatalogueProductVariant[];
1130
+ /** Get total quantity for a product across all its variants */
1131
+ getProductQuantity(product: CatalogueProduct): number;
1132
+ /** Get quantity for a specific product+variant combination */
1133
+ getVariantQuantity(product: CatalogueProduct, variantId: string): number;
1134
+ addProduct(product: CatalogueProduct): void;
1135
+ removeProduct(product: CatalogueProduct): void;
1136
+ removeVariantSelection(product: CatalogueProduct, variantId: string): void;
1137
+ updateProductQuantity(product: CatalogueProduct, quantity: number): void;
1138
+ updateVariantQuantity(product: CatalogueProduct, variantId: string, quantity: number): void;
1139
+ validateSelection(): void;
1140
+ getPackagingLabel(product: CatalogueProduct): string;
1141
+ getVariantTitle(product: CatalogueProduct, variantId: string): string;
1142
+ trackKey(product: CatalogueProduct): unknown;
1143
+ /** The price a variant (or a simple product, `variant` null) is shown at under the active list. */
1144
+ private priceOf;
1145
+ formatPrice(product: CatalogueProduct): string;
1146
+ formatVariantPrice(variant: CatalogueProductVariant, product?: CatalogueProduct): string;
1147
+ private formatCurrency;
1148
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfProductCatalogueComponent, never>;
1149
+ 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; }; "priceList": { "alias": "priceList"; "required": false; "isSignal": true; }; "localeCode": { "alias": "localeCode"; "required": false; "isSignal": true; }; }, { "productsValidated": "productsValidated"; }, ["productThumbnailTpl"], never, true, never>;
1150
+ }
1151
+
955
1152
  /**
956
1153
  * The app-wide confirmation modal, wearing the same skin as `ef-dialog`.
957
1154
  *
@@ -1009,7 +1206,7 @@ declare class EfThemeToggleComponent {
1009
1206
  * Where the tooltip opens. Below suits a top bar (the app bar, the
1010
1207
  * login header); a toggle placed low on a page can open it above.
1011
1208
  */
1012
- readonly tooltipPosition: _angular_core.InputSignal<"top" | "left" | "bottom" | "right">;
1209
+ readonly tooltipPosition: _angular_core.InputSignal<"top" | "bottom" | "left" | "right">;
1013
1210
  /** Translation key for the accessible name (also the tooltip text). */
1014
1211
  readonly ariaLabelKey: _angular_core.InputSignal<string>;
1015
1212
  readonly isDark: _angular_core.Signal<boolean>;
@@ -1034,7 +1231,7 @@ declare class EfThemeToggleComponent {
1034
1231
  declare class EfBuildStampComponent {
1035
1232
  /** Copyright holder shown before the build. Omit for the build alone. */
1036
1233
  readonly owner: _angular_core.InputSignal<string>;
1037
- protected readonly info: _elasticias_core.EfBuildInfo;
1234
+ protected readonly info: _elasticias_core.EfBuildInfo | null;
1038
1235
  protected readonly year: number;
1039
1236
  /**
1040
1237
  * A released build is known by its version; an unreleased one has no
@@ -1103,7 +1300,7 @@ declare class EfAboutDialogComponent {
1103
1300
  readonly initial: _angular_core.InputSignal<string>;
1104
1301
  /** Copyright holder on the last line. */
1105
1302
  readonly owner: _angular_core.InputSignal<string>;
1106
- protected readonly info: _elasticias_core.EfBuildInfo;
1303
+ protected readonly info: _elasticias_core.EfBuildInfo | null;
1107
1304
  protected readonly year: number;
1108
1305
  protected readonly packages: _angular_core.Signal<EfBuildPackage[]>;
1109
1306
  /**
@@ -1222,7 +1419,7 @@ declare class EfShortcutsDialogComponent {
1222
1419
  declare class EfSessionIdleDialogComponent {
1223
1420
  protected readonly idle: EfSessionIdleService;
1224
1421
  private readonly session;
1225
- protected readonly secondsLeft: _angular_core.Signal<number>;
1422
+ protected readonly secondsLeft: _angular_core.Signal<number | null>;
1226
1423
  protected readonly actions: EfDialogAction[];
1227
1424
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfSessionIdleDialogComponent, never>;
1228
1425
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfSessionIdleDialogComponent, "ef-session-idle-dialog", never, {}, {}, never, never, true, never>;
@@ -1233,11 +1430,18 @@ declare class EfFieldsetComponent {
1233
1430
  legend?: string;
1234
1431
  /** Translation key — takes priority over legend */
1235
1432
  legendKey?: string;
1433
+ /**
1434
+ * `framed` (default) is the bordered PrimeNG fieldset. `plain` drops the
1435
+ * frame and padding and sets the legend as a section heading: use it inside
1436
+ * an `ef-card`, so fields keep their full width at 390px. It stays a real
1437
+ * fieldset/legend, and it is never toggleable.
1438
+ */
1439
+ variant: 'framed' | 'plain';
1236
1440
  toggleable: boolean;
1237
1441
  collapsed: boolean;
1238
1442
  styleClass?: string;
1239
1443
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfFieldsetComponent, never>;
1240
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfFieldsetComponent, "ef-fieldset", never, { "legend": { "alias": "legend"; "required": false; }; "legendKey": { "alias": "legendKey"; "required": false; }; "toggleable": { "alias": "toggleable"; "required": false; }; "collapsed": { "alias": "collapsed"; "required": false; }; "styleClass": { "alias": "styleClass"; "required": false; }; }, {}, never, ["*"], true, never>;
1444
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfFieldsetComponent, "ef-fieldset", never, { "legend": { "alias": "legend"; "required": false; }; "legendKey": { "alias": "legendKey"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "toggleable": { "alias": "toggleable"; "required": false; }; "collapsed": { "alias": "collapsed"; "required": false; }; "styleClass": { "alias": "styleClass"; "required": false; }; }, {}, never, ["*"], true, never>;
1241
1445
  static ngAcceptInputType_toggleable: unknown;
1242
1446
  static ngAcceptInputType_collapsed: unknown;
1243
1447
  }
@@ -1267,12 +1471,19 @@ type EfButtonSeverity = EfButtonPrimeNGSeverity | EfButtonComptoirSeverity;
1267
1471
  * `disabled`, `loading`, `pTooltip` (via `tooltipKey`), and emit the
1268
1472
  * same `clickEvent`.
1269
1473
  */
1270
- declare class EfButtonComponent {
1474
+ declare class EfButtonComponent implements OnInit {
1271
1475
  /** Direct label text (not translated). */
1272
1476
  label?: string;
1273
1477
  /** Translation key — takes priority over `label`. */
1274
1478
  labelKey?: string;
1275
1479
  icon?: string;
1480
+ /**
1481
+ * Translation key for the button's accessible name (`aria-label`). Required
1482
+ * in practice for an icon-only button: a tooltip is not read as the name.
1483
+ * A button with a visible label does not need it. An icon-only button with
1484
+ * neither a label nor this key logs a dev-mode warning.
1485
+ */
1486
+ ariaLabelKey?: string;
1276
1487
  /**
1277
1488
  * Which rendering surface to use. Default `'primeng'` keeps v1
1278
1489
  * consumers unchanged. V2 detail/list screens pass `'comptoir'`
@@ -1348,15 +1559,16 @@ declare class EfButtonComponent {
1348
1559
  * `'small' | 'large'`, with the default (unset) being the normal height, so
1349
1560
  * `'base'` maps to `null` rather than being forwarded verbatim.
1350
1561
  */
1351
- get pnSize(): 'small' | 'large' | null;
1562
+ get pnSize(): 'small' | 'large' | undefined;
1352
1563
  /** Composed class string for the Comptoir variant —
1353
1564
  * `btn btn-{severity} btn-sm` plus any caller-supplied `styleClass`. */
1354
1565
  get comptoirClass(): string;
1355
1566
  /** Inline danger tint for the `severity="danger"` Comptoir variant. */
1356
1567
  get comptoirInlineColor(): string | null;
1568
+ ngOnInit(): void;
1357
1569
  handleClick(event: Event): void;
1358
1570
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfButtonComponent, never>;
1359
- 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; }; "autofocus": { "alias": "autofocus"; "required": false; }; }, { "clickEvent": "clickEvent"; }, never, never, true, never>;
1571
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfButtonComponent, "ef-button", never, { "label": { "alias": "label"; "required": false; }; "labelKey": { "alias": "labelKey"; "required": false; }; "icon": { "alias": "icon"; "required": false; }; "ariaLabelKey": { "alias": "ariaLabelKey"; "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; }; "autofocus": { "alias": "autofocus"; "required": false; }; }, { "clickEvent": "clickEvent"; }, never, never, true, never>;
1360
1572
  static ngAcceptInputType_disabled: unknown;
1361
1573
  static ngAcceptInputType_rounded: unknown;
1362
1574
  static ngAcceptInputType_outlined: unknown;
@@ -1513,7 +1725,16 @@ declare class EfBadgeComponent {
1513
1725
  /**
1514
1726
  * Badge label text
1515
1727
  */
1516
- label: _angular_core.InputSignal<string>;
1728
+ label: _angular_core.InputSignal<string | undefined>;
1729
+ /**
1730
+ * Translation key for the label; wins over `label`.
1731
+ */
1732
+ labelKey: _angular_core.InputSignal<string | undefined>;
1733
+ /**
1734
+ * Opt in to the pointer cursor and hover lift. Off by default so a
1735
+ * read-only badge does not look like a button.
1736
+ */
1737
+ clickable: _angular_core.InputSignalWithTransform<boolean, unknown>;
1517
1738
  /**
1518
1739
  * Badge color (Tailwind color name)
1519
1740
  */
@@ -1533,7 +1754,7 @@ declare class EfBadgeComponent {
1533
1754
  /**
1534
1755
  * Icon class (e.g., PrimeIcons)
1535
1756
  */
1536
- icon: _angular_core.InputSignal<string>;
1757
+ icon: _angular_core.InputSignal<string | undefined>;
1537
1758
  /**
1538
1759
  * Whether to show the dot indicator
1539
1760
  */
@@ -1555,7 +1776,7 @@ declare class EfBadgeComponent {
1555
1776
  */
1556
1777
  iconClasses: _angular_core.Signal<string>;
1557
1778
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfBadgeComponent, never>;
1558
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfBadgeComponent, "ef-badge", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "styleClass": { "alias": "styleClass"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "showDot": { "alias": "showDot"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1779
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfBadgeComponent, "ef-badge", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "clickable": { "alias": "clickable"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "styleClass": { "alias": "styleClass"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "showDot": { "alias": "showDot"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1559
1780
  }
1560
1781
 
1561
1782
  declare class EfToolbarComponent {
@@ -1666,7 +1887,7 @@ declare class EfAppShellComponent {
1666
1887
  readonly shortcutHint: string;
1667
1888
  toggleSide(): void;
1668
1889
  constructor();
1669
- readonly activeId: _angular_core.Signal<_elasticias_core.EfModuleId>;
1890
+ readonly activeId: _angular_core.Signal<_elasticias_core.EfModuleId | null>;
1670
1891
  readonly switcherOpen: _angular_core.WritableSignal<boolean>;
1671
1892
  /** Five tabs is what fits a phone; the rest live in the sheet. */
1672
1893
  readonly tabs: _angular_core.Signal<EfModule[]>;
@@ -1718,7 +1939,7 @@ declare class EfAppShellMobileComponent {
1718
1939
  private readonly perms;
1719
1940
  private readonly active;
1720
1941
  private readonly router;
1721
- readonly activeId: _angular_core.Signal<_elasticias_core.EfModuleId>;
1942
+ readonly activeId: _angular_core.Signal<_elasticias_core.EfModuleId | null>;
1722
1943
  readonly switcherOpen: _angular_core.WritableSignal<boolean>;
1723
1944
  readonly tabs: _angular_core.Signal<EfModule[]>;
1724
1945
  readonly allVisible: _angular_core.Signal<readonly EfModule[]>;
@@ -1750,7 +1971,7 @@ declare class EfAppMainComponent {
1750
1971
  /** Render the 3px module-color stripe at the top of the content area. */
1751
1972
  stripe: boolean;
1752
1973
  /** `data-module` value bound to the host. Null when no module is active. */
1753
- readonly moduleId: _angular_core.Signal<_elasticias_core.EfModuleId>;
1974
+ readonly moduleId: _angular_core.Signal<_elasticias_core.EfModuleId | null>;
1754
1975
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfAppMainComponent, never>;
1755
1976
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfAppMainComponent, "ef-app-main", never, { "stripe": { "alias": "stripe"; "required": false; }; }, {}, never, ["*"], true, never>;
1756
1977
  static ngAcceptInputType_stripe: unknown;
@@ -1785,7 +2006,7 @@ declare class EfAppTopComponent {
1785
2006
  */
1786
2007
  readonly logout: _angular_core.InputSignalWithTransform<boolean, unknown>;
1787
2008
  /** Present only when the app provides `EF_SESSION`. */
1788
- protected readonly session: _elasticias_core.EfSession;
2009
+ protected readonly session: _elasticias_core.EfSession | null;
1789
2010
  protected signOut(): void;
1790
2011
  /** A touch or pen press at least this long is a hold, not a tap. */
1791
2012
  static readonly HOLD_MS = 500;
@@ -1803,8 +2024,8 @@ declare class EfAppTopComponent {
1803
2024
  protected forgetSignOutPress(): void;
1804
2025
  protected onSignOutClick(event: MouseEvent): void;
1805
2026
  private readonly active;
1806
- readonly module: _angular_core.Signal<_elasticias_core.EfModule>;
1807
- readonly navItem: _angular_core.Signal<_elasticias_core.EfNavItem>;
2027
+ readonly module: _angular_core.Signal<_elasticias_core.EfModule | null>;
2028
+ readonly navItem: _angular_core.Signal<_elasticias_core.EfNavItem | null>;
1808
2029
  readonly hasBreadcrumb: _angular_core.Signal<boolean>;
1809
2030
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfAppTopComponent, never>;
1810
2031
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfAppTopComponent, "ef-app-top", never, { "themeToggle": { "alias": "themeToggle"; "required": false; "isSignal": true; }; "logout": { "alias": "logout"; "required": false; "isSignal": true; }; }, {}, never, ["[breadcrumb]", "[actions]"], true, never>;
@@ -1847,7 +2068,7 @@ declare class EfModuleRailComponent {
1847
2068
  /** Optional notification badges per module id. */
1848
2069
  badges: Partial<Record<EfModuleId, string | number>>;
1849
2070
  readonly visibleModules: _angular_core.Signal<readonly EfModule[]>;
1850
- readonly activeId: _angular_core.Signal<EfModuleId>;
2071
+ readonly activeId: _angular_core.Signal<EfModuleId | null>;
1851
2072
  /** Visible modules interleaved with dividers at group boundaries. */
1852
2073
  readonly rows: _angular_core.Signal<RailRow[]>;
1853
2074
  readonly hasBadges: _angular_core.Signal<boolean>;
@@ -1870,8 +2091,8 @@ declare class EfModuleRailComponent {
1870
2091
  declare class EfModuleSideComponent {
1871
2092
  private readonly active;
1872
2093
  private readonly perms;
1873
- readonly module: _angular_core.Signal<_elasticias_core.EfModule>;
1874
- readonly moduleId: _angular_core.Signal<_elasticias_core.EfModuleId>;
2094
+ readonly module: _angular_core.Signal<_elasticias_core.EfModule | null>;
2095
+ readonly moduleId: _angular_core.Signal<_elasticias_core.EfModuleId | null>;
1875
2096
  readonly visibleSections: _angular_core.Signal<EfNavSection[]>;
1876
2097
  trackSection(_: number, s: EfNavSection): string;
1877
2098
  trackItem(_: number, i: EfNavItem): string;
@@ -1976,8 +2197,15 @@ interface EfPillItem<T = string> {
1976
2197
  */
1977
2198
  declare class EfPillGroupComponent<T = string> {
1978
2199
  readonly items: _angular_core.InputSignal<readonly EfPillItem<T>[]>;
1979
- /** Two-way bound selection. */
1980
- readonly value: _angular_core.ModelSignal<T>;
2200
+ /** Selected value; `null` when nothing is selected. Two-way bindable
2201
+ * with `[(value)]`. */
2202
+ readonly value: _angular_core.InputSignal<T | null>;
2203
+ /** Emits the picked pill's value. Never `null`: a click always picks
2204
+ * a pill, so handlers can take `T` without a null check. */
2205
+ readonly valueChange: _angular_core.OutputEmitterRef<T>;
2206
+ /** What the pills render: the bound value, or the last pick when the
2207
+ * caller only listens to `valueChange`. */
2208
+ protected readonly selected: _angular_core.WritableSignal<T | null>;
1981
2209
  /** Translation key for the group's accessible name. */
1982
2210
  readonly ariaLabelKey: _angular_core.InputSignal<string>;
1983
2211
  /** Hide counts even when items provide them. */
@@ -1997,7 +2225,7 @@ declare class EfPillGroupComponent<T = string> {
1997
2225
  private measure;
1998
2226
  private scrollActiveIntoView;
1999
2227
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfPillGroupComponent<any>, never>;
2000
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfPillGroupComponent<any>, "ef-pill-group", never, { "items": { "alias": "items"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "ariaLabelKey": { "alias": "ariaLabelKey"; "required": false; "isSignal": true; }; "hideCounts": { "alias": "hideCounts"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; }, never, never, true, never>;
2228
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfPillGroupComponent<any>, "ef-pill-group", never, { "items": { "alias": "items"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "ariaLabelKey": { "alias": "ariaLabelKey"; "required": false; "isSignal": true; }; "hideCounts": { "alias": "hideCounts"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
2001
2229
  }
2002
2230
 
2003
2231
  /** Visual treatment of the tabset. `'pill'` is the default. */
@@ -2076,7 +2304,10 @@ declare class EfTabsComponent<T = string> implements AfterContentInit {
2076
2304
  overflow: boolean;
2077
2305
  /** Hide count badges even when items provide them. */
2078
2306
  hideCounts: boolean;
2307
+ /** Untranslated accessible name of the tablist. Prefer `ariaLabelKey`. */
2079
2308
  ariaLabel: string;
2309
+ /** Translation key for the tablist's accessible name; wins over `ariaLabel`. */
2310
+ ariaLabelKey?: string;
2080
2311
  /** Translation key used as the `aria-label` of the dirty dot.
2081
2312
  * Defaults to `'common.dirty'` — apps add the matching entry to
2082
2313
  * `fr.json` / `ar.json`. */
@@ -2097,7 +2328,7 @@ declare class EfTabsComponent<T = string> implements AfterContentInit {
2097
2328
  onKeydown(event: KeyboardEvent, index: number): void;
2098
2329
  trackValue(_: number, item: EfTabItem<T>): T;
2099
2330
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfTabsComponent<any>, never>;
2100
- 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>;
2331
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfTabsComponent<any>, "ef-tabs", never, { "items": { "alias": "items"; "required": true; }; "value": { "alias": "value"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "module": { "alias": "module"; "required": false; }; "overflow": { "alias": "overflow"; "required": false; }; "hideCounts": { "alias": "hideCounts"; "required": false; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; }; "ariaLabelKey": { "alias": "ariaLabelKey"; "required": false; }; "dirtyLabelKey": { "alias": "dirtyLabelKey"; "required": false; }; }, { "valueChange": "valueChange"; }, ["panels"], never, true, never>;
2101
2332
  static ngAcceptInputType_overflow: unknown;
2102
2333
  static ngAcceptInputType_hideCounts: unknown;
2103
2334
  }
@@ -2327,7 +2558,7 @@ declare class EfPulseComponent {
2327
2558
  declare class EfPdfPreviewComponent {
2328
2559
  private readonly sanitizer;
2329
2560
  /** The PDF blob to preview. Setting it (re)creates the object URL. */
2330
- blob: _angular_core.InputSignal<Blob>;
2561
+ blob: _angular_core.InputSignal<Blob | null>;
2331
2562
  /** File name shown in the header + used for download / share. */
2332
2563
  fileName: _angular_core.InputSignal<string>;
2333
2564
  /** Two-way dialog visibility. */
@@ -2339,11 +2570,16 @@ declare class EfPdfPreviewComponent {
2339
2570
  showOpenInNewTab: _angular_core.InputSignalWithTransform<boolean, unknown>;
2340
2571
  /** Show the WhatsApp share button (Web Share API → wa.me fallback). */
2341
2572
  showWhatsappShare: _angular_core.InputSignalWithTransform<boolean, unknown>;
2342
- /** Optional recipient phone for the wa.me fallback (digits / +). */
2573
+ /** Optional recipient phone for the wa.me fallback, as typed: international
2574
+ * (`+`/`00`) or national with `defaultCallingCode`. */
2343
2575
  whatsappPhone: _angular_core.InputSignal<string>;
2576
+ /** Calling code (`'212'` or `'+212'`) that turns a national
2577
+ * `whatsappPhone` (`0612345678`) into the international number wa.me
2578
+ * needs. Without it a national number opens the contact picker. */
2579
+ defaultCallingCode: _angular_core.InputSignal<string>;
2344
2580
  private readonly objectUrl;
2345
2581
  private currentUrl;
2346
- readonly safeUrl: _angular_core.Signal<SafeResourceUrl>;
2582
+ readonly safeUrl: _angular_core.Signal<SafeResourceUrl | null>;
2347
2583
  /** Footer buttons, rendered by `ef-dialog`'s `[actions]`. `dismiss:false`
2348
2584
  * keeps the preview open after download/print/share/open. */
2349
2585
  readonly actions: _angular_core.Signal<EfDialogAction[]>;
@@ -2356,7 +2592,7 @@ declare class EfPdfPreviewComponent {
2356
2592
  shareWhatsapp(): void;
2357
2593
  private openWhatsappWeb;
2358
2594
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfPdfPreviewComponent, never>;
2359
- 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>;
2595
+ 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; }; "defaultCallingCode": { "alias": "defaultCallingCode"; "required": false; "isSignal": true; }; }, { "visible": "visibleChange"; }, never, never, true, never>;
2360
2596
  }
2361
2597
 
2362
2598
  /**
@@ -2649,6 +2885,52 @@ declare class EfFormGridComponent {
2649
2885
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfFormGridComponent, "ef-form-grid", never, {}, {}, never, ["*"], true, never>;
2650
2886
  }
2651
2887
 
2888
+ /**
2889
+ * Declarative row-action item consumed by `ef-row-actions`.
2890
+ *
2891
+ * The component renders a native Comptoir-styled popup (no PrimeNG
2892
+ * Menu) so the design — kbd hint, danger tone, divider, ink-active
2893
+ * trigger when open — matches the prototype 1:1.
2894
+ *
2895
+ * Use `command` for actions, `separator: true` for a divider line.
2896
+ */
2897
+ interface EfRowAction {
2898
+ /** Stable identifier for trackBy / tests. */
2899
+ id?: string;
2900
+ /** Translation key — preferred. */
2901
+ labelKey?: string;
2902
+ /** Direct label — used only when `labelKey` is empty. */
2903
+ label?: string;
2904
+ /** PrimeIcons class (e.g. `'pi pi-pencil'`). */
2905
+ icon?: string;
2906
+ /** Optional keyboard-shortcut hint shown right-aligned (e.g. `'↵'`, `'E'`, `'⌘D'`). */
2907
+ kbd?: string;
2908
+ /** Tone: `'danger'` styles destructive items in the cancelled palette. */
2909
+ severity?: 'default' | 'danger';
2910
+ /** Render a divider in this slot — all other fields ignored. */
2911
+ separator?: boolean;
2912
+ /** Disable without removing. The item stays focusable and is exposed
2913
+ * as `aria-disabled`, so a keyboard user can land on it and read why. */
2914
+ disabled?: boolean;
2915
+ /** Translation key saying why the item is disabled. Shown under the
2916
+ * label, as its tooltip, and as its `aria-describedby`. */
2917
+ reasonKey?: string;
2918
+ /**
2919
+ * Hide the item explicitly. Combined with `permission` and any
2920
+ * caller-supplied filter; defaults to `true` (visible).
2921
+ */
2922
+ visible?: boolean;
2923
+ /**
2924
+ * If set, the item is hidden unless the bound `ScreenContext`
2925
+ * grants this permission, or every permission in the array (e.g.
2926
+ * Duplicate needs `[Duplicate, Create]`, since a duplicate is saved
2927
+ * through create). Items with no `permission` always show.
2928
+ */
2929
+ permission?: Permissions | ReadonlyArray<Permissions>;
2930
+ /** Click handler. The container handles closing the popup. */
2931
+ command?: () => void;
2932
+ }
2933
+
2652
2934
  /**
2653
2935
  * Sticky detail-screen toolbar. Two slots and an optional declarative
2654
2936
  * action API:
@@ -2685,6 +2967,20 @@ declare class EfFormGridComponent {
2685
2967
  * </ef-detail-toolbar>
2686
2968
  * ```
2687
2969
  *
2970
+ * Phone overflow: on the mobile tier, when the visible actions (custom and
2971
+ * standard, Save included) outnumber `[overflowAfter]`, Save and any custom
2972
+ * action flagged `keepInView` stay in view and every other action moves
2973
+ * into a kebab menu (`ef-row-actions`, so it is
2974
+ * a `role="menu"` with Escape, outside-click and RTL placement). The split
2975
+ * happens after permission and `requiresWrite` filtering, so a hidden action
2976
+ * is hidden in the menu too. The projected `[actions]` slot is not moved.
2977
+ * Buttons are 48px on a coarse pointer (see `.detail-toolbar` in patterns).
2978
+ *
2979
+ * Tablet and desktop reflow: the identity has a basis
2980
+ * (`--detail-toolbar-identity-min`, 22rem), so when it and the actions
2981
+ * cannot share a row the actions wrap below it, rather than squeezing the
2982
+ * identity until its notices are cut.
2983
+ *
2688
2984
  * Custom actions can declare `permission` and `visible` like
2689
2985
  * `EfRowAction`. With `[context]` bound, the toolbar filters them
2690
2986
  * against `ScreenContext.isGranted()` automatically.
@@ -2704,7 +3000,7 @@ declare class EfDetailToolbarComponent {
2704
3000
  /** Optional ScreenContext — when present, custom actions with a
2705
3001
  * `permission` field and standard actions Print / Duplicate /
2706
3002
  * Delete are filtered against `context.isGranted(...)`. */
2707
- readonly context: _angular_core.InputSignal<ScreenContext>;
3003
+ readonly context: _angular_core.InputSignal<ScreenContext | undefined>;
2708
3004
  /** Render the standard Print action (left of the standard group). */
2709
3005
  readonly showPrintAction: _angular_core.InputSignalWithTransform<boolean, unknown>;
2710
3006
  /** Render the standard Duplicate action. */
@@ -2718,6 +3014,11 @@ declare class EfDetailToolbarComponent {
2718
3014
  /** i18n key for the Save button label. Screens typically pass
2719
3015
  * `'common_create'` in create mode and `'common_save'` in edit. */
2720
3016
  readonly saveLabelKey: _angular_core.InputSignal<string>;
3017
+ /** Most actions (Save included) kept inline on a phone; more than this
3018
+ * moves all but Save into the overflow menu. Desktop and tablet never
3019
+ * overflow. */
3020
+ readonly overflowAfter: _angular_core.InputSignalWithTransform<number, string | number>;
3021
+ private readonly viewport;
2721
3022
  /** Fired when a `customActions` button is clicked. The toast
2722
3023
  * service or screen handler should consume `id`. The `command`
2723
3024
  * callback on the action is also invoked. */
@@ -2738,8 +3039,23 @@ declare class EfDetailToolbarComponent {
2738
3039
  readonly canSave: _angular_core.Signal<boolean>;
2739
3040
  readonly hasStandardActions: _angular_core.Signal<boolean>;
2740
3041
  readonly hasCustomActions: _angular_core.Signal<boolean>;
3042
+ /** The phone is showing the overflow menu instead of inline buttons. */
3043
+ readonly overflowing: _angular_core.Signal<boolean>;
3044
+ /** Visible custom actions that stay inline on a phone even when the rest overflow. */
3045
+ readonly keptInView: _angular_core.Signal<readonly EfDetailToolbarAction[]>;
3046
+ /** Everything but Save and the keepInView actions, as menu items, in toolbar order. */
3047
+ readonly overflowItems: _angular_core.Signal<readonly EfRowAction[]>;
2741
3048
  /** Whether the `|` separator should render — both groups present. */
2742
3049
  readonly showSeparator: _angular_core.Signal<boolean>;
3050
+ private readonly host;
3051
+ private readonly destroyRef;
3052
+ /** The standard group (separator, Print … Save) starts a row of its own,
3053
+ * below the custom actions: its leading separator would separate nothing. */
3054
+ readonly standardWrapped: _angular_core.WritableSignal<boolean>;
3055
+ constructor();
3056
+ /** Compares the standard group's row with the first item of the right
3057
+ * group. Public for specs: jsdom lays nothing out. */
3058
+ measureWrap(): void;
2743
3059
  onCustomClick(action: EfDetailToolbarAction): void;
2744
3060
  private isCustomVisible;
2745
3061
  /** Standard action visibility: `show*Action` flag AND permission
@@ -2748,7 +3064,7 @@ declare class EfDetailToolbarComponent {
2748
3064
  /** Stable trackBy across renders. */
2749
3065
  trackByAction: (i: number, a: EfDetailToolbarAction) => unknown;
2750
3066
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfDetailToolbarComponent, never>;
2751
- 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>;
3067
+ 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; }; "overflowAfter": { "alias": "overflowAfter"; "required": false; "isSignal": true; }; }, { "customAction": "customAction"; "print": "print"; "duplicate": "duplicate"; "delete": "delete"; "save": "save"; }, never, ["*", "[actions]"], true, never>;
2752
3068
  }
2753
3069
 
2754
3070
  /**
@@ -2801,7 +3117,7 @@ declare class EfSearchToolbarComponent {
2801
3117
  /** Optional ScreenContext — when present, custom actions with a
2802
3118
  * `permission` field and standard actions Export / Add are
2803
3119
  * filtered against `context.isGranted(...)`. */
2804
- readonly context: _angular_core.InputSignal<ScreenContext>;
3120
+ readonly context: _angular_core.InputSignal<ScreenContext | undefined>;
2805
3121
  /** Render the standard Export action (left of the standard group). */
2806
3122
  readonly showExportAction: _angular_core.InputSignalWithTransform<boolean, unknown>;
2807
3123
  /** Render the standard Add action (rightmost, tenant-styled). */
@@ -2886,7 +3202,7 @@ interface EfTotalsGrand {
2886
3202
  */
2887
3203
  declare class EfTotalsComponent {
2888
3204
  readonly rows: _angular_core.InputSignal<readonly EfTotalsRow[]>;
2889
- readonly grand: _angular_core.InputSignal<EfTotalsGrand>;
3205
+ readonly grand: _angular_core.InputSignal<EfTotalsGrand | undefined>;
2890
3206
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfTotalsComponent, never>;
2891
3207
  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>;
2892
3208
  }
@@ -3092,6 +3408,10 @@ declare class EfKpiCardComponent {
3092
3408
  readonly delta: _angular_core.InputSignal<string>;
3093
3409
  /** Delta tone — drives the arrow color. */
3094
3410
  readonly deltaTone: _angular_core.InputSignal<EfKpiDeltaTone>;
3411
+ /** Translation key of a secondary line under the value (e.g. "of which delivery: 120 MAD"). */
3412
+ readonly hintKey: _angular_core.InputSignal<string>;
3413
+ /** Interpolation params for `hintKey`. */
3414
+ readonly hintParams: _angular_core.InputSignal<Record<string, unknown>>;
3095
3415
  /** Raw series for the sparkline. */
3096
3416
  readonly spark: _angular_core.InputSignal<EfKpiSparkline>;
3097
3417
  /** Stroke color for the sparkline polyline. Defaults to the active
@@ -3101,9 +3421,9 @@ declare class EfKpiCardComponent {
3101
3421
  /** Computed `points="x,y x,y …"` attribute, normalized into the
3102
3422
  * 200×28 viewBox so the polyline always fits. Returns null
3103
3423
  * when the series is empty. */
3104
- readonly sparkPoints: _angular_core.Signal<string>;
3424
+ readonly sparkPoints: _angular_core.Signal<string | null>;
3105
3425
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfKpiCardComponent, never>;
3106
- 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>;
3426
+ 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; }; "hintKey": { "alias": "hintKey"; "required": false; "isSignal": true; }; "hintParams": { "alias": "hintParams"; "required": false; "isSignal": true; }; "spark": { "alias": "spark"; "required": false; "isSignal": true; }; "sparkColor": { "alias": "sparkColor"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3107
3427
  }
3108
3428
 
3109
3429
  type EfChartType = 'line' | 'bar' | 'donut';
@@ -3166,6 +3486,78 @@ declare class EfRankListComponent {
3166
3486
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfRankListComponent, "ef-rank-list", never, { "rows": { "alias": "rows"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3167
3487
  }
3168
3488
 
3489
+ type EfIdentityThumbSize = 'sm' | 'md';
3490
+ /**
3491
+ * The square that stands for a record: a collection in its detail toolbar, a
3492
+ * product in a picker row. Shows the record's image when it has one and two
3493
+ * initials of its name when it does not (or when the image fails to load).
3494
+ *
3495
+ * Built on the `.thumb` pattern from `_patterns.scss`; the size is a host
3496
+ * class (`sm` 32px, `md` 40px), never an inline style. The image is
3497
+ * decorative (`alt=""`): the name always sits next to it.
3498
+ *
3499
+ * ```html
3500
+ * <ef-identity-thumb [imageUrl]="imageUrl()" [name]="title()" size="sm" />
3501
+ * ```
3502
+ */
3503
+ declare class EfIdentityThumbComponent {
3504
+ readonly imageUrl: _angular_core.InputSignal<string | null | undefined>;
3505
+ readonly name: _angular_core.InputSignal<string | null | undefined>;
3506
+ readonly size: _angular_core.InputSignal<EfIdentityThumbSize>;
3507
+ /** Set when the image fails; reset whenever the url changes. */
3508
+ protected readonly failed: _angular_core.WritableSignal<boolean>;
3509
+ protected readonly showImage: _angular_core.Signal<boolean>;
3510
+ /**
3511
+ * First letter of the first two words, or the first two letters of a
3512
+ * single word. `Array.from` splits by code point, so a letter outside the
3513
+ * BMP is never cut in half.
3514
+ */
3515
+ protected readonly initials: _angular_core.Signal<string>;
3516
+ constructor();
3517
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfIdentityThumbComponent, never>;
3518
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfIdentityThumbComponent, "ef-identity-thumb", never, { "imageUrl": { "alias": "imageUrl"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3519
+ }
3520
+
3521
+ /**
3522
+ * What a search engine would show for a page: the title, the address and the
3523
+ * description, cut where the engine cuts them (60 and 160 characters, the
3524
+ * same figures the SEO counters advise). One preview for every SEO card.
3525
+ *
3526
+ * It depicts a search result, so it keeps its own light surface and the
3527
+ * familiar link colours in both themes. The address is always left to right;
3528
+ * the title and description follow the direction of their own text, so an
3529
+ * Arabic page previews right to left inside an LTR or RTL app.
3530
+ *
3531
+ * ```html
3532
+ * <ef-seo-preview
3533
+ * [pageTitle]="seoTitle() || title()"
3534
+ * [url]="'shop.example/collections/' + slug()"
3535
+ * [description]="seoDescription() || description()"
3536
+ * contentLang="fr" />
3537
+ * ```
3538
+ */
3539
+ declare class EfSeoPreviewComponent {
3540
+ /** The page title. Named `pageTitle` so it does not become the host's tooltip. */
3541
+ readonly pageTitle: _angular_core.InputSignal<string | null | undefined>;
3542
+ /** The address as shown (no scheme needed), always rendered left to right. */
3543
+ readonly url: _angular_core.InputSignal<string | null | undefined>;
3544
+ readonly description: _angular_core.InputSignal<string | null | undefined>;
3545
+ /** i18n key of the small heading above the preview. */
3546
+ readonly labelKey: _angular_core.InputSignal<string>;
3547
+ /** Characters kept of the title; `null` shows it whole. */
3548
+ readonly titleMaxLength: _angular_core.InputSignal<number | null>;
3549
+ /** Characters kept of the description; `null` shows it whole. */
3550
+ readonly descriptionMaxLength: _angular_core.InputSignal<number | null>;
3551
+ /** Language of the previewed text (`fr`, `ar`, `en`), for screen readers and hyphenation. */
3552
+ readonly contentLang: _angular_core.InputSignal<string | null>;
3553
+ protected readonly labelId: string;
3554
+ protected readonly shownTitle: _angular_core.Signal<string>;
3555
+ protected readonly shownUrl: _angular_core.Signal<string>;
3556
+ protected readonly shownDescription: _angular_core.Signal<string>;
3557
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfSeoPreviewComponent, never>;
3558
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfSeoPreviewComponent, "ef-seo-preview", never, { "pageTitle": { "alias": "pageTitle"; "required": false; "isSignal": true; }; "url": { "alias": "url"; "required": false; "isSignal": true; }; "description": { "alias": "description"; "required": false; "isSignal": true; }; "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "titleMaxLength": { "alias": "titleMaxLength"; "required": false; "isSignal": true; }; "descriptionMaxLength": { "alias": "descriptionMaxLength"; "required": false; "isSignal": true; }; "contentLang": { "alias": "contentLang"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3559
+ }
3560
+
3169
3561
  /**
3170
3562
  * Shared base for every Comptoir form control (`ef-input-text`,
3171
3563
  * `ef-textarea`, `ef-select`, `ef-input-number`, `ef-checkbox`, …).
@@ -3186,8 +3578,9 @@ declare class EfRankListComponent {
3186
3578
  * - **ARIA** — `ariaLabelKey` / `ariaLabel` for screen-reader-only
3187
3579
  * labels; `effectiveAriaLabel` returns `null` when a visible
3188
3580
  * label is rendered (which already serves as the ARIA name).
3189
- * `errorsId` provides a stable id for the error/hint container
3190
- * so subclasses can wire `aria-describedby`.
3581
+ * `errorsId` and `hintId` give the error container and the hint
3582
+ * stable ids, and `describedBy()` joins them into the value
3583
+ * subclasses bind to `aria-describedby`.
3191
3584
  * - **State** — `required` / `readonly` / `disabled` shared inputs,
3192
3585
  * used directly in subclass templates and toggled by CVA's
3193
3586
  * `setDisabledState` in subclasses.
@@ -3224,6 +3617,14 @@ declare abstract class AbstractEfFormControl {
3224
3617
  readonly labelKey: _angular_core.InputSignal<string>;
3225
3618
  readonly placeholder: _angular_core.InputSignal<string>;
3226
3619
  readonly placeholderKey: _angular_core.InputSignal<string>;
3620
+ /** Short help text shown under the control. Use `hintKey` for i18n. */
3621
+ readonly hint: _angular_core.InputSignal<string>;
3622
+ /** Translation key for the hint, preferred over `hint`. The hint is
3623
+ * linked to the control through `aria-describedby` (see
3624
+ * {@link describedBy}), so a screen reader reads it with the field.
3625
+ * Rendered by ef-input-text, ef-textarea, ef-inputnumber and ef-select;
3626
+ * the other controls inherit the input but do not render it yet. */
3627
+ readonly hintKey: _angular_core.InputSignal<string>;
3227
3628
  readonly ariaLabel: _angular_core.InputSignal<string>;
3228
3629
  readonly ariaLabelKey: _angular_core.InputSignal<string>;
3229
3630
  readonly required: _angular_core.InputSignalWithTransform<boolean, unknown>;
@@ -3274,7 +3675,7 @@ declare abstract class AbstractEfFormControl {
3274
3675
  * Falls back to the `NgControl` `serverError` path when neither is
3275
3676
  * present, so `ngModel`-based (v1) screens keep working unchanged.
3276
3677
  */
3277
- readonly errors: _angular_core.InputSignal<string[]>;
3678
+ readonly errors: _angular_core.InputSignal<string[] | null | undefined>;
3278
3679
  /** Optional enclosing `[efServerErrors]` scope (auto-link by `name`). */
3279
3680
  private readonly serverErrorsScope;
3280
3681
  /**
@@ -3284,7 +3685,7 @@ declare abstract class AbstractEfFormControl {
3284
3685
  * the subclass `serverErrors` / `isInvalid` getters track the source
3285
3686
  * signals under zoneless change detection.
3286
3687
  */
3287
- readonly resolvedExternalErrors: _angular_core.Signal<string[]>;
3688
+ readonly resolvedExternalErrors: _angular_core.Signal<string[] | null>;
3288
3689
  protected readonly translateService: TranslateService;
3289
3690
  /** Resolved DOM id. Always non-empty so `<label for>` and
3290
3691
  * `<input id>` can match unambiguously. */
@@ -3298,12 +3699,26 @@ declare abstract class AbstractEfFormControl {
3298
3699
  * accessible name and duplicate `aria-label` would override it.
3299
3700
  */
3300
3701
  get effectiveAriaLabel(): string | null;
3301
- /** Stable id for the error / hint container — wired through
3702
+ /** Ids of elements outside the control that describe it too (a character
3703
+ * counter, a help block), appended to `aria-describedby`. */
3704
+ readonly describedByExtra: _angular_core.InputSignal<string>;
3705
+ /** Translated hint (or empty string). */
3706
+ get effectiveHint(): string;
3707
+ /** Stable id of the hint element, `<id>-hint`. */
3708
+ get hintId(): string;
3709
+ /**
3710
+ * The control's `aria-describedby`: the hint id when a hint is shown,
3711
+ * then the errors id when `showErrors` (the subclass knows when its
3712
+ * error container renders), then any `extra` ids. `null` (no
3713
+ * attribute) when there is nothing to point at.
3714
+ */
3715
+ describedBy(showErrors: boolean, ...extra: (string | null | undefined)[]): string | null;
3716
+ /** Stable id for the error container, wired through
3302
3717
  * `aria-describedby` on the input so screen readers announce
3303
3718
  * the validation message together with the field name. */
3304
3719
  get errorsId(): string;
3305
3720
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AbstractEfFormControl, never>;
3306
- 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>;
3721
+ 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; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "hintKey": { "alias": "hintKey"; "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; }; "describedByExtra": { "alias": "describedByExtra"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3307
3722
  }
3308
3723
 
3309
3724
  declare class EfInputTextComponent extends AbstractEfFormControl implements ControlValueAccessor {
@@ -3321,7 +3736,7 @@ declare class EfInputTextComponent extends AbstractEfFormControl implements Cont
3321
3736
  * Rendering variant — same dual-mode pattern as `ef-button`.
3322
3737
  * - `'comptoir'` (default) — native `<input class="ef-input">` against
3323
3738
  * the Comptoir pattern styles. Renders at the
3324
- * canonical `--hit-base` (40px) control height.
3739
+ * canonical `--hit-field` height (40px, 48px on touch).
3325
3740
  * Required for `prefix` / `suffix`.
3326
3741
  * - `'primeng'` — wraps `<input pInputText>` (v1). Opt in when
3327
3742
  * you specifically need PrimeNG input behaviour.
@@ -3341,7 +3756,7 @@ declare class EfInputTextComponent extends AbstractEfFormControl implements Cont
3341
3756
  value: string;
3342
3757
  private onChange;
3343
3758
  private onTouched;
3344
- readonly ngControl: NgControl;
3759
+ readonly ngControl: NgControl | null;
3345
3760
  constructor();
3346
3761
  get showRequired(): boolean;
3347
3762
  get serverErrors(): string[] | null;
@@ -3408,7 +3823,7 @@ declare class EfSelectComponent extends AbstractEfFormControl implements Control
3408
3823
  group: boolean;
3409
3824
  optionGroupLabel: string;
3410
3825
  optionGroupChildren: string;
3411
- /** Control height. Default (undefined) = the canonical `--hit-base` (40px),
3826
+ /** Control height. Default (undefined) = the canonical `--hit-field` (40px, 48px on touch),
3412
3827
  * matching native inputs (ADR-009). `'small'` opts into the dense 32px
3413
3828
  * variant for compact contexts (e.g. table-row editors). */
3414
3829
  size?: 'small' | 'large';
@@ -3469,8 +3884,8 @@ declare class EfSelectComponent extends AbstractEfFormControl implements Control
3469
3884
  * the `.es` / `.es__panel` patterns (the custom
3470
3885
  * dropdown: filter, styled options, animated panel
3471
3886
  * — see `components.html` Section 04). Also pinned
3472
- * to the canonical `--hit-base` (40px) trigger
3473
- * height (`.es.p-select { min-height: --hit-base }`),
3887
+ * to the canonical `--hit-field` (40px, 48px on touch) trigger
3888
+ * height (`.es.p-select { min-height: --hit-field }`),
3474
3889
  * so it aligns with inputs/buttons in a row.
3475
3890
  * - `'comptoir'` — lite native `<select class="ef-select-native">`.
3476
3891
  * No filter / panel / multi / templates; use it for
@@ -3505,11 +3920,28 @@ declare class EfSelectComponent extends AbstractEfFormControl implements Control
3505
3920
  ngControl: NgControl | null;
3506
3921
  private readonly injector;
3507
3922
  ngOnInit(): void;
3923
+ /**
3924
+ * The text an unset select shows: the placeholder, or nothing. A blank or
3925
+ * dash-only placeholder (a translation of " " or "—") counts as nothing, so
3926
+ * the unset state can never read as "— —". The native variant used to wrap
3927
+ * any truthy placeholder in dashes, which turned " " into exactly that.
3928
+ */
3929
+ get placeholderText(): string;
3508
3930
  /** Translated filter placeholder. Falls back to a generic key, then
3509
3931
  * to "Search…" so it always renders something readable. */
3510
3932
  get effectiveFilterPlaceholder(): string;
3511
3933
  get effectiveEmptyMessage(): string;
3512
3934
  get effectiveEmptyFilterMessage(): string;
3935
+ private _describedByPt?;
3936
+ /**
3937
+ * PrimeNG pass-through that puts `aria-describedby` (hint, then errors)
3938
+ * on the element the `<label for>` points at: `p-select`'s
3939
+ * `span[role=combobox]` (`label`) and `p-multiselect`'s hidden focus
3940
+ * input (`hiddenInput`). Neither component has an input for it. Cached
3941
+ * per id list so change detection does not hand PrimeNG a new object
3942
+ * every pass.
3943
+ */
3944
+ get describedByPt(): Record<string, Record<string, string | null>>;
3513
3945
  /** Always include `.es` so the global Comptoir skin in
3514
3946
  * `_patterns.scss` always finds a hook, regardless of what the
3515
3947
  * consumer passes via `styleClass`. */
@@ -3609,7 +4041,7 @@ declare class EfCheckboxComponent extends AbstractEfFormControl implements Contr
3609
4041
  checkedChange: EventEmitter<any>;
3610
4042
  private onChange;
3611
4043
  private onTouched;
3612
- readonly ngControl: NgControl;
4044
+ readonly ngControl: NgControl | null;
3613
4045
  constructor();
3614
4046
  get showRequired(): boolean;
3615
4047
  get serverErrors(): string[] | null;
@@ -3643,6 +4075,11 @@ declare class EfCheckboxComponent extends AbstractEfFormControl implements Contr
3643
4075
  * ariaLabelKey="sales_clients_toggle_active_aria"
3644
4076
  * (checkedChange)="setActive(row, $event)" />
3645
4077
  * ```
4078
+ *
4079
+ * With a label, the host is a flex row: the switch keeps its size and a
4080
+ * sentence-long label wraps beside it, not under it, at phone width.
4081
+ * Screens no longer restyle `ef-toggleswitch` for this. Without a label
4082
+ * (a table row's switch) the host keeps its inline box.
3646
4083
  */
3647
4084
  declare class EfToggleSwitchComponent extends AbstractEfFormControl implements ControlValueAccessor {
3648
4085
  /**
@@ -3658,7 +4095,7 @@ declare class EfToggleSwitchComponent extends AbstractEfFormControl implements C
3658
4095
  checkedChange: EventEmitter<boolean>;
3659
4096
  private onChange;
3660
4097
  private onTouched;
3661
- readonly ngControl: NgControl;
4098
+ readonly ngControl: NgControl | null;
3662
4099
  constructor();
3663
4100
  writeValue(value: unknown): void;
3664
4101
  registerOnChange(fn: (value: boolean) => void): void;
@@ -3720,7 +4157,7 @@ declare class EfDatepickerComponent implements ControlValueAccessor {
3720
4157
  value: any;
3721
4158
  private onChange;
3722
4159
  private onTouched;
3723
- readonly ngControl: NgControl;
4160
+ readonly ngControl: NgControl | null;
3724
4161
  private readonly translateService;
3725
4162
  constructor();
3726
4163
  get effectivePlaceholder(): string;
@@ -3746,38 +4183,6 @@ declare class EfDatepickerComponent implements ControlValueAccessor {
3746
4183
  static ngAcceptInputType_autofocus: unknown;
3747
4184
  }
3748
4185
 
3749
- /**
3750
- * Comptoir number input.
3751
- *
3752
- * Two render variants share the same API:
3753
- * - `'comptoir'` (default) — native `<input type="text" inputmode="decimal">`
3754
- * styled against `.ef-input` / `.ef-input-group`. Lean, no locale
3755
- * formatting on display, but data entry accepts both `,` and `.` as
3756
- * the decimal separator (fr-MA users type commas) and ignores
3757
- * space group separators; pair with `suffix="MAD"` for unit display
3758
- * in dense grids (variants table, inventory adjustments, etc.).
3759
- * See ADR-009.
3760
- * - `'primeng'` — wraps `p-inputnumber`. Full locale-aware
3761
- * currency / decimal formatting, stepper buttons, prefix / suffix.
3762
- *
3763
- * Identity / label / placeholder / aria / required / readonly /
3764
- * disabled are inherited from `AbstractEfFormControl`.
3765
- *
3766
- * ```html
3767
- * <ef-inputnumber
3768
- * labelKey="catalog.price"
3769
- * variant="primeng"
3770
- * mode="currency" currency="MAD" locale="fr-MA"
3771
- * [(value)]="variant.price" />
3772
- *
3773
- * <ef-inputnumber
3774
- * labelKey="catalog.inventory"
3775
- * variant="comptoir"
3776
- * [showButtons]="true" buttonLayout="horizontal"
3777
- * [min]="0" [step]="1"
3778
- * [(value)]="variant.inventoryQuantity" />
3779
- * ```
3780
- */
3781
4186
  declare class EfInputNumberComponent extends AbstractEfFormControl implements ControlValueAccessor {
3782
4187
  inputNumber?: InputNumber;
3783
4188
  value?: Nullable<number>;
@@ -3804,7 +4209,7 @@ declare class EfInputNumberComponent extends AbstractEfFormControl implements Co
3804
4209
  step: number;
3805
4210
  allowEmpty: boolean;
3806
4211
  inputSize?: number;
3807
- /** Default (undefined) = canonical `--hit-base` (40px); `'small'` = dense 32px. */
4212
+ /** PrimeNG variant only: `'small'` = dense 32px. The comptoir variant ignores it and is always `--hit-field` (40px, 48px on touch). */
3808
4213
  size?: 'small' | 'large';
3809
4214
  maxlength?: number;
3810
4215
  minlength?: number;
@@ -3827,7 +4232,7 @@ declare class EfInputNumberComponent extends AbstractEfFormControl implements Co
3827
4232
  * Render variant.
3828
4233
  * - `'comptoir'` (default) — native `<input type="text" inputmode="decimal">`
3829
4234
  * styled against `.ef-input` / `.ef-input-group`,
3830
- * at the canonical `--hit-base` (40px) height.
4235
+ * at the canonical `--hit-field` height (40px, 48px on touch).
3831
4236
  * Accepts `,` and `.` as decimal separator.
3832
4237
  * - `'primeng'` — wraps `p-inputnumber`. Opt in for PrimeNG's
3833
4238
  * formatted stepper behaviour.
@@ -3848,9 +4253,54 @@ declare class EfInputNumberComponent extends AbstractEfFormControl implements Co
3848
4253
  /** A number field is clearable when it holds any value (including 0). */
3849
4254
  protected get hasValue(): boolean;
3850
4255
  clearValue(): void;
4256
+ /** True while the comptoir input has focus in currency mode. */
4257
+ private focused;
4258
+ /** What the user typed since focus, shown as is (never reformatted). */
4259
+ private editText;
4260
+ /** A change that did not come from typing (clear, stepper, form write)
4261
+ * replaces the edit text while focused, or the box would keep the old
4262
+ * amount and the next keystroke would append to it. Marks the view,
4263
+ * since a programmatic call (zoneless) would not. */
4264
+ private syncEditText;
4265
+ /** Comptoir currency formatting applies: `mode="currency"` and a currency code. */
4266
+ get isCurrency(): boolean;
4267
+ private get effectiveLocale();
4268
+ /** Decimals: explicit inputs, else the currency's own (2 for MAD). */
4269
+ private get fractionDigits();
4270
+ /**
4271
+ * The amount as text, in Latin digits and without bidi marks: grouped
4272
+ * for display, ungrouped for editing (so the text always parses back
4273
+ * through {@link parseComptoirValue}).
4274
+ */
4275
+ private formatAmount;
4276
+ /**
4277
+ * The currency addon: the locale's symbol and the side it goes on, or
4278
+ * `null` outside currency mode or when an explicit prefix / suffix
4279
+ * already shows the unit.
4280
+ */
4281
+ get currencyAddon(): {
4282
+ symbol: string;
4283
+ side: 'prefix' | 'suffix';
4284
+ } | null;
4285
+ /**
4286
+ * Currency mode, before the generic parse: drops the currency's names (a
4287
+ * pasted "1.234,50 MAD" or "1.234,50 د.م."), then, when the currency has
4288
+ * at most 2 decimals, reads the locale's own group separator followed by
4289
+ * groups of exactly 3 digits as grouping. "1.500" in a fr-MA or ar-MA MAD
4290
+ * field is 1 500, not 1,50: a 3-digit tail cannot be a decimal there.
4291
+ * Only the locale's group character counts, so a French "1,500" stays
4292
+ * 1,5, and "0.500" or "12.50" stay decimals.
4293
+ */
4294
+ private stripCurrencyGrouping;
4295
+ /** Id of the currency addon, referenced by the input's aria-describedby. */
4296
+ get currencyId(): string;
4297
+ /** Text bound to the comptoir input. */
4298
+ get displayText(): string | number;
3851
4299
  private onChange;
3852
4300
  private onTouched;
3853
- readonly ngControl: NgControl;
4301
+ readonly ngControl: NgControl | null;
4302
+ private readonly localeId;
4303
+ private readonly cdr;
3854
4304
  constructor();
3855
4305
  get showRequired(): boolean;
3856
4306
  get serverErrors(): string[] | null;
@@ -3871,6 +4321,8 @@ declare class EfInputNumberComponent extends AbstractEfFormControl implements Co
3871
4321
  * dot before parsing, and spaces (JS `\s` covers NBSP / narrow-NBSP
3872
4322
  * group separators) are stripped. Both `23,50` and `23.50` parse to
3873
4323
  * 23.5; unparseable text yields `null` (existing empty semantics).
4324
+ * Bidi marks (an Arabic formatted amount carries U+200F) are dropped,
4325
+ * and when both `.` and `,` appear the last one is the decimal.
3874
4326
  */
3875
4327
  private parseComptoirValue;
3876
4328
  /**
@@ -3918,7 +4370,7 @@ declare class EfPasswordComponent extends AbstractEfFormControl implements Contr
3918
4370
  value: string;
3919
4371
  private onChange;
3920
4372
  private onTouched;
3921
- readonly ngControl: NgControl;
4373
+ readonly ngControl: NgControl | null;
3922
4374
  constructor();
3923
4375
  get showRequired(): boolean;
3924
4376
  get serverErrors(): string[] | null;
@@ -3958,7 +4410,7 @@ declare class EfMultiSelectComponent extends AbstractEfFormControl implements Co
3958
4410
  selectedValues: any[];
3959
4411
  private propagateChange;
3960
4412
  private propagateTouched;
3961
- readonly ngControl: NgControl;
4413
+ readonly ngControl: NgControl | null;
3962
4414
  constructor();
3963
4415
  get showRequired(): boolean;
3964
4416
  get serverErrors(): string[] | null;
@@ -3987,7 +4439,7 @@ declare class EfSelectButtonComponent extends AbstractEfFormControl implements C
3987
4439
  value: any;
3988
4440
  private onChange;
3989
4441
  private onTouched;
3990
- readonly ngControl: NgControl;
4442
+ readonly ngControl: NgControl | null;
3991
4443
  constructor();
3992
4444
  writeValue(value: any): void;
3993
4445
  registerOnChange(fn: (value: any) => void): void;
@@ -4053,7 +4505,7 @@ declare class EfQuantityStepperComponent implements ControlValueAccessor {
4053
4505
  /**
4054
4506
  * Callback to invoke when the value changes
4055
4507
  */
4056
- valueChange: EventEmitter<number>;
4508
+ valueChange: EventEmitter<number | null>;
4057
4509
  /**
4058
4510
  * Pass Through configuration for custom styling
4059
4511
  */
@@ -4286,23 +4738,23 @@ declare class EfDatatableComponent implements OnInit {
4286
4738
  /**
4287
4739
  * Action column configuration (edit/delete/duplicate buttons)
4288
4740
  */
4289
- actions: _angular_core.InputSignal<EfDatatableActions>;
4741
+ actions: _angular_core.InputSignal<EfDatatableActions | undefined>;
4290
4742
  /**
4291
4743
  * SearchEntity containing items, pagination, etc.
4292
4744
  */
4293
- searchEntity: _angular_core.InputSignal<DatatableSearchEntity>;
4745
+ searchEntity: _angular_core.InputSignal<DatatableSearchEntity | undefined>;
4294
4746
  /**
4295
4747
  * ScreenContext for reference data access
4296
4748
  */
4297
- context: _angular_core.InputSignal<ScreenContext>;
4749
+ context: _angular_core.InputSignal<ScreenContext | undefined>;
4298
4750
  /**
4299
4751
  * PassThrough options for deep DOM customization
4300
4752
  */
4301
- pt: _angular_core.InputSignal<Record<string, unknown>>;
4753
+ pt: _angular_core.InputSignal<Record<string, unknown> | undefined>;
4302
4754
  /**
4303
4755
  * Screen state key for state persistence (auto-generates stateKey if not provided in config)
4304
4756
  */
4305
- screenStateKey: _angular_core.InputSignal<string>;
4757
+ screenStateKey: _angular_core.InputSignal<string | undefined>;
4306
4758
  /**
4307
4759
  * Emitted when edit button is clicked (emits row ID)
4308
4760
  */
@@ -4363,7 +4815,7 @@ declare class EfDatatableComponent implements OnInit {
4363
4815
  /**
4364
4816
  * Computed sort field from searchEntity for restoring table state
4365
4817
  */
4366
- sortField: _angular_core.Signal<string>;
4818
+ sortField: _angular_core.Signal<string | undefined>;
4367
4819
  /**
4368
4820
  * Computed sort order from searchEntity for restoring table state
4369
4821
  * 1 = ascending, -1 = descending
@@ -4471,50 +4923,16 @@ type EfDataCardMobileLayout = 'row' | 'card';
4471
4923
  */
4472
4924
  declare const EF_DATA_CARD_MOBILE_LAYOUT: InjectionToken<EfDataCardMobileLayout>;
4473
4925
 
4474
- /**
4475
- * Declarative row-action item consumed by `ef-row-actions`.
4476
- *
4477
- * The component renders a native Comptoir-styled popup (no PrimeNG
4478
- * Menu) so the design — kbd hint, danger tone, divider, ink-active
4479
- * trigger when open — matches the prototype 1:1.
4480
- *
4481
- * Use `command` for actions, `separator: true` for a divider line.
4482
- */
4483
- interface EfRowAction {
4484
- /** Stable identifier for trackBy / tests. */
4485
- id?: string;
4486
- /** Translation key — preferred. */
4487
- labelKey?: string;
4488
- /** Direct label — used only when `labelKey` is empty. */
4489
- label?: string;
4490
- /** PrimeIcons class (e.g. `'pi pi-pencil'`). */
4491
- icon?: string;
4492
- /** Optional keyboard-shortcut hint shown right-aligned (e.g. `'↵'`, `'E'`, `'⌘D'`). */
4493
- kbd?: string;
4494
- /** Tone: `'danger'` styles destructive items in the cancelled palette. */
4495
- severity?: 'default' | 'danger';
4496
- /** Render a divider in this slot — all other fields ignored. */
4497
- separator?: boolean;
4498
- /** Disable without removing. */
4499
- disabled?: boolean;
4500
- /**
4501
- * Hide the item explicitly. Combined with `permission` and any
4502
- * caller-supplied filter; defaults to `true` (visible).
4503
- */
4504
- visible?: boolean;
4505
- /**
4506
- * If set, the item is hidden unless the bound `ScreenContext`
4507
- * grants this permission, or every permission in the array (e.g.
4508
- * Duplicate needs `[Duplicate, Create]`, since a duplicate is saved
4509
- * through create). Items with no `permission` always show.
4510
- */
4511
- permission?: Permissions | ReadonlyArray<Permissions>;
4512
- /** Click handler. The container handles closing the popup. */
4513
- command?: () => void;
4514
- }
4515
-
4516
4926
  /** Action identifier emitted by `ef-data-card`'s auto row-actions cell. */
4517
4927
  type EfDataCardRowAction = 'view' | 'edit' | 'duplicate' | 'delete';
4928
+ /** What `[rowActionState]` answers for one action on one row: `'hidden'`
4929
+ * leaves it out of the menu, `'disabled'` (or `{ state: 'disabled', reasonKey }`, which also says why, announced
4930
+ * and shown under the item) shows it greyed and inert, and
4931
+ * `null` / `undefined` leaves it as it is. */
4932
+ type EfDataCardRowActionState = 'hidden' | 'disabled' | {
4933
+ state: 'disabled';
4934
+ reasonKey?: string;
4935
+ } | null | undefined;
4518
4936
  /** Row height presets offered by the Density control. */
4519
4937
  type EfTableDensity = 'compact' | 'default' | 'comfortable';
4520
4938
  /**
@@ -4567,7 +4985,7 @@ declare class EfDataCardComponent<TRow = any> implements AfterContentInit {
4567
4985
  private readonly configuredMobileLayout;
4568
4986
  readonly isMobile: _angular_core.Signal<boolean>;
4569
4987
  /** Per-screen override of the app-wide default. */
4570
- readonly mobileLayout: _angular_core.InputSignal<EfDataCardMobileLayout>;
4988
+ readonly mobileLayout: _angular_core.InputSignal<EfDataCardMobileLayout | null>;
4571
4989
  protected readonly effectiveMobileLayout: _angular_core.Signal<EfDataCardMobileLayout>;
4572
4990
  /** Rows currently showing their detail, keyed the same way as trackByRow. */
4573
4991
  private readonly expandedRows;
@@ -4602,8 +5020,8 @@ declare class EfDataCardComponent<TRow = any> implements AfterContentInit {
4602
5020
  type?: string;
4603
5021
  }): string;
4604
5022
  private readonly derivedPrimaryId;
4605
- protected readonly primaryColumn: _angular_core.Signal<EfDataCardColumn>;
4606
- protected readonly statusColumn: _angular_core.Signal<EfDataCardColumn>;
5023
+ protected readonly primaryColumn: _angular_core.Signal<EfDataCardColumn | undefined>;
5024
+ protected readonly statusColumn: _angular_core.Signal<EfDataCardColumn | undefined>;
4607
5025
  protected readonly secondaryColumns: _angular_core.Signal<EfDataCardColumn[]>;
4608
5026
  /**
4609
5027
  * Fields shown once a row opens. The primary column is excluded: the
@@ -4637,7 +5055,7 @@ declare class EfDataCardComponent<TRow = any> implements AfterContentInit {
4637
5055
  readonly pageSizeOptions: _angular_core.InputSignal<readonly number[]>;
4638
5056
  readonly hidePager: _angular_core.InputSignalWithTransform<boolean, unknown>;
4639
5057
  /** Currently active sort. Header click toggles asc / desc. */
4640
- readonly sort: _angular_core.InputSignal<EfDataCardSort>;
5058
+ readonly sort: _angular_core.InputSignal<EfDataCardSort | undefined>;
4641
5059
  /**
4642
5060
  * First load and every refresh. While true with no rows yet, the body
4643
5061
  * shimmers `ef-skeleton` placeholders instead of the empty state — a
@@ -4694,12 +5112,48 @@ declare class EfDataCardComponent<TRow = any> implements AfterContentInit {
4694
5112
  * their native behaviour (toggle selection, run an action, …).
4695
5113
  */
4696
5114
  readonly rowDoubleClickable: _angular_core.InputSignalWithTransform<boolean, unknown>;
5115
+ /**
5116
+ * Turns an activatable row into a toggle button (APG button pattern):
5117
+ * the row, already `role="button"` under `[rowDoubleClickable]`, gets
5118
+ * `aria-pressed` from this function, on the desktop `<tr>` and on the
5119
+ * phone row head alike. Enter and Space both activate. For a
5120
+ * multi-select picker whose rows are the selection.
5121
+ *
5122
+ * `aria-pressed` rather than `aria-selected`: selection belongs to a
5123
+ * grid or listbox, which own arrow-key focus management this table does
5124
+ * not have, and `aria-selected` is not allowed on a button. Unset, no
5125
+ * attribute is written.
5126
+ */
5127
+ readonly rowPressed: _angular_core.InputSignal<((row: TRow) => boolean) | null>;
5128
+ /**
5129
+ * A row that is shown but cannot be activated (a product already in the
5130
+ * collection): `.is-disabled` dims it, `aria-disabled` says so, and
5131
+ * click, Enter and Space are ignored. It stays focusable, so a screen
5132
+ * reader user can still find it and hear why nothing happens.
5133
+ */
5134
+ readonly rowDisabled: _angular_core.InputSignal<((row: TRow) => boolean) | null>;
5135
+ protected pressedAttr(row: TRow): 'true' | 'false' | null;
5136
+ protected isRowDisabled(row: TRow): boolean;
4697
5137
  readonly hasActionsColumn: _angular_core.InputSignalWithTransform<boolean, unknown>;
4698
- readonly rowActionsContext: _angular_core.InputSignal<ScreenContext>;
5138
+ readonly rowActionsContext: _angular_core.InputSignal<ScreenContext | undefined>;
4699
5139
  readonly showViewAction: _angular_core.InputSignalWithTransform<boolean, unknown>;
4700
5140
  readonly showEditAction: _angular_core.InputSignalWithTransform<boolean, unknown>;
4701
5141
  readonly showDuplicateAction: _angular_core.InputSignalWithTransform<boolean, unknown>;
4702
5142
  readonly showDeleteAction: _angular_core.InputSignalWithTransform<boolean, unknown>;
5143
+ /**
5144
+ * Per-row visibility of the built-in row actions, e.g. no Delete on a
5145
+ * row that is not a draft:
5146
+ *
5147
+ * ```ts
5148
+ * rowActionState = (action, row) =>
5149
+ * action === 'delete' && row.status !== 'draft' ? 'hidden' : null;
5150
+ * ```
5151
+ *
5152
+ * Applied on top of the `show*Action` flags and the permission filter,
5153
+ * on desktop rows and phone cards alike. A row whose actions are all
5154
+ * hidden gets a disabled trigger. Unset, every row keeps every action.
5155
+ */
5156
+ readonly rowActionState: _angular_core.InputSignal<((action: EfDataCardRowAction, row: TRow) => EfDataCardRowActionState) | null>;
4703
5157
  /** Render the Density control in the head row. */
4704
5158
  readonly showDensityControl: _angular_core.InputSignalWithTransform<boolean, unknown>;
4705
5159
  /** Render the Columns picker in the head row. */
@@ -4723,7 +5177,7 @@ declare class EfDataCardComponent<TRow = any> implements AfterContentInit {
4723
5177
  * moving Export out of `ef-search-toolbar` does not quietly drop the
4724
5178
  * permission check that lived there.
4725
5179
  */
4726
- readonly screenContext: _angular_core.InputSignal<ScreenContext>;
5180
+ readonly screenContext: _angular_core.InputSignal<ScreenContext | undefined>;
4727
5181
  /**
4728
5182
  * Mirrors `ef-search-toolbar`'s rule exactly: hidden unless asked for,
4729
5183
  * and with a context present it obeys `hasExportPermission`. No
@@ -4739,9 +5193,9 @@ declare class EfDataCardComponent<TRow = any> implements AfterContentInit {
4739
5193
  readonly searchTerm: _angular_core.InputSignal<string>;
4740
5194
  /**
4741
5195
  * Stable key used to remember density and hidden columns for this
4742
- * table. Preferences are a per-viewer convenience, so they live in
4743
- * localStorage and are read defensively -- a private window or
4744
- * cleared site data simply falls back to the defaults.
5196
+ * table, through `EF_PREFERENCE_STORE`. They are read defensively: a
5197
+ * store that throws or holds nothing falls back to the defaults
5198
+ * (`defaultDensity`, and each column's `defaultHidden`).
4745
5199
  */
4746
5200
  readonly tableKey: _angular_core.InputSignal<string>;
4747
5201
  readonly pageChange: _angular_core.OutputEmitterRef<number>;
@@ -4804,7 +5258,7 @@ declare class EfDataCardComponent<TRow = any> implements AfterContentInit {
4804
5258
  /** Build an Angular DigitInfo string from min/max fraction-digit hints. */
4805
5259
  numberFormat(col: EfDataCardColumn): string;
4806
5260
  /** Which tools panel is open, if any. */
4807
- readonly openTool: _angular_core.WritableSignal<"density" | "columns">;
5261
+ readonly openTool: _angular_core.WritableSignal<"density" | "columns" | null>;
4808
5262
  /**
4809
5263
  * Density this table starts at.
4810
5264
  *
@@ -4828,8 +5282,8 @@ declare class EfDataCardComponent<TRow = any> implements AfterContentInit {
4828
5282
  to: number;
4829
5283
  }>;
4830
5284
  /** Index being dragged, and the index it would land on. */
4831
- readonly dragFrom: _angular_core.WritableSignal<number>;
4832
- readonly dragTo: _angular_core.WritableSignal<number>;
5285
+ readonly dragFrom: _angular_core.WritableSignal<number | null>;
5286
+ readonly dragTo: _angular_core.WritableSignal<number | null>;
4833
5287
  onDragPointerDown(index: number, ev: PointerEvent): void;
4834
5288
  onDragPointerMove(ev: PointerEvent): void;
4835
5289
  onDragPointerUp(): void;
@@ -4856,8 +5310,17 @@ declare class EfDataCardComponent<TRow = any> implements AfterContentInit {
4856
5310
  readonly fixedLayout: _angular_core.InputSignalWithTransform<boolean, unknown>;
4857
5311
  /** Row density. Applied as a class on `.tbl-wrap`. */
4858
5312
  readonly density: _angular_core.WritableSignal<EfTableDensity>;
4859
- /** Column ids the viewer has hidden. */
4860
- readonly hiddenColumnIds: _angular_core.WritableSignal<readonly string[]>;
5313
+ /**
5314
+ * The viewer's own column choice: from the saved preferences, or from
5315
+ * the Columns picker. `null` until there is one, so the columns'
5316
+ * `defaultHidden` flags decide.
5317
+ */
5318
+ private readonly chosenHiddenColumnIds;
5319
+ /** Columns hidden until the viewer says otherwise. Only ones the
5320
+ * picker lists, so a default can always be undone. */
5321
+ private readonly defaultHiddenColumnIds;
5322
+ /** Column ids hidden right now: the viewer's choice, else the defaults. */
5323
+ readonly hiddenColumnIds: _angular_core.Signal<readonly string[]>;
4861
5324
  readonly densityOptions: ReadonlyArray<{
4862
5325
  value: EfTableDensity;
4863
5326
  labelKey: string;
@@ -4894,6 +5357,7 @@ declare class EfDataCardComponent<TRow = any> implements AfterContentInit {
4894
5357
  setDensity(value: EfTableDensity): void;
4895
5358
  isColumnHidden(id: string): boolean;
4896
5359
  toggleColumn(id: string): void;
5360
+ /** "Show all columns": every column, the `defaultHidden` ones included. */
4897
5361
  resetColumns(): void;
4898
5362
  /** Close an open panel on an outside click or Escape. */
4899
5363
  onDocumentClick(event: MouseEvent): void;
@@ -4960,11 +5424,17 @@ declare class EfDataCardComponent<TRow = any> implements AfterContentInit {
4960
5424
  * only missing half of the interaction.
4961
5425
  */
4962
5426
  onRowKeydown(row: TRow, event: Event): void;
5427
+ /**
5428
+ * Space activates a button as Enter does (APG). Only when the row itself
5429
+ * has focus: Space on a checkbox or button inside the row is theirs.
5430
+ * preventDefault keeps the page (or a dialog body) from scrolling.
5431
+ */
5432
+ onRowSpace(row: TRow, event: Event): void;
4963
5433
  onHeaderClick(col: EfDataCardColumn): void;
4964
5434
  trackByRow: (_: number, row: TRow) => unknown;
4965
5435
  trackByColumn: (_: number, col: EfDataCardColumn) => string;
4966
5436
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfDataCardComponent<any>, never>;
4967
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfDataCardComponent<any>, "ef-data-card", never, { "mobileLayout": { "alias": "mobileLayout"; "required": false; "isSignal": true; }; "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; }; "showDensityControl": { "alias": "showDensityControl"; "required": false; "isSignal": true; }; "showColumnPicker": { "alias": "showColumnPicker"; "required": false; "isSignal": true; }; "showExportControl": { "alias": "showExportControl"; "required": false; "isSignal": true; }; "exporting": { "alias": "exporting"; "required": false; "isSignal": true; }; "screenContext": { "alias": "screenContext"; "required": false; "isSignal": true; }; "searchTerm": { "alias": "searchTerm"; "required": false; "isSignal": true; }; "tableKey": { "alias": "tableKey"; "required": false; "isSignal": true; }; "defaultDensity": { "alias": "defaultDensity"; "required": false; "isSignal": true; }; "reorderable": { "alias": "reorderable"; "required": false; "isSignal": true; }; "fixedLayout": { "alias": "fixedLayout"; "required": false; "isSignal": true; }; }, { "pageChange": "pageChange"; "pageSizeChange": "pageSizeChange"; "sortChange": "sortChange"; "rowDoubleClick": "rowDoubleClick"; "rowAction": "rowAction"; "exportRequest": "exportRequest"; "clearSearch": "clearSearch"; "retry": "retry"; "rowReorder": "rowReorder"; }, ["bodyTemplates", "headerTemplates"], ["[tbl-head-info]", "[tbl-head-actions]", "[empty-state-mobile]", "[empty-state]"], true, never>;
5437
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfDataCardComponent<any>, "ef-data-card", never, { "mobileLayout": { "alias": "mobileLayout"; "required": false; "isSignal": true; }; "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; }; "rowPressed": { "alias": "rowPressed"; "required": false; "isSignal": true; }; "rowDisabled": { "alias": "rowDisabled"; "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; }; "rowActionState": { "alias": "rowActionState"; "required": false; "isSignal": true; }; "showDensityControl": { "alias": "showDensityControl"; "required": false; "isSignal": true; }; "showColumnPicker": { "alias": "showColumnPicker"; "required": false; "isSignal": true; }; "showExportControl": { "alias": "showExportControl"; "required": false; "isSignal": true; }; "exporting": { "alias": "exporting"; "required": false; "isSignal": true; }; "screenContext": { "alias": "screenContext"; "required": false; "isSignal": true; }; "searchTerm": { "alias": "searchTerm"; "required": false; "isSignal": true; }; "tableKey": { "alias": "tableKey"; "required": false; "isSignal": true; }; "defaultDensity": { "alias": "defaultDensity"; "required": false; "isSignal": true; }; "reorderable": { "alias": "reorderable"; "required": false; "isSignal": true; }; "fixedLayout": { "alias": "fixedLayout"; "required": false; "isSignal": true; }; }, { "pageChange": "pageChange"; "pageSizeChange": "pageSizeChange"; "sortChange": "sortChange"; "rowDoubleClick": "rowDoubleClick"; "rowAction": "rowAction"; "exportRequest": "exportRequest"; "clearSearch": "clearSearch"; "retry": "retry"; "rowReorder": "rowReorder"; }, ["bodyTemplates", "headerTemplates"], ["[tbl-head-info]", "[tbl-head-actions]", "[empty-state-mobile]", "[empty-state]"], true, never>;
4968
5438
  }
4969
5439
 
4970
5440
  /**
@@ -5095,6 +5565,7 @@ declare class EfOverflowTooltipDirective {
5095
5565
  declare class EfRowActionsComponent {
5096
5566
  private readonly host;
5097
5567
  private readonly shortcuts;
5568
+ private readonly injector;
5098
5569
  /** Distinguishes this instance's registrations from every other row's:
5099
5570
  * each row on screen constructs its own component, and ids must not
5100
5571
  * collide or one row's disposer (and its `when`) would clobber
@@ -5107,7 +5578,7 @@ declare class EfRowActionsComponent {
5107
5578
  readonly items: _angular_core.InputSignal<readonly EfRowAction[]>;
5108
5579
  /** Optional context — when present, items with a `permission` are
5109
5580
  * filtered against `context.isGranted(permission)`. */
5110
- readonly context: _angular_core.InputSignal<ScreenContext>;
5581
+ readonly context: _angular_core.InputSignal<ScreenContext | undefined>;
5111
5582
  /** Override the trigger icon (default ellipsis). */
5112
5583
  readonly triggerIcon: _angular_core.InputSignal<string>;
5113
5584
  /** Override the trigger aria-label translation key. */
@@ -5140,6 +5611,17 @@ declare class EfRowActionsComponent {
5140
5611
  private static openInstance;
5141
5612
  toggle(event: MouseEvent): void;
5142
5613
  runCommand(action: EfRowAction, event: MouseEvent): void;
5614
+ /** Id of the visible reason a disabled item carries. */
5615
+ reasonId(index: number): string;
5616
+ /** ArrowDown on the closed trigger opens the menu (APG menu button). */
5617
+ openFromKeyboard(event: Event): void;
5618
+ /** Arrow Up/Down (wrapping), Home and End move between items; Escape
5619
+ * closes and hands focus back to the trigger. */
5620
+ onMenuKeydown(event: KeyboardEvent): void;
5621
+ private menuButtons;
5622
+ private focusTrigger;
5623
+ /** Once the menu is rendered, focus its first enabled item (or the first). */
5624
+ private focusItem;
5143
5625
  close(): void;
5144
5626
  /** Gap between the trigger and the menu, plus a little breathing room
5145
5627
  * against the viewport edge. Matches `top: calc(100% + 8px)`. */
@@ -5163,7 +5645,7 @@ declare class EfRowActionsComponent {
5163
5645
  bottom: string;
5164
5646
  left: string;
5165
5647
  maxHeight: string;
5166
- }>;
5648
+ } | null>;
5167
5649
  /**
5168
5650
  * Fixed rather than absolute: inside ef-data-card the table sits in a
5169
5651
  * horizontal scroller, and `overflow-x: auto` makes the block axis clip
@@ -5385,7 +5867,7 @@ declare class EfSmartBarComponent {
5385
5867
  /** Keyboard shortcut that focuses the search input. `null` opts out
5386
5868
  * (a screen with two smart bars registers the binding on only one
5387
5869
  * of them). Defaults to `'mod+k'`. */
5388
- readonly shortcut: _angular_core.InputSignal<string>;
5870
+ readonly shortcut: _angular_core.InputSignal<string | null>;
5389
5871
  /**
5390
5872
  * Accessible name for the search field: the placeholder text, which
5391
5873
  * already says what this box searches ("Search by order N, customer,
@@ -5450,7 +5932,7 @@ declare class EfFilterPillComponent {
5450
5932
  /** Current value displayed after the label. */
5451
5933
  readonly value: _angular_core.InputSignal<string>;
5452
5934
  /** `'active'` paints ink-active; `'tenant'` paints the tenant-50 surface. */
5453
- readonly tone: _angular_core.InputSignal<"active" | "tenant" | "neutral">;
5935
+ readonly tone: _angular_core.InputSignal<"tenant" | "neutral" | "active">;
5454
5936
  /** Show the ✕ clear button at the end of the pill. */
5455
5937
  readonly hasClear: _angular_core.InputSignalWithTransform<boolean, unknown>;
5456
5938
  /** Show a chevron + position the projected `[popover]` slot. */
@@ -5521,7 +6003,7 @@ declare class EfServerErrorsDirective {
5521
6003
  /** Field-name → messages map (camelCase keys, matching control `name`). */
5522
6004
  readonly errors: _angular_core.InputSignal<{
5523
6005
  [key: string]: string[];
5524
- }>;
6006
+ } | null>;
5525
6007
  /** Messages for a control by its `name`, or `null` when none. */
5526
6008
  errorsFor(name: string | undefined | null): string[] | null;
5527
6009
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfServerErrorsDirective, never>;
@@ -5645,7 +6127,7 @@ declare class EfRadioComponent extends AbstractEfFormControl implements ControlV
5645
6127
  selectionChangeEvent: EventEmitter<any>;
5646
6128
  private onChange;
5647
6129
  private onTouched;
5648
- readonly ngControl: NgControl;
6130
+ readonly ngControl: NgControl | null;
5649
6131
  constructor();
5650
6132
  get hasLabel(): boolean;
5651
6133
  /** Shared `name` so the browser treats the options as one group. */
@@ -5685,12 +6167,16 @@ declare class EfRadioComponent extends AbstractEfFormControl implements ControlV
5685
6167
  * [rows]="4"
5686
6168
  * [(value)]="description"
5687
6169
  * />
6170
+ * <ef-textarea labelKey="store_pages_details_body_fr" mono [rows]="12" [(value)]="bodyFr" />
5688
6171
  * ```
5689
6172
  */
5690
6173
  declare class EfTextareaComponent extends AbstractEfFormControl implements ControlValueAccessor {
5691
6174
  rows: number;
5692
6175
  maxlength?: number;
5693
6176
  autofocus: boolean;
6177
+ /** Monospace face for source text (an HTML body, a JSON snippet), where
6178
+ * tags and indentation must stay readable. */
6179
+ mono: boolean;
5694
6180
  valueChangeEvent: EventEmitter<string>;
5695
6181
  /** Two-way-bindable as `[value]` (one-way) or
5696
6182
  * `[(value)]="signal()"` (set + listen via `valueChangeEvent`). */
@@ -5700,7 +6186,7 @@ declare class EfTextareaComponent extends AbstractEfFormControl implements Contr
5700
6186
  clearValue(): void;
5701
6187
  private onChange;
5702
6188
  private onTouched;
5703
- readonly ngControl: NgControl;
6189
+ readonly ngControl: NgControl | null;
5704
6190
  constructor();
5705
6191
  get isInvalid(): boolean;
5706
6192
  get serverErrors(): string[] | null;
@@ -5712,8 +6198,123 @@ declare class EfTextareaComponent extends AbstractEfFormControl implements Contr
5712
6198
  handleInput(value: string): void;
5713
6199
  handleBlur(): void;
5714
6200
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfTextareaComponent, never>;
5715
- 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>;
6201
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfTextareaComponent, "ef-textarea", never, { "rows": { "alias": "rows"; "required": false; }; "maxlength": { "alias": "maxlength"; "required": false; }; "autofocus": { "alias": "autofocus"; "required": false; }; "mono": { "alias": "mono"; "required": false; }; "value": { "alias": "value"; "required": false; }; }, { "valueChangeEvent": "valueChangeEvent"; }, never, never, true, never>;
5716
6202
  static ngAcceptInputType_autofocus: unknown;
6203
+ static ngAcceptInputType_mono: unknown;
6204
+ }
6205
+
6206
+ /** A language the SEO texts are written in. */
6207
+ interface EfSeoLocale {
6208
+ code: string;
6209
+ labelKey: string;
6210
+ dir: 'auto' | 'rtl' | 'ltr';
6211
+ }
6212
+ /** What the fields write back: French is the scalar, every other language a dictionary entry. */
6213
+ interface EfSeoPatch {
6214
+ seoTitle?: string;
6215
+ seoDescription?: string;
6216
+ seoTitleTranslations?: Record<string, string>;
6217
+ seoDescriptionTranslations?: Record<string, string>;
6218
+ }
6219
+ type Field = 'title' | 'description';
6220
+ declare const EF_SEO_DEFAULT_LOCALES: ReadonlyArray<EfSeoLocale>;
6221
+ /**
6222
+ * SEO title and description per language, each with a counter against what a
6223
+ * search engine shows (60 / 160), capped at the server's limits (256 / 512),
6224
+ * and the shared `ef-seo-preview` of the French result with the entity's own
6225
+ * title and description as the fallback the shop uses. Card-less: each screen
6226
+ * wraps it in its own `ef-card`.
6227
+ *
6228
+ * The French controls carry the backend names (`seoTitle`, `seoDescription`)
6229
+ * so `[efServerErrors]` links their errors; the others (`seoTitleAr`, ...) are
6230
+ * dictionary entries whose errors are toasted (ADR-010).
6231
+ *
6232
+ * ```html
6233
+ * <ef-seo-fields [seoTitle]="p.seoTitle" ... [previewUrl]="url()" (seoChange)="patch($event)" />
6234
+ * ```
6235
+ */
6236
+ declare class EfSeoFieldsComponent {
6237
+ readonly seoTitle: _angular_core.InputSignal<string | null | undefined>;
6238
+ readonly seoDescription: _angular_core.InputSignal<string | null | undefined>;
6239
+ readonly seoTitleTranslations: _angular_core.InputSignal<Record<string, string> | null | undefined>;
6240
+ readonly seoDescriptionTranslations: _angular_core.InputSignal<Record<string, string> | null | undefined>;
6241
+ /** The entity's own texts, which the shop uses when SEO is empty. */
6242
+ readonly fallbackTitle: _angular_core.InputSignal<string | null | undefined>;
6243
+ readonly fallbackDescription: _angular_core.InputSignal<string | null | undefined>;
6244
+ /** The page address shown in the preview, computed by the app. */
6245
+ readonly previewUrl: _angular_core.InputSignal<string>;
6246
+ readonly titleMax: _angular_core.InputSignal<number>;
6247
+ readonly descriptionMax: _angular_core.InputSignal<number>;
6248
+ readonly titleAdvised: _angular_core.InputSignal<number>;
6249
+ readonly descriptionAdvised: _angular_core.InputSignal<number>;
6250
+ readonly locales: _angular_core.InputSignal<readonly EfSeoLocale[]>;
6251
+ readonly seoChange: _angular_core.OutputEmitterRef<EfSeoPatch>;
6252
+ text(locale: string, field: Field): string;
6253
+ /** Id of a counter, which the field it counts points at with `aria-describedby`. */
6254
+ counterId(locale: string, field: Field): string;
6255
+ private readonly uid;
6256
+ /** French matches the backend property; the others are unique for the label link. */
6257
+ name(locale: string, field: Field): string;
6258
+ setText(locale: string, field: Field, value: string): void;
6259
+ protected readonly previewTitle: _angular_core.Signal<string>;
6260
+ protected readonly previewDescription: _angular_core.Signal<string>;
6261
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfSeoFieldsComponent, never>;
6262
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfSeoFieldsComponent, "ef-seo-fields", never, { "seoTitle": { "alias": "seoTitle"; "required": false; "isSignal": true; }; "seoDescription": { "alias": "seoDescription"; "required": false; "isSignal": true; }; "seoTitleTranslations": { "alias": "seoTitleTranslations"; "required": false; "isSignal": true; }; "seoDescriptionTranslations": { "alias": "seoDescriptionTranslations"; "required": false; "isSignal": true; }; "fallbackTitle": { "alias": "fallbackTitle"; "required": false; "isSignal": true; }; "fallbackDescription": { "alias": "fallbackDescription"; "required": false; "isSignal": true; }; "previewUrl": { "alias": "previewUrl"; "required": false; "isSignal": true; }; "titleMax": { "alias": "titleMax"; "required": false; "isSignal": true; }; "descriptionMax": { "alias": "descriptionMax"; "required": false; "isSignal": true; }; "titleAdvised": { "alias": "titleAdvised"; "required": false; "isSignal": true; }; "descriptionAdvised": { "alias": "descriptionAdvised"; "required": false; "isSignal": true; }; "locales": { "alias": "locales"; "required": false; "isSignal": true; }; }, { "seoChange": "seoChange"; }, never, never, true, never>;
6263
+ }
6264
+
6265
+ type EfFileRejectReason = 'size' | 'type';
6266
+ interface EfFileRejection {
6267
+ file: File;
6268
+ reason: EfFileRejectReason;
6269
+ }
6270
+ /**
6271
+ * A button that opens the native file picker and emits the chosen `File`.
6272
+ * It never talks to the network: the app uploads the emitted file through its
6273
+ * NSwag client (`client.uploadLogo({ data: file, fileName: file.name })`).
6274
+ *
6275
+ * - The button is a real `<button>`, so Enter/Space work and a screen reader
6276
+ * announces it by its label (`labelKey`) or `ariaLabelKey` when icon-only.
6277
+ * The native input is hidden from the tab order and the accessibility tree.
6278
+ * - `accept` is the usual `<input accept>` list (MIME types, `image/*`,
6279
+ * `.ext`); it is checked again on the chosen file, because the picker's
6280
+ * filter can be bypassed.
6281
+ * - `maxSizeBytes` rejects larger files. A rejected file is not emitted;
6282
+ * `(fileRejected)` fires and a live region shows `sizeErrorKey` or
6283
+ * `typeErrorKey` (translated).
6284
+ * - On a coarse pointer (touch phone, tablet, touch laptop) the painted button
6285
+ * is `--hit-touch` (48px), as `ef-pill-group` does.
6286
+ *
6287
+ * ```html
6288
+ * <ef-file-upload labelKey="tenant_logo_upload" accept="image/png,image/jpeg"
6289
+ * [maxSizeBytes]="2097152" sizeErrorKey="file_too_large"
6290
+ * (fileSelected)="upload($event)" />
6291
+ * ```
6292
+ */
6293
+ declare class EfFileUploadComponent {
6294
+ /** Visible label (translation key). Omit for an icon-only button and set `ariaLabelKey`. */
6295
+ readonly labelKey: _angular_core.InputSignal<string | undefined>;
6296
+ readonly ariaLabelKey: _angular_core.InputSignal<string | undefined>;
6297
+ readonly icon: _angular_core.InputSignal<string | undefined>;
6298
+ readonly severity: _angular_core.InputSignal<"primary" | "danger" | "tenant" | "ghost">;
6299
+ /** The `<input accept>` list: MIME types, `type/*` and `.ext`, comma separated. */
6300
+ readonly accept: _angular_core.InputSignal<string | undefined>;
6301
+ readonly maxSizeBytes: _angular_core.InputSignal<number | undefined>;
6302
+ /** Translation key shown when the file is over `maxSizeBytes`. */
6303
+ readonly sizeErrorKey: _angular_core.InputSignal<string>;
6304
+ /** Translation key shown when the file does not match `accept`. */
6305
+ readonly typeErrorKey: _angular_core.InputSignal<string>;
6306
+ readonly disabled: _angular_core.InputSignal<boolean>;
6307
+ readonly fileSelected: _angular_core.OutputEmitterRef<File>;
6308
+ readonly fileRejected: _angular_core.OutputEmitterRef<EfFileRejection>;
6309
+ private readonly picker;
6310
+ private readonly reason;
6311
+ protected readonly errorKey: _angular_core.Signal<string | null>;
6312
+ protected open(): void;
6313
+ protected onChange(event: Event): void;
6314
+ private check;
6315
+ private matchesAccept;
6316
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfFileUploadComponent, never>;
6317
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfFileUploadComponent, "ef-file-upload", never, { "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "ariaLabelKey": { "alias": "ariaLabelKey"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "severity": { "alias": "severity"; "required": false; "isSignal": true; }; "accept": { "alias": "accept"; "required": false; "isSignal": true; }; "maxSizeBytes": { "alias": "maxSizeBytes"; "required": false; "isSignal": true; }; "sizeErrorKey": { "alias": "sizeErrorKey"; "required": false; "isSignal": true; }; "typeErrorKey": { "alias": "typeErrorKey"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, { "fileSelected": "fileSelected"; "fileRejected": "fileRejected"; }, never, never, true, never>;
5717
6318
  }
5718
6319
 
5719
6320
  interface EfPresetItem<T = string> {
@@ -5750,9 +6351,9 @@ declare class EfPresetPillComponent<T = string> {
5750
6351
  readonly icon: _angular_core.InputSignal<string>;
5751
6352
  readonly labelKey: _angular_core.InputSignal<string>;
5752
6353
  readonly label: _angular_core.InputSignal<string>;
5753
- readonly tone: _angular_core.InputSignal<"active" | "tenant" | "neutral">;
6354
+ readonly tone: _angular_core.InputSignal<"tenant" | "neutral" | "active">;
5754
6355
  readonly presets: _angular_core.InputSignal<readonly EfPresetItem<T>[]>;
5755
- readonly activeKey: _angular_core.ModelSignal<T>;
6356
+ readonly activeKey: _angular_core.ModelSignal<T | null>;
5756
6357
  /** Two-way; consumer can hoist for controlled mode. */
5757
6358
  readonly open: _angular_core.ModelSignal<boolean>;
5758
6359
  /** Auto-close the popover after selection (default on). */
@@ -5797,6 +6398,8 @@ interface EfDatePreset {
5797
6398
  * a `[start, end]` tuple for a range — so `ef-datepicker-advanced` is a
5798
6399
  * drop-in replacement for the legacy `ef-datepicker` in forms. */
5799
6400
  type EfDateFieldValue = Date | [Date, Date] | null;
6401
+ /** What the form receives: a Date shape, or with `valueFormat="local-datetime"` wall-time strings. */
6402
+ type EfDateFieldOutput = EfDateFieldValue | string | [string, string];
5800
6403
  /**
5801
6404
  * Comptoir period selector for smart-bars — a pill trigger ("Date — 30
5802
6405
  * derniers jours v") that opens a popover with a preset list and an
@@ -5836,7 +6439,7 @@ declare class EfDatepickerAdvancedComponent extends AbstractEfFormControl implem
5836
6439
  * `NG_VALUE_ACCESSOR` provider is needed and there's no circular DI.
5837
6440
  * `null` in filter mode (the output-only smart-bar usage).
5838
6441
  */
5839
- readonly ngControl: NgControl;
6442
+ readonly ngControl: NgControl | null;
5840
6443
  /** Always-present wrapper — the anchor for portal positioning (the
5841
6444
  * trigger itself lives behind an `@if`, so we anchor to the host box). */
5842
6445
  private anchorEl;
@@ -5858,13 +6461,13 @@ declare class EfDatepickerAdvancedComponent extends AbstractEfFormControl implem
5858
6461
  * `last_90_days`, `this_quarter`, `this_year` — pass anything to
5859
6462
  * replace them outright (e.g. include a `last_year` preset).
5860
6463
  */
5861
- readonly presets: _angular_core.InputSignal<readonly EfDatePreset[]>;
6464
+ readonly presets: _angular_core.InputSignal<readonly EfDatePreset[] | undefined>;
5862
6465
  /** Currently active preset key (controlled). */
5863
- readonly activePreset: _angular_core.InputSignal<string>;
6466
+ readonly activePreset: _angular_core.InputSignal<string | undefined>;
5864
6467
  /** Currently selected start (controlled — pairs with `activePreset`). */
5865
- readonly start: _angular_core.InputSignal<Date>;
6468
+ readonly start: _angular_core.InputSignal<Date | null | undefined>;
5866
6469
  /** Currently selected end. */
5867
- readonly end: _angular_core.InputSignal<Date>;
6470
+ readonly end: _angular_core.InputSignal<Date | null | undefined>;
5868
6471
  /** Show a `'Personnalisé…'` row that opens the calendar view. */
5869
6472
  readonly allowCustomRange: _angular_core.InputSignalWithTransform<boolean, unknown>;
5870
6473
  /**
@@ -5891,6 +6494,17 @@ declare class EfDatepickerAdvancedComponent extends AbstractEfFormControl implem
5891
6494
  * start-of-day) and the trigger renders `DD/MM/YYYY HH:mm`.
5892
6495
  */
5893
6496
  readonly showTime: _angular_core.InputSignalWithTransform<boolean, unknown>;
6497
+ /**
6498
+ * Field mode only: the value shape the form sees.
6499
+ * - `'date'` (default): a `Date` (or `[start, end]`).
6500
+ * - `'local-datetime'`: wall time as a `yyyy-MM-ddTHH:mm` string (or a
6501
+ * pair), read and written from the date's local components, never
6502
+ * through UTC. For times that belong to another zone than the
6503
+ * browser's (a shop's opening time, an offer's start), where the
6504
+ * server converts and the browser must not. A string in any other
6505
+ * shape (seconds, `Z`, an offset) is ignored rather than guessed.
6506
+ */
6507
+ readonly valueFormat: _angular_core.InputSignal<"date" | "local-datetime">;
5894
6508
  /** Emitted whenever a preset or applied custom range changes. */
5895
6509
  readonly rangeChange: _angular_core.OutputEmitterRef<EfDateRange>;
5896
6510
  /** Forwarded when the popover opens / closes (mirrors ef-row-actions). */
@@ -5910,8 +6524,8 @@ declare class EfDatepickerAdvancedComponent extends AbstractEfFormControl implem
5910
6524
  /** Custom view: month being displayed (1st of the month, 00:00). */
5911
6525
  readonly visibleMonth: _angular_core.WritableSignal<Date>;
5912
6526
  /** Custom view: working start/end, before the user clicks Apply. */
5913
- readonly draftStart: _angular_core.WritableSignal<Date>;
5914
- readonly draftEnd: _angular_core.WritableSignal<Date>;
6527
+ readonly draftStart: _angular_core.WritableSignal<Date | null>;
6528
+ readonly draftEnd: _angular_core.WritableSignal<Date | null>;
5915
6529
  /** Effective preset list — defaults if none provided. */
5916
6530
  readonly effectivePresets: _angular_core.Signal<readonly EfDatePreset[]>;
5917
6531
  /** Current value resolved against today's date. */
@@ -5955,8 +6569,8 @@ declare class EfDatepickerAdvancedComponent extends AbstractEfFormControl implem
5955
6569
  /** Committed value pushed in via `writeValue` / day-pick / Apply. */
5956
6570
  private readonly fieldValue;
5957
6571
  /** Committed start / end derived from `fieldValue`. */
5958
- readonly fieldStart: _angular_core.Signal<Date>;
5959
- readonly fieldEnd: _angular_core.Signal<Date>;
6572
+ readonly fieldStart: _angular_core.Signal<Date | null>;
6573
+ readonly fieldEnd: _angular_core.Signal<Date | null>;
5960
6574
  /** Whether the field currently holds a value (vs. placeholder). */
5961
6575
  readonly hasFieldValue: _angular_core.Signal<boolean>;
5962
6576
  /** Text rendered in the field-mode trigger (date, +time when enabled). */
@@ -5980,8 +6594,8 @@ declare class EfDatepickerAdvancedComponent extends AbstractEfFormControl implem
5980
6594
  get serverErrors(): string[] | null;
5981
6595
  /** Invalid + interacted — drives the red border on the field box. */
5982
6596
  get isInvalid(): boolean;
5983
- writeValue(value: Date | Date[] | string | number | null | undefined): void;
5984
- registerOnChange(fn: (value: EfDateFieldValue) => void): void;
6597
+ writeValue(value: Date | Date[] | string | string[] | number | null | undefined): void;
6598
+ registerOnChange(fn: (value: EfDateFieldOutput) => void): void;
5985
6599
  registerOnTouched(fn: () => void): void;
5986
6600
  setDisabledState(isDisabled: boolean): void;
5987
6601
  protected get hasValue(): boolean;
@@ -5990,6 +6604,13 @@ declare class EfDatepickerAdvancedComponent extends AbstractEfFormControl implem
5990
6604
  * Tolerates `null`, `[start, null]`, strings/epochs, and stray
5991
6605
  * multi-element arrays (keeps the first two). */
5992
6606
  private coerceValue;
6607
+ /** `local-datetime` in: `yyyy-MM-ddTHH:mm` built into a Date from its
6608
+ * parts (local time), never through `Date` string parsing. */
6609
+ private coerceLocal;
6610
+ private parseLocalDateTime;
6611
+ /** `local-datetime` out: the Date's local components, zero-padded. */
6612
+ private formatLocalDateTime;
6613
+ private toOutput;
5993
6614
  private toDate;
5994
6615
  /** Commit a field-mode value: update internal state, notify the form,
5995
6616
  * and close the popover. `single` → `Date`, `range` → `[start, end]`. */
@@ -6076,7 +6697,7 @@ declare class EfDatepickerAdvancedComponent extends AbstractEfFormControl implem
6076
6697
  }) => number;
6077
6698
  trackByDow: (_: number, label: string) => string;
6078
6699
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfDatepickerAdvancedComponent, never>;
6079
- 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; }; "allowAllTime": { "alias": "allowAllTime"; "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>;
6700
+ 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; }; "allowAllTime": { "alias": "allowAllTime"; "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; }; "valueFormat": { "alias": "valueFormat"; "required": false; "isSignal": true; }; }, { "rangeChange": "rangeChange"; "opened": "opened"; "closed": "closed"; }, never, never, true, never>;
6080
6701
  }
6081
6702
 
6082
6703
  /**
@@ -6141,13 +6762,17 @@ declare class EfActivationFilterComponent {
6141
6762
  readonly allLabelKey: _angular_core.InputSignal<string>;
6142
6763
  readonly activeLabelKey: _angular_core.InputSignal<string>;
6143
6764
  readonly inactiveLabelKey: _angular_core.InputSignal<string>;
6144
- readonly valueChange: _angular_core.OutputEmitterRef<boolean>;
6765
+ readonly valueChange: _angular_core.OutputEmitterRef<boolean | undefined>;
6145
6766
  readonly selected: _angular_core.Signal<"active" | "inactive" | "all">;
6146
- readonly buckets: _angular_core.Signal<{
6767
+ readonly buckets: _angular_core.Signal<({
6768
+ key: string;
6769
+ labelKey: string;
6770
+ value: undefined;
6771
+ } | {
6147
6772
  key: string;
6148
6773
  labelKey: string;
6149
6774
  value: boolean;
6150
- }[]>;
6775
+ })[]>;
6151
6776
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfActivationFilterComponent, never>;
6152
6777
  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>;
6153
6778
  }
@@ -6179,7 +6804,7 @@ declare class EfThemeConfiguratorComponent implements OnInit {
6179
6804
  presetsDisplayNameKeys: Record<string, string>;
6180
6805
  get ripple(): boolean;
6181
6806
  set ripple(value: boolean);
6182
- get isRTL(): boolean;
6807
+ get isRTL(): boolean | undefined;
6183
6808
  config: PrimeNG;
6184
6809
  configService: EfThemeConfigService;
6185
6810
  platformId: Object;
@@ -6203,9 +6828,9 @@ declare class EfThemeConfiguratorComponent implements OnInit {
6203
6828
  950: string;
6204
6829
  };
6205
6830
  }[];
6206
- selectedPrimaryColor: _angular_core.Signal<string>;
6207
- selectedSurfaceColor: _angular_core.Signal<string>;
6208
- selectedPreset: _angular_core.Signal<string>;
6831
+ selectedPrimaryColor: _angular_core.Signal<string | undefined>;
6832
+ selectedSurfaceColor: _angular_core.Signal<string | undefined>;
6833
+ selectedPreset: _angular_core.Signal<string | undefined>;
6209
6834
  primaryColors: _angular_core.Signal<{
6210
6835
  name: string;
6211
6836
  palette: Record<string, string>;
@@ -6246,7 +6871,7 @@ declare class EfEmailMetricsCardComponent {
6246
6871
  protected readonly noAutofocus: {
6247
6872
  autofocus: boolean;
6248
6873
  };
6249
- metrics: _angular_core.InputSignal<EmailMetrics>;
6874
+ metrics: _angular_core.InputSignal<EmailMetrics | null>;
6250
6875
  recentEvents: _angular_core.InputSignal<EmailEventRecord[]>;
6251
6876
  loading: _angular_core.InputSignal<boolean>;
6252
6877
  title: _angular_core.InputSignal<string>;
@@ -6275,5 +6900,5 @@ declare class EfEmailMetricsCardComponent {
6275
6900
  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>;
6276
6901
  }
6277
6902
 
6278
- export { EF_BADGE_BG_COLORS, EF_BADGE_BG_LIGHT_COLORS, EF_BADGE_BORDER_COLORS, EF_BADGE_DEFAULTS, EF_BADGE_TEXT_COLORS, EF_CHART_PALETTE, EF_DATATABLE_ACTIONS_DEFAULTS, EF_DATATABLE_COLUMN_DEFAULTS, EF_DATATABLE_DEFAULTS, EF_DATA_CARD_MOBILE_LAYOUT, EF_STATUS_COLORS, EF_STATUS_COLOR_ALIASES, EfAboutDialogComponent, EfActivationFilterComponent, EfAppMainComponent, EfAppShellComponent, EfAppShellMobileComponent, EfAppTopComponent, EfBadgeComponent, EfBlockUiComponent, EfBlockableDivComponent, EfBottomSheetComponent, EfBuildStampComponent, EfBulkBarComponent, EfButtonComponent, EfButtonGroupComponent, EfCardComponent, EfChangeHistoryComponent, EfChangePasswordComponent, EfChartComponent, EfCheckboxComponent, EfClearButtonComponent, EfColumnHeaderTemplateDirective, EfColumnTemplateDirective, EfCompactPipe, EfConfirmDialogComponent, EfCurrencyPipe, EfDataCardComponent, EfDatatableActionBarComponent, EfDatatableComponent, EfDatepickerAdvancedComponent, EfDatepickerComponent, EfDetailToolbarComponent, EfDialogComponent, EfEmailMetricsCardComponent, EfEmptyStateComponent, EfFabComponent, EfFieldsetComponent, EfFilterDrawerComponent, EfFilterPillComponent, EfFormGridComponent, EfInputNumberComponent, EfInputTextComponent, EfKpiCardComponent, EfLabelComponent, EfModuleRailComponent, EfModuleSideComponent, EfMultiSelectComponent, EfOrderBuilderComponent, EfOrderSummaryComponent, EfOverflowTooltipDirective, EfPagerComponent, EfPasswordComponent, EfPdfPreviewComponent, EfPillGroupComponent, EfPresetPillComponent, EfPriceDisplayComponent, EfProductCatalogueComponent, EfProductCatalogueFilterComponent, EfProductTypeaheadComponent, EfProfileChipComponent, EfProfileMenuComponent, EfPulseComponent, EfQuantitySelectorComponent, EfQuantityStepperComponent, EfRadioComponent, EfRankListComponent, EfReadOnlyDirective, EfRowActionsComponent, EfSearchToolbarComponent, EfSelectButtonComponent, EfSelectComponent, EfServerErrorsDirective, EfSessionIdleDialogComponent, EfShortcutsDialogComponent, EfSkeletonComponent, EfSmartBarComponent, EfStatsComponent, EfStatusChipComponent, EfTabPanelDirective, EfTabsComponent, EfTextareaComponent, EfThemeConfiguratorComponent, EfThemeToggleComponent, EfTimelineComponent, EfToastRegionComponent, EfToggleSwitchComponent, EfToolbarComponent, EfTooltipDirective, EfTotalsComponent, OrderEntityHelper, OrderLineItemHelper, TruncatePipe, buildChartConfig, getColumnAlignment, mergeColumnDefaults, resolveEfStatusColor };
6279
- export type { CatalogueProduct, CatalogueProductVariant, CategoryAssignment, CategoryGroup, CategoryItem, DatatableSearchEntity, DocumentConfig, EfAboutServer, EfBadgeColor, EfBadgeConfig, EfBadgeSize, EfBadgeVariant, EfButtonComptoirSeverity, EfButtonPrimeNGSeverity, EfButtonSeverity, EfChangeHistoryEntry, EfChangeHistoryFieldChange, EfChangePasswordSubmit, EfChartSeries, EfChartType, EfDataCardMobileLayout, EfDataCardRowAction, EfDatatableActions, EfDatatableColumn, EfDatatableColumnAlign, EfDatatableColumnType, EfDatatableConfig, EfDateFieldValue, EfDatePreset, EfDialogAction, EfDialogSeverity, EfDialogSize, EfFabPosition, EfKpiDeltaTone, EfKpiSparkline, EfPillItem, EfPresetItem, EfProfileMenuItem, 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 };
6903
+ export { EF_BADGE_BG_COLORS, EF_BADGE_BG_LIGHT_COLORS, EF_BADGE_BORDER_COLORS, EF_BADGE_DEFAULTS, EF_BADGE_TEXT_COLORS, EF_CHART_PALETTE, EF_DATATABLE_ACTIONS_DEFAULTS, EF_DATATABLE_COLUMN_DEFAULTS, EF_DATATABLE_DEFAULTS, EF_DATA_CARD_MOBILE_LAYOUT, EF_SEO_DEFAULT_LOCALES, EF_STATUS_COLORS, EF_STATUS_COLOR_ALIASES, EfAboutDialogComponent, EfActivationFilterComponent, EfAppMainComponent, EfAppShellComponent, EfAppShellMobileComponent, EfAppTopComponent, EfBadgeComponent, EfBlockUiComponent, EfBlockableDivComponent, EfBottomSheetComponent, EfBuildStampComponent, EfBulkBarComponent, EfButtonComponent, EfButtonGroupComponent, EfCardComponent, EfChangeHistoryComponent, EfChangePasswordComponent, EfChartComponent, EfCheckboxComponent, EfClearButtonComponent, EfColumnHeaderTemplateDirective, EfColumnTemplateDirective, EfCompactPipe, EfConfirmDialogComponent, EfCurrencyPipe, EfDataCardComponent, EfDatatableActionBarComponent, EfDatatableComponent, EfDatepickerAdvancedComponent, EfDatepickerComponent, EfDetailToolbarComponent, EfDialogComponent, EfEmailMetricsCardComponent, EfEmptyStateComponent, EfFabComponent, EfFieldsetComponent, EfFileUploadComponent, EfFilterDrawerComponent, EfFilterPillComponent, EfFormGridComponent, EfIdentityThumbComponent, EfInputNumberComponent, EfInputTextComponent, EfKpiCardComponent, EfLabelComponent, EfModuleRailComponent, EfModuleSideComponent, EfMultiSelectComponent, EfOrderBuilderComponent, EfOrderSummaryComponent, EfOverflowTooltipDirective, EfPagerComponent, EfPasswordComponent, EfPdfPreviewComponent, EfPillGroupComponent, EfPresetPillComponent, EfPriceDisplayComponent, EfProductCatalogueComponent, EfProductCatalogueFilterComponent, EfProductPickerComponent, EfProductTypeaheadComponent, EfProfileChipComponent, EfProfileMenuComponent, EfPulseComponent, EfQuantitySelectorComponent, EfQuantityStepperComponent, EfRadioComponent, EfRankListComponent, EfReadOnlyDirective, EfRowActionsComponent, EfSearchToolbarComponent, EfSelectButtonComponent, EfSelectComponent, EfSeoFieldsComponent, EfSeoPreviewComponent, EfServerErrorsDirective, EfSessionIdleDialogComponent, EfShortcutsDialogComponent, EfSkeletonComponent, EfSmartBarComponent, EfStatsComponent, EfStatusChipComponent, EfTabPanelDirective, EfTabsComponent, EfTextareaComponent, EfThemeConfiguratorComponent, EfThemeToggleComponent, EfTimelineComponent, EfToastRegionComponent, EfToggleSwitchComponent, EfToolbarComponent, EfTooltipDirective, EfTotalsComponent, OrderEntityHelper, OrderLineItemHelper, TruncatePipe, buildChartConfig, defaultUnitPrice, getColumnAlignment, mergeColumnDefaults, resolveEfStatusColor };
6904
+ export type { CatalogueProduct, CatalogueProductVariant, CategoryAssignment, CategoryGroup, CategoryItem, DatatableSearchEntity, DocumentConfig, EfAboutServer, EfBadgeColor, EfBadgeConfig, EfBadgeSize, EfBadgeVariant, EfButtonComptoirSeverity, EfButtonPrimeNGSeverity, EfButtonSeverity, EfChangeHistoryEntry, EfChangeHistoryFieldChange, EfChangePasswordSubmit, EfChartSeries, EfChartType, EfDataCardMobileLayout, EfDataCardRowAction, EfDataCardRowActionState, EfDatatableActions, EfDatatableColumn, EfDatatableColumnAlign, EfDatatableColumnType, EfDatatableConfig, EfDateFieldOutput, EfDateFieldValue, EfDatePreset, EfDialogAction, EfDialogSeverity, EfDialogSize, EfFabPosition, EfFileRejectReason, EfFileRejection, EfIdentityThumbSize, EfKpiDeltaTone, EfKpiSparkline, EfOrderPriceList, EfOrderRecipient, EfPillItem, EfPresetItem, EfProductPickerItem, EfProductPickerQuery, EfProfileMenuItem, EfRankRow, EfRowAction, EfSeoLocale, EfSeoPatch, EfSkeletonVariant, EfStatsRow, EfStatusColor, EfStatusReferenceItem, EfTabItem, EfTabsModule, EfTabsVariant, EfTimelineItem, EfTimelineState, EfToastRegionPosition, EfTotalsGrand, EfTotalsRow, HighlightSegment, OrderChangeInfo, OrderEntity, OrderLineItem, OrderProductsSummary, OrderSummaryItem, ProductCountGroup, ProductCountGroupLabel, ProductTypeaheadRow, ProductTypeaheadSelection, ProductWithCountGroup, SelectedFilter, ValidationResult };