@elasticias/ui 1.0.10 → 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';
@@ -4876,6 +4878,14 @@ class EfAboutDialogComponent {
4876
4878
  product = input('', ...(ngDevMode ? [{ debugName: "product" }] : /* istanbul ignore next */ []));
4877
4879
  /** One muted line under the product — a tenant, a plan, an edition. */
4878
4880
  edition = input('', ...(ngDevMode ? [{ debugName: "edition" }] : /* istanbul ignore next */ []));
4881
+ /** Which organisation this deployment serves, as a labelled row. */
4882
+ organisation = input('', ...(ngDevMode ? [{ debugName: "organisation" }] : /* istanbul ignore next */ []));
4883
+ /** What the API reports about itself. Leave empty to hide the group. */
4884
+ server = input({}, ...(ngDevMode ? [{ debugName: "server" }] : /* istanbul ignore next */ []));
4885
+ /** Outbound link, already carrying whatever query the app wants on it. */
4886
+ websiteUrl = input('', ...(ngDevMode ? [{ debugName: "websiteUrl" }] : /* istanbul ignore next */ []));
4887
+ /** What the link reads as — a domain, not a sentence. */
4888
+ websiteLabel = input('', ...(ngDevMode ? [{ debugName: "websiteLabel" }] : /* istanbul ignore next */ []));
4879
4889
  /** Brand mark. A logo wins over the initial when both are set; empty
4880
4890
  * means "none", the same way `edition` and `initial` read. */
4881
4891
  logoUrl = input('', ...(ngDevMode ? [{ debugName: "logoUrl" }] : /* istanbul ignore next */ []));
@@ -4892,27 +4902,74 @@ class EfAboutDialogComponent {
4892
4902
  */
4893
4903
  rows = computed(() => {
4894
4904
  const i = this.info;
4895
- if (!i)
4896
- return [];
4897
4905
  return [
4898
- { labelKey: 'common_about_version', value: i.version ? `v${i.version}` : '' },
4899
- { labelKey: 'common_about_environment', value: i.environment },
4900
- { labelKey: 'common_about_branch', value: i.branch },
4901
- { labelKey: 'common_about_commit', value: i.commit },
4902
- { labelKey: 'common_about_date', value: i.date },
4906
+ { labelKey: 'common_about_organisation', value: this.organisation() },
4907
+ { labelKey: 'common_about_version', value: i?.version ? `v${i.version}` : '' },
4908
+ { labelKey: 'common_about_environment', value: i?.environment ?? '' },
4909
+ { labelKey: 'common_about_branch', value: i?.branch ?? '' },
4910
+ { labelKey: 'common_about_commit', value: i?.commit ?? '' },
4911
+ { labelKey: 'common_about_date', value: i?.date ?? '' },
4903
4912
  ].filter(row => !!row.value);
4904
4913
  }, ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
4914
+ /** The API's own identity, above the assemblies it loaded. */
4915
+ serverRows = computed(() => {
4916
+ const s = this.server();
4917
+ return [
4918
+ { labelKey: 'common_about_environment', value: s.environment ?? '' },
4919
+ { labelKey: 'common_about_runtime', value: s.runtime ?? '' },
4920
+ { labelKey: 'common_about_version', value: s.version ?? '' },
4921
+ ].filter(row => !!row.value);
4922
+ }, ...(ngDevMode ? [{ debugName: "serverRows" }] : /* istanbul ignore next */ []));
4923
+ serverPackages = computed(() => this.server().packages ?? [], ...(ngDevMode ? [{ debugName: "serverPackages" }] : /* istanbul ignore next */ []));
4924
+ hasServer = computed(() => this.serverRows().length > 0 || this.serverPackages().length > 0, ...(ngDevMode ? [{ debugName: "hasServer" }] : /* istanbul ignore next */ []));
4905
4925
  /** A footer button, not only the header ✕: a thumb needs a real target. */
4906
4926
  closeAction = [
4907
4927
  { labelKey: 'common_close', severity: 'ghost' },
4908
4928
  ];
4909
4929
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfAboutDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4910
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfAboutDialogComponent, isStandalone: true, selector: "ef-about-dialog", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, product: { classPropertyName: "product", publicName: "product", isSignal: true, isRequired: false, transformFunction: null }, edition: { classPropertyName: "edition", publicName: "edition", isSignal: true, isRequired: false, transformFunction: null }, logoUrl: { classPropertyName: "logoUrl", publicName: "logoUrl", isSignal: true, isRequired: false, transformFunction: null }, initial: { classPropertyName: "initial", publicName: "initial", isSignal: true, isRequired: false, transformFunction: null }, owner: { classPropertyName: "owner", publicName: "owner", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { visible: "visibleChange" }, ngImport: i0, 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--scroll\">\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 <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--scroll{max-height:40vh;overflow-y:auto;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__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__rows--scroll{max-height:32vh}}\n"], dependencies: [{ 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 });
4930
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: EfAboutDialogComponent, isStandalone: true, selector: "ef-about-dialog", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, product: { classPropertyName: "product", publicName: "product", isSignal: true, isRequired: false, transformFunction: null }, edition: { classPropertyName: "edition", publicName: "edition", isSignal: true, isRequired: false, transformFunction: null }, organisation: { classPropertyName: "organisation", publicName: "organisation", isSignal: true, isRequired: false, transformFunction: null }, server: { classPropertyName: "server", publicName: "server", isSignal: true, isRequired: false, transformFunction: null }, websiteUrl: { classPropertyName: "websiteUrl", publicName: "websiteUrl", isSignal: true, isRequired: false, transformFunction: null }, websiteLabel: { classPropertyName: "websiteLabel", publicName: "websiteLabel", isSignal: true, isRequired: false, transformFunction: null }, logoUrl: { classPropertyName: "logoUrl", publicName: "logoUrl", isSignal: true, isRequired: false, transformFunction: null }, initial: { classPropertyName: "initial", publicName: "initial", isSignal: true, isRequired: false, transformFunction: null }, owner: { classPropertyName: "owner", publicName: "owner", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { visible: "visibleChange" }, ngImport: i0, 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"], dependencies: [{ 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 });
4911
4931
  }
4912
4932
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfAboutDialogComponent, decorators: [{
4913
4933
  type: Component,
4914
- 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--scroll\">\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 <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--scroll{max-height:40vh;overflow-y:auto;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__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__rows--scroll{max-height:32vh}}\n"] }]
4915
- }], 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 }] }], 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
+ 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"] }]
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 }] }] } });
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"] }] } });
4916
4973
 
4917
4974
  class EfFieldsetComponent {
4918
4975
  /** Direct legend text (not translated) */
@@ -6189,6 +6246,72 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
6189
6246
  type: Output
6190
6247
  }] } });
6191
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
+
6192
6315
  class EfBlockUiComponent {
6193
6316
  isLoading = false;
6194
6317
  contentPanel;
@@ -8668,7 +8791,7 @@ class EfDatatableComponent {
8668
8791
  return this.pTable?.filters;
8669
8792
  }
8670
8793
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfDatatableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8671
- 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 });
8672
8795
  }
8673
8796
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfDatatableComponent, decorators: [{
8674
8797
  type: Component,
@@ -8797,6 +8920,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
8797
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" }]
8798
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"] }] } });
8799
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
+ ];
8800
8932
  /**
8801
8933
  * Row-actions dropdown — V2 successor to `ef-datatable-actionbar`.
8802
8934
  *
@@ -8805,11 +8937,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
8805
8937
  *
8806
8938
  * ```ts
8807
8939
  * actions: ReadonlyArray<EfRowAction> = [
8808
- * { id: 'view', labelKey: 'common_view', icon: 'pi pi-eye', kbd: '',
8940
+ * { id: 'view', labelKey: 'common_view', icon: 'pi pi-eye', kbd: formatShortcut('enter'),
8809
8941
  * permission: Permissions.Read, command: () => view(row) },
8810
- * { id: 'edit', labelKey: 'common_edit', icon: 'pi pi-pencil', kbd: 'E',
8942
+ * { id: 'edit', labelKey: 'common_edit', icon: 'pi pi-pencil', kbd: formatShortcut('e'),
8811
8943
  * permission: Permissions.Edit, command: () => edit(row) },
8812
- * { id: 'duplicate', labelKey: 'common_duplicate', icon: 'pi pi-copy',
8944
+ * { id: 'duplicate', labelKey: 'common_duplicate', icon: 'pi pi-copy', kbd: formatShortcut('mod+d'),
8813
8945
  * command: () => duplicate(row) },
8814
8946
  * { separator: true },
8815
8947
  * { id: 'delete', labelKey: 'common_delete', icon: 'pi pi-trash', severity: 'danger',
@@ -8835,9 +8967,50 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
8835
8967
  * Permission filtering: pass `[context]="..."` (a `ScreenContext`); items
8836
8968
  * declaring a `permission` are hidden unless that permission is granted.
8837
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.
8838
8977
  */
8839
8978
  class EfRowActionsComponent {
8840
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
+ }
8841
9014
  /** Action descriptors. */
8842
9015
  items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
8843
9016
  /** Optional context — when present, items with a `permission` are
@@ -8991,7 +9164,7 @@ class EfRowActionsComponent {
8991
9164
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfRowActionsComponent, decorators: [{
8992
9165
  type: Component,
8993
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"] }]
8994
- }], 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: [{
8995
9168
  type: HostListener,
8996
9169
  args: ['document:click', ['$event']]
8997
9170
  }], onEscape: [{
@@ -9304,7 +9477,11 @@ class EfDataCardComponent {
9304
9477
  /** Items array for the auto row-actions cell. Rebuilt per call so
9305
9478
  * the ef-row-actions component receives a fresh closure per row.
9306
9479
  * Visibility is filtered later by ef-row-actions against
9307
- * `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. */
9308
9485
  defaultRowActions(row) {
9309
9486
  const items = [];
9310
9487
  if (this.showViewAction()) {
@@ -9312,7 +9489,7 @@ class EfDataCardComponent {
9312
9489
  id: 'view',
9313
9490
  labelKey: 'common_view',
9314
9491
  icon: 'pi pi-eye',
9315
- kbd: '',
9492
+ kbd: formatShortcut('enter'),
9316
9493
  permission: Permissions.Read,
9317
9494
  command: () => this.rowAction.emit({ action: 'view', row }),
9318
9495
  });
@@ -9322,7 +9499,7 @@ class EfDataCardComponent {
9322
9499
  id: 'edit',
9323
9500
  labelKey: 'common_edit',
9324
9501
  icon: 'pi pi-pencil',
9325
- kbd: 'E',
9502
+ kbd: formatShortcut('e'),
9326
9503
  permission: Permissions.Edit,
9327
9504
  command: () => this.rowAction.emit({ action: 'edit', row }),
9328
9505
  });
@@ -9332,7 +9509,7 @@ class EfDataCardComponent {
9332
9509
  id: 'duplicate',
9333
9510
  labelKey: 'common_duplicate',
9334
9511
  icon: 'pi pi-copy',
9335
- kbd: '⌘D',
9512
+ kbd: formatShortcut('mod+d'),
9336
9513
  permission: Permissions.Duplicate,
9337
9514
  command: () => this.rowAction.emit({ action: 'duplicate', row }),
9338
9515
  });
@@ -9938,6 +10115,90 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
9938
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" }]
9939
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"] }] } });
9940
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
+
9941
10202
  /**
9942
10203
  * Native Comptoir textarea — no PrimeNG wrapper. Pairs with the
9943
10204
  * `.ef-textarea` rule in `@elasticias/ui/styles/patterns` (focus
@@ -10770,7 +11031,7 @@ class EfEmailMetricsCardComponent {
10770
11031
  return 'warn';
10771
11032
  }
10772
11033
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfEmailMetricsCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10773
- 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" }] });
10774
11035
  }
10775
11036
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: EfEmailMetricsCardComponent, decorators: [{
10776
11037
  type: Component,
@@ -10793,5 +11054,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
10793
11054
  * Generated bundle index. Do not edit.
10794
11055
  */
10795
11056
 
10796
- 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 };
10797
11058
  //# sourceMappingURL=elasticias-ui.mjs.map