@elasticias/ui 1.0.11 → 1.0.13

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.
@@ -42,7 +42,7 @@ import { MenuModule } from 'primeng/menu';
42
42
  import * as i2$3 from 'primeng/api';
43
43
  import * as i1$8 from 'primeng/confirmdialog';
44
44
  import { ConfirmDialogModule } from 'primeng/confirmdialog';
45
- import { EfThemeConfigService, EF_BUILD_INFO, EfPermissionService, EfActiveModuleService, EF_SESSION, EfViewportService, EfToastService } from '@elasticias/core';
45
+ import { EfThemeConfigService, EF_BUILD_INFO, EfShortcutService, isMacPlatform, formatShortcut, EfPermissionService, EfActiveModuleService, EF_SESSION, EfViewportService, EfToastService } from '@elasticias/core';
46
46
  import * as i1$9 from 'primeng/buttongroup';
47
47
  import { ButtonGroupModule } from 'primeng/buttongroup';
48
48
  import * as i1$a from 'primeng/toolbar';
@@ -50,6 +50,8 @@ import { ToolbarModule } from 'primeng/toolbar';
50
50
  import { ScreenStateEnum, SCREEN_REF_DATA_SERVICE } from '@elasticias/screens';
51
51
  import { Router, RouterLink, RouterLinkActive } from '@angular/router';
52
52
  import { Permissions } from '@elasticias/types';
53
+ import * as i2$4 from 'primeng/popover';
54
+ import { PopoverModule } from 'primeng/popover';
53
55
  import * as i1$b from 'primeng/blockui';
54
56
  import { BlockUIModule } from 'primeng/blockui';
55
57
  import { DomSanitizer } from '@angular/platform-browser';
@@ -62,7 +64,7 @@ import * as i1$e from 'primeng/password';
62
64
  import { PasswordModule } from 'primeng/password';
63
65
  import * as i1$f from 'primeng/selectbutton';
64
66
  import { SelectButtonModule, SelectButton } from 'primeng/selectbutton';
65
- import * as i2$4 from 'primeng/table';
67
+ import * as i2$5 from 'primeng/table';
66
68
  import { TableModule } from 'primeng/table';
67
69
  import { updatePreset, updateSurfacePalette, $t } from '@primeng/themes';
68
70
  import Aura from '@primeng/themes/aura';
@@ -4932,6 +4934,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
4932
4934
  args: [{ selector: 'ef-about-dialog', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [TranslateModule, EfDialogComponent], template: "<!-- appendTo=\"body\": ef-app-main is `overflow: hidden`, so a dialog left in\n place inside the content column is clipped by it. -->\n<ef-dialog\n [visible]=\"visible()\"\n (visibleChange)=\"visible.set($event)\"\n headerKey=\"common_about\"\n size=\"sm\"\n appendTo=\"body\"\n dismissableMask\n [actions]=\"closeAction\"\n>\n <div class=\"ef-about\">\n <span class=\"ef-about__mark\" aria-hidden=\"true\">\n @if (logoUrl()) {\n <img [src]=\"logoUrl()\" alt=\"\" />\n } @else {\n {{ initial() }}\n }\n </span>\n\n <p class=\"ef-about__product\">{{ product() }}</p>\n @if (edition()) {\n <p class=\"ef-about__edition\">{{ edition() }}</p>\n }\n\n @if (rows().length) {\n <dl class=\"ef-about__rows\">\n @for (row of rows(); track row.labelKey) {\n <div class=\"ef-about__row\">\n <dt>{{ row.labelKey | translate }}</dt>\n <dd>{{ row.value }}</dd>\n </div>\n }\n </dl>\n }\n\n @if (packages().length) {\n <h3 class=\"ef-about__group\">{{ 'common_about_packages' | translate }}</h3>\n <dl class=\"ef-about__rows ef-about__rows--tight\">\n @for (pkg of packages(); track pkg.name) {\n <div class=\"ef-about__row\">\n <dt>{{ pkg.name }}</dt>\n <dd>{{ pkg.version }}</dd>\n </div>\n }\n </dl>\n }\n\n <!-- The API's half of the answer. An app that exposes no such endpoint\n passes no [server] and this whole block collapses. -->\n @if (hasServer()) {\n <h3 class=\"ef-about__group\">{{ 'common_about_server' | translate }}</h3>\n <dl class=\"ef-about__rows ef-about__rows--tight\">\n @for (row of serverRows(); track row.labelKey) {\n <div class=\"ef-about__row\">\n <dt>{{ row.labelKey | translate }}</dt>\n <dd>{{ row.value }}</dd>\n </div>\n }\n @for (pkg of serverPackages(); track pkg.name) {\n <div class=\"ef-about__row\">\n <dt>{{ pkg.name }}</dt>\n <dd>{{ pkg.version }}</dd>\n </div>\n }\n </dl>\n }\n\n @if (websiteUrl()) {\n <a\n class=\"ef-about__link\"\n [href]=\"websiteUrl()\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <span>{{ websiteLabel() || websiteUrl() }}</span>\n <i class=\"pi pi-external-link\" aria-hidden=\"true\"></i>\n </a>\n }\n\n <p class=\"ef-about__owner\">\u00A9 {{ year }} {{ owner() }}</p>\n </div>\n</ef-dialog>\n", styles: ["@charset \"UTF-8\";:host{display:contents}.ef-about{display:grid;justify-items:center;gap:10px;text-align:center}.ef-about__mark{display:grid;place-items:center;width:56px;height:56px;border-radius:var(--r-pill, 999px);background:var(--tenant-500);color:#fff;font-family:Bricolage Grotesque,system-ui,sans-serif;font-variation-settings:\"opsz\" 12,\"wght\" 700,\"wdth\" 80;font-size:22px;line-height:1;overflow:hidden}.ef-about__mark img{width:100%;height:100%;object-fit:cover}.ef-about__product{margin:2px 0 0;font-family:Bricolage Grotesque,system-ui,sans-serif;font-variation-settings:\"opsz\" 14,\"wght\" 700,\"wdth\" 90;font-size:20px;letter-spacing:-.02em;color:var(--text)}.ef-about__edition{margin:-6px 0 0;max-width:100%;font-size:12px;color:var(--text-mute);overflow-wrap:anywhere}.ef-about__rows{width:100%;min-width:0;margin:0;padding:12px 0 0;display:grid;gap:7px;border-top:1px solid var(--rule);text-align:start}.ef-about__row{display:grid;grid-template-columns:auto minmax(0,1fr);gap:12px;align-items:baseline}.ef-about__row dt{min-width:0;font-size:12px;color:var(--text-mute);overflow-wrap:anywhere}.ef-about__row dd{min-width:0;margin:0;font-family:JetBrains Mono,ui-monospace,monospace;font-size:11.5px;color:var(--text);text-align:end;overflow-wrap:anywhere}.ef-about__rows--tight{padding-top:0;border-top:0}.ef-about__group{width:100%;margin:4px 0 0;padding-top:12px;border-top:1px solid var(--rule);font-family:JetBrains Mono,ui-monospace,monospace;font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.22em;color:var(--text-soft);text-align:start}.ef-about__link{display:inline-flex;align-items:center;gap:6px;min-height:40px;margin-top:2px;padding:0 10px;border-radius:var(--r-pill, 999px);color:var(--tenant-600, var(--tenant-500));font-size:12.5px;font-weight:700;text-decoration:none}.ef-about__link i{font-size:11px;opacity:.75}.ef-about__link:hover{background:color-mix(in srgb,var(--tenant-500) 10%,transparent);text-decoration:underline}.ef-about__link:focus-visible{outline:2px solid var(--tenant-400);outline-offset:2px}.ef-about__owner{margin:6px 0 0;font-size:11px;color:var(--text-soft)}@media(max-width:767px){.ef-about__mark{width:48px;height:48px;font-size:19px}.ef-about__link{min-height:var(--hit-touch, 48px)}}\n"] }]
4933
4935
  }], propDecorators: { visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }, { type: i0.Output, args: ["visibleChange"] }], product: [{ type: i0.Input, args: [{ isSignal: true, alias: "product", required: false }] }], edition: [{ type: i0.Input, args: [{ isSignal: true, alias: "edition", required: false }] }], organisation: [{ type: i0.Input, args: [{ isSignal: true, alias: "organisation", required: false }] }], server: [{ type: i0.Input, args: [{ isSignal: true, alias: "server", required: false }] }], websiteUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "websiteUrl", required: false }] }], websiteLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "websiteLabel", required: false }] }], logoUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "logoUrl", required: false }] }], initial: [{ type: i0.Input, args: [{ isSignal: true, alias: "initial", required: false }] }], owner: [{ type: i0.Input, args: [{ isSignal: true, alias: "owner", required: false }] }] } });
4934
4936
 
4937
+ /**
4938
+ * The keyboard-shortcuts reference: every registration currently sitting
4939
+ * in `EfShortcutService`, grouped and rendered with the same `.kbd` chip
4940
+ * `ef-row-actions` and `ef-menu` already use, so a chip means one thing
4941
+ * everywhere it appears.
4942
+ *
4943
+ * Reads `EfShortcutService.list()` directly, so it stays live: open it
4944
+ * while a row menu is on screen and its row-scoped shortcuts are already
4945
+ * listed under "Row actions". Close the row menu and, since a row's
4946
+ * shortcuts unregister with it, they're gone from here too.
4947
+ *
4948
+ * ```html
4949
+ * <ef-shortcuts-dialog [(visible)]="shortcutsOpen" />
4950
+ * ```
4951
+ */
4952
+ class EfShortcutsDialogComponent {
4953
+ shortcutService = inject(EfShortcutService);
4954
+ /** Two-way open state. */
4955
+ visible = model(false, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
4956
+ groups = this.shortcutService.list;
4957
+ /** Only macOS reads `⌘` at a glance. Everyone else needs the line
4958
+ * spelling out that it means Ctrl. */
4959
+ isMacPlatform = isMacPlatform;
4960
+ closeAction = [
4961
+ { labelKey: 'common_close', severity: 'ghost' },
4962
+ ];
4963
+ formatKeys(keys) {
4964
+ return formatShortcut(keys);
4965
+ }
4966
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfShortcutsDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4967
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfShortcutsDialogComponent, isStandalone: true, selector: "ef-shortcuts-dialog", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { visible: "visibleChange" }, ngImport: i0, template: "<!-- appendTo=\"body\": a dialog left inside the content column can be clipped\n by an ancestor's overflow rule (see ef-about-dialog). -->\n<ef-dialog\n [visible]=\"visible()\"\n (visibleChange)=\"visible.set($event)\"\n headerKey=\"shortcuts_dialog_title\"\n size=\"sm\"\n appendTo=\"body\"\n dismissableMask\n [actions]=\"closeAction\"\n>\n <div class=\"shortcuts-dialog\">\n @if (!isMacPlatform) {\n <p class=\"shortcuts-dialog__hint\">{{ 'shortcuts_dialog_mod_hint' | translate }}</p>\n }\n\n @if (groups().length) {\n @for (group of groups(); track group.group) {\n <section class=\"shortcuts-dialog__group\">\n <h3 class=\"shortcuts-dialog__group-title\">{{ group.group | translate }}</h3>\n <ul class=\"shortcuts-dialog__list\">\n @for (shortcut of group.shortcuts; track shortcut.id) {\n <li class=\"shortcuts-dialog__item\">\n <span class=\"lbl\">{{ shortcut.labelKey | translate }}</span>\n <!-- Not aria-hidden here: the key IS the content this\n dialog exists to say. Hiding it would leave a\n screen-reader user hearing the action names and\n never the shortcut that triggers them. -->\n <span class=\"kbd\">{{ formatKeys(shortcut.keys) }}</span>\n </li>\n }\n </ul>\n </section>\n }\n } @else {\n <p class=\"shortcuts-dialog__empty\">{{ 'shortcuts_dialog_empty' | translate }}</p>\n }\n </div>\n</ef-dialog>\n", styles: [":host{display:contents}.shortcuts-dialog{display:grid;gap:16px}.shortcuts-dialog__hint{margin:0;padding:8px 10px;border-radius:8px;background:var(--paper-soft, var(--paper));border:1px solid var(--rule);font-size:12px;color:var(--text-mute)}.shortcuts-dialog__empty{margin:0;padding:12px 0;font-size:13px;color:var(--text-mute);text-align:center}.shortcuts-dialog__group+.shortcuts-dialog__group{padding-top:12px;border-top:1px solid var(--rule)}.shortcuts-dialog__group-title{margin:0 0 8px;font-family:JetBrains Mono,ui-monospace,monospace;font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.22em;color:var(--text-soft)}.shortcuts-dialog__list{display:grid;gap:6px;margin:0;padding:0;list-style:none}.shortcuts-dialog__item{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0}.shortcuts-dialog__item .lbl{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;color:var(--text)}.shortcuts-dialog__item .kbd{flex:none;font-family:JetBrains Mono,monospace;font-size:10px;font-weight:600;color:var(--text-mute);background:var(--paper);border:1px solid var(--rule);border-radius:6px;padding:2px 6px;line-height:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfDialogComponent, selector: "ef-dialog", inputs: ["header", "headerKey", "subtitle", "subtitleKey", "visible", "modal", "closable", "draggable", "resizable", "dismissableMask", "closeOnEscape", "severity", "icon", "size", "style", "styleClass", "position", "appendTo", "variant", "actions", "defaultActions", "saveLabelKey", "cancelLabelKey", "saveLabel", "cancelLabel", "saveIcon", "cancelIcon", "saveDisabled", "saveStyleClass", "cancelStyleClass", "saveSeverity", "cancelSeverity"], outputs: ["visibleChange", "showEvent", "hideEvent", "saveAction", "cancelAction", "actionClick"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4968
+ }
4969
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfShortcutsDialogComponent, decorators: [{
4970
+ type: Component,
4971
+ args: [{ selector: 'ef-shortcuts-dialog', standalone: true, imports: [CommonModule, TranslateModule, EfDialogComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- appendTo=\"body\": a dialog left inside the content column can be clipped\n by an ancestor's overflow rule (see ef-about-dialog). -->\n<ef-dialog\n [visible]=\"visible()\"\n (visibleChange)=\"visible.set($event)\"\n headerKey=\"shortcuts_dialog_title\"\n size=\"sm\"\n appendTo=\"body\"\n dismissableMask\n [actions]=\"closeAction\"\n>\n <div class=\"shortcuts-dialog\">\n @if (!isMacPlatform) {\n <p class=\"shortcuts-dialog__hint\">{{ 'shortcuts_dialog_mod_hint' | translate }}</p>\n }\n\n @if (groups().length) {\n @for (group of groups(); track group.group) {\n <section class=\"shortcuts-dialog__group\">\n <h3 class=\"shortcuts-dialog__group-title\">{{ group.group | translate }}</h3>\n <ul class=\"shortcuts-dialog__list\">\n @for (shortcut of group.shortcuts; track shortcut.id) {\n <li class=\"shortcuts-dialog__item\">\n <span class=\"lbl\">{{ shortcut.labelKey | translate }}</span>\n <!-- Not aria-hidden here: the key IS the content this\n dialog exists to say. Hiding it would leave a\n screen-reader user hearing the action names and\n never the shortcut that triggers them. -->\n <span class=\"kbd\">{{ formatKeys(shortcut.keys) }}</span>\n </li>\n }\n </ul>\n </section>\n }\n } @else {\n <p class=\"shortcuts-dialog__empty\">{{ 'shortcuts_dialog_empty' | translate }}</p>\n }\n </div>\n</ef-dialog>\n", styles: [":host{display:contents}.shortcuts-dialog{display:grid;gap:16px}.shortcuts-dialog__hint{margin:0;padding:8px 10px;border-radius:8px;background:var(--paper-soft, var(--paper));border:1px solid var(--rule);font-size:12px;color:var(--text-mute)}.shortcuts-dialog__empty{margin:0;padding:12px 0;font-size:13px;color:var(--text-mute);text-align:center}.shortcuts-dialog__group+.shortcuts-dialog__group{padding-top:12px;border-top:1px solid var(--rule)}.shortcuts-dialog__group-title{margin:0 0 8px;font-family:JetBrains Mono,ui-monospace,monospace;font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.22em;color:var(--text-soft)}.shortcuts-dialog__list{display:grid;gap:6px;margin:0;padding:0;list-style:none}.shortcuts-dialog__item{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0}.shortcuts-dialog__item .lbl{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;color:var(--text)}.shortcuts-dialog__item .kbd{flex:none;font-family:JetBrains Mono,monospace;font-size:10px;font-weight:600;color:var(--text-mute);background:var(--paper);border:1px solid var(--rule);border-radius:6px;padding:2px 6px;line-height:1}\n"] }]
4972
+ }], propDecorators: { visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }, { type: i0.Output, args: ["visibleChange"] }] } });
4973
+
4935
4974
  class EfFieldsetComponent {
4936
4975
  /** Direct legend text (not translated) */
4937
4976
  legend;
@@ -6207,6 +6246,72 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
6207
6246
  type: Output
6208
6247
  }] } });
6209
6248
 
6249
+ /**
6250
+ * The signed-in user's menu.
6251
+ *
6252
+ * Two renderings of one `items` array. `variant="chip"` (the default) shows
6253
+ * an `ef-profile-chip` that opens a popover, for the side footer on a
6254
+ * desktop. `variant="list"` renders the same rows flat, with no popover,
6255
+ * for the mobile sheet: `ef-app-shell` does not render the side footer on a
6256
+ * phone, and this menu is the only way to sign out, so it cannot live only
6257
+ * there.
6258
+ *
6259
+ * ```html
6260
+ * <ef-profile-menu
6261
+ * side-footer
6262
+ * [name]="userName()"
6263
+ * [secondary]="userEmail()"
6264
+ * [initial]="userInitial()"
6265
+ * [items]="menuItems()"
6266
+ * />
6267
+ * ```
6268
+ */
6269
+ class EfProfileMenuComponent {
6270
+ /** Display name, shown on the chip and in the panel header. */
6271
+ name = input('', ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
6272
+ /** Second header line. The email, usually. */
6273
+ secondary = input('', ...(ngDevMode ? [{ debugName: "secondary" }] : /* istanbul ignore next */ []));
6274
+ /** Chip subtitle. Falls back to `secondary` when empty. */
6275
+ role = input('', ...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
6276
+ /** Avatar glyph. Up to two characters; falls back to the first of `name`. */
6277
+ initial = input('', ...(ngDevMode ? [{ debugName: "initial" }] : /* istanbul ignore next */ []));
6278
+ tone = input('tenant', ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
6279
+ items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
6280
+ variant = input('chip', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
6281
+ itemSelected = output();
6282
+ /** Id of the one expanded group, or null. One at a time: a menu that
6283
+ * grows in two directions at once stops being scannable. */
6284
+ expandedId = signal(null, ...(ngDevMode ? [{ debugName: "expandedId" }] : /* istanbul ignore next */ []));
6285
+ chipRole = computed(() => this.role() || this.secondary(), ...(ngDevMode ? [{ debugName: "chipRole" }] : /* istanbul ignore next */ []));
6286
+ isExpanded(id) {
6287
+ return this.expandedId() === id;
6288
+ }
6289
+ /**
6290
+ * Choose a row. A row with children toggles its group and leaves the
6291
+ * popover open; a leaf runs its command, reports it, and dismisses.
6292
+ */
6293
+ select(item, popover) {
6294
+ if (item.children?.length) {
6295
+ this.expandedId.update((current) => (current === item.id ? null : item.id));
6296
+ // Reposition once the taller panel has actually rendered. Without this
6297
+ // the panel keeps the top it was placed at and grows downward, which
6298
+ // runs it off the bottom of the screen when the trigger sits in the
6299
+ // side footer. Realigning lets it grow upward from the trigger instead.
6300
+ setTimeout(() => popover?.align?.(), 0);
6301
+ return;
6302
+ }
6303
+ item.command?.();
6304
+ this.itemSelected.emit(item);
6305
+ popover?.hide();
6306
+ }
6307
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfProfileMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6308
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfProfileMenuComponent, isStandalone: true, selector: "ef-profile-menu", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, secondary: { classPropertyName: "secondary", publicName: "secondary", isSignal: true, isRequired: false, transformFunction: null }, role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: false, transformFunction: null }, initial: { classPropertyName: "initial", publicName: "initial", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemSelected: "itemSelected" }, ngImport: i0, template: "@if (variant() === 'chip') {\n <ef-profile-chip\n [name]=\"name()\"\n [role]=\"chipRole()\"\n [initial]=\"initial()\"\n [tone]=\"tone()\"\n (clickEvent)=\"op.toggle($event)\"\n />\n <!-- The popover is reduced to a placement mechanism: `.ef-menu` inside it\n draws the surface, so `ef-menu-popover` strips PrimeNG's own panel and\n arrow rather than letting two panels stack. -->\n <p-popover #op styleClass=\"ef-menu-popover\" appendTo=\"body\">\n <ng-container *ngTemplateOutlet=\"panel; context: { $implicit: op }\" />\n </p-popover>\n} @else {\n <ng-container *ngTemplateOutlet=\"panel\" />\n}\n\n<ng-template #panel let-op>\n <div class=\"ef-menu\" role=\"menu\" [attr.aria-label]=\"name() || null\">\n @if (name() || secondary()) {\n <div class=\"ef-menu__head\">\n @if (name()) {\n <span class=\"ef-menu__head-name\">{{ name() }}</span>\n }\n @if (secondary()) {\n <span class=\"ef-menu__head-sub\">{{ secondary() }}</span>\n }\n </div>\n }\n\n @for (item of items(); track item.id) {\n @if (item.groupStart) {\n <div class=\"ef-menu__divider\" role=\"separator\"></div>\n }\n\n <button\n type=\"button\"\n class=\"ef-menu__item\"\n role=\"menuitem\"\n [class.danger]=\"item.severity === 'danger'\"\n [attr.aria-expanded]=\"item.children?.length ? isExpanded(item.id) : null\"\n (click)=\"select(item, op)\"\n >\n @if (item.icon) {\n <i [class]=\"'ico ' + item.icon\" aria-hidden=\"true\"></i>\n }\n <span class=\"ef-menu__label\">{{ item.labelKey | translate }}</span>\n @if (item.badge !== undefined && item.badge !== null) {\n <span class=\"ef-menu__badge\">{{ item.badge }}</span>\n }\n @if (item.children?.length) {\n <i\n class=\"ef-menu__caret pi\"\n [class.pi-chevron-down]=\"!isExpanded(item.id)\"\n [class.pi-chevron-up]=\"isExpanded(item.id)\"\n aria-hidden=\"true\"\n ></i>\n }\n </button>\n\n @if (item.children?.length && isExpanded(item.id)) {\n <div class=\"ef-menu__sublist\" role=\"group\">\n @for (child of item.children; track child.id) {\n <button\n type=\"button\"\n class=\"ef-menu__item\"\n role=\"menuitem\"\n (click)=\"select(child, op)\"\n >\n @if (child.icon) {\n <i [class]=\"'ico ' + child.icon\" aria-hidden=\"true\"></i>\n }\n <span class=\"ef-menu__label\">{{ child.labelKey | translate }}</span>\n </button>\n }\n </div>\n }\n }\n </div>\n</ng-template>\n", styles: [":host{display:block;min-width:0}.ef-menu{min-width:232px;max-width:288px;max-height:calc(100vh - 32px);overflow-y:auto}@media(max-width:767px){.ef-menu{min-width:0}.ef-menu__item{min-height:var(--hit-touch);padding-block:0;font-size:14px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i2$4.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: EfProfileChipComponent, selector: "ef-profile-chip", inputs: ["name", "role", "roleKey", "initial", "tone", "readonly"], outputs: ["clickEvent"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6309
+ }
6310
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfProfileMenuComponent, decorators: [{
6311
+ type: Component,
6312
+ args: [{ selector: 'ef-profile-menu', standalone: true, imports: [CommonModule, TranslateModule, PopoverModule, EfProfileChipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (variant() === 'chip') {\n <ef-profile-chip\n [name]=\"name()\"\n [role]=\"chipRole()\"\n [initial]=\"initial()\"\n [tone]=\"tone()\"\n (clickEvent)=\"op.toggle($event)\"\n />\n <!-- The popover is reduced to a placement mechanism: `.ef-menu` inside it\n draws the surface, so `ef-menu-popover` strips PrimeNG's own panel and\n arrow rather than letting two panels stack. -->\n <p-popover #op styleClass=\"ef-menu-popover\" appendTo=\"body\">\n <ng-container *ngTemplateOutlet=\"panel; context: { $implicit: op }\" />\n </p-popover>\n} @else {\n <ng-container *ngTemplateOutlet=\"panel\" />\n}\n\n<ng-template #panel let-op>\n <div class=\"ef-menu\" role=\"menu\" [attr.aria-label]=\"name() || null\">\n @if (name() || secondary()) {\n <div class=\"ef-menu__head\">\n @if (name()) {\n <span class=\"ef-menu__head-name\">{{ name() }}</span>\n }\n @if (secondary()) {\n <span class=\"ef-menu__head-sub\">{{ secondary() }}</span>\n }\n </div>\n }\n\n @for (item of items(); track item.id) {\n @if (item.groupStart) {\n <div class=\"ef-menu__divider\" role=\"separator\"></div>\n }\n\n <button\n type=\"button\"\n class=\"ef-menu__item\"\n role=\"menuitem\"\n [class.danger]=\"item.severity === 'danger'\"\n [attr.aria-expanded]=\"item.children?.length ? isExpanded(item.id) : null\"\n (click)=\"select(item, op)\"\n >\n @if (item.icon) {\n <i [class]=\"'ico ' + item.icon\" aria-hidden=\"true\"></i>\n }\n <span class=\"ef-menu__label\">{{ item.labelKey | translate }}</span>\n @if (item.badge !== undefined && item.badge !== null) {\n <span class=\"ef-menu__badge\">{{ item.badge }}</span>\n }\n @if (item.children?.length) {\n <i\n class=\"ef-menu__caret pi\"\n [class.pi-chevron-down]=\"!isExpanded(item.id)\"\n [class.pi-chevron-up]=\"isExpanded(item.id)\"\n aria-hidden=\"true\"\n ></i>\n }\n </button>\n\n @if (item.children?.length && isExpanded(item.id)) {\n <div class=\"ef-menu__sublist\" role=\"group\">\n @for (child of item.children; track child.id) {\n <button\n type=\"button\"\n class=\"ef-menu__item\"\n role=\"menuitem\"\n (click)=\"select(child, op)\"\n >\n @if (child.icon) {\n <i [class]=\"'ico ' + child.icon\" aria-hidden=\"true\"></i>\n }\n <span class=\"ef-menu__label\">{{ child.labelKey | translate }}</span>\n </button>\n }\n </div>\n }\n }\n </div>\n</ng-template>\n", styles: [":host{display:block;min-width:0}.ef-menu{min-width:232px;max-width:288px;max-height:calc(100vh - 32px);overflow-y:auto}@media(max-width:767px){.ef-menu{min-width:0}.ef-menu__item{min-height:var(--hit-touch);padding-block:0;font-size:14px}}\n"] }]
6313
+ }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], secondary: [{ type: i0.Input, args: [{ isSignal: true, alias: "secondary", required: false }] }], role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: false }] }], initial: [{ type: i0.Input, args: [{ isSignal: true, alias: "initial", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], itemSelected: [{ type: i0.Output, args: ["itemSelected"] }] } });
6314
+
6210
6315
  class EfBlockUiComponent {
6211
6316
  isLoading = false;
6212
6317
  contentPanel;
@@ -8686,7 +8791,7 @@ class EfDatatableComponent {
8686
8791
  return this.pTable?.filters;
8687
8792
  }
8688
8793
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfDatatableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8689
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfDatatableComponent, isStandalone: true, selector: "ef-datatable", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, searchEntity: { classPropertyName: "searchEntity", publicName: "searchEntity", isSignal: true, isRequired: false, transformFunction: null }, context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: false, transformFunction: null }, pt: { classPropertyName: "pt", publicName: "pt", isSignal: true, isRequired: false, transformFunction: null }, screenStateKey: { classPropertyName: "screenStateKey", publicName: "screenStateKey", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { editRow: "editRow", deleteRow: "deleteRow", duplicateRow: "duplicateRow", lazyLoad: "lazyLoad", rowSelect: "rowSelect" }, viewQueries: [{ propertyName: "pTable", first: true, predicate: ["pTable"], descendants: true }], ngImport: i0, template: "<p-table #pTable\n [value]=\"searchEntity()?.items || []\"\n [columns]=\"processedColumns()\"\n [dataKey]=\"mergedConfig.dataKey\"\n\n [lazy]=\"mergedConfig.lazy\"\n (onLazyLoad)=\"handleLazyLoad($event)\"\n\n [paginator]=\"mergedConfig.paginator\"\n [rows]=\"searchEntity()?.pagination?.pageSize || mergedConfig.rows\"\n [rowsPerPageOptions]=\"mergedConfig.rowsPerPageOptions\"\n [totalRecords]=\"searchEntity()?.totalCount || 0\"\n [first]=\"searchEntity()?.pagination ? (searchEntity()!.pagination!.pageNumber - 1) * searchEntity()!.pagination!.pageSize : 0\"\n\n [scrollable]=\"mergedConfig.scrollable\"\n [scrollHeight]=\"mergedConfig.scrollHeight\"\n\n [resizableColumns]=\"mergedConfig.resizableColumns\"\n [columnResizeMode]=\"mergedConfig.columnResizeMode\"\n\n [stateStorage]=\"mergedConfig.stateStorage\"\n [stateKey]=\"mergedConfig.stateKey\"\n\n [size]=\"mergedConfig.size\"\n [class]=\"mergedConfig.styleClass\"\n [tableStyle]=\"mergedConfig.tableStyle\"\n\n [currentPageReportTemplate]=\"searchEntity()?.totalCount ? 'Affichage de {first} a {last} sur {totalRecords} entrees' : mergedConfig.emptyMessage\"\n [showCurrentPageReport]=\"true\"\n [sortField]=\"sortField()\"\n [sortOrder]=\"sortOrder()\"\n [loading]=\"loading()\"\n\n [pt]=\"pt()\"\n\n selectionMode=\"single\" [(selection)]=\"selectedItem\">\n\n <!-- Header Template -->\n <ng-template #header>\n <tr>\n <!-- Action column at start if configured -->\n @if (mergedActions.show && mergedActions.position === 'start') {\n <th [class]=\"mergedActions.width\"></th>\n }\n\n <!-- Data columns -->\n @for (column of processedColumns(); track column.field) {\n @if (column.sortable && column.resizable) {\n <th\n [pSortableColumn]=\"column.field\"\n pResizableColumn\n [class]=\"column.width || ''\"\n [ngClass]=\"column.styleClass\">\n\n @if (column.headerTemplate) {\n <ng-container *ngTemplateOutlet=\"column.headerTemplate; context: { $implicit: column }\"></ng-container>\n } @else {\n {{ column.header | translate }}\n <p-sortIcon [field]=\"column.field\" />\n }\n </th>\n } @else if (column.sortable) {\n <th\n [pSortableColumn]=\"column.field\"\n [class]=\"column.width || ''\"\n [ngClass]=\"column.styleClass\">\n\n @if (column.headerTemplate) {\n <ng-container *ngTemplateOutlet=\"column.headerTemplate; context: { $implicit: column }\"></ng-container>\n } @else {\n {{ column.header | translate }}\n <p-sortIcon [field]=\"column.field\" />\n }\n </th>\n } @else if (column.resizable) {\n <th\n pResizableColumn\n [class]=\"column.width || ''\"\n [ngClass]=\"column.styleClass\">\n\n @if (column.headerTemplate) {\n <ng-container *ngTemplateOutlet=\"column.headerTemplate; context: { $implicit: column }\"></ng-container>\n } @else {\n {{ column.header | translate }}\n }\n </th>\n } @else {\n <th\n [class]=\"column.width || ''\"\n [ngClass]=\"column.styleClass\">\n\n @if (column.headerTemplate) {\n <ng-container *ngTemplateOutlet=\"column.headerTemplate; context: { $implicit: column }\"></ng-container>\n } @else {\n {{ column.header | translate }}\n }\n </th>\n }\n }\n\n <!-- Action column at end if configured -->\n @if (mergedActions.show && mergedActions.position === 'end') {\n <th [class]=\"mergedActions.width\"></th>\n }\n </tr>\n </ng-template>\n\n <!-- Body Template -->\n <ng-template #body let-rowData let-rowIndex=\"rowIndex\">\n <tr (dblclick)=\"handleEdit(rowData)\" style=\"cursor: pointer\"\n [pSelectableRow]=\"rowData\" [pSelectableRowIndex]=\"rowIndex\">\n <!-- Action column at start if configured -->\n @if (mergedActions.show && mergedActions.position === 'start') {\n <td>\n <ef-datatable-actionbar\n [context]=\"context()\"\n (edit)=\"handleEdit(rowData)\"\n (delete)=\"handleDelete(rowData)\"\n (duplicate)=\"handleDuplicate(rowData)\">\n </ef-datatable-actionbar>\n </td>\n }\n\n <!-- Data columns -->\n @for (column of processedColumns(); track column.field) {\n <td [ngClass]=\"getAlignmentClass(column.align)\">\n @if (column.template) {\n <ng-container *ngTemplateOutlet=\"column.template; context: { $implicit: rowData, column: column }\"></ng-container>\n } @else {\n {{ formatCellValue(rowData, column) }}\n }\n </td>\n }\n\n <!-- Action column at end if configured -->\n @if (mergedActions.show && mergedActions.position === 'end') {\n <td>\n <ef-datatable-actionbar\n [context]=\"context()\"\n (edit)=\"handleEdit(rowData)\"\n (delete)=\"handleDelete(rowData)\"\n (duplicate)=\"handleDuplicate(rowData)\">\n </ef-datatable-actionbar>\n </td>\n }\n </tr>\n </ng-template>\n</p-table>\n", styles: [":host{display:block}:host ::ng-deep .text-end{text-align:right!important}:host ::ng-deep .text-center{text-align:center!important}:host ::ng-deep .text-start{text-align:left!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i2$4.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i2$4.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "directive", type: i2$4.SelectableRow, selector: "[pSelectableRow]", inputs: ["pSelectableRow", "pSelectableRowIndex", "pSelectableRowDisabled"] }, { kind: "directive", type: i2$4.ResizableColumn, selector: "[pResizableColumn]", inputs: ["pResizableColumnDisabled"] }, { kind: "component", type: i2$4.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfDatatableActionBarComponent, selector: "ef-datatable-actionbar", inputs: ["context", "state", "duplicateButton", "editButton", "deleteButton"], outputs: ["duplicate", "edit", "delete"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
8794
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfDatatableComponent, isStandalone: true, selector: "ef-datatable", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, searchEntity: { classPropertyName: "searchEntity", publicName: "searchEntity", isSignal: true, isRequired: false, transformFunction: null }, context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: false, transformFunction: null }, pt: { classPropertyName: "pt", publicName: "pt", isSignal: true, isRequired: false, transformFunction: null }, screenStateKey: { classPropertyName: "screenStateKey", publicName: "screenStateKey", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { editRow: "editRow", deleteRow: "deleteRow", duplicateRow: "duplicateRow", lazyLoad: "lazyLoad", rowSelect: "rowSelect" }, viewQueries: [{ propertyName: "pTable", first: true, predicate: ["pTable"], descendants: true }], ngImport: i0, template: "<p-table #pTable\n [value]=\"searchEntity()?.items || []\"\n [columns]=\"processedColumns()\"\n [dataKey]=\"mergedConfig.dataKey\"\n\n [lazy]=\"mergedConfig.lazy\"\n (onLazyLoad)=\"handleLazyLoad($event)\"\n\n [paginator]=\"mergedConfig.paginator\"\n [rows]=\"searchEntity()?.pagination?.pageSize || mergedConfig.rows\"\n [rowsPerPageOptions]=\"mergedConfig.rowsPerPageOptions\"\n [totalRecords]=\"searchEntity()?.totalCount || 0\"\n [first]=\"searchEntity()?.pagination ? (searchEntity()!.pagination!.pageNumber - 1) * searchEntity()!.pagination!.pageSize : 0\"\n\n [scrollable]=\"mergedConfig.scrollable\"\n [scrollHeight]=\"mergedConfig.scrollHeight\"\n\n [resizableColumns]=\"mergedConfig.resizableColumns\"\n [columnResizeMode]=\"mergedConfig.columnResizeMode\"\n\n [stateStorage]=\"mergedConfig.stateStorage\"\n [stateKey]=\"mergedConfig.stateKey\"\n\n [size]=\"mergedConfig.size\"\n [class]=\"mergedConfig.styleClass\"\n [tableStyle]=\"mergedConfig.tableStyle\"\n\n [currentPageReportTemplate]=\"searchEntity()?.totalCount ? 'Affichage de {first} a {last} sur {totalRecords} entrees' : mergedConfig.emptyMessage\"\n [showCurrentPageReport]=\"true\"\n [sortField]=\"sortField()\"\n [sortOrder]=\"sortOrder()\"\n [loading]=\"loading()\"\n\n [pt]=\"pt()\"\n\n selectionMode=\"single\" [(selection)]=\"selectedItem\">\n\n <!-- Header Template -->\n <ng-template #header>\n <tr>\n <!-- Action column at start if configured -->\n @if (mergedActions.show && mergedActions.position === 'start') {\n <th [class]=\"mergedActions.width\"></th>\n }\n\n <!-- Data columns -->\n @for (column of processedColumns(); track column.field) {\n @if (column.sortable && column.resizable) {\n <th\n [pSortableColumn]=\"column.field\"\n pResizableColumn\n [class]=\"column.width || ''\"\n [ngClass]=\"column.styleClass\">\n\n @if (column.headerTemplate) {\n <ng-container *ngTemplateOutlet=\"column.headerTemplate; context: { $implicit: column }\"></ng-container>\n } @else {\n {{ column.header | translate }}\n <p-sortIcon [field]=\"column.field\" />\n }\n </th>\n } @else if (column.sortable) {\n <th\n [pSortableColumn]=\"column.field\"\n [class]=\"column.width || ''\"\n [ngClass]=\"column.styleClass\">\n\n @if (column.headerTemplate) {\n <ng-container *ngTemplateOutlet=\"column.headerTemplate; context: { $implicit: column }\"></ng-container>\n } @else {\n {{ column.header | translate }}\n <p-sortIcon [field]=\"column.field\" />\n }\n </th>\n } @else if (column.resizable) {\n <th\n pResizableColumn\n [class]=\"column.width || ''\"\n [ngClass]=\"column.styleClass\">\n\n @if (column.headerTemplate) {\n <ng-container *ngTemplateOutlet=\"column.headerTemplate; context: { $implicit: column }\"></ng-container>\n } @else {\n {{ column.header | translate }}\n }\n </th>\n } @else {\n <th\n [class]=\"column.width || ''\"\n [ngClass]=\"column.styleClass\">\n\n @if (column.headerTemplate) {\n <ng-container *ngTemplateOutlet=\"column.headerTemplate; context: { $implicit: column }\"></ng-container>\n } @else {\n {{ column.header | translate }}\n }\n </th>\n }\n }\n\n <!-- Action column at end if configured -->\n @if (mergedActions.show && mergedActions.position === 'end') {\n <th [class]=\"mergedActions.width\"></th>\n }\n </tr>\n </ng-template>\n\n <!-- Body Template -->\n <ng-template #body let-rowData let-rowIndex=\"rowIndex\">\n <tr (dblclick)=\"handleEdit(rowData)\" style=\"cursor: pointer\"\n [pSelectableRow]=\"rowData\" [pSelectableRowIndex]=\"rowIndex\">\n <!-- Action column at start if configured -->\n @if (mergedActions.show && mergedActions.position === 'start') {\n <td>\n <ef-datatable-actionbar\n [context]=\"context()\"\n (edit)=\"handleEdit(rowData)\"\n (delete)=\"handleDelete(rowData)\"\n (duplicate)=\"handleDuplicate(rowData)\">\n </ef-datatable-actionbar>\n </td>\n }\n\n <!-- Data columns -->\n @for (column of processedColumns(); track column.field) {\n <td [ngClass]=\"getAlignmentClass(column.align)\">\n @if (column.template) {\n <ng-container *ngTemplateOutlet=\"column.template; context: { $implicit: rowData, column: column }\"></ng-container>\n } @else {\n {{ formatCellValue(rowData, column) }}\n }\n </td>\n }\n\n <!-- Action column at end if configured -->\n @if (mergedActions.show && mergedActions.position === 'end') {\n <td>\n <ef-datatable-actionbar\n [context]=\"context()\"\n (edit)=\"handleEdit(rowData)\"\n (delete)=\"handleDelete(rowData)\"\n (duplicate)=\"handleDuplicate(rowData)\">\n </ef-datatable-actionbar>\n </td>\n }\n </tr>\n </ng-template>\n</p-table>\n", styles: [":host{display:block}:host ::ng-deep .text-end{text-align:right!important}:host ::ng-deep .text-center{text-align:center!important}:host ::ng-deep .text-start{text-align:left!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i2$5.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i2$5.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "directive", type: i2$5.SelectableRow, selector: "[pSelectableRow]", inputs: ["pSelectableRow", "pSelectableRowIndex", "pSelectableRowDisabled"] }, { kind: "directive", type: i2$5.ResizableColumn, selector: "[pResizableColumn]", inputs: ["pResizableColumnDisabled"] }, { kind: "component", type: i2$5.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfDatatableActionBarComponent, selector: "ef-datatable-actionbar", inputs: ["context", "state", "duplicateButton", "editButton", "deleteButton"], outputs: ["duplicate", "edit", "delete"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
8690
8795
  }
8691
8796
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfDatatableComponent, decorators: [{
8692
8797
  type: Component,
@@ -8815,6 +8920,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
8815
8920
  args: [{ selector: 'ef-pager', standalone: true, imports: [CommonModule, TranslateModule, EfSelectComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"pager\">\n @if (!hideSize()) {\n <div class=\"size\">\n <span>{{ linesPerPageLabelKey() | translate }}</span>\n <ef-select\n size=\"small\"\n appendTo=\"body\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [options]=\"sizeOptions()\"\n [value]=\"pageSize()\"\n [ariaLabelKey]=\"linesPerPageLabelKey()\"\n [showClear]=\"false\"\n (selectionChangeEvent)=\"onSizeChange($event)\"\n />\n @if (totalCount() > 0) {\n <span>\u00B7 {{ range().start }}\u2013{{ range().end }} {{ ofLabelKey() | translate }} {{ totalCount() }}</span>\n }\n </div>\n } @else {\n <span></span>\n }\n\n <div class=\"pages\">\n <button\n class=\"pg\"\n type=\"button\"\n [disabled]=\"pageNumber() <= 1\"\n [attr.aria-label]=\"prevLabelKey() | translate\"\n (click)=\"goToPage(pageNumber() - 1)\"\n >\u2039</button>\n\n @for (btn of pageButtons(); track trackBtn($index, btn)) {\n @if (btn.kind === 'page') {\n <button\n class=\"pg\"\n type=\"button\"\n [class.active]=\"btn.value === pageNumber()\"\n (click)=\"goToPage(btn.value)\"\n >{{ btn.value }}</button>\n } @else {\n <span class=\"pg\" aria-hidden=\"true\" style=\"cursor: default;\">\u2026</span>\n }\n }\n\n <button\n class=\"pg\"\n type=\"button\"\n [disabled]=\"pageNumber() >= totalPages()\"\n [attr.aria-label]=\"nextLabelKey() | translate\"\n (click)=\"goToPage(pageNumber() + 1)\"\n >\u203A</button>\n </div>\n</div>\n" }]
8816
8921
  }], propDecorators: { pageNumber: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageNumber", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], totalCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalCount", required: false }] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], linesPerPageLabelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "linesPerPageLabelKey", required: false }] }], ofLabelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "ofLabelKey", required: false }] }], prevLabelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "prevLabelKey", required: false }] }], nextLabelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabelKey", required: false }] }], hideSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideSize", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], pageSizeChange: [{ type: i0.Output, args: ["pageSizeChange"] }] } });
8817
8922
 
8923
+ /** Row-scoped shortcuts, wired once per instance and gated on `open()` so
8924
+ * they're inert until this row's menu is on screen. The action `id` is a
8925
+ * convention shared with `ef-data-card.defaultRowActions()`: an instance
8926
+ * with no matching item is simply a no-op for that key. */
8927
+ const ROW_SHORTCUTS = [
8928
+ { actionId: 'view', keys: 'enter', labelKey: 'common_view' },
8929
+ { actionId: 'edit', keys: 'e', labelKey: 'common_edit' },
8930
+ { actionId: 'duplicate', keys: 'mod+d', labelKey: 'common_duplicate' },
8931
+ ];
8818
8932
  /**
8819
8933
  * Row-actions dropdown — V2 successor to `ef-datatable-actionbar`.
8820
8934
  *
@@ -8823,11 +8937,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
8823
8937
  *
8824
8938
  * ```ts
8825
8939
  * actions: ReadonlyArray<EfRowAction> = [
8826
- * { id: 'view', labelKey: 'common_view', icon: 'pi pi-eye', kbd: '',
8940
+ * { id: 'view', labelKey: 'common_view', icon: 'pi pi-eye', kbd: formatShortcut('enter'),
8827
8941
  * permission: Permissions.Read, command: () => view(row) },
8828
- * { id: 'edit', labelKey: 'common_edit', icon: 'pi pi-pencil', kbd: 'E',
8942
+ * { id: 'edit', labelKey: 'common_edit', icon: 'pi pi-pencil', kbd: formatShortcut('e'),
8829
8943
  * permission: Permissions.Edit, command: () => edit(row) },
8830
- * { id: 'duplicate', labelKey: 'common_duplicate', icon: 'pi pi-copy',
8944
+ * { id: 'duplicate', labelKey: 'common_duplicate', icon: 'pi pi-copy', kbd: formatShortcut('mod+d'),
8831
8945
  * command: () => duplicate(row) },
8832
8946
  * { separator: true },
8833
8947
  * { id: 'delete', labelKey: 'common_delete', icon: 'pi pi-trash', severity: 'danger',
@@ -8853,9 +8967,50 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
8853
8967
  * Permission filtering: pass `[context]="..."` (a `ScreenContext`); items
8854
8968
  * declaring a `permission` are hidden unless that permission is granted.
8855
8969
  * Items with no `permission` always show.
8970
+ *
8971
+ * The `kbd` hint is decorative unless it's one of `view` / `edit` /
8972
+ * `duplicate`: this component registers Enter, `E` and `mod+D` against
8973
+ * `EfShortcutService`, gated on `open()`, so they only fire while this
8974
+ * row's menu is on screen. Render `kbd` through `formatShortcut()` (as
8975
+ * above): a hardcoded `'⌘D'` is wrong for a Windows user, which is
8976
+ * exactly the bug this wiring fixes.
8856
8977
  */
8857
8978
  class EfRowActionsComponent {
8858
8979
  host = inject(ElementRef);
8980
+ shortcuts = inject(EfShortcutService);
8981
+ /** Distinguishes this instance's registrations from every other row's:
8982
+ * each row on screen constructs its own component, and ids must not
8983
+ * collide or one row's disposer (and its `when`) would clobber
8984
+ * another's still-live registration. */
8985
+ static nextInstanceId = 0;
8986
+ instanceId = EfRowActionsComponent.nextInstanceId++;
8987
+ constructor() {
8988
+ for (const { actionId, keys, labelKey } of ROW_SHORTCUTS) {
8989
+ this.shortcuts.register({
8990
+ id: `row-actions.${this.instanceId}.${actionId}`,
8991
+ keys,
8992
+ labelKey,
8993
+ group: 'shortcut_group_row_actions',
8994
+ // Gated on the trigger, not merely on `open()`: once a
8995
+ // keyboard user tabs from the trigger into the menu itself,
8996
+ // Enter is the button's own native activation key for
8997
+ // whichever item has focus. Without this, the `enter`
8998
+ // registration would run "view" from underneath the
8999
+ // focused "delete" button and swallow its click.
9000
+ when: () => this.open() && !this.isFocusInsideMenu(),
9001
+ handler: () => {
9002
+ const action = this.visibleItems().find(a => a.id === actionId);
9003
+ if (!action || action.disabled)
9004
+ return;
9005
+ action.command?.();
9006
+ this.close();
9007
+ },
9008
+ });
9009
+ }
9010
+ }
9011
+ isFocusInsideMenu() {
9012
+ return !!document.activeElement?.closest('.row-actions__menu');
9013
+ }
8859
9014
  /** Action descriptors. */
8860
9015
  items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
8861
9016
  /** Optional context — when present, items with a `permission` are
@@ -9009,7 +9164,7 @@ class EfRowActionsComponent {
9009
9164
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfRowActionsComponent, decorators: [{
9010
9165
  type: Component,
9011
9166
  args: [{ selector: 'ef-row-actions', standalone: true, imports: [CommonModule, TranslateModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"row-actions\">\n <button\n type=\"button\"\n class=\"row-actions__trigger\"\n [class.open]=\"open()\"\n [disabled]=\"disabled() || visibleItems().length === 0\"\n [attr.aria-haspopup]=\"'menu'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-label]=\"triggerAriaKey() | translate\"\n (click)=\"toggle($event)\"\n (dblclick)=\"onDblClick($event)\"\n >\n <i [class]=\"triggerIcon()\" aria-hidden=\"true\"></i>\n </button>\n\n @if (open()) {\n <div\n class=\"row-actions__menu open\"\n [class.left]=\"menuLeft()\"\n [class.up]=\"dropUp()\"\n role=\"menu\"\n [attr.aria-label]=\"triggerAriaKey() | translate\"\n (dblclick)=\"onDblClick($event)\"\n >\n @for (action of visibleItems(); track trackByItem($index, action)) {\n @if (action.separator) {\n <div class=\"row-actions__divider\" role=\"separator\"></div>\n } @else {\n <button\n type=\"button\"\n class=\"row-actions__item\"\n [class.danger]=\"action.severity === 'danger'\"\n [disabled]=\"action.disabled\"\n role=\"menuitem\"\n (click)=\"runCommand(action, $event)\"\n >\n @if (action.icon) {\n <i class=\"ico\" [class]=\"'ico ' + action.icon\" aria-hidden=\"true\"></i>\n }\n <span class=\"lbl\">\n {{ action.labelKey ? (action.labelKey | translate) : action.label }}\n </span>\n @if (action.kbd) {\n <span class=\"kbd\" aria-hidden=\"true\">{{ action.kbd }}</span>\n }\n </button>\n }\n }\n </div>\n }\n</div>\n", styles: [":host{display:inline-flex;align-items:center;justify-content:flex-end}\n"] }]
9012
- }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: false }] }], triggerIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "triggerIcon", required: false }] }], triggerAriaKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "triggerAriaKey", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], menuLeft: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuLeft", required: false }] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }], onDocumentClick: [{
9167
+ }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: false }] }], triggerIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "triggerIcon", required: false }] }], triggerAriaKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "triggerAriaKey", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], menuLeft: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuLeft", required: false }] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }], onDocumentClick: [{
9013
9168
  type: HostListener,
9014
9169
  args: ['document:click', ['$event']]
9015
9170
  }], onEscape: [{
@@ -9322,7 +9477,11 @@ class EfDataCardComponent {
9322
9477
  /** Items array for the auto row-actions cell. Rebuilt per call so
9323
9478
  * the ef-row-actions component receives a fresh closure per row.
9324
9479
  * Visibility is filtered later by ef-row-actions against
9325
- * `rowActionsContext` (ScreenContext.isGranted). */
9480
+ * `rowActionsContext` (ScreenContext.isGranted).
9481
+ *
9482
+ * `kbd` is rendered through `formatShortcut()`, not a literal glyph:
9483
+ * `ef-row-actions` binds these same keys for real while the row's
9484
+ * menu is open, and a hardcoded `'⌘D'` would lie to a Windows user. */
9326
9485
  defaultRowActions(row) {
9327
9486
  const items = [];
9328
9487
  if (this.showViewAction()) {
@@ -9330,7 +9489,7 @@ class EfDataCardComponent {
9330
9489
  id: 'view',
9331
9490
  labelKey: 'common_view',
9332
9491
  icon: 'pi pi-eye',
9333
- kbd: '',
9492
+ kbd: formatShortcut('enter'),
9334
9493
  permission: Permissions.Read,
9335
9494
  command: () => this.rowAction.emit({ action: 'view', row }),
9336
9495
  });
@@ -9340,7 +9499,7 @@ class EfDataCardComponent {
9340
9499
  id: 'edit',
9341
9500
  labelKey: 'common_edit',
9342
9501
  icon: 'pi pi-pencil',
9343
- kbd: 'E',
9502
+ kbd: formatShortcut('e'),
9344
9503
  permission: Permissions.Edit,
9345
9504
  command: () => this.rowAction.emit({ action: 'edit', row }),
9346
9505
  });
@@ -9350,7 +9509,7 @@ class EfDataCardComponent {
9350
9509
  id: 'duplicate',
9351
9510
  labelKey: 'common_duplicate',
9352
9511
  icon: 'pi pi-copy',
9353
- kbd: '⌘D',
9512
+ kbd: formatShortcut('mod+d'),
9354
9513
  permission: Permissions.Duplicate,
9355
9514
  command: () => this.rowAction.emit({ action: 'duplicate', row }),
9356
9515
  });
@@ -9956,6 +10115,90 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
9956
10115
  args: [{ selector: 'ef-filter-pill', standalone: true, imports: [CommonModule, TranslateModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n type=\"button\"\n class=\"f-pill\"\n [class.active]=\"tone() === 'active'\"\n [class.tenant]=\"tone() === 'tenant'\"\n [class.has-pop]=\"hasPopover()\"\n [attr.aria-haspopup]=\"hasPopover() ? 'listbox' : null\"\n [attr.aria-expanded]=\"hasPopover() ? popoverOpen() : null\"\n (click)=\"onPillClick()\"\n>\n @if (icon()) {\n <i class=\"ico\" [class]=\"icon()\" aria-hidden=\"true\"></i>\n }\n\n @if (labelKey() || label()) {\n <span class=\"lbl\">\n {{ labelKey() ? (labelKey() | translate) : label() }}\n </span>\n }\n\n @if (value()) {\n <span class=\"val\">{{ value() }}</span>\n }\n\n @if (hasClear()) {\n <span\n class=\"clear\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"clearAriaKey() | translate\"\n (click)=\"onClearClick($event)\"\n (keydown.enter)=\"onClearClick($event)\"\n (keydown.space)=\"onClearClick($event)\"\n >\n <i class=\"pi pi-times\" aria-hidden=\"true\" style=\"font-size: 8px;\"></i>\n </span>\n } @else if (hasPopover()) {\n <i class=\"caret pi pi-chevron-down\" aria-hidden=\"true\"></i>\n }\n\n @if (hasPopover()) {\n <div class=\"popover\" [class.open]=\"popoverOpen()\" role=\"listbox\">\n <ng-content select=\"[popover]\"></ng-content>\n </div>\n }\n</button>\n" }]
9957
10116
  }], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], labelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelKey", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], hasClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasClear", required: false }] }], hasPopover: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasPopover", required: false }] }], popoverOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "popoverOpen", required: false }] }], clearAriaKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearAriaKey", required: false }] }], pillClick: [{ type: i0.Output, args: ["pillClick"] }], clearClick: [{ type: i0.Output, args: ["clearClick"] }] } });
9958
10117
 
10118
+ /**
10119
+ * Password form, deliberately without an API client.
10120
+ *
10121
+ * `@elasticias/ui` cannot import an app's generated HTTP client, and that
10122
+ * limitation is what makes one component serve two different endpoints: a
10123
+ * user changing their own password proves the old one, an administrator
10124
+ * resetting someone else's does not. The host reads `canSubmit()`, calls
10125
+ * `submit()` from its dialog footer, and listens to `submitted`.
10126
+ *
10127
+ * ```html
10128
+ * <ef-change-password
10129
+ * #form
10130
+ * [requireCurrent]="!userId()"
10131
+ * [busy]="saving()"
10132
+ * [errors]="serverErrors()"
10133
+ * (submitted)="save($event)"
10134
+ * />
10135
+ * ```
10136
+ *
10137
+ * There is no `reset()`: hosts mount it under `@if`, so every open is a
10138
+ * fresh instance.
10139
+ */
10140
+ class EfChangePasswordComponent {
10141
+ /** Ask for the existing password. False for an administrator reset. */
10142
+ requireCurrent = input(true, { ...(ngDevMode ? { debugName: "requireCurrent" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
10143
+ /** A request is in flight: the form refuses to submit again. */
10144
+ busy = input(false, { ...(ngDevMode ? { debugName: "busy" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
10145
+ /** Already-translated server messages, rendered under the fields. */
10146
+ errors = input([], ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
10147
+ /** Minimum length. Kept in step with the backend validator's 8. */
10148
+ minLength = input(8, ...(ngDevMode ? [{ debugName: "minLength" }] : /* istanbul ignore next */ []));
10149
+ submitted = output();
10150
+ /* The template writes these directly; they are public so a host can
10151
+ drive the component in a test without touching the DOM. */
10152
+ currentPassword = signal('', ...(ngDevMode ? [{ debugName: "currentPassword" }] : /* istanbul ignore next */ []));
10153
+ newPassword = signal('', ...(ngDevMode ? [{ debugName: "newPassword" }] : /* istanbul ignore next */ []));
10154
+ confirmation = signal('', ...(ngDevMode ? [{ debugName: "confirmation" }] : /* istanbul ignore next */ []));
10155
+ /**
10156
+ * Translation keys for every rule the current input breaks. Empty while
10157
+ * the user has typed nothing, so the form does not scold on open.
10158
+ */
10159
+ validationKeys = computed(() => {
10160
+ const next = this.newPassword();
10161
+ const confirmation = this.confirmation();
10162
+ const keys = [];
10163
+ if (next && next.length < this.minLength()) {
10164
+ keys.push('ef_change_password_too_short');
10165
+ }
10166
+ if (next && !/[A-Z]/.test(next)) {
10167
+ keys.push('ef_change_password_uppercase');
10168
+ }
10169
+ if (next && !/[0-9]/.test(next)) {
10170
+ keys.push('ef_change_password_digit');
10171
+ }
10172
+ if (next && confirmation && next !== confirmation) {
10173
+ keys.push('ef_change_password_mismatch');
10174
+ }
10175
+ return keys;
10176
+ }, ...(ngDevMode ? [{ debugName: "validationKeys" }] : /* istanbul ignore next */ []));
10177
+ canSubmit = computed(() => {
10178
+ if (this.busy())
10179
+ return false;
10180
+ if (this.requireCurrent() && !this.currentPassword())
10181
+ return false;
10182
+ if (!this.newPassword() || !this.confirmation())
10183
+ return false;
10184
+ return this.validationKeys().length === 0;
10185
+ }, ...(ngDevMode ? [{ debugName: "canSubmit" }] : /* istanbul ignore next */ []));
10186
+ submit() {
10187
+ if (!this.canSubmit())
10188
+ return;
10189
+ this.submitted.emit({
10190
+ currentPassword: this.requireCurrent() ? this.currentPassword() : undefined,
10191
+ newPassword: this.newPassword(),
10192
+ });
10193
+ }
10194
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfChangePasswordComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10195
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfChangePasswordComponent, isStandalone: true, selector: "ef-change-password", inputs: { requireCurrent: { classPropertyName: "requireCurrent", publicName: "requireCurrent", isSignal: true, isRequired: false, transformFunction: null }, busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, minLength: { classPropertyName: "minLength", publicName: "minLength", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { submitted: "submitted" }, ngImport: i0, template: "<ef-form-grid>\n @if (requireCurrent()) {\n <ef-password\n class=\"col-12\"\n name=\"currentPassword\"\n labelKey=\"ef_change_password_current\"\n autocomplete=\"current-password\"\n required\n (valueChangeEvent)=\"currentPassword.set($event)\"\n />\n }\n <ef-password\n class=\"col-6\"\n name=\"newPassword\"\n labelKey=\"ef_change_password_new\"\n autocomplete=\"new-password\"\n required\n (valueChangeEvent)=\"newPassword.set($event)\"\n />\n <ef-password\n class=\"col-6\"\n name=\"newPasswordConfirmation\"\n labelKey=\"ef_change_password_confirm\"\n autocomplete=\"new-password\"\n required\n (valueChangeEvent)=\"confirmation.set($event)\"\n />\n</ef-form-grid>\n\n@if (validationKeys().length || errors().length) {\n <ul class=\"ef-change-password__errors\">\n @for (key of validationKeys(); track $index) {\n <li>{{ key | translate }}</li>\n }\n <!-- Server messages arrive already translated (Identity returns prose,\n not keys), so they are printed verbatim. -->\n @for (error of errors(); track $index) {\n <li>{{ error }}</li>\n }\n </ul>\n}\n", styles: [":host{display:grid;gap:var(--s-2, 8px);min-width:0}.ef-change-password__errors{display:grid;gap:4px;margin:0;padding:0;list-style:none;min-width:0;font-size:12px;line-height:1.4;color:var(--st-cancelled-fg, #b42318)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EfPasswordComponent, selector: "ef-password", inputs: ["size", "fluid", "inline", "toggleMask", "feedback", "autocomplete"], outputs: ["valueChangeEvent"] }, { kind: "component", type: EfFormGridComponent, selector: "ef-form-grid" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10196
+ }
10197
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfChangePasswordComponent, decorators: [{
10198
+ type: Component,
10199
+ args: [{ selector: 'ef-change-password', standalone: true, imports: [CommonModule, TranslateModule, EfPasswordComponent, EfFormGridComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ef-form-grid>\n @if (requireCurrent()) {\n <ef-password\n class=\"col-12\"\n name=\"currentPassword\"\n labelKey=\"ef_change_password_current\"\n autocomplete=\"current-password\"\n required\n (valueChangeEvent)=\"currentPassword.set($event)\"\n />\n }\n <ef-password\n class=\"col-6\"\n name=\"newPassword\"\n labelKey=\"ef_change_password_new\"\n autocomplete=\"new-password\"\n required\n (valueChangeEvent)=\"newPassword.set($event)\"\n />\n <ef-password\n class=\"col-6\"\n name=\"newPasswordConfirmation\"\n labelKey=\"ef_change_password_confirm\"\n autocomplete=\"new-password\"\n required\n (valueChangeEvent)=\"confirmation.set($event)\"\n />\n</ef-form-grid>\n\n@if (validationKeys().length || errors().length) {\n <ul class=\"ef-change-password__errors\">\n @for (key of validationKeys(); track $index) {\n <li>{{ key | translate }}</li>\n }\n <!-- Server messages arrive already translated (Identity returns prose,\n not keys), so they are printed verbatim. -->\n @for (error of errors(); track $index) {\n <li>{{ error }}</li>\n }\n </ul>\n}\n", styles: [":host{display:grid;gap:var(--s-2, 8px);min-width:0}.ef-change-password__errors{display:grid;gap:4px;margin:0;padding:0;list-style:none;min-width:0;font-size:12px;line-height:1.4;color:var(--st-cancelled-fg, #b42318)}\n"] }]
10200
+ }], propDecorators: { requireCurrent: [{ type: i0.Input, args: [{ isSignal: true, alias: "requireCurrent", required: false }] }], busy: [{ type: i0.Input, args: [{ isSignal: true, alias: "busy", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], minLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minLength", required: false }] }], submitted: [{ type: i0.Output, args: ["submitted"] }] } });
10201
+
9959
10202
  /**
9960
10203
  * Native Comptoir textarea — no PrimeNG wrapper. Pairs with the
9961
10204
  * `.ef-textarea` rule in `@elasticias/ui/styles/patterns` (focus
@@ -10788,7 +11031,7 @@ class EfEmailMetricsCardComponent {
10788
11031
  return 'warn';
10789
11032
  }
10790
11033
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfEmailMetricsCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10791
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfEmailMetricsCardComponent, isStandalone: true, selector: "ef-email-metrics-card", inputs: { metrics: { classPropertyName: "metrics", publicName: "metrics", isSignal: true, isRequired: false, transformFunction: null }, recentEvents: { classPropertyName: "recentEvents", publicName: "recentEvents", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rangeChange: "rangeChange" }, ngImport: i0, template: "<p-card styleClass=\"ef-email-metrics-card\">\n <ng-template pTemplate=\"header\">\n <div class=\"ef-email-metrics-card__header\">\n <div>\n <h3 class=\"ef-email-metrics-card__title\">{{ title() }}</h3>\n @if (metrics(); as m) {\n <p class=\"ef-email-metrics-card__subtitle\">\n {{ m.from | date: 'dd MMM' }} \u2013 {{ m.to | date: 'dd MMM yyyy' }}\n </p>\n }\n </div>\n <p-selectButton\n [options]=\"rangeOptions\"\n [(ngModel)]=\"selectedRange\"\n optionLabel=\"label\"\n optionValue=\"value\"\n styleClass=\"ef-email-metrics-card__range\"\n (onChange)=\"onRangeChange($event.value)\"\n />\n </div>\n </ng-template>\n\n @if (metrics(); as m) {\n <div class=\"ef-email-metrics-card__kpis\">\n @for (kpi of kpis(); track kpi.label) {\n <div class=\"ef-email-metrics-card__kpi\" [attr.data-tone]=\"kpi.tone\">\n <span class=\"ef-email-metrics-card__kpi-label\">{{ kpi.label }}</span>\n <span class=\"ef-email-metrics-card__kpi-value\">\n @if (kpi.rate) {\n {{ kpi.value | number: '1.1-1' : 'fr' }}\n <span class=\"ef-email-metrics-card__kpi-unit\">%</span>\n } @else {\n {{ kpi.value | number: '1.0-0' : 'fr' }}\n }\n </span>\n </div>\n }\n </div>\n\n @if (m.categories.length > 0) {\n <div class=\"ef-email-metrics-card__section\">\n <h4 class=\"ef-email-metrics-card__section-title\">Par cat\u00E9gorie</h4>\n <p-table [value]=\"m.categories\" styleClass=\"ef-email-metrics-card__table\" [scrollable]=\"true\" scrollHeight=\"180px\">\n <ng-template pTemplate=\"header\">\n <tr>\n <th>Cat\u00E9gorie</th>\n <th class=\"ef-email-metrics-card__num\">Envoy\u00E9s</th>\n <th class=\"ef-email-metrics-card__num\">Ouverts</th>\n <th class=\"ef-email-metrics-card__num\">Cliqu\u00E9s</th>\n <th class=\"ef-email-metrics-card__num\">Rebonds</th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-row>\n <tr>\n <td class=\"ef-email-metrics-card__category\">{{ row.category }}</td>\n <td class=\"ef-email-metrics-card__num\">{{ row.sent }}</td>\n <td class=\"ef-email-metrics-card__num\">{{ row.opened }}</td>\n <td class=\"ef-email-metrics-card__num\">{{ row.clicked }}</td>\n <td class=\"ef-email-metrics-card__num\" [class.ef-email-metrics-card__num--danger]=\"row.bounced > 0\">{{ row.bounced }}</td>\n </tr>\n </ng-template>\n </p-table>\n </div>\n }\n\n @if (recentEvents().length > 0) {\n <div class=\"ef-email-metrics-card__section\">\n <h4 class=\"ef-email-metrics-card__section-title\">\u00C9v\u00E9nements r\u00E9cents</h4>\n <p-table [value]=\"recentEvents()\" styleClass=\"ef-email-metrics-card__table\" [scrollable]=\"true\" scrollHeight=\"240px\">\n <ng-template pTemplate=\"header\">\n <tr>\n <th>Quand</th>\n <th>Destinataire</th>\n <th>Cat\u00E9gorie</th>\n <th>Type</th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-row>\n <tr>\n <td class=\"ef-email-metrics-card__when\">{{ row.occurredAt | date: 'dd/MM HH:mm' }}</td>\n <td class=\"ef-email-metrics-card__recipient\">{{ row.recipient }}</td>\n <td>{{ row.category || '\u2014' }}</td>\n <td>\n <p-tag [severity]=\"kindSeverity(row.kind)\" [value]=\"row.kind\" styleClass=\"ef-email-metrics-card__kind\" />\n </td>\n </tr>\n </ng-template>\n </p-table>\n </div>\n }\n } @else {\n <div class=\"ef-email-metrics-card__empty\">\n <p-button label=\"Aucune donn\u00E9e encore\" [text]=\"true\" icon=\"pi pi-envelope\" [disabled]=\"true\" />\n </div>\n }\n</p-card>\n", styles: [":host{display:block}.ef-email-metrics-card__header{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;padding:1rem 1.25rem 0;flex-wrap:wrap}.ef-email-metrics-card__title{margin:0;font-size:1rem;font-weight:600;letter-spacing:-.01em;color:var(--p-text-color)}.ef-email-metrics-card__subtitle{margin:.15rem 0 0;font-size:.8125rem;color:var(--p-text-muted-color)}.ef-email-metrics-card__range :global(.p-selectbutton){border-radius:999px}.ef-email-metrics-card__range :global(.p-selectbutton .p-button){border-radius:999px;padding:.25rem .75rem;font-size:.75rem}.ef-email-metrics-card__kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:.75rem;padding:1rem 0}.ef-email-metrics-card__kpi{display:flex;flex-direction:column;gap:.25rem;padding:.85rem 1rem;border-radius:12px;background:var(--p-surface-50, #f8fafc);border:1px solid var(--p-surface-200, #e2e8f0)}.ef-email-metrics-card__kpi[data-tone=good]{background:color-mix(in srgb,var(--p-green-500, #10b981) 8%,transparent);border-color:color-mix(in srgb,var(--p-green-500, #10b981) 25%,transparent)}.ef-email-metrics-card__kpi[data-tone=warn]{background:color-mix(in srgb,var(--p-orange-500, #f97316) 8%,transparent);border-color:color-mix(in srgb,var(--p-orange-500, #f97316) 25%,transparent)}.ef-email-metrics-card__kpi-label{font-size:.75rem;font-weight:500;text-transform:uppercase;letter-spacing:.06em;color:var(--p-text-muted-color)}.ef-email-metrics-card__kpi-value{font-size:1.5rem;font-weight:700;letter-spacing:-.02em;color:var(--p-text-color)}.ef-email-metrics-card__kpi-unit{font-size:.875rem;font-weight:500;margin-left:.125rem;color:var(--p-text-muted-color)}.ef-email-metrics-card__section{margin-top:.75rem;padding-top:.75rem;border-top:1px solid var(--p-surface-200, #e2e8f0)}.ef-email-metrics-card__section-title{margin:0 0 .5rem;font-size:.8125rem;font-weight:600;color:var(--p-text-color)}.ef-email-metrics-card__table{font-size:.8125rem}.ef-email-metrics-card__num{text-align:right;font-variant-numeric:tabular-nums}.ef-email-metrics-card__num--danger{color:var(--p-red-600, #dc2626);font-weight:600}.ef-email-metrics-card__category{text-transform:capitalize;font-weight:500}.ef-email-metrics-card__recipient{font-family:JetBrains Mono,ui-monospace,monospace;font-size:.75rem;color:var(--p-text-muted-color)}.ef-email-metrics-card__when{font-variant-numeric:tabular-nums;white-space:nowrap;font-size:.75rem}.ef-email-metrics-card__kind{border-radius:999px;padding:.1rem .6rem;font-size:.7rem;letter-spacing:.02em}.ef-email-metrics-card__empty{padding:2rem 1.25rem;text-align:center;color:var(--p-text-muted-color)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i5$1.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "directive", type: i2$3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i2$4.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i5$2.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SelectButtonModule }, { kind: "component", type: i1$f.SelectButton, selector: "p-selectButton, p-selectbutton, p-select-button", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "styleClass", "ariaLabelledBy", "dataKey", "autofocus", "size", "fluid"], outputs: ["onOptionClick", "onChange"] }, { kind: "pipe", type: i1$3.DecimalPipe, name: "number" }, { kind: "pipe", type: i1$3.DatePipe, name: "date" }] });
11034
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfEmailMetricsCardComponent, isStandalone: true, selector: "ef-email-metrics-card", inputs: { metrics: { classPropertyName: "metrics", publicName: "metrics", isSignal: true, isRequired: false, transformFunction: null }, recentEvents: { classPropertyName: "recentEvents", publicName: "recentEvents", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rangeChange: "rangeChange" }, ngImport: i0, template: "<p-card styleClass=\"ef-email-metrics-card\">\n <ng-template pTemplate=\"header\">\n <div class=\"ef-email-metrics-card__header\">\n <div>\n <h3 class=\"ef-email-metrics-card__title\">{{ title() }}</h3>\n @if (metrics(); as m) {\n <p class=\"ef-email-metrics-card__subtitle\">\n {{ m.from | date: 'dd MMM' }} \u2013 {{ m.to | date: 'dd MMM yyyy' }}\n </p>\n }\n </div>\n <p-selectButton\n [options]=\"rangeOptions\"\n [(ngModel)]=\"selectedRange\"\n optionLabel=\"label\"\n optionValue=\"value\"\n styleClass=\"ef-email-metrics-card__range\"\n (onChange)=\"onRangeChange($event.value)\"\n />\n </div>\n </ng-template>\n\n @if (metrics(); as m) {\n <div class=\"ef-email-metrics-card__kpis\">\n @for (kpi of kpis(); track kpi.label) {\n <div class=\"ef-email-metrics-card__kpi\" [attr.data-tone]=\"kpi.tone\">\n <span class=\"ef-email-metrics-card__kpi-label\">{{ kpi.label }}</span>\n <span class=\"ef-email-metrics-card__kpi-value\">\n @if (kpi.rate) {\n {{ kpi.value | number: '1.1-1' : 'fr' }}\n <span class=\"ef-email-metrics-card__kpi-unit\">%</span>\n } @else {\n {{ kpi.value | number: '1.0-0' : 'fr' }}\n }\n </span>\n </div>\n }\n </div>\n\n @if (m.categories.length > 0) {\n <div class=\"ef-email-metrics-card__section\">\n <h4 class=\"ef-email-metrics-card__section-title\">Par cat\u00E9gorie</h4>\n <p-table [value]=\"m.categories\" styleClass=\"ef-email-metrics-card__table\" [scrollable]=\"true\" scrollHeight=\"180px\">\n <ng-template pTemplate=\"header\">\n <tr>\n <th>Cat\u00E9gorie</th>\n <th class=\"ef-email-metrics-card__num\">Envoy\u00E9s</th>\n <th class=\"ef-email-metrics-card__num\">Ouverts</th>\n <th class=\"ef-email-metrics-card__num\">Cliqu\u00E9s</th>\n <th class=\"ef-email-metrics-card__num\">Rebonds</th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-row>\n <tr>\n <td class=\"ef-email-metrics-card__category\">{{ row.category }}</td>\n <td class=\"ef-email-metrics-card__num\">{{ row.sent }}</td>\n <td class=\"ef-email-metrics-card__num\">{{ row.opened }}</td>\n <td class=\"ef-email-metrics-card__num\">{{ row.clicked }}</td>\n <td class=\"ef-email-metrics-card__num\" [class.ef-email-metrics-card__num--danger]=\"row.bounced > 0\">{{ row.bounced }}</td>\n </tr>\n </ng-template>\n </p-table>\n </div>\n }\n\n @if (recentEvents().length > 0) {\n <div class=\"ef-email-metrics-card__section\">\n <h4 class=\"ef-email-metrics-card__section-title\">\u00C9v\u00E9nements r\u00E9cents</h4>\n <p-table [value]=\"recentEvents()\" styleClass=\"ef-email-metrics-card__table\" [scrollable]=\"true\" scrollHeight=\"240px\">\n <ng-template pTemplate=\"header\">\n <tr>\n <th>Quand</th>\n <th>Destinataire</th>\n <th>Cat\u00E9gorie</th>\n <th>Type</th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-row>\n <tr>\n <td class=\"ef-email-metrics-card__when\">{{ row.occurredAt | date: 'dd/MM HH:mm' }}</td>\n <td class=\"ef-email-metrics-card__recipient\">{{ row.recipient }}</td>\n <td>{{ row.category || '\u2014' }}</td>\n <td>\n <p-tag [severity]=\"kindSeverity(row.kind)\" [value]=\"row.kind\" styleClass=\"ef-email-metrics-card__kind\" />\n </td>\n </tr>\n </ng-template>\n </p-table>\n </div>\n }\n } @else {\n <div class=\"ef-email-metrics-card__empty\">\n <p-button label=\"Aucune donn\u00E9e encore\" [text]=\"true\" icon=\"pi pi-envelope\" [disabled]=\"true\" />\n </div>\n }\n</p-card>\n", styles: [":host{display:block}.ef-email-metrics-card__header{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;padding:1rem 1.25rem 0;flex-wrap:wrap}.ef-email-metrics-card__title{margin:0;font-size:1rem;font-weight:600;letter-spacing:-.01em;color:var(--p-text-color)}.ef-email-metrics-card__subtitle{margin:.15rem 0 0;font-size:.8125rem;color:var(--p-text-muted-color)}.ef-email-metrics-card__range :global(.p-selectbutton){border-radius:999px}.ef-email-metrics-card__range :global(.p-selectbutton .p-button){border-radius:999px;padding:.25rem .75rem;font-size:.75rem}.ef-email-metrics-card__kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:.75rem;padding:1rem 0}.ef-email-metrics-card__kpi{display:flex;flex-direction:column;gap:.25rem;padding:.85rem 1rem;border-radius:12px;background:var(--p-surface-50, #f8fafc);border:1px solid var(--p-surface-200, #e2e8f0)}.ef-email-metrics-card__kpi[data-tone=good]{background:color-mix(in srgb,var(--p-green-500, #10b981) 8%,transparent);border-color:color-mix(in srgb,var(--p-green-500, #10b981) 25%,transparent)}.ef-email-metrics-card__kpi[data-tone=warn]{background:color-mix(in srgb,var(--p-orange-500, #f97316) 8%,transparent);border-color:color-mix(in srgb,var(--p-orange-500, #f97316) 25%,transparent)}.ef-email-metrics-card__kpi-label{font-size:.75rem;font-weight:500;text-transform:uppercase;letter-spacing:.06em;color:var(--p-text-muted-color)}.ef-email-metrics-card__kpi-value{font-size:1.5rem;font-weight:700;letter-spacing:-.02em;color:var(--p-text-color)}.ef-email-metrics-card__kpi-unit{font-size:.875rem;font-weight:500;margin-left:.125rem;color:var(--p-text-muted-color)}.ef-email-metrics-card__section{margin-top:.75rem;padding-top:.75rem;border-top:1px solid var(--p-surface-200, #e2e8f0)}.ef-email-metrics-card__section-title{margin:0 0 .5rem;font-size:.8125rem;font-weight:600;color:var(--p-text-color)}.ef-email-metrics-card__table{font-size:.8125rem}.ef-email-metrics-card__num{text-align:right;font-variant-numeric:tabular-nums}.ef-email-metrics-card__num--danger{color:var(--p-red-600, #dc2626);font-weight:600}.ef-email-metrics-card__category{text-transform:capitalize;font-weight:500}.ef-email-metrics-card__recipient{font-family:JetBrains Mono,ui-monospace,monospace;font-size:.75rem;color:var(--p-text-muted-color)}.ef-email-metrics-card__when{font-variant-numeric:tabular-nums;white-space:nowrap;font-size:.75rem}.ef-email-metrics-card__kind{border-radius:999px;padding:.1rem .6rem;font-size:.7rem;letter-spacing:.02em}.ef-email-metrics-card__empty{padding:2rem 1.25rem;text-align:center;color:var(--p-text-muted-color)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i5$1.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "directive", type: i2$3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i2$5.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i5$2.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SelectButtonModule }, { kind: "component", type: i1$f.SelectButton, selector: "p-selectButton, p-selectbutton, p-select-button", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "styleClass", "ariaLabelledBy", "dataKey", "autofocus", "size", "fluid"], outputs: ["onOptionClick", "onChange"] }, { kind: "pipe", type: i1$3.DecimalPipe, name: "number" }, { kind: "pipe", type: i1$3.DatePipe, name: "date" }] });
10792
11035
  }
10793
11036
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfEmailMetricsCardComponent, decorators: [{
10794
11037
  type: Component,
@@ -10811,5 +11054,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
10811
11054
  * Generated bundle index. Do not edit.
10812
11055
  */
10813
11056
 
10814
- 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, 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, EfPagerComponent, EfPasswordComponent, EfPdfPreviewComponent, EfPillGroupComponent, EfPresetPillComponent, EfPriceDisplayComponent, EfProductCatalogueComponent, EfProductCatalogueFilterComponent, EfProductTypeaheadComponent, EfProfileChipComponent, EfPulseComponent, EfQuantitySelectorComponent, EfQuantityStepperComponent, EfRankListComponent, EfRowActionsComponent, EfSearchToolbarComponent, EfSelectButtonComponent, EfSelectComponent, EfServerErrorsDirective, EfSkeletonComponent, EfSmartBarComponent, EfStatsComponent, EfStatusChipComponent, EfTabPanelDirective, EfTabsComponent, EfTextareaComponent, EfThemeConfiguratorComponent, EfThemeToggleComponent, EfTimelineComponent, EfToastRegionComponent, EfToggleSwitchComponent, EfToolbarComponent, EfTooltipDirective, EfTotalsComponent, OrderEntityHelper, OrderLineItemHelper, TruncatePipe, buildChartConfig, getColumnAlignment, mergeColumnDefaults, resolveEfStatusColor };
11057
+ 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, EfPagerComponent, EfPasswordComponent, EfPdfPreviewComponent, EfPillGroupComponent, EfPresetPillComponent, EfPriceDisplayComponent, EfProductCatalogueComponent, EfProductCatalogueFilterComponent, EfProductTypeaheadComponent, EfProfileChipComponent, EfProfileMenuComponent, EfPulseComponent, EfQuantitySelectorComponent, EfQuantityStepperComponent, EfRankListComponent, EfRowActionsComponent, EfSearchToolbarComponent, EfSelectButtonComponent, EfSelectComponent, EfServerErrorsDirective, EfShortcutsDialogComponent, EfSkeletonComponent, EfSmartBarComponent, EfStatsComponent, EfStatusChipComponent, EfTabPanelDirective, EfTabsComponent, EfTextareaComponent, EfThemeConfiguratorComponent, EfThemeToggleComponent, EfTimelineComponent, EfToastRegionComponent, EfToggleSwitchComponent, EfToolbarComponent, EfTooltipDirective, EfTotalsComponent, OrderEntityHelper, OrderLineItemHelper, TruncatePipe, buildChartConfig, getColumnAlignment, mergeColumnDefaults, resolveEfStatusColor };
10815
11058
  //# sourceMappingURL=elasticias-ui.mjs.map