@masterteam/client-components 0.0.83 → 0.0.84
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, Injectable, signal, computed, input, output, Component, viewChild, effect, untracked, ChangeDetectionStrategy
|
|
2
|
+
import { inject, Injectable, signal, computed, input, output, Component, viewChild, effect, untracked, ChangeDetectionStrategy } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common';
|
|
4
4
|
import { CommonModule } from '@angular/common';
|
|
5
5
|
import * as i4 from '@jsverse/transloco';
|
|
@@ -22,9 +22,6 @@ import { MTDateFormatPipe, readableTextOn } from '@masterteam/components';
|
|
|
22
22
|
import * as i3 from 'primeng/popover';
|
|
23
23
|
import { PopoverModule } from 'primeng/popover';
|
|
24
24
|
import { DashboardViewer } from '@masterteam/dashboard-builder';
|
|
25
|
-
import * as i1$1 from '@angular/forms';
|
|
26
|
-
import { FormsModule } from '@angular/forms';
|
|
27
|
-
import { TextField } from '@masterteam/components/text-field';
|
|
28
25
|
|
|
29
26
|
const DEFAULT_INCLUDE_STATE = ['escalation'];
|
|
30
27
|
class ClientListApiService {
|
|
@@ -895,11 +892,11 @@ class ClientListTableView {
|
|
|
895
892
|
};
|
|
896
893
|
}
|
|
897
894
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientListTableView, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
898
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ClientListTableView, isStandalone: true, selector: "mt-client-list-table-view", inputs: { state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: true, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, actionShape: { classPropertyName: "actionShape", publicName: "actionShape", isSignal: true, isRequired: false, transformFunction: null }, rowActionsLoadingFn: { classPropertyName: "rowActionsLoadingFn", publicName: "rowActionsLoadingFn", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { lazyLoad: "lazyLoad", rowClick: "rowClick", rowActionsRequested: "rowActionsRequested" }, ngImport: i0, template: "<div class=\"grid gap-4\" [style.gridTemplateColumns]=\"gridTemplateColumns\">\
|
|
895
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ClientListTableView, isStandalone: true, selector: "mt-client-list-table-view", inputs: { state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: true, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, actionShape: { classPropertyName: "actionShape", publicName: "actionShape", isSignal: true, isRequired: false, transformFunction: null }, rowActionsLoadingFn: { classPropertyName: "rowActionsLoadingFn", publicName: "rowActionsLoadingFn", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { lazyLoad: "lazyLoad", rowClick: "rowClick", rowActionsRequested: "rowActionsRequested" }, ngImport: i0, template: "<div class=\"grid gap-4\" [style.gridTemplateColumns]=\"gridTemplateColumns\">\n @if (table().config.contentStart) {\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.startSpan)\">\n <ng-container\n [ngTemplateOutlet]=\"table().config.contentStart\"\n [ngTemplateOutletContext]=\"templateContext(table())\"\n />\n </div>\n }\n\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.tableSpan)\">\n @if (table().loading && table().rows.length === 0) {\n <div class=\"flex flex-col gap-3 py-3\">\n <p-skeleton height=\"3rem\" />\n <p-skeleton height=\"3rem\" />\n <p-skeleton height=\"3rem\" />\n </div>\n } @else if (table().error) {\n <div\n class=\"rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-700\"\n >\n {{ table().error }}\n </div>\n } @else {\n <mt-table\n [noCard]=\"true\"\n [data]=\"table().rows\"\n [columns]=\"table().columns\"\n dataKey=\"Id\"\n [storageKey]=\"tableStorageKey()\"\n [persistStateExclude]=\"table().config.table.persistStateExclude\"\n [captionStartTemplate]=\"table().config.toolbarStart ?? null\"\n [captionEndTemplate]=\"table().config.toolbarEnd ?? null\"\n [rowActions]=\"rowActions()\"\n [actionShape]=\"actionShape()\"\n [rowActionsLoadingFn]=\"rowActionsLoadingFn()\"\n (rowActionsRequested)=\"rowActionsRequested.emit($event)\"\n [generalSearch]=\"table().config.table.searchable\"\n [showFilters]=\"true\"\n filterMode=\"column\"\n [printable]=\"true\"\n [groupable]=\"true\"\n [cellClickFilter]=\"true\"\n [freezeActions]=\"true\"\n [exportable]=\"table().config.table.exportable\"\n [loading]=\"table().loading\"\n [clickableRows]=\"true\"\n [lazy]=\"table().config.isPaginated\"\n [lazyTotalRecords]=\"table().totalCount\"\n [virtualScroll]=\"!table().config.isPaginated\"\n [virtualScrollItemSize]=\"56\"\n scrollHeight=\"520px\"\n [pageSize]=\"pageSize()\"\n [filters]=\"bucket().filters()\"\n (filtersChange)=\"bucket().filters.set($event)\"\n [filterTerm]=\"bucket().filterTerm()\"\n (filterTermChange)=\"bucket().filterTerm.set($event)\"\n [groupBy]=\"bucket().groupBy()\"\n (groupByChange)=\"bucket().groupBy.set($event)\"\n [activeTab]=\"bucket().activeTab()\"\n (activeTabChange)=\"bucket().activeTab.set($event)\"\n (lazyLoad)=\"lazyLoad.emit($event)\"\n (rowClick)=\"rowClick.emit($event)\"\n />\n }\n </div>\n\n @if (table().config.contentEnd) {\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.endSpan)\">\n <ng-container\n [ngTemplateOutlet]=\"table().config.contentEnd\"\n [ngTemplateOutletContext]=\"templateContext(table())\"\n />\n </div>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "filterMode", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "persistStateExclude", "exportable", "printable", "groupable", "groupCountMap", "cellClickFilter", "freezeActions", "virtualScroll", "virtualScrollItemSize", "scrollHeight", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "captionStartTemplate", "captionEndTemplate", "emptyTitle", "emptyDescription", "emptyActionLabel", "emptyActionIcon", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy", "sortField", "sortDirection"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "emptyAction", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange", "sortFieldChange", "sortDirectionChange"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }] });
|
|
899
896
|
}
|
|
900
897
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientListTableView, decorators: [{
|
|
901
898
|
type: Component,
|
|
902
|
-
args: [{ selector: 'mt-client-list-table-view', standalone: true, imports: [CommonModule, Table, SkeletonModule], template: "<div class=\"grid gap-4\" [style.gridTemplateColumns]=\"gridTemplateColumns\">\
|
|
899
|
+
args: [{ selector: 'mt-client-list-table-view', standalone: true, imports: [CommonModule, Table, SkeletonModule], template: "<div class=\"grid gap-4\" [style.gridTemplateColumns]=\"gridTemplateColumns\">\n @if (table().config.contentStart) {\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.startSpan)\">\n <ng-container\n [ngTemplateOutlet]=\"table().config.contentStart\"\n [ngTemplateOutletContext]=\"templateContext(table())\"\n />\n </div>\n }\n\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.tableSpan)\">\n @if (table().loading && table().rows.length === 0) {\n <div class=\"flex flex-col gap-3 py-3\">\n <p-skeleton height=\"3rem\" />\n <p-skeleton height=\"3rem\" />\n <p-skeleton height=\"3rem\" />\n </div>\n } @else if (table().error) {\n <div\n class=\"rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-700\"\n >\n {{ table().error }}\n </div>\n } @else {\n <mt-table\n [noCard]=\"true\"\n [data]=\"table().rows\"\n [columns]=\"table().columns\"\n dataKey=\"Id\"\n [storageKey]=\"tableStorageKey()\"\n [persistStateExclude]=\"table().config.table.persistStateExclude\"\n [captionStartTemplate]=\"table().config.toolbarStart ?? null\"\n [captionEndTemplate]=\"table().config.toolbarEnd ?? null\"\n [rowActions]=\"rowActions()\"\n [actionShape]=\"actionShape()\"\n [rowActionsLoadingFn]=\"rowActionsLoadingFn()\"\n (rowActionsRequested)=\"rowActionsRequested.emit($event)\"\n [generalSearch]=\"table().config.table.searchable\"\n [showFilters]=\"true\"\n filterMode=\"column\"\n [printable]=\"true\"\n [groupable]=\"true\"\n [cellClickFilter]=\"true\"\n [freezeActions]=\"true\"\n [exportable]=\"table().config.table.exportable\"\n [loading]=\"table().loading\"\n [clickableRows]=\"true\"\n [lazy]=\"table().config.isPaginated\"\n [lazyTotalRecords]=\"table().totalCount\"\n [virtualScroll]=\"!table().config.isPaginated\"\n [virtualScrollItemSize]=\"56\"\n scrollHeight=\"520px\"\n [pageSize]=\"pageSize()\"\n [filters]=\"bucket().filters()\"\n (filtersChange)=\"bucket().filters.set($event)\"\n [filterTerm]=\"bucket().filterTerm()\"\n (filterTermChange)=\"bucket().filterTerm.set($event)\"\n [groupBy]=\"bucket().groupBy()\"\n (groupByChange)=\"bucket().groupBy.set($event)\"\n [activeTab]=\"bucket().activeTab()\"\n (activeTabChange)=\"bucket().activeTab.set($event)\"\n (lazyLoad)=\"lazyLoad.emit($event)\"\n (rowClick)=\"rowClick.emit($event)\"\n />\n }\n </div>\n\n @if (table().config.contentEnd) {\n <div [style.gridColumn]=\"slotGridSpan(table().config.layout.endSpan)\">\n <ng-container\n [ngTemplateOutlet]=\"table().config.contentEnd\"\n [ngTemplateOutletContext]=\"templateContext(table())\"\n />\n </div>\n }\n</div>\n" }]
|
|
903
900
|
}], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }], rowActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActions", required: false }] }], actionShape: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionShape", required: false }] }], rowActionsLoadingFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActionsLoadingFn", required: false }] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], rowActionsRequested: [{ type: i0.Output, args: ["rowActionsRequested"] }] } });
|
|
904
901
|
|
|
905
902
|
const EMPTY_KEY_SET = new Set();
|
|
@@ -1621,7 +1618,7 @@ class ClientListCardsView {
|
|
|
1621
1618
|
});
|
|
1622
1619
|
}
|
|
1623
1620
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientListCardsView, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1624
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ClientListCardsView, isStandalone: true, selector: "mt-client-list-cards-view", inputs: { state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: true, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, rowActionsLoadingFn: { classPropertyName: "rowActionsLoadingFn", publicName: "rowActionsLoadingFn", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { lazyLoad: "lazyLoad", loadMore: "loadMore", cardClick: "cardClick", rowActionsRequested: "rowActionsRequested" }, providers: [MTDateFormatPipe], viewQueries: [{ propertyName: "loadMoreSentinel", first: true, predicate: ["loadMoreSentinel"], descendants: true, isSignal: true }], ngImport: i0, template: "<mt-table-caption\r\n [generalSearch]=\"true\"\r\n [showFilters]=\"true\"\r\n filterMode=\"popover\"\r\n [printable]=\"true\"\r\n [groupable]=\"true\"\r\n [exportable]=\"true\"\r\n [columns]=\"columns()\"\r\n [data]=\"rowRepresentations()\"\r\n [groupColumns]=\"grouping.groupableColumns()\"\r\n [actions]=\"toolbarActions()\"\r\n [filters]=\"bucket().filters()\"\r\n (filtersChange)=\"bucket().filters.set($event)\"\r\n [filterTerm]=\"bucket().filterTerm()\"\r\n (filterTermChange)=\"bucket().filterTerm.set($event)\"\r\n [groupBy]=\"bucket().groupBy()\"\r\n (groupByChange)=\"bucket().groupBy.set($event)\"\r\n [activeTab]=\"bucket().activeTab()\"\r\n (activeTabChange)=\"bucket().activeTab.set($event)\"\r\n (exportRequested)=\"exportExcel()\"\r\n (printRequested)=\"printPdf()\"\r\n/>\r\n\r\n@if (badgeLegend().length > 0) {\r\n <!--\r\n Decodes the coloured count pills on the cards below. Rendered once for the\r\n whole board rather than per card, so it costs one line of vertical space\r\n instead of repeating a key inside every tile (finding M-04).\r\n -->\r\n <div\r\n class=\"mb-3 flex flex-wrap items-center gap-x-4 gap-y-2 px-1\"\r\n role=\"list\"\r\n [attr.aria-label]=\"\r\n 'components.clientComponents.list.badgeLegend' | transloco\r\n \"\r\n >\r\n <span class=\"text-xs font-medium text-gray-500 dark:text-gray-400\">\r\n {{ \"components.clientComponents.list.badgeLegend\" | transloco }}\r\n </span>\r\n @for (entry of badgeLegend(); track entry.key) {\r\n <span class=\"flex items-center gap-1.5\" role=\"listitem\">\r\n <span\r\n class=\"inline-flex h-4 min-w-4 items-center justify-center rounded text-[10px] font-bold\"\r\n [style.background-color]=\"entry.color\"\r\n [style.color]=\"entry.textColor\"\r\n aria-hidden=\"true\"\r\n >\r\n #\r\n </span>\r\n <span class=\"text-xs text-gray-600 dark:text-gray-300\">{{\r\n entry.label\r\n }}</span>\r\n </span>\r\n }\r\n </div>\r\n}\r\n\r\n@if (cardsState().loading && cardsState().cards.length === 0) {\r\n <div class=\"mt-cards-grid\">\r\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\r\n <p-skeleton height=\"20rem\" class=\"rounded-lg\" />\r\n }\r\n </div>\r\n} @else if (cardsState().error) {\r\n <div class=\"rounded-lg border border-red-200 bg-red-50 p-6 text-red-600\">\r\n {{ cardsState().error }}\r\n </div>\r\n} @else if (visibleCards().length === 0) {\r\n <!--\r\n The same empty state the tables use, so a board and a list stop looking like\r\n two products (finding H-12). A board narrowed by a search or a filter says\r\n so and offers to clear it, instead of the bare grey \"No cards found.\"\r\n -->\r\n <mt-empty-state\r\n [variant]=\"hasActiveCardFilters() ? 'filtered' : 'empty'\"\r\n [title]=\"\r\n hasActiveCardFilters()\r\n ? ''\r\n : ('components.clientComponents.list.noCardsFound' | transloco)\r\n \"\r\n [actionLabel]=\"\r\n hasActiveCardFilters()\r\n ? ('components.emptyState.clearFilters' | transloco)\r\n : ''\r\n \"\r\n actionIcon=\"general.x-close\"\r\n (actionClick)=\"clearCardFilters()\"\r\n />\r\n} @else if (grouping.groupingActive()) {\r\n <div class=\"flex flex-col gap-6\">\r\n @for (group of groupedCards(); track group.key) {\r\n <section class=\"flex flex-col gap-3\">\r\n <header\r\n class=\"flex items-center gap-3 px-2 py-2 bg-surface-50 dark:bg-surface-900 border-y border-surface-200 dark:border-surface-700 rounded\"\r\n [style.--mt-group-accent]=\"group.accent ?? 'var(--p-primary-color)'\"\r\n >\r\n <span\r\n class=\"inline-block w-1 h-5 rounded-full\"\r\n [style.background]=\"'var(--mt-group-accent)'\"\r\n ></span>\r\n <span\r\n class=\"text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400\"\r\n >\r\n {{ group.columnLabel }}\r\n </span>\r\n <span class=\"text-sm font-semibold text-gray-900 dark:text-gray-100\">\r\n {{ group.label }}\r\n </span>\r\n <span\r\n class=\"ml-auto inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-primary-50 text-primary-700 dark:bg-primary-950 dark:text-primary-300\"\r\n >\r\n {{ group.count }}\r\n </span>\r\n </header>\r\n <div class=\"mt-cards-grid\">\r\n @for (card of group.cards; track card.id) {\r\n <mt-card\r\n class=\"relative cursor-pointer p-5 shadow-sm transition-shadow hover:shadow-md\"\r\n (click)=\"cardClick.emit(card)\"\r\n >\r\n <ng-template #headless>\r\n <ng-container\r\n [ngTemplateOutlet]=\"cardActionsMenu\"\r\n [ngTemplateOutletContext]=\"{ $implicit: card }\"\r\n />\r\n <mt-entities-preview\r\n [entities]=\"card.entities\"\r\n attachmentShape=\"compact\"\r\n [stackOnNarrow]=\"false\"\r\n [clickableKeys]=\"clickableEntityKeys()\"\r\n [activeKeys]=\"activeKeysForCard(card)\"\r\n [clickableTooltip]=\"filterByThisLabel()\"\r\n [activeTooltip]=\"clearFilterLabel()\"\r\n (entityClick)=\"onEntityFilterClick(card, $event)\"\r\n />\r\n </ng-template>\r\n </mt-card>\r\n }\r\n </div>\r\n </section>\r\n }\r\n </div>\r\n} @else {\r\n <div class=\"mt-cards-grid\">\r\n @for (card of visibleCards(); track card.id) {\r\n @defer (on viewport) {\r\n <mt-card\r\n class=\"relative cursor-pointer p-5 shadow-sm transition-shadow hover:shadow-md\"\r\n (click)=\"cardClick.emit(card)\"\r\n >\r\n <ng-template #headless>\r\n <ng-container\r\n [ngTemplateOutlet]=\"cardActionsMenu\"\r\n [ngTemplateOutletContext]=\"{ $implicit: card }\"\r\n />\r\n <mt-entities-preview\r\n [entities]=\"card.entities\"\r\n attachmentShape=\"compact\"\r\n [stackOnNarrow]=\"false\"\r\n [clickableKeys]=\"clickableEntityKeys()\"\r\n [activeKeys]=\"activeKeysForCard(card)\"\r\n [clickableTooltip]=\"filterByThisLabel()\"\r\n [activeTooltip]=\"clearFilterLabel()\"\r\n (entityClick)=\"onEntityFilterClick(card, $event)\"\r\n />\r\n </ng-template>\r\n </mt-card>\r\n } @placeholder {\r\n <div class=\"min-h-[20rem]\">\r\n <p-skeleton height=\"20rem\" class=\"rounded-lg\" />\r\n </div>\r\n }\r\n }\r\n </div>\r\n}\r\n\r\n<!--\r\n Infinite-scroll trigger. Deliberately outside the @if/@else chain above:\r\n inside it the element would be torn down and rebuilt every time the view\r\n swapped branch (error, empty filter result, group-by), and the observer\r\n would be left watching a detached node. Kept in the DOM and hidden via CSS\r\n when there is nothing left to load, so appending a page never recreates it.\r\n It also has to sit outside so infinite scroll keeps working while a\r\n group-by is active \u2014 grouping is a pure FE view over whatever has been\r\n fetched, not a reason to stop fetching.\r\n-->\r\n<div\r\n #loadMoreSentinel\r\n class=\"mt-cards-grid py-4\"\r\n [class.hidden]=\"!hasMoreCards()\"\r\n>\r\n @for (i of [1, 2, 3]; track i) {\r\n <p-skeleton height=\"20rem\" class=\"rounded-lg\" />\r\n }\r\n</div>\r\n\r\n<!--\r\n Per-card escalation marker and 3-dots actions menu. The compact corner cover\r\n only indicates active escalation state; its preview action lives in the menu\r\n so the marker never displaces or competes with the menu trigger.\r\n-->\r\n<ng-template #cardActionsMenu let-card>\r\n @let escalationAction = escalationMarkerAction(card);\r\n @if (escalationAction) {\r\n <span\r\n class=\"mt-escalation-marker absolute end-0 top-0 z-10 h-[18px] w-[20px]\"\r\n role=\"img\"\r\n tabindex=\"0\"\r\n [attr.aria-label]=\"escalationLabel()\"\r\n [mtTooltip]=\"escalationLabel()\"\r\n tooltipPosition=\"top\"\r\n ></span>\r\n }\r\n @if (rowActions().length > 0) {\r\n <div\r\n class=\"absolute end-2 top-2 z-20 flex items-center gap-1\"\r\n (click)=\"$event.stopPropagation()\"\r\n >\r\n @for (action of inlineActionsForCard(card); track $index) {\r\n <mt-button\r\n [icon]=\"action.icon\"\r\n [severity]=\"action.color\"\r\n [variant]=\"action.variant\"\r\n [size]=\"actionSize(action) || 'small'\"\r\n [tooltip]=\"action.tooltip\"\r\n [label]=\"action.label\"\r\n [loading]=\"resolveActionLoading(action, card)\"\r\n (onClick)=\"runCardAction($event, action, card)\"\r\n />\r\n }\r\n @if (showCardActionsMenu(card)) {\r\n <button\r\n type=\"button\"\r\n class=\"flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent p-0 text-base text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 dark:text-gray-300 dark:hover:bg-surface-700 dark:hover:text-gray-100\"\r\n [attr.aria-label]=\"actionsLabel()\"\r\n [mtTooltip]=\"actionsLabel()\"\r\n tooltipPosition=\"top\"\r\n (click)=\"onCardActionsToggle($event, cardPopover, card)\"\r\n >\r\n <mt-icon icon=\"general.dots-vertical\" class=\"text-base\" />\r\n </button>\r\n <p-popover #cardPopover appendTo=\"body\">\r\n <div class=\"flex min-w-44 flex-col gap-1 p-1\">\r\n @if (isCardActionsLoading(card)) {\r\n <div class=\"flex flex-col gap-2 p-1\">\r\n <p-skeleton height=\"1.75rem\" />\r\n <p-skeleton height=\"1.75rem\" />\r\n <p-skeleton height=\"1.75rem\" />\r\n </div>\r\n } @else {\r\n @for (action of menuActionsForCard(card); track $index) {\r\n @if (isFirstDestructiveCardAction(card, $index)) {\r\n <hr\r\n class=\"my-1 border-0 border-t border-surface-200 dark:border-surface-700\"\r\n />\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"flex cursor-pointer items-center gap-2 rounded px-2 py-2 text-start text-sm transition-colors\"\r\n [class]=\"\r\n action.color === 'danger'\r\n ? 'text-red-600 hover:bg-red-50 dark:hover:bg-red-950'\r\n : action.color === 'warn'\r\n ? 'text-orange-600 hover:bg-orange-50 dark:text-orange-400 dark:hover:bg-orange-950'\r\n : 'text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-surface-700'\r\n \"\r\n (click)=\"\r\n runCardAction($event, action, card); cardPopover.hide()\r\n \"\r\n >\r\n @if (action.icon) {\r\n <mt-icon [icon]=\"action.icon\" class=\"text-base\" />\r\n }\r\n <span>{{ action.label || action.tooltip }}</span>\r\n </button>\r\n }\r\n }\r\n </div>\r\n </p-popover>\r\n }\r\n </div>\r\n }\r\n</ng-template>\r\n", styles: [".mt-cards-grid{display:grid;gap:1.25rem;grid-template-columns:repeat(auto-fill,minmax(min(340px,100%),1fr))}.mt-cards-grid.hidden,.mt-cards-grid[hidden]{display:none}.mt-escalation-marker{border-start-end-radius:.5rem;background:var(--p-button-text-warn-color, var(--p-orange-500, #f59e0b));clip-path:polygon(0 0,100% 0,100% 100%);transition:filter .15s ease}.mt-escalation-marker:dir(rtl){clip-path:polygon(0 0,100% 0,0 100%)}.mt-escalation-marker:hover{filter:brightness(.92)}.mt-escalation-marker:focus-visible{filter:brightness(.92) drop-shadow(0 0 2px var(--p-button-text-warn-color, #f59e0b));outline:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs", "ariaLabel"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: Tooltip, selector: "[mtTooltip]" }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: EntitiesPreview, selector: "mt-entities-preview", inputs: ["entities", "attachmentShape", "stackOnNarrow", "clickableKeys", "activeKeys", "clickableTooltip", "activeTooltip"], outputs: ["entityClick"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i3.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: TableCaption, selector: "mt-table-caption", inputs: ["generalSearch", "showFilters", "filterMode", "exportable", "printable", "groupable", "columns", "data", "groupColumns", "tabs", "tabsOptionLabel", "tabsOptionValue", "actions", "captionStart", "captionEnd", "activeTab", "filters", "filterTerm", "groupBy"], outputs: ["activeTabChange", "filtersChange", "filterTermChange", "groupByChange", "exportRequested", "printRequested", "onTabChange", "searchChange", "filterApplied", "filterReset"] }, { kind: "component", type: EmptyState, selector: "mt-empty-state", inputs: ["variant", "illustration", "title", "description", "actionLabel", "actionIcon"], outputs: ["actionClick"] }, { kind: "pipe", type: i4.TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [i1.NgTemplateOutlet, Card,
|
|
1621
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ClientListCardsView, isStandalone: true, selector: "mt-client-list-cards-view", inputs: { state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: true, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, rowActionsLoadingFn: { classPropertyName: "rowActionsLoadingFn", publicName: "rowActionsLoadingFn", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { lazyLoad: "lazyLoad", loadMore: "loadMore", cardClick: "cardClick", rowActionsRequested: "rowActionsRequested" }, providers: [MTDateFormatPipe], viewQueries: [{ propertyName: "loadMoreSentinel", first: true, predicate: ["loadMoreSentinel"], descendants: true, isSignal: true }], ngImport: i0, template: "<mt-table-caption\n [generalSearch]=\"true\"\n [showFilters]=\"true\"\n filterMode=\"popover\"\n [printable]=\"true\"\n [groupable]=\"true\"\n [exportable]=\"true\"\n [columns]=\"columns()\"\n [data]=\"rowRepresentations()\"\n [groupColumns]=\"grouping.groupableColumns()\"\n [actions]=\"toolbarActions()\"\n [captionStart]=\"cardsState().config.toolbarStart ?? null\"\n [captionEnd]=\"cardsState().config.toolbarEnd ?? null\"\n [filters]=\"bucket().filters()\"\n (filtersChange)=\"bucket().filters.set($event)\"\n [filterTerm]=\"bucket().filterTerm()\"\n (filterTermChange)=\"bucket().filterTerm.set($event)\"\n [groupBy]=\"bucket().groupBy()\"\n (groupByChange)=\"bucket().groupBy.set($event)\"\n [activeTab]=\"bucket().activeTab()\"\n (activeTabChange)=\"bucket().activeTab.set($event)\"\n (exportRequested)=\"exportExcel()\"\n (printRequested)=\"printPdf()\"\n/>\n\n@if (badgeLegend().length > 0) {\n <!--\n Decodes the coloured count pills on the cards below. Rendered once for the\n whole board rather than per card, so it costs one line of vertical space\n instead of repeating a key inside every tile (finding M-04).\n -->\n <div\n class=\"mb-3 flex flex-wrap items-center gap-x-4 gap-y-2 px-1\"\n role=\"list\"\n [attr.aria-label]=\"\n 'components.clientComponents.list.badgeLegend' | transloco\n \"\n >\n <span class=\"text-xs font-medium text-gray-500 dark:text-gray-400\">\n {{ \"components.clientComponents.list.badgeLegend\" | transloco }}\n </span>\n @for (entry of badgeLegend(); track entry.key) {\n <span class=\"flex items-center gap-1.5\" role=\"listitem\">\n <span\n class=\"inline-flex h-4 min-w-4 items-center justify-center rounded text-[10px] font-bold\"\n [style.background-color]=\"entry.color\"\n [style.color]=\"entry.textColor\"\n aria-hidden=\"true\"\n >\n #\n </span>\n <span class=\"text-xs text-gray-600 dark:text-gray-300\">{{\n entry.label\n }}</span>\n </span>\n }\n </div>\n}\n\n@if (cardsState().loading && cardsState().cards.length === 0) {\n <div class=\"mt-cards-grid\">\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\n <p-skeleton height=\"20rem\" class=\"rounded-lg\" />\n }\n </div>\n} @else if (cardsState().error) {\n <div class=\"rounded-lg border border-red-200 bg-red-50 p-6 text-red-600\">\n {{ cardsState().error }}\n </div>\n} @else if (visibleCards().length === 0) {\n <!--\n The same empty state the tables use, so a board and a list stop looking like\n two products (finding H-12). A board narrowed by a search or a filter says\n so and offers to clear it, instead of the bare grey \"No cards found.\"\n -->\n <mt-empty-state\n [variant]=\"hasActiveCardFilters() ? 'filtered' : 'empty'\"\n [title]=\"\n hasActiveCardFilters()\n ? ''\n : ('components.clientComponents.list.noCardsFound' | transloco)\n \"\n [actionLabel]=\"\n hasActiveCardFilters()\n ? ('components.emptyState.clearFilters' | transloco)\n : ''\n \"\n actionIcon=\"general.x-close\"\n (actionClick)=\"clearCardFilters()\"\n />\n} @else if (grouping.groupingActive()) {\n <div class=\"flex flex-col gap-6\">\n @for (group of groupedCards(); track group.key) {\n <section class=\"flex flex-col gap-3\">\n <header\n class=\"flex items-center gap-3 px-2 py-2 bg-surface-50 dark:bg-surface-900 border-y border-surface-200 dark:border-surface-700 rounded\"\n [style.--mt-group-accent]=\"group.accent ?? 'var(--p-primary-color)'\"\n >\n <span\n class=\"inline-block w-1 h-5 rounded-full\"\n [style.background]=\"'var(--mt-group-accent)'\"\n ></span>\n <span\n class=\"text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400\"\n >\n {{ group.columnLabel }}\n </span>\n <span class=\"text-sm font-semibold text-gray-900 dark:text-gray-100\">\n {{ group.label }}\n </span>\n <span\n class=\"ml-auto inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-primary-50 text-primary-700 dark:bg-primary-950 dark:text-primary-300\"\n >\n {{ group.count }}\n </span>\n </header>\n <div class=\"mt-cards-grid\">\n @for (card of group.cards; track card.id) {\n <mt-card\n class=\"relative cursor-pointer p-5 shadow-sm transition-shadow hover:shadow-md\"\n (click)=\"cardClick.emit(card)\"\n >\n <ng-template #headless>\n <ng-container\n [ngTemplateOutlet]=\"cardActionsMenu\"\n [ngTemplateOutletContext]=\"{ $implicit: card }\"\n />\n <mt-entities-preview\n [entities]=\"card.entities\"\n attachmentShape=\"compact\"\n [stackOnNarrow]=\"false\"\n [clickableKeys]=\"clickableEntityKeys()\"\n [activeKeys]=\"activeKeysForCard(card)\"\n [clickableTooltip]=\"filterByThisLabel()\"\n [activeTooltip]=\"clearFilterLabel()\"\n (entityClick)=\"onEntityFilterClick(card, $event)\"\n />\n </ng-template>\n </mt-card>\n }\n </div>\n </section>\n }\n </div>\n} @else {\n <div class=\"mt-cards-grid\">\n @for (card of visibleCards(); track card.id) {\n @defer (on viewport) {\n <mt-card\n class=\"relative cursor-pointer p-5 shadow-sm transition-shadow hover:shadow-md\"\n (click)=\"cardClick.emit(card)\"\n >\n <ng-template #headless>\n <ng-container\n [ngTemplateOutlet]=\"cardActionsMenu\"\n [ngTemplateOutletContext]=\"{ $implicit: card }\"\n />\n <mt-entities-preview\n [entities]=\"card.entities\"\n attachmentShape=\"compact\"\n [stackOnNarrow]=\"false\"\n [clickableKeys]=\"clickableEntityKeys()\"\n [activeKeys]=\"activeKeysForCard(card)\"\n [clickableTooltip]=\"filterByThisLabel()\"\n [activeTooltip]=\"clearFilterLabel()\"\n (entityClick)=\"onEntityFilterClick(card, $event)\"\n />\n </ng-template>\n </mt-card>\n } @placeholder {\n <div class=\"min-h-[20rem]\">\n <p-skeleton height=\"20rem\" class=\"rounded-lg\" />\n </div>\n }\n }\n </div>\n}\n\n<!--\n Infinite-scroll trigger. Deliberately outside the @if/@else chain above:\n inside it the element would be torn down and rebuilt every time the view\n swapped branch (error, empty filter result, group-by), and the observer\n would be left watching a detached node. Kept in the DOM and hidden via CSS\n when there is nothing left to load, so appending a page never recreates it.\n It also has to sit outside so infinite scroll keeps working while a\n group-by is active \u2014 grouping is a pure FE view over whatever has been\n fetched, not a reason to stop fetching.\n-->\n<div\n #loadMoreSentinel\n class=\"mt-cards-grid py-4\"\n [class.hidden]=\"!hasMoreCards()\"\n>\n @for (i of [1, 2, 3]; track i) {\n <p-skeleton height=\"20rem\" class=\"rounded-lg\" />\n }\n</div>\n\n<!--\n Per-card escalation marker and 3-dots actions menu. The compact corner cover\n only indicates active escalation state; its preview action lives in the menu\n so the marker never displaces or competes with the menu trigger.\n-->\n<ng-template #cardActionsMenu let-card>\n @let escalationAction = escalationMarkerAction(card);\n @if (escalationAction) {\n <span\n class=\"mt-escalation-marker absolute end-0 top-0 z-10 h-[18px] w-[20px]\"\n role=\"img\"\n tabindex=\"0\"\n [attr.aria-label]=\"escalationLabel()\"\n [mtTooltip]=\"escalationLabel()\"\n tooltipPosition=\"top\"\n ></span>\n }\n @if (rowActions().length > 0) {\n <div\n class=\"absolute end-2 top-2 z-20 flex items-center gap-1\"\n (click)=\"$event.stopPropagation()\"\n >\n @for (action of inlineActionsForCard(card); track $index) {\n <mt-button\n [icon]=\"action.icon\"\n [severity]=\"action.color\"\n [variant]=\"action.variant\"\n [size]=\"actionSize(action) || 'small'\"\n [tooltip]=\"action.tooltip\"\n [label]=\"action.label\"\n [loading]=\"resolveActionLoading(action, card)\"\n (onClick)=\"runCardAction($event, action, card)\"\n />\n }\n @if (showCardActionsMenu(card)) {\n <button\n type=\"button\"\n class=\"flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent p-0 text-base text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 dark:text-gray-300 dark:hover:bg-surface-700 dark:hover:text-gray-100\"\n [attr.aria-label]=\"actionsLabel()\"\n [mtTooltip]=\"actionsLabel()\"\n tooltipPosition=\"top\"\n (click)=\"onCardActionsToggle($event, cardPopover, card)\"\n >\n <mt-icon icon=\"general.dots-vertical\" class=\"text-base\" />\n </button>\n <p-popover #cardPopover appendTo=\"body\">\n <div class=\"flex min-w-44 flex-col gap-1 p-1\">\n @if (isCardActionsLoading(card)) {\n <div class=\"flex flex-col gap-2 p-1\">\n <p-skeleton height=\"1.75rem\" />\n <p-skeleton height=\"1.75rem\" />\n <p-skeleton height=\"1.75rem\" />\n </div>\n } @else {\n @for (action of menuActionsForCard(card); track $index) {\n @if (isFirstDestructiveCardAction(card, $index)) {\n <hr\n class=\"my-1 border-0 border-t border-surface-200 dark:border-surface-700\"\n />\n }\n <button\n type=\"button\"\n class=\"flex cursor-pointer items-center gap-2 rounded px-2 py-2 text-start text-sm transition-colors\"\n [class]=\"\n action.color === 'danger'\n ? 'text-red-600 hover:bg-red-50 dark:hover:bg-red-950'\n : action.color === 'warn'\n ? 'text-orange-600 hover:bg-orange-50 dark:text-orange-400 dark:hover:bg-orange-950'\n : 'text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-surface-700'\n \"\n (click)=\"\n runCardAction($event, action, card); cardPopover.hide()\n \"\n >\n @if (action.icon) {\n <mt-icon [icon]=\"action.icon\" class=\"text-base\" />\n }\n <span>{{ action.label || action.tooltip }}</span>\n </button>\n }\n }\n </div>\n </p-popover>\n }\n </div>\n }\n</ng-template>\n", styles: [".mt-cards-grid{--mt-cards-gap: 1.25rem;--mt-cards-min: 380px;--mt-cards-track: max( var(--mt-cards-min), (100% - 4 * var(--mt-cards-gap)) / 5 );display:grid;gap:var(--mt-cards-gap);grid-template-columns:repeat(auto-fill,minmax(min(100%,var(--mt-cards-track)),1fr))}.mt-cards-grid.hidden,.mt-cards-grid[hidden]{display:none}.mt-escalation-marker{border-start-end-radius:.5rem;background:var(--p-button-text-warn-color, var(--p-orange-500, #f59e0b));clip-path:polygon(0 0,100% 0,100% 100%);transition:filter .15s ease}.mt-escalation-marker:dir(rtl){clip-path:polygon(0 0,100% 0,0 100%)}.mt-escalation-marker:hover{filter:brightness(.92)}.mt-escalation-marker:focus-visible{filter:brightness(.92) drop-shadow(0 0 2px var(--p-button-text-warn-color, #f59e0b));outline:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs", "ariaLabel"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: Tooltip, selector: "[mtTooltip]" }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: EntitiesPreview, selector: "mt-entities-preview", inputs: ["entities", "attachmentShape", "stackOnNarrow", "clickableKeys", "activeKeys", "clickableTooltip", "activeTooltip"], outputs: ["entityClick"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i3.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: TableCaption, selector: "mt-table-caption", inputs: ["generalSearch", "showFilters", "filterMode", "exportable", "printable", "groupable", "columns", "data", "groupColumns", "tabs", "tabsOptionLabel", "tabsOptionValue", "actions", "captionStart", "captionEnd", "activeTab", "filters", "filterTerm", "groupBy"], outputs: ["activeTabChange", "filtersChange", "filterTermChange", "groupByChange", "exportRequested", "printRequested", "onTabChange", "searchChange", "filterApplied", "filterReset"] }, { kind: "component", type: EmptyState, selector: "mt-empty-state", inputs: ["variant", "illustration", "title", "description", "actionLabel", "actionIcon"], outputs: ["actionClick"] }, { kind: "pipe", type: i4.TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [i1.NgTemplateOutlet, Card,
|
|
1625
1622
|
EntitiesPreview]] });
|
|
1626
1623
|
}
|
|
1627
1624
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientListCardsView, decorators: [{
|
|
@@ -1638,7 +1635,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1638
1635
|
PopoverModule,
|
|
1639
1636
|
TableCaption,
|
|
1640
1637
|
EmptyState,
|
|
1641
|
-
], providers: [MTDateFormatPipe], template: "<mt-table-caption\r\n [generalSearch]=\"true\"\r\n [showFilters]=\"true\"\r\n filterMode=\"popover\"\r\n [printable]=\"true\"\r\n [groupable]=\"true\"\r\n [exportable]=\"true\"\r\n [columns]=\"columns()\"\r\n [data]=\"rowRepresentations()\"\r\n [groupColumns]=\"grouping.groupableColumns()\"\r\n [actions]=\"toolbarActions()\"\r\n [filters]=\"bucket().filters()\"\r\n (filtersChange)=\"bucket().filters.set($event)\"\r\n [filterTerm]=\"bucket().filterTerm()\"\r\n (filterTermChange)=\"bucket().filterTerm.set($event)\"\r\n [groupBy]=\"bucket().groupBy()\"\r\n (groupByChange)=\"bucket().groupBy.set($event)\"\r\n [activeTab]=\"bucket().activeTab()\"\r\n (activeTabChange)=\"bucket().activeTab.set($event)\"\r\n (exportRequested)=\"exportExcel()\"\r\n (printRequested)=\"printPdf()\"\r\n/>\r\n\r\n@if (badgeLegend().length > 0) {\r\n <!--\r\n Decodes the coloured count pills on the cards below. Rendered once for the\r\n whole board rather than per card, so it costs one line of vertical space\r\n instead of repeating a key inside every tile (finding M-04).\r\n -->\r\n <div\r\n class=\"mb-3 flex flex-wrap items-center gap-x-4 gap-y-2 px-1\"\r\n role=\"list\"\r\n [attr.aria-label]=\"\r\n 'components.clientComponents.list.badgeLegend' | transloco\r\n \"\r\n >\r\n <span class=\"text-xs font-medium text-gray-500 dark:text-gray-400\">\r\n {{ \"components.clientComponents.list.badgeLegend\" | transloco }}\r\n </span>\r\n @for (entry of badgeLegend(); track entry.key) {\r\n <span class=\"flex items-center gap-1.5\" role=\"listitem\">\r\n <span\r\n class=\"inline-flex h-4 min-w-4 items-center justify-center rounded text-[10px] font-bold\"\r\n [style.background-color]=\"entry.color\"\r\n [style.color]=\"entry.textColor\"\r\n aria-hidden=\"true\"\r\n >\r\n #\r\n </span>\r\n <span class=\"text-xs text-gray-600 dark:text-gray-300\">{{\r\n entry.label\r\n }}</span>\r\n </span>\r\n }\r\n </div>\r\n}\r\n\r\n@if (cardsState().loading && cardsState().cards.length === 0) {\r\n <div class=\"mt-cards-grid\">\r\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\r\n <p-skeleton height=\"20rem\" class=\"rounded-lg\" />\r\n }\r\n </div>\r\n} @else if (cardsState().error) {\r\n <div class=\"rounded-lg border border-red-200 bg-red-50 p-6 text-red-600\">\r\n {{ cardsState().error }}\r\n </div>\r\n} @else if (visibleCards().length === 0) {\r\n <!--\r\n The same empty state the tables use, so a board and a list stop looking like\r\n two products (finding H-12). A board narrowed by a search or a filter says\r\n so and offers to clear it, instead of the bare grey \"No cards found.\"\r\n -->\r\n <mt-empty-state\r\n [variant]=\"hasActiveCardFilters() ? 'filtered' : 'empty'\"\r\n [title]=\"\r\n hasActiveCardFilters()\r\n ? ''\r\n : ('components.clientComponents.list.noCardsFound' | transloco)\r\n \"\r\n [actionLabel]=\"\r\n hasActiveCardFilters()\r\n ? ('components.emptyState.clearFilters' | transloco)\r\n : ''\r\n \"\r\n actionIcon=\"general.x-close\"\r\n (actionClick)=\"clearCardFilters()\"\r\n />\r\n} @else if (grouping.groupingActive()) {\r\n <div class=\"flex flex-col gap-6\">\r\n @for (group of groupedCards(); track group.key) {\r\n <section class=\"flex flex-col gap-3\">\r\n <header\r\n class=\"flex items-center gap-3 px-2 py-2 bg-surface-50 dark:bg-surface-900 border-y border-surface-200 dark:border-surface-700 rounded\"\r\n [style.--mt-group-accent]=\"group.accent ?? 'var(--p-primary-color)'\"\r\n >\r\n <span\r\n class=\"inline-block w-1 h-5 rounded-full\"\r\n [style.background]=\"'var(--mt-group-accent)'\"\r\n ></span>\r\n <span\r\n class=\"text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400\"\r\n >\r\n {{ group.columnLabel }}\r\n </span>\r\n <span class=\"text-sm font-semibold text-gray-900 dark:text-gray-100\">\r\n {{ group.label }}\r\n </span>\r\n <span\r\n class=\"ml-auto inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-primary-50 text-primary-700 dark:bg-primary-950 dark:text-primary-300\"\r\n >\r\n {{ group.count }}\r\n </span>\r\n </header>\r\n <div class=\"mt-cards-grid\">\r\n @for (card of group.cards; track card.id) {\r\n <mt-card\r\n class=\"relative cursor-pointer p-5 shadow-sm transition-shadow hover:shadow-md\"\r\n (click)=\"cardClick.emit(card)\"\r\n >\r\n <ng-template #headless>\r\n <ng-container\r\n [ngTemplateOutlet]=\"cardActionsMenu\"\r\n [ngTemplateOutletContext]=\"{ $implicit: card }\"\r\n />\r\n <mt-entities-preview\r\n [entities]=\"card.entities\"\r\n attachmentShape=\"compact\"\r\n [stackOnNarrow]=\"false\"\r\n [clickableKeys]=\"clickableEntityKeys()\"\r\n [activeKeys]=\"activeKeysForCard(card)\"\r\n [clickableTooltip]=\"filterByThisLabel()\"\r\n [activeTooltip]=\"clearFilterLabel()\"\r\n (entityClick)=\"onEntityFilterClick(card, $event)\"\r\n />\r\n </ng-template>\r\n </mt-card>\r\n }\r\n </div>\r\n </section>\r\n }\r\n </div>\r\n} @else {\r\n <div class=\"mt-cards-grid\">\r\n @for (card of visibleCards(); track card.id) {\r\n @defer (on viewport) {\r\n <mt-card\r\n class=\"relative cursor-pointer p-5 shadow-sm transition-shadow hover:shadow-md\"\r\n (click)=\"cardClick.emit(card)\"\r\n >\r\n <ng-template #headless>\r\n <ng-container\r\n [ngTemplateOutlet]=\"cardActionsMenu\"\r\n [ngTemplateOutletContext]=\"{ $implicit: card }\"\r\n />\r\n <mt-entities-preview\r\n [entities]=\"card.entities\"\r\n attachmentShape=\"compact\"\r\n [stackOnNarrow]=\"false\"\r\n [clickableKeys]=\"clickableEntityKeys()\"\r\n [activeKeys]=\"activeKeysForCard(card)\"\r\n [clickableTooltip]=\"filterByThisLabel()\"\r\n [activeTooltip]=\"clearFilterLabel()\"\r\n (entityClick)=\"onEntityFilterClick(card, $event)\"\r\n />\r\n </ng-template>\r\n </mt-card>\r\n } @placeholder {\r\n <div class=\"min-h-[20rem]\">\r\n <p-skeleton height=\"20rem\" class=\"rounded-lg\" />\r\n </div>\r\n }\r\n }\r\n </div>\r\n}\r\n\r\n<!--\r\n Infinite-scroll trigger. Deliberately outside the @if/@else chain above:\r\n inside it the element would be torn down and rebuilt every time the view\r\n swapped branch (error, empty filter result, group-by), and the observer\r\n would be left watching a detached node. Kept in the DOM and hidden via CSS\r\n when there is nothing left to load, so appending a page never recreates it.\r\n It also has to sit outside so infinite scroll keeps working while a\r\n group-by is active \u2014 grouping is a pure FE view over whatever has been\r\n fetched, not a reason to stop fetching.\r\n-->\r\n<div\r\n #loadMoreSentinel\r\n class=\"mt-cards-grid py-4\"\r\n [class.hidden]=\"!hasMoreCards()\"\r\n>\r\n @for (i of [1, 2, 3]; track i) {\r\n <p-skeleton height=\"20rem\" class=\"rounded-lg\" />\r\n }\r\n</div>\r\n\r\n<!--\r\n Per-card escalation marker and 3-dots actions menu. The compact corner cover\r\n only indicates active escalation state; its preview action lives in the menu\r\n so the marker never displaces or competes with the menu trigger.\r\n-->\r\n<ng-template #cardActionsMenu let-card>\r\n @let escalationAction = escalationMarkerAction(card);\r\n @if (escalationAction) {\r\n <span\r\n class=\"mt-escalation-marker absolute end-0 top-0 z-10 h-[18px] w-[20px]\"\r\n role=\"img\"\r\n tabindex=\"0\"\r\n [attr.aria-label]=\"escalationLabel()\"\r\n [mtTooltip]=\"escalationLabel()\"\r\n tooltipPosition=\"top\"\r\n ></span>\r\n }\r\n @if (rowActions().length > 0) {\r\n <div\r\n class=\"absolute end-2 top-2 z-20 flex items-center gap-1\"\r\n (click)=\"$event.stopPropagation()\"\r\n >\r\n @for (action of inlineActionsForCard(card); track $index) {\r\n <mt-button\r\n [icon]=\"action.icon\"\r\n [severity]=\"action.color\"\r\n [variant]=\"action.variant\"\r\n [size]=\"actionSize(action) || 'small'\"\r\n [tooltip]=\"action.tooltip\"\r\n [label]=\"action.label\"\r\n [loading]=\"resolveActionLoading(action, card)\"\r\n (onClick)=\"runCardAction($event, action, card)\"\r\n />\r\n }\r\n @if (showCardActionsMenu(card)) {\r\n <button\r\n type=\"button\"\r\n class=\"flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent p-0 text-base text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 dark:text-gray-300 dark:hover:bg-surface-700 dark:hover:text-gray-100\"\r\n [attr.aria-label]=\"actionsLabel()\"\r\n [mtTooltip]=\"actionsLabel()\"\r\n tooltipPosition=\"top\"\r\n (click)=\"onCardActionsToggle($event, cardPopover, card)\"\r\n >\r\n <mt-icon icon=\"general.dots-vertical\" class=\"text-base\" />\r\n </button>\r\n <p-popover #cardPopover appendTo=\"body\">\r\n <div class=\"flex min-w-44 flex-col gap-1 p-1\">\r\n @if (isCardActionsLoading(card)) {\r\n <div class=\"flex flex-col gap-2 p-1\">\r\n <p-skeleton height=\"1.75rem\" />\r\n <p-skeleton height=\"1.75rem\" />\r\n <p-skeleton height=\"1.75rem\" />\r\n </div>\r\n } @else {\r\n @for (action of menuActionsForCard(card); track $index) {\r\n @if (isFirstDestructiveCardAction(card, $index)) {\r\n <hr\r\n class=\"my-1 border-0 border-t border-surface-200 dark:border-surface-700\"\r\n />\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"flex cursor-pointer items-center gap-2 rounded px-2 py-2 text-start text-sm transition-colors\"\r\n [class]=\"\r\n action.color === 'danger'\r\n ? 'text-red-600 hover:bg-red-50 dark:hover:bg-red-950'\r\n : action.color === 'warn'\r\n ? 'text-orange-600 hover:bg-orange-50 dark:text-orange-400 dark:hover:bg-orange-950'\r\n : 'text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-surface-700'\r\n \"\r\n (click)=\"\r\n runCardAction($event, action, card); cardPopover.hide()\r\n \"\r\n >\r\n @if (action.icon) {\r\n <mt-icon [icon]=\"action.icon\" class=\"text-base\" />\r\n }\r\n <span>{{ action.label || action.tooltip }}</span>\r\n </button>\r\n }\r\n }\r\n </div>\r\n </p-popover>\r\n }\r\n </div>\r\n }\r\n</ng-template>\r\n", styles: [".mt-cards-grid{display:grid;gap:1.25rem;grid-template-columns:repeat(auto-fill,minmax(min(340px,100%),1fr))}.mt-cards-grid.hidden,.mt-cards-grid[hidden]{display:none}.mt-escalation-marker{border-start-end-radius:.5rem;background:var(--p-button-text-warn-color, var(--p-orange-500, #f59e0b));clip-path:polygon(0 0,100% 0,100% 100%);transition:filter .15s ease}.mt-escalation-marker:dir(rtl){clip-path:polygon(0 0,100% 0,0 100%)}.mt-escalation-marker:hover{filter:brightness(.92)}.mt-escalation-marker:focus-visible{filter:brightness(.92) drop-shadow(0 0 2px var(--p-button-text-warn-color, #f59e0b));outline:none}\n"] }]
|
|
1638
|
+
], providers: [MTDateFormatPipe], template: "<mt-table-caption\n [generalSearch]=\"true\"\n [showFilters]=\"true\"\n filterMode=\"popover\"\n [printable]=\"true\"\n [groupable]=\"true\"\n [exportable]=\"true\"\n [columns]=\"columns()\"\n [data]=\"rowRepresentations()\"\n [groupColumns]=\"grouping.groupableColumns()\"\n [actions]=\"toolbarActions()\"\n [captionStart]=\"cardsState().config.toolbarStart ?? null\"\n [captionEnd]=\"cardsState().config.toolbarEnd ?? null\"\n [filters]=\"bucket().filters()\"\n (filtersChange)=\"bucket().filters.set($event)\"\n [filterTerm]=\"bucket().filterTerm()\"\n (filterTermChange)=\"bucket().filterTerm.set($event)\"\n [groupBy]=\"bucket().groupBy()\"\n (groupByChange)=\"bucket().groupBy.set($event)\"\n [activeTab]=\"bucket().activeTab()\"\n (activeTabChange)=\"bucket().activeTab.set($event)\"\n (exportRequested)=\"exportExcel()\"\n (printRequested)=\"printPdf()\"\n/>\n\n@if (badgeLegend().length > 0) {\n <!--\n Decodes the coloured count pills on the cards below. Rendered once for the\n whole board rather than per card, so it costs one line of vertical space\n instead of repeating a key inside every tile (finding M-04).\n -->\n <div\n class=\"mb-3 flex flex-wrap items-center gap-x-4 gap-y-2 px-1\"\n role=\"list\"\n [attr.aria-label]=\"\n 'components.clientComponents.list.badgeLegend' | transloco\n \"\n >\n <span class=\"text-xs font-medium text-gray-500 dark:text-gray-400\">\n {{ \"components.clientComponents.list.badgeLegend\" | transloco }}\n </span>\n @for (entry of badgeLegend(); track entry.key) {\n <span class=\"flex items-center gap-1.5\" role=\"listitem\">\n <span\n class=\"inline-flex h-4 min-w-4 items-center justify-center rounded text-[10px] font-bold\"\n [style.background-color]=\"entry.color\"\n [style.color]=\"entry.textColor\"\n aria-hidden=\"true\"\n >\n #\n </span>\n <span class=\"text-xs text-gray-600 dark:text-gray-300\">{{\n entry.label\n }}</span>\n </span>\n }\n </div>\n}\n\n@if (cardsState().loading && cardsState().cards.length === 0) {\n <div class=\"mt-cards-grid\">\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\n <p-skeleton height=\"20rem\" class=\"rounded-lg\" />\n }\n </div>\n} @else if (cardsState().error) {\n <div class=\"rounded-lg border border-red-200 bg-red-50 p-6 text-red-600\">\n {{ cardsState().error }}\n </div>\n} @else if (visibleCards().length === 0) {\n <!--\n The same empty state the tables use, so a board and a list stop looking like\n two products (finding H-12). A board narrowed by a search or a filter says\n so and offers to clear it, instead of the bare grey \"No cards found.\"\n -->\n <mt-empty-state\n [variant]=\"hasActiveCardFilters() ? 'filtered' : 'empty'\"\n [title]=\"\n hasActiveCardFilters()\n ? ''\n : ('components.clientComponents.list.noCardsFound' | transloco)\n \"\n [actionLabel]=\"\n hasActiveCardFilters()\n ? ('components.emptyState.clearFilters' | transloco)\n : ''\n \"\n actionIcon=\"general.x-close\"\n (actionClick)=\"clearCardFilters()\"\n />\n} @else if (grouping.groupingActive()) {\n <div class=\"flex flex-col gap-6\">\n @for (group of groupedCards(); track group.key) {\n <section class=\"flex flex-col gap-3\">\n <header\n class=\"flex items-center gap-3 px-2 py-2 bg-surface-50 dark:bg-surface-900 border-y border-surface-200 dark:border-surface-700 rounded\"\n [style.--mt-group-accent]=\"group.accent ?? 'var(--p-primary-color)'\"\n >\n <span\n class=\"inline-block w-1 h-5 rounded-full\"\n [style.background]=\"'var(--mt-group-accent)'\"\n ></span>\n <span\n class=\"text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400\"\n >\n {{ group.columnLabel }}\n </span>\n <span class=\"text-sm font-semibold text-gray-900 dark:text-gray-100\">\n {{ group.label }}\n </span>\n <span\n class=\"ml-auto inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-primary-50 text-primary-700 dark:bg-primary-950 dark:text-primary-300\"\n >\n {{ group.count }}\n </span>\n </header>\n <div class=\"mt-cards-grid\">\n @for (card of group.cards; track card.id) {\n <mt-card\n class=\"relative cursor-pointer p-5 shadow-sm transition-shadow hover:shadow-md\"\n (click)=\"cardClick.emit(card)\"\n >\n <ng-template #headless>\n <ng-container\n [ngTemplateOutlet]=\"cardActionsMenu\"\n [ngTemplateOutletContext]=\"{ $implicit: card }\"\n />\n <mt-entities-preview\n [entities]=\"card.entities\"\n attachmentShape=\"compact\"\n [stackOnNarrow]=\"false\"\n [clickableKeys]=\"clickableEntityKeys()\"\n [activeKeys]=\"activeKeysForCard(card)\"\n [clickableTooltip]=\"filterByThisLabel()\"\n [activeTooltip]=\"clearFilterLabel()\"\n (entityClick)=\"onEntityFilterClick(card, $event)\"\n />\n </ng-template>\n </mt-card>\n }\n </div>\n </section>\n }\n </div>\n} @else {\n <div class=\"mt-cards-grid\">\n @for (card of visibleCards(); track card.id) {\n @defer (on viewport) {\n <mt-card\n class=\"relative cursor-pointer p-5 shadow-sm transition-shadow hover:shadow-md\"\n (click)=\"cardClick.emit(card)\"\n >\n <ng-template #headless>\n <ng-container\n [ngTemplateOutlet]=\"cardActionsMenu\"\n [ngTemplateOutletContext]=\"{ $implicit: card }\"\n />\n <mt-entities-preview\n [entities]=\"card.entities\"\n attachmentShape=\"compact\"\n [stackOnNarrow]=\"false\"\n [clickableKeys]=\"clickableEntityKeys()\"\n [activeKeys]=\"activeKeysForCard(card)\"\n [clickableTooltip]=\"filterByThisLabel()\"\n [activeTooltip]=\"clearFilterLabel()\"\n (entityClick)=\"onEntityFilterClick(card, $event)\"\n />\n </ng-template>\n </mt-card>\n } @placeholder {\n <div class=\"min-h-[20rem]\">\n <p-skeleton height=\"20rem\" class=\"rounded-lg\" />\n </div>\n }\n }\n </div>\n}\n\n<!--\n Infinite-scroll trigger. Deliberately outside the @if/@else chain above:\n inside it the element would be torn down and rebuilt every time the view\n swapped branch (error, empty filter result, group-by), and the observer\n would be left watching a detached node. Kept in the DOM and hidden via CSS\n when there is nothing left to load, so appending a page never recreates it.\n It also has to sit outside so infinite scroll keeps working while a\n group-by is active \u2014 grouping is a pure FE view over whatever has been\n fetched, not a reason to stop fetching.\n-->\n<div\n #loadMoreSentinel\n class=\"mt-cards-grid py-4\"\n [class.hidden]=\"!hasMoreCards()\"\n>\n @for (i of [1, 2, 3]; track i) {\n <p-skeleton height=\"20rem\" class=\"rounded-lg\" />\n }\n</div>\n\n<!--\n Per-card escalation marker and 3-dots actions menu. The compact corner cover\n only indicates active escalation state; its preview action lives in the menu\n so the marker never displaces or competes with the menu trigger.\n-->\n<ng-template #cardActionsMenu let-card>\n @let escalationAction = escalationMarkerAction(card);\n @if (escalationAction) {\n <span\n class=\"mt-escalation-marker absolute end-0 top-0 z-10 h-[18px] w-[20px]\"\n role=\"img\"\n tabindex=\"0\"\n [attr.aria-label]=\"escalationLabel()\"\n [mtTooltip]=\"escalationLabel()\"\n tooltipPosition=\"top\"\n ></span>\n }\n @if (rowActions().length > 0) {\n <div\n class=\"absolute end-2 top-2 z-20 flex items-center gap-1\"\n (click)=\"$event.stopPropagation()\"\n >\n @for (action of inlineActionsForCard(card); track $index) {\n <mt-button\n [icon]=\"action.icon\"\n [severity]=\"action.color\"\n [variant]=\"action.variant\"\n [size]=\"actionSize(action) || 'small'\"\n [tooltip]=\"action.tooltip\"\n [label]=\"action.label\"\n [loading]=\"resolveActionLoading(action, card)\"\n (onClick)=\"runCardAction($event, action, card)\"\n />\n }\n @if (showCardActionsMenu(card)) {\n <button\n type=\"button\"\n class=\"flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent p-0 text-base text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 dark:text-gray-300 dark:hover:bg-surface-700 dark:hover:text-gray-100\"\n [attr.aria-label]=\"actionsLabel()\"\n [mtTooltip]=\"actionsLabel()\"\n tooltipPosition=\"top\"\n (click)=\"onCardActionsToggle($event, cardPopover, card)\"\n >\n <mt-icon icon=\"general.dots-vertical\" class=\"text-base\" />\n </button>\n <p-popover #cardPopover appendTo=\"body\">\n <div class=\"flex min-w-44 flex-col gap-1 p-1\">\n @if (isCardActionsLoading(card)) {\n <div class=\"flex flex-col gap-2 p-1\">\n <p-skeleton height=\"1.75rem\" />\n <p-skeleton height=\"1.75rem\" />\n <p-skeleton height=\"1.75rem\" />\n </div>\n } @else {\n @for (action of menuActionsForCard(card); track $index) {\n @if (isFirstDestructiveCardAction(card, $index)) {\n <hr\n class=\"my-1 border-0 border-t border-surface-200 dark:border-surface-700\"\n />\n }\n <button\n type=\"button\"\n class=\"flex cursor-pointer items-center gap-2 rounded px-2 py-2 text-start text-sm transition-colors\"\n [class]=\"\n action.color === 'danger'\n ? 'text-red-600 hover:bg-red-50 dark:hover:bg-red-950'\n : action.color === 'warn'\n ? 'text-orange-600 hover:bg-orange-50 dark:text-orange-400 dark:hover:bg-orange-950'\n : 'text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-surface-700'\n \"\n (click)=\"\n runCardAction($event, action, card); cardPopover.hide()\n \"\n >\n @if (action.icon) {\n <mt-icon [icon]=\"action.icon\" class=\"text-base\" />\n }\n <span>{{ action.label || action.tooltip }}</span>\n </button>\n }\n }\n </div>\n </p-popover>\n }\n </div>\n }\n</ng-template>\n", styles: [".mt-cards-grid{--mt-cards-gap: 1.25rem;--mt-cards-min: 380px;--mt-cards-track: max( var(--mt-cards-min), (100% - 4 * var(--mt-cards-gap)) / 5 );display:grid;gap:var(--mt-cards-gap);grid-template-columns:repeat(auto-fill,minmax(min(100%,var(--mt-cards-track)),1fr))}.mt-cards-grid.hidden,.mt-cards-grid[hidden]{display:none}.mt-escalation-marker{border-start-end-radius:.5rem;background:var(--p-button-text-warn-color, var(--p-orange-500, #f59e0b));clip-path:polygon(0 0,100% 0,100% 100%);transition:filter .15s ease}.mt-escalation-marker:dir(rtl){clip-path:polygon(0 0,100% 0,0 100%)}.mt-escalation-marker:hover{filter:brightness(.92)}.mt-escalation-marker:focus-visible{filter:brightness(.92) drop-shadow(0 0 2px var(--p-button-text-warn-color, #f59e0b));outline:none}\n"] }]
|
|
1642
1639
|
}], ctorParameters: () => [], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }], rowActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActions", required: false }] }], rowActionsLoadingFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActionsLoadingFn", required: false }] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }], loadMore: [{ type: i0.Output, args: ["loadMore"] }], cardClick: [{ type: i0.Output, args: ["cardClick"] }], rowActionsRequested: [{ type: i0.Output, args: ["rowActionsRequested"] }], loadMoreSentinel: [{ type: i0.ViewChild, args: ['loadMoreSentinel', { isSignal: true }] }] } });
|
|
1643
1640
|
|
|
1644
1641
|
function toDashboardExtraFilters(filters = []) {
|
|
@@ -1681,108 +1678,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1681
1678
|
args: [{ selector: 'mt-client-list-informative-view', standalone: true, imports: [CommonModule, Card, DashboardViewer, SkeletonModule, TranslocoPipe], template: "<div class=\"grid gap-4\" [style.gridTemplateColumns]=\"gridTemplateColumns\">\r\n @if (informativeState().config.contentStart) {\r\n <div\r\n [style.gridColumn]=\"\r\n slotGridSpan(informativeState().config.layout.startSpan)\r\n \"\r\n >\r\n <ng-container\r\n [ngTemplateOutlet]=\"informativeState().config.contentStart\"\r\n [ngTemplateOutletContext]=\"templateContext(informativeState())\"\r\n />\r\n </div>\r\n }\r\n\r\n <div\r\n [style.gridColumn]=\"\r\n slotGridSpan(informativeState().config.layout.tableSpan)\r\n \"\r\n >\r\n <mt-card>\r\n @if (\r\n informativeState().loading &&\r\n !informativeState().dashboardData?.charts?.length\r\n ) {\r\n <div class=\"flex flex-col gap-3 py-3\">\r\n <p-skeleton height=\"6rem\" />\r\n <p-skeleton height=\"12rem\" />\r\n <p-skeleton height=\"12rem\" />\r\n </div>\r\n } @else if (informativeState().error) {\r\n <div\r\n class=\"rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-700\"\r\n >\r\n {{ informativeState().error }}\r\n </div>\r\n } @else if (!informativeState().dashboardData?.charts?.length) {\r\n <div class=\"p-6 text-center text-gray-400\">\r\n {{\r\n \"components.clientComponents.list.noInformativeDashboard\"\r\n | transloco\r\n }}\r\n </div>\r\n } @else {\r\n <div class=\"min-h-[420px]\">\r\n <mt-dashboard-viewer\r\n [isPage]=\"false\"\r\n [showFilters]=\"false\"\r\n [dashboardData]=\"informativeState().dashboardData\"\r\n [extraFilters]=\"dashboardExtraFilters()\"\r\n />\r\n </div>\r\n }\r\n </mt-card>\r\n </div>\r\n\r\n @if (informativeState().config.contentEnd) {\r\n <div\r\n [style.gridColumn]=\"\r\n slotGridSpan(informativeState().config.layout.endSpan)\r\n \"\r\n >\r\n <ng-container\r\n [ngTemplateOutlet]=\"informativeState().config.contentEnd\"\r\n [ngTemplateOutletContext]=\"templateContext(informativeState())\"\r\n />\r\n </div>\r\n }\r\n</div>\r\n" }]
|
|
1682
1679
|
}], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }] } });
|
|
1683
1680
|
|
|
1684
|
-
/**
|
|
1685
|
-
* Below this many lists the filter box is noise — the whole rail fits on
|
|
1686
|
-
* screen and scanning it is faster than typing.
|
|
1687
|
-
*/
|
|
1688
|
-
const FILTER_VISIBLE_FROM = 6;
|
|
1689
|
-
/**
|
|
1690
|
-
* The master rail of the client list's master/detail layout: one row per
|
|
1691
|
-
* configured list, with its record count, and the selected row highlighted.
|
|
1692
|
-
*
|
|
1693
|
-
* Purely presentational — it renders the entries it is handed and emits the
|
|
1694
|
-
* key that was picked. Selection, persistence and loading all stay in
|
|
1695
|
-
* `ClientList`.
|
|
1696
|
-
*
|
|
1697
|
-
* Two shapes, picked by `ClientList` from the width it measures on itself and
|
|
1698
|
-
* handed down as `.mt-cl-nav--rail`: the vertical rail when there is room for
|
|
1699
|
-
* a second column, a horizontally scrolling strip of chips above the content
|
|
1700
|
-
* when there is not. Never keyed off the viewport — see the stylesheet.
|
|
1701
|
-
*/
|
|
1702
|
-
class ClientListNavigator {
|
|
1703
|
-
host = inject((ElementRef));
|
|
1704
|
-
entries = input.required(...(ngDevMode ? [{ debugName: "entries" }] : /* istanbul ignore next */ []));
|
|
1705
|
-
activeKey = input(null, ...(ngDevMode ? [{ debugName: "activeKey" }] : /* istanbul ignore next */ []));
|
|
1706
|
-
/** Id of the content pane, for each tab's `aria-controls`. */
|
|
1707
|
-
panelId = input('', ...(ngDevMode ? [{ debugName: "panelId" }] : /* istanbul ignore next */ []));
|
|
1708
|
-
selected = output();
|
|
1709
|
-
filterTerm = signal('', ...(ngDevMode ? [{ debugName: "filterTerm" }] : /* istanbul ignore next */ []));
|
|
1710
|
-
showFilter = computed(() => this.entries().length >= FILTER_VISIBLE_FROM, ...(ngDevMode ? [{ debugName: "showFilter" }] : /* istanbul ignore next */ []));
|
|
1711
|
-
visibleEntries = computed(() => {
|
|
1712
|
-
const term = this.filterTerm().trim().toLocaleLowerCase();
|
|
1713
|
-
const entries = this.entries();
|
|
1714
|
-
if (!term || !this.showFilter())
|
|
1715
|
-
return entries;
|
|
1716
|
-
return entries.filter((entry) => entry.label.toLocaleLowerCase().includes(term));
|
|
1717
|
-
}, ...(ngDevMode ? [{ debugName: "visibleEntries" }] : /* istanbul ignore next */ []));
|
|
1718
|
-
onFilterChange(value) {
|
|
1719
|
-
this.filterTerm.set(value ?? '');
|
|
1720
|
-
}
|
|
1721
|
-
/**
|
|
1722
|
-
* Roving arrow-key navigation across the rail, per the ARIA tabs pattern.
|
|
1723
|
-
* Up/Down always work; Left/Right too (the rail is horizontal in a narrow
|
|
1724
|
-
* container) and swap meaning under RTL, where "next" is to the left.
|
|
1725
|
-
*/
|
|
1726
|
-
onKeydown(event) {
|
|
1727
|
-
const entries = this.visibleEntries();
|
|
1728
|
-
if (entries.length === 0)
|
|
1729
|
-
return;
|
|
1730
|
-
const rtl = this.isRtl();
|
|
1731
|
-
let step;
|
|
1732
|
-
let absolute = null;
|
|
1733
|
-
switch (event.key) {
|
|
1734
|
-
case 'ArrowDown':
|
|
1735
|
-
step = 1;
|
|
1736
|
-
break;
|
|
1737
|
-
case 'ArrowUp':
|
|
1738
|
-
step = -1;
|
|
1739
|
-
break;
|
|
1740
|
-
case 'ArrowRight':
|
|
1741
|
-
step = rtl ? -1 : 1;
|
|
1742
|
-
break;
|
|
1743
|
-
case 'ArrowLeft':
|
|
1744
|
-
step = rtl ? 1 : -1;
|
|
1745
|
-
break;
|
|
1746
|
-
case 'Home':
|
|
1747
|
-
step = 0;
|
|
1748
|
-
absolute = 0;
|
|
1749
|
-
break;
|
|
1750
|
-
case 'End':
|
|
1751
|
-
step = 0;
|
|
1752
|
-
absolute = entries.length - 1;
|
|
1753
|
-
break;
|
|
1754
|
-
default:
|
|
1755
|
-
return;
|
|
1756
|
-
}
|
|
1757
|
-
const current = entries.findIndex((entry) => entry.key === this.activeKey());
|
|
1758
|
-
const from = current === -1 ? 0 : current;
|
|
1759
|
-
const next = absolute ?? (from + step + entries.length) % entries.length;
|
|
1760
|
-
event.preventDefault();
|
|
1761
|
-
this.selected.emit(entries[next].key);
|
|
1762
|
-
this.focusEntry(next);
|
|
1763
|
-
}
|
|
1764
|
-
focusEntry(index) {
|
|
1765
|
-
// The freshly selected row is re-rendered before it can take focus, so
|
|
1766
|
-
// hand the browser one frame to swap the roving tabindex over.
|
|
1767
|
-
requestAnimationFrame(() => {
|
|
1768
|
-
const element = this.host.nativeElement.querySelector(`[data-nav-index="${index}"]`);
|
|
1769
|
-
element?.focus();
|
|
1770
|
-
});
|
|
1771
|
-
}
|
|
1772
|
-
isRtl() {
|
|
1773
|
-
if (typeof getComputedStyle !== 'function')
|
|
1774
|
-
return false;
|
|
1775
|
-
return (getComputedStyle(this.host.nativeElement).direction ===
|
|
1776
|
-
'rtl');
|
|
1777
|
-
}
|
|
1778
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientListNavigator, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1779
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ClientListNavigator, isStandalone: true, selector: "mt-client-list-navigator", inputs: { entries: { classPropertyName: "entries", publicName: "entries", isSignal: true, isRequired: true, transformFunction: null }, activeKey: { classPropertyName: "activeKey", publicName: "activeKey", isSignal: true, isRequired: false, transformFunction: null }, panelId: { classPropertyName: "panelId", publicName: "panelId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selected" }, ngImport: i0, template: "@if (showFilter()) {\n <div class=\"mt-cl-nav__filter\">\n <mt-text-field\n [field]=\"false\"\n [ngModel]=\"filterTerm()\"\n (ngModelChange)=\"onFilterChange($event)\"\n icon=\"general.search-lg\"\n [placeholder]=\"\n 'components.clientComponents.list.navigator.filter' | transloco\n \"\n />\n </div>\n}\n\n<div\n class=\"mt-cl-nav__list\"\n role=\"tablist\"\n [attr.aria-label]=\"\n 'components.clientComponents.list.navigator.label' | transloco\n \"\n (keydown)=\"onKeydown($event)\"\n>\n @for (entry of visibleEntries(); track entry.key; let i = $index) {\n <button\n type=\"button\"\n role=\"tab\"\n class=\"mt-cl-nav__item\"\n [class.mt-cl-nav__item--active]=\"entry.key === activeKey()\"\n [attr.aria-selected]=\"entry.key === activeKey()\"\n [attr.tabindex]=\"entry.key === activeKey() ? 0 : -1\"\n [attr.aria-controls]=\"panelId() || null\"\n [attr.data-nav-index]=\"i\"\n [title]=\"entry.label\"\n (click)=\"selected.emit(entry.key)\"\n >\n <span class=\"mt-cl-nav__label\">{{ entry.label }}</span>\n\n @if (entry.hasError) {\n <mt-icon\n class=\"mt-cl-nav__error\"\n icon=\"alert.alert-circle\"\n role=\"img\"\n [attr.aria-label]=\"\n 'components.clientComponents.list.navigator.loadFailed' | transloco\n \"\n />\n } @else if (entry.loading && entry.count === null) {\n <span\n class=\"mt-cl-nav__count mt-cl-nav__count--loading\"\n aria-hidden=\"true\"\n ></span>\n } @else if (entry.count !== null) {\n <span\n class=\"mt-cl-nav__count\"\n [class.mt-cl-nav__count--empty]=\"entry.count === 0\"\n >\n {{ entry.count === 0 ? \"\u2013\" : entry.count }}\n </span>\n }\n </button>\n } @empty {\n <p class=\"mt-cl-nav__empty\">\n {{ \"components.clientComponents.list.navigator.noMatches\" | transloco }}\n </p>\n }\n</div>\n\n<p class=\"mt-cl-nav__summary\">\n {{\n \"components.clientComponents.list.navigator.summary\"\n | transloco: { count: entries().length }\n }}\n</p>\n", styles: [":host{display:block;min-width:0}.mt-cl-nav__filter{display:none;margin-block-end:.5rem}.mt-cl-nav__filter mt-text-field{grid-template-columns:minmax(0,1fr)}.mt-cl-nav__list{display:flex;flex-direction:row;gap:.375rem;overflow-x:auto;overscroll-behavior-inline:contain;scrollbar-width:thin;padding-block-end:.375rem}.mt-cl-nav__item{display:flex;flex:0 0 auto;align-items:center;gap:.5rem;min-width:0;padding:.4375rem .75rem;border:1px solid var(--p-content-border-color, #e5e7eb);border-radius:999px;background:var(--p-content-background, #ffffff);color:var(--p-text-muted-color, #6b7280);font-size:.875rem;font-weight:500;line-height:1.25rem;text-align:start;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,color .15s ease}.mt-cl-nav__item:hover{color:var(--p-text-color, #111827);background:color-mix(in srgb,var(--p-primary-color, #2aaec0) 8%,var(--p-content-background, #ffffff))}.mt-cl-nav__item:focus-visible{outline:2px solid var(--p-primary-color, #2aaec0);outline-offset:2px}.mt-cl-nav__item--active,.mt-cl-nav__item--active:hover{border-color:color-mix(in srgb,var(--p-primary-color, #2aaec0) 45%,transparent);background:color-mix(in srgb,var(--p-primary-color, #2aaec0) 12%,var(--p-content-background, #ffffff));color:var(--p-primary-color, #2aaec0);font-weight:600}.mt-cl-nav__label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mt-cl-nav__count{display:inline-flex;flex:0 0 auto;align-items:center;justify-content:center;min-width:1.75rem;height:1.25rem;padding-inline:.375rem;border-radius:999px;background:var(--p-surface-100, #f3f4f6);color:var(--p-text-muted-color, #6b7280);font-size:.75rem;font-weight:600;font-variant-numeric:tabular-nums}.mt-cl-nav__count--empty{background:transparent;color:var(--p-text-muted-color, #9ca3af);font-weight:500}.mt-cl-nav__count--loading{animation:mt-cl-nav-pulse 1.4s ease-in-out infinite}.mt-cl-nav__item--active .mt-cl-nav__count{background:color-mix(in srgb,var(--p-primary-color, #2aaec0) 20%,transparent);color:var(--p-primary-color, #2aaec0)}.mt-cl-nav__error{flex:0 0 auto;color:var(--p-red-500, #ef4444)}.mt-cl-nav__empty,.mt-cl-nav__summary{margin:0;color:var(--p-text-muted-color, #6b7280);font-size:.75rem}.mt-cl-nav__empty{padding:.75rem .25rem}.mt-cl-nav__summary{display:none}@keyframes mt-cl-nav-pulse{0%,to{opacity:1}50%{opacity:.45}}@media(prefers-reduced-motion:reduce){.mt-cl-nav__item{transition:none}.mt-cl-nav__count--loading{animation:none}}:host(.mt-cl-nav--rail) .mt-cl-nav__filter{display:block}:host(.mt-cl-nav--rail) .mt-cl-nav__list{flex-direction:column;gap:.125rem;overflow-x:visible;overflow-y:auto;max-height:32rem;padding-block-end:0}:host(.mt-cl-nav--rail) .mt-cl-nav__item{flex:0 0 auto;width:100%;padding:.625rem .75rem;border:0;border-inline-start:3px solid transparent;border-radius:0;border-start-end-radius:.375rem;border-end-end-radius:.375rem;background:transparent}:host(.mt-cl-nav--rail) .mt-cl-nav__item:hover{background:color-mix(in srgb,var(--p-primary-color, #2aaec0) 6%,transparent)}:host(.mt-cl-nav--rail) .mt-cl-nav__item--active,:host(.mt-cl-nav--rail) .mt-cl-nav__item--active:hover{border-inline-start-color:var(--p-primary-color, #2aaec0);background:color-mix(in srgb,var(--p-primary-color, #2aaec0) 10%,transparent)}:host(.mt-cl-nav--rail) .mt-cl-nav__label{flex:1 1 auto}:host(.mt-cl-nav--rail) .mt-cl-nav__summary{display:block;margin-block-start:.75rem;padding-block-start:.75rem;padding-inline:.75rem;border-block-start:1px solid var(--p-content-border-color, #e5e7eb)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "pipe", type: i4.TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1780
|
-
}
|
|
1781
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientListNavigator, decorators: [{
|
|
1782
|
-
type: Component,
|
|
1783
|
-
args: [{ selector: 'mt-client-list-navigator', standalone: true, imports: [FormsModule, TranslocoModule, Icon, TextField], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (showFilter()) {\n <div class=\"mt-cl-nav__filter\">\n <mt-text-field\n [field]=\"false\"\n [ngModel]=\"filterTerm()\"\n (ngModelChange)=\"onFilterChange($event)\"\n icon=\"general.search-lg\"\n [placeholder]=\"\n 'components.clientComponents.list.navigator.filter' | transloco\n \"\n />\n </div>\n}\n\n<div\n class=\"mt-cl-nav__list\"\n role=\"tablist\"\n [attr.aria-label]=\"\n 'components.clientComponents.list.navigator.label' | transloco\n \"\n (keydown)=\"onKeydown($event)\"\n>\n @for (entry of visibleEntries(); track entry.key; let i = $index) {\n <button\n type=\"button\"\n role=\"tab\"\n class=\"mt-cl-nav__item\"\n [class.mt-cl-nav__item--active]=\"entry.key === activeKey()\"\n [attr.aria-selected]=\"entry.key === activeKey()\"\n [attr.tabindex]=\"entry.key === activeKey() ? 0 : -1\"\n [attr.aria-controls]=\"panelId() || null\"\n [attr.data-nav-index]=\"i\"\n [title]=\"entry.label\"\n (click)=\"selected.emit(entry.key)\"\n >\n <span class=\"mt-cl-nav__label\">{{ entry.label }}</span>\n\n @if (entry.hasError) {\n <mt-icon\n class=\"mt-cl-nav__error\"\n icon=\"alert.alert-circle\"\n role=\"img\"\n [attr.aria-label]=\"\n 'components.clientComponents.list.navigator.loadFailed' | transloco\n \"\n />\n } @else if (entry.loading && entry.count === null) {\n <span\n class=\"mt-cl-nav__count mt-cl-nav__count--loading\"\n aria-hidden=\"true\"\n ></span>\n } @else if (entry.count !== null) {\n <span\n class=\"mt-cl-nav__count\"\n [class.mt-cl-nav__count--empty]=\"entry.count === 0\"\n >\n {{ entry.count === 0 ? \"\u2013\" : entry.count }}\n </span>\n }\n </button>\n } @empty {\n <p class=\"mt-cl-nav__empty\">\n {{ \"components.clientComponents.list.navigator.noMatches\" | transloco }}\n </p>\n }\n</div>\n\n<p class=\"mt-cl-nav__summary\">\n {{\n \"components.clientComponents.list.navigator.summary\"\n | transloco: { count: entries().length }\n }}\n</p>\n", styles: [":host{display:block;min-width:0}.mt-cl-nav__filter{display:none;margin-block-end:.5rem}.mt-cl-nav__filter mt-text-field{grid-template-columns:minmax(0,1fr)}.mt-cl-nav__list{display:flex;flex-direction:row;gap:.375rem;overflow-x:auto;overscroll-behavior-inline:contain;scrollbar-width:thin;padding-block-end:.375rem}.mt-cl-nav__item{display:flex;flex:0 0 auto;align-items:center;gap:.5rem;min-width:0;padding:.4375rem .75rem;border:1px solid var(--p-content-border-color, #e5e7eb);border-radius:999px;background:var(--p-content-background, #ffffff);color:var(--p-text-muted-color, #6b7280);font-size:.875rem;font-weight:500;line-height:1.25rem;text-align:start;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,color .15s ease}.mt-cl-nav__item:hover{color:var(--p-text-color, #111827);background:color-mix(in srgb,var(--p-primary-color, #2aaec0) 8%,var(--p-content-background, #ffffff))}.mt-cl-nav__item:focus-visible{outline:2px solid var(--p-primary-color, #2aaec0);outline-offset:2px}.mt-cl-nav__item--active,.mt-cl-nav__item--active:hover{border-color:color-mix(in srgb,var(--p-primary-color, #2aaec0) 45%,transparent);background:color-mix(in srgb,var(--p-primary-color, #2aaec0) 12%,var(--p-content-background, #ffffff));color:var(--p-primary-color, #2aaec0);font-weight:600}.mt-cl-nav__label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mt-cl-nav__count{display:inline-flex;flex:0 0 auto;align-items:center;justify-content:center;min-width:1.75rem;height:1.25rem;padding-inline:.375rem;border-radius:999px;background:var(--p-surface-100, #f3f4f6);color:var(--p-text-muted-color, #6b7280);font-size:.75rem;font-weight:600;font-variant-numeric:tabular-nums}.mt-cl-nav__count--empty{background:transparent;color:var(--p-text-muted-color, #9ca3af);font-weight:500}.mt-cl-nav__count--loading{animation:mt-cl-nav-pulse 1.4s ease-in-out infinite}.mt-cl-nav__item--active .mt-cl-nav__count{background:color-mix(in srgb,var(--p-primary-color, #2aaec0) 20%,transparent);color:var(--p-primary-color, #2aaec0)}.mt-cl-nav__error{flex:0 0 auto;color:var(--p-red-500, #ef4444)}.mt-cl-nav__empty,.mt-cl-nav__summary{margin:0;color:var(--p-text-muted-color, #6b7280);font-size:.75rem}.mt-cl-nav__empty{padding:.75rem .25rem}.mt-cl-nav__summary{display:none}@keyframes mt-cl-nav-pulse{0%,to{opacity:1}50%{opacity:.45}}@media(prefers-reduced-motion:reduce){.mt-cl-nav__item{transition:none}.mt-cl-nav__count--loading{animation:none}}:host(.mt-cl-nav--rail) .mt-cl-nav__filter{display:block}:host(.mt-cl-nav--rail) .mt-cl-nav__list{flex-direction:column;gap:.125rem;overflow-x:visible;overflow-y:auto;max-height:32rem;padding-block-end:0}:host(.mt-cl-nav--rail) .mt-cl-nav__item{flex:0 0 auto;width:100%;padding:.625rem .75rem;border:0;border-inline-start:3px solid transparent;border-radius:0;border-start-end-radius:.375rem;border-end-end-radius:.375rem;background:transparent}:host(.mt-cl-nav--rail) .mt-cl-nav__item:hover{background:color-mix(in srgb,var(--p-primary-color, #2aaec0) 6%,transparent)}:host(.mt-cl-nav--rail) .mt-cl-nav__item--active,:host(.mt-cl-nav--rail) .mt-cl-nav__item--active:hover{border-inline-start-color:var(--p-primary-color, #2aaec0);background:color-mix(in srgb,var(--p-primary-color, #2aaec0) 10%,transparent)}:host(.mt-cl-nav--rail) .mt-cl-nav__label{flex:1 1 auto}:host(.mt-cl-nav--rail) .mt-cl-nav__summary{display:block;margin-block-start:.75rem;padding-block-start:.75rem;padding-inline:.75rem;border-block-start:1px solid var(--p-content-border-color, #e5e7eb)}\n"] }]
|
|
1784
|
-
}], propDecorators: { entries: [{ type: i0.Input, args: [{ isSignal: true, alias: "entries", required: true }] }], activeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeKey", required: false }] }], panelId: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelId", required: false }] }], selected: [{ type: i0.Output, args: ["selected"] }] } });
|
|
1785
|
-
|
|
1786
1681
|
function mergeRuntimeFilters(configured = [], runtime = {}, columns = []) {
|
|
1787
1682
|
const filters = [...configured];
|
|
1788
1683
|
for (const [rawKey, rawValue] of Object.entries(runtime)) {
|
|
@@ -1877,86 +1772,21 @@ const DEFAULT_COLLAPSE_ICON = 'arrow.chevron-up';
|
|
|
1877
1772
|
const DEFAULT_EXPAND_ICON = 'arrow.chevron-down';
|
|
1878
1773
|
const DEFAULT_SIDE_CONTENT_SPAN = 3;
|
|
1879
1774
|
const DEFAULT_GRID_COLUMNS = 12;
|
|
1880
|
-
/**
|
|
1881
|
-
* Host width, in px, from which the navigator switches from a chip strip
|
|
1882
|
-
* above the content to a rail beside it. Below this a 16.5rem rail plus a
|
|
1883
|
-
* table leaves the table too narrow to be worth the split.
|
|
1884
|
-
*/
|
|
1885
|
-
const RAIL_LAYOUT_MIN_WIDTH = 896;
|
|
1886
|
-
/** localStorage prefix for the per-group remembered selection. */
|
|
1887
|
-
const ACTIVE_KEY_STORAGE_PREFIX = 'mt-client-list:active:';
|
|
1888
1775
|
const ESCALATION_STATE_KEY = 'escalation';
|
|
1889
1776
|
const VIEW_ESCALATION_ACTION_KEY = 'viewEscalation';
|
|
1890
1777
|
const VIEW_ESCALATION_OPERATION_KEY = 'ViewEscalation';
|
|
1891
|
-
/** Makes each instance's `aria-controls` target unique on the page. */
|
|
1892
|
-
let panelIdCounter = 0;
|
|
1893
1778
|
class ClientList {
|
|
1894
1779
|
api = inject(ClientListApiService);
|
|
1895
1780
|
state = inject(ClientListStateService);
|
|
1896
1781
|
runtimeActions = inject(ClientListRuntimeActionsService);
|
|
1897
1782
|
runtimeRunner = inject(RuntimeActionRunner);
|
|
1898
1783
|
transloco = inject(TranslocoService);
|
|
1899
|
-
host = inject(ElementRef);
|
|
1900
1784
|
configurations = input.required(...(ngDevMode ? [{ debugName: "configurations" }] : /* istanbul ignore next */ []));
|
|
1901
1785
|
defaultTake = input(DEFAULT_SERVER_PAGE_SIZE, ...(ngDevMode ? [{ debugName: "defaultTake" }] : /* istanbul ignore next */ []));
|
|
1902
|
-
/**
|
|
1903
|
-
* Arrangement for MORE THAN ONE configuration — see
|
|
1904
|
-
* {@link ClientListMultiLayout}. A single configuration ignores it and
|
|
1905
|
-
* renders exactly as it always has.
|
|
1906
|
-
*/
|
|
1907
|
-
multiLayout = input('navigator', ...(ngDevMode ? [{ debugName: "multiLayout" }] : /* istanbul ignore next */ []));
|
|
1908
1786
|
loaded = output();
|
|
1909
1787
|
errored = output();
|
|
1910
1788
|
itemClicked = output();
|
|
1911
|
-
/** The list the navigator switched to. Never fires in `stacked`. */
|
|
1912
|
-
activeListChanged = output();
|
|
1913
1789
|
items = this.state.items;
|
|
1914
|
-
/** Ties each rail tab to the pane it controls, for screen readers. */
|
|
1915
|
-
panelId = `mt-client-list-panel-${++panelIdCounter}`;
|
|
1916
|
-
activeKeyOverride = signal(null, ...(ngDevMode ? [{ debugName: "activeKeyOverride" }] : /* istanbul ignore next */ []));
|
|
1917
|
-
railLayout = signal(false, ...(ngDevMode ? [{ debugName: "railLayout" }] : /* istanbul ignore next */ []));
|
|
1918
|
-
resizeObserver = null;
|
|
1919
|
-
useNavigator = computed(() => this.multiLayout() === 'navigator' && this.items().length > 1, ...(ngDevMode ? [{ debugName: "useNavigator" }] : /* istanbul ignore next */ []));
|
|
1920
|
-
isRailLayout = this.railLayout.asReadonly();
|
|
1921
|
-
/**
|
|
1922
|
-
* The selected list. Falls back to the first one whenever the override is
|
|
1923
|
-
* unset or points at a list the host has since removed — so a config change
|
|
1924
|
-
* can never leave the pane blank.
|
|
1925
|
-
*/
|
|
1926
|
-
activeKey = computed(() => {
|
|
1927
|
-
const items = this.items();
|
|
1928
|
-
if (items.length === 0)
|
|
1929
|
-
return null;
|
|
1930
|
-
const override = this.activeKeyOverride();
|
|
1931
|
-
return override && items.some((item) => item.key === override)
|
|
1932
|
-
? override
|
|
1933
|
-
: items[0].key;
|
|
1934
|
-
}, ...(ngDevMode ? [{ debugName: "activeKey" }] : /* istanbul ignore next */ []));
|
|
1935
|
-
/**
|
|
1936
|
-
* Identity of the current SET of lists. A string so it settles: every load
|
|
1937
|
-
* tick rebuilds the items array, but the signature only changes when the
|
|
1938
|
-
* host adds, removes or re-keys a list.
|
|
1939
|
-
*/
|
|
1940
|
-
groupSignature = computed(() => this.items()
|
|
1941
|
-
.map((item) => item.key)
|
|
1942
|
-
.join(''), ...(ngDevMode ? [{ debugName: "groupSignature" }] : /* istanbul ignore next */ []));
|
|
1943
|
-
activeItem = computed(() => {
|
|
1944
|
-
const key = this.activeKey();
|
|
1945
|
-
return key ? (this.state.itemsByKey()[key] ?? null) : null;
|
|
1946
|
-
}, ...(ngDevMode ? [{ debugName: "activeItem" }] : /* istanbul ignore next */ []));
|
|
1947
|
-
navigatorEntries = computed(() => this.items().map((item) => ({
|
|
1948
|
-
key: item.key,
|
|
1949
|
-
label: item.title || item.moduleKey || this.defaultTitle(item),
|
|
1950
|
-
// Informative lists count charts, not records — a number there would
|
|
1951
|
-
// read as a record count next to every other row in the rail. A list
|
|
1952
|
-
// still loading its first page has no count yet either; `0` would
|
|
1953
|
-
// claim it is empty.
|
|
1954
|
-
count: item.type === 'informative' || (item.loading && item.totalCount === 0)
|
|
1955
|
-
? null
|
|
1956
|
-
: item.totalCount,
|
|
1957
|
-
loading: item.loading,
|
|
1958
|
-
hasError: !!item.error,
|
|
1959
|
-
})), ...(ngDevMode ? [{ debugName: "navigatorEntries" }] : /* istanbul ignore next */ []));
|
|
1960
1790
|
subscriptions = new Map();
|
|
1961
1791
|
inFlightRequestSignatures = new Map();
|
|
1962
1792
|
fulfilledRequestSignatures = new Map();
|
|
@@ -1971,122 +1801,6 @@ class ClientList {
|
|
|
1971
1801
|
});
|
|
1972
1802
|
});
|
|
1973
1803
|
});
|
|
1974
|
-
effect(() => {
|
|
1975
|
-
// Depends on the signature, not on `items()` itself: every load tick
|
|
1976
|
-
// produces a fresh items array, and re-reading storage on each one
|
|
1977
|
-
// would be pure waste. The string only changes when the set of lists
|
|
1978
|
-
// does, which is exactly when the remembered selection is worth
|
|
1979
|
-
// consulting again.
|
|
1980
|
-
this.groupSignature();
|
|
1981
|
-
untracked(() => this.restoreActiveKey(this.items().map((item) => item.key)));
|
|
1982
|
-
});
|
|
1983
|
-
this.observeHostWidth();
|
|
1984
|
-
}
|
|
1985
|
-
/**
|
|
1986
|
-
* Switches the pane. Remembered per list group, so coming back to a
|
|
1987
|
-
* workspace tab lands on the module you were last reading rather than
|
|
1988
|
-
* resetting to the first one.
|
|
1989
|
-
*/
|
|
1990
|
-
setActiveKey(key) {
|
|
1991
|
-
if (key === this.activeKey())
|
|
1992
|
-
return;
|
|
1993
|
-
if (!this.state.itemsByKey()[key])
|
|
1994
|
-
return;
|
|
1995
|
-
this.activeKeyOverride.set(key);
|
|
1996
|
-
this.persistActiveKey(key);
|
|
1997
|
-
this.activeListChanged.emit(key);
|
|
1998
|
-
}
|
|
1999
|
-
/**
|
|
2000
|
-
* Subtitle under the pane title: the load failure if there is one, the
|
|
2001
|
-
* record count once it is known, nothing while the first page is still in
|
|
2002
|
-
* flight (a "0 records" that flips to "12 records" reads as a bug).
|
|
2003
|
-
*/
|
|
2004
|
-
paneMeta(item) {
|
|
2005
|
-
if (item.error)
|
|
2006
|
-
return item.error;
|
|
2007
|
-
if (item.type === 'informative')
|
|
2008
|
-
return '';
|
|
2009
|
-
if (item.loading && item.totalCount === 0)
|
|
2010
|
-
return '';
|
|
2011
|
-
if (item.totalCount === 0) {
|
|
2012
|
-
return this.transloco.translate('components.clientComponents.list.navigator.noRecords');
|
|
2013
|
-
}
|
|
2014
|
-
return this.transloco.translate(item.totalCount === 1
|
|
2015
|
-
? 'components.clientComponents.list.navigator.oneRecord'
|
|
2016
|
-
: 'components.clientComponents.list.navigator.records', { count: item.totalCount });
|
|
2017
|
-
}
|
|
2018
|
-
/**
|
|
2019
|
-
* Both layouts render a list body through one `ng-template`, whose context
|
|
2020
|
-
* is untyped. Re-narrowing here keeps strict template checking on every
|
|
2021
|
-
* binding inside it instead of silently degrading them to `any`.
|
|
2022
|
-
*/
|
|
2023
|
-
asItem(value) {
|
|
2024
|
-
return value;
|
|
2025
|
-
}
|
|
2026
|
-
/**
|
|
2027
|
-
* Rail beside the content, or chip strip above it. Measured from the host
|
|
2028
|
-
* rather than the viewport: this component renders inside an app shell, so
|
|
2029
|
-
* viewport width says nothing about the room it actually has.
|
|
2030
|
-
*/
|
|
2031
|
-
observeHostWidth() {
|
|
2032
|
-
if (typeof ResizeObserver === 'undefined') {
|
|
2033
|
-
// No measurement available (SSR/tests): the single-column chip strip is
|
|
2034
|
-
// the layout that works at any width.
|
|
2035
|
-
return;
|
|
2036
|
-
}
|
|
2037
|
-
const element = this.host.nativeElement;
|
|
2038
|
-
this.resizeObserver = new ResizeObserver((entries) => {
|
|
2039
|
-
const width = entries[0]?.contentRect.width ?? 0;
|
|
2040
|
-
// Only internal layout changes, so this can't feed its own next
|
|
2041
|
-
// notification — the host's width stays whatever its parent gives it.
|
|
2042
|
-
this.railLayout.set(width >= RAIL_LAYOUT_MIN_WIDTH);
|
|
2043
|
-
});
|
|
2044
|
-
this.resizeObserver.observe(element);
|
|
2045
|
-
}
|
|
2046
|
-
restoreActiveKey(keys) {
|
|
2047
|
-
if (keys.length < 2)
|
|
2048
|
-
return;
|
|
2049
|
-
const override = this.activeKeyOverride();
|
|
2050
|
-
if (override && keys.includes(override))
|
|
2051
|
-
return;
|
|
2052
|
-
const stored = this.readStoredActiveKey(this.groupStorageKey(keys));
|
|
2053
|
-
if (stored && keys.includes(stored)) {
|
|
2054
|
-
this.activeKeyOverride.set(stored);
|
|
2055
|
-
}
|
|
2056
|
-
}
|
|
2057
|
-
/**
|
|
2058
|
-
* Identifies this exact set of lists, so the remembered selection follows
|
|
2059
|
-
* the workspace tab it belongs to and a different tab starts fresh. Hashed
|
|
2060
|
-
* because the keys themselves carry ids and filters and get long.
|
|
2061
|
-
*/
|
|
2062
|
-
groupStorageKey(keys) {
|
|
2063
|
-
const source = [...keys].sort().join('|');
|
|
2064
|
-
let hash = 5381;
|
|
2065
|
-
for (let i = 0; i < source.length; i++) {
|
|
2066
|
-
hash = ((hash << 5) + hash + source.charCodeAt(i)) | 0;
|
|
2067
|
-
}
|
|
2068
|
-
return `${ACTIVE_KEY_STORAGE_PREFIX}${(hash >>> 0).toString(36)}`;
|
|
2069
|
-
}
|
|
2070
|
-
readStoredActiveKey(storageKey) {
|
|
2071
|
-
try {
|
|
2072
|
-
return globalThis.localStorage?.getItem(storageKey) ?? null;
|
|
2073
|
-
}
|
|
2074
|
-
catch {
|
|
2075
|
-
// Storage disabled (private mode, blocked cookies) — selection just
|
|
2076
|
-
// won't survive a reload.
|
|
2077
|
-
return null;
|
|
2078
|
-
}
|
|
2079
|
-
}
|
|
2080
|
-
persistActiveKey(key) {
|
|
2081
|
-
const keys = this.items().map((item) => item.key);
|
|
2082
|
-
if (keys.length < 2)
|
|
2083
|
-
return;
|
|
2084
|
-
try {
|
|
2085
|
-
globalThis.localStorage?.setItem(this.groupStorageKey(keys), key);
|
|
2086
|
-
}
|
|
2087
|
-
catch {
|
|
2088
|
-
/* see readStoredActiveKey */
|
|
2089
|
-
}
|
|
2090
1804
|
}
|
|
2091
1805
|
onLazyLoad(itemKey, event) {
|
|
2092
1806
|
const item = this.state.itemsByKey()[itemKey];
|
|
@@ -2431,8 +2145,6 @@ class ClientList {
|
|
|
2431
2145
|
: 'components.clientComponents.list.table');
|
|
2432
2146
|
}
|
|
2433
2147
|
ngOnDestroy() {
|
|
2434
|
-
this.resizeObserver?.disconnect();
|
|
2435
|
-
this.resizeObserver = null;
|
|
2436
2148
|
this.subscriptions.forEach((sub) => sub.unsubscribe());
|
|
2437
2149
|
this.subscriptions.clear();
|
|
2438
2150
|
this.rowActionsLoadingFnCache.clear();
|
|
@@ -2747,6 +2459,8 @@ class ClientList {
|
|
|
2747
2459
|
table: this.resolveTableDisplayConfig(formConfig, take, isPaginated),
|
|
2748
2460
|
headerStart: config.headerStart,
|
|
2749
2461
|
headerEnd: config.headerEnd,
|
|
2462
|
+
toolbarStart: config.toolbarStart,
|
|
2463
|
+
toolbarEnd: config.toolbarEnd,
|
|
2750
2464
|
contentStart: config.contentStart,
|
|
2751
2465
|
contentEnd: config.contentEnd,
|
|
2752
2466
|
templateContent: config.templateContent,
|
|
@@ -2955,11 +2669,11 @@ class ClientList {
|
|
|
2955
2669
|
return mergeRuntimeFilters(config.filters, this.runtimeFilters.get(key), this.state.itemsByKey()[key]?.columns);
|
|
2956
2670
|
}
|
|
2957
2671
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2958
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ClientList, isStandalone: true, selector: "mt-client-list", inputs: { configurations: { classPropertyName: "configurations", publicName: "configurations", isSignal: true, isRequired: true, transformFunction: null }, defaultTake: { classPropertyName: "defaultTake", publicName: "defaultTake", isSignal: true, isRequired: false, transformFunction: null }
|
|
2672
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ClientList, isStandalone: true, selector: "mt-client-list", inputs: { configurations: { classPropertyName: "configurations", publicName: "configurations", isSignal: true, isRequired: true, transformFunction: null }, defaultTake: { classPropertyName: "defaultTake", publicName: "defaultTake", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { loaded: "loaded", errored: "errored", itemClicked: "itemClicked" }, providers: [
|
|
2959
2673
|
ClientListStateService,
|
|
2960
2674
|
ClientListRuntimeActionsService,
|
|
2961
2675
|
ClientListToolbarService,
|
|
2962
|
-
], ngImport: i0, template: "
|
|
2676
|
+
], ngImport: i0, template: "<div class=\"flex flex-col gap-4\">\r\n @for (item of items(); track item.key) {\r\n <section class=\"flex flex-col gap-4\">\r\n @if (item.config.showHeader) {\r\n <div class=\"flex w-full items-center gap-2\">\r\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\r\n @if (item.config.collapse.enabled) {\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"secondary\"\r\n [icon]=\"\r\n item.expanded\r\n ? item.config.collapse.collapseIcon\r\n : item.config.collapse.expandIcon\r\n \"\r\n (onClick)=\"toggleExpanded(item.key)\"\r\n />\r\n }\r\n <h3 class=\"m-0 text-lg font-semibold\">\r\n {{ item.title || item.moduleKey || defaultTitle(item) }}\r\n </h3>\r\n @if (item.config.headerStart) {\r\n <div class=\"flex items-center gap-2\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"item.config.headerStart\"\r\n [ngTemplateOutletContext]=\"templateContext(item)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n @if (item.config.headerEnd) {\r\n <div class=\"ml-auto flex shrink-0 items-center gap-2\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"item.config.headerEnd\"\r\n [ngTemplateOutletContext]=\"templateContext(item)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (item.expanded || !item.config.collapse.enabled) {\r\n @if (item.config.templateContent) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"item.config.templateContent\"\r\n [ngTemplateOutletContext]=\"templateContext(item)\"\r\n />\r\n } @else if (item.type === \"informative\") {\r\n <mt-client-list-informative-view [state]=\"item\" />\r\n } @else if (item.areaType === \"table\") {\r\n <mt-client-list-table-view\r\n [state]=\"item\"\r\n [rowActions]=\"rowActionsFor(item)\"\r\n [actionShape]=\"item.config.actionShape\"\r\n [rowActionsLoadingFn]=\"rowActionsLoadingFnFor(item)\"\r\n (rowActionsRequested)=\"onRowActionsRequested(item, $event)\"\r\n (lazyLoad)=\"onLazyLoad(item.key, $event)\"\r\n (rowClick)=\"onTableRowClick(item, $event)\"\r\n />\r\n } @else {\r\n <mt-client-list-cards-view\r\n [state]=\"item\"\r\n [rowActions]=\"rowActionsFor(item)\"\r\n [rowActionsLoadingFn]=\"rowActionsLoadingFnFor(item)\"\r\n (rowActionsRequested)=\"onRowActionsRequested(item, $event)\"\r\n (lazyLoad)=\"onLazyLoad(item.key, $event)\"\r\n (loadMore)=\"loadMoreCards(item.key)\"\r\n (cardClick)=\"onCardClick(item, $event)\"\r\n />\r\n }\r\n }\r\n </section>\r\n }\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs", "ariaLabel"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: ClientListTableView, selector: "mt-client-list-table-view", inputs: ["state", "rowActions", "actionShape", "rowActionsLoadingFn"], outputs: ["lazyLoad", "rowClick", "rowActionsRequested"] }, { kind: "component", type: ClientListCardsView, selector: "mt-client-list-cards-view", inputs: ["state", "rowActions", "rowActionsLoadingFn"], outputs: ["lazyLoad", "loadMore", "cardClick", "rowActionsRequested"] }, { kind: "component", type: ClientListInformativeView, selector: "mt-client-list-informative-view", inputs: ["state"] }] });
|
|
2963
2677
|
}
|
|
2964
2678
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientList, decorators: [{
|
|
2965
2679
|
type: Component,
|
|
@@ -2969,13 +2683,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
2969
2683
|
ClientListTableView,
|
|
2970
2684
|
ClientListCardsView,
|
|
2971
2685
|
ClientListInformativeView,
|
|
2972
|
-
ClientListNavigator,
|
|
2973
2686
|
], providers: [
|
|
2974
2687
|
ClientListStateService,
|
|
2975
2688
|
ClientListRuntimeActionsService,
|
|
2976
2689
|
ClientListToolbarService,
|
|
2977
|
-
], template: "
|
|
2978
|
-
}], ctorParameters: () => [], propDecorators: { configurations: [{ type: i0.Input, args: [{ isSignal: true, alias: "configurations", required: true }] }], defaultTake: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultTake", required: false }] }],
|
|
2690
|
+
], template: "<div class=\"flex flex-col gap-4\">\r\n @for (item of items(); track item.key) {\r\n <section class=\"flex flex-col gap-4\">\r\n @if (item.config.showHeader) {\r\n <div class=\"flex w-full items-center gap-2\">\r\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\r\n @if (item.config.collapse.enabled) {\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"secondary\"\r\n [icon]=\"\r\n item.expanded\r\n ? item.config.collapse.collapseIcon\r\n : item.config.collapse.expandIcon\r\n \"\r\n (onClick)=\"toggleExpanded(item.key)\"\r\n />\r\n }\r\n <h3 class=\"m-0 text-lg font-semibold\">\r\n {{ item.title || item.moduleKey || defaultTitle(item) }}\r\n </h3>\r\n @if (item.config.headerStart) {\r\n <div class=\"flex items-center gap-2\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"item.config.headerStart\"\r\n [ngTemplateOutletContext]=\"templateContext(item)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n @if (item.config.headerEnd) {\r\n <div class=\"ml-auto flex shrink-0 items-center gap-2\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"item.config.headerEnd\"\r\n [ngTemplateOutletContext]=\"templateContext(item)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (item.expanded || !item.config.collapse.enabled) {\r\n @if (item.config.templateContent) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"item.config.templateContent\"\r\n [ngTemplateOutletContext]=\"templateContext(item)\"\r\n />\r\n } @else if (item.type === \"informative\") {\r\n <mt-client-list-informative-view [state]=\"item\" />\r\n } @else if (item.areaType === \"table\") {\r\n <mt-client-list-table-view\r\n [state]=\"item\"\r\n [rowActions]=\"rowActionsFor(item)\"\r\n [actionShape]=\"item.config.actionShape\"\r\n [rowActionsLoadingFn]=\"rowActionsLoadingFnFor(item)\"\r\n (rowActionsRequested)=\"onRowActionsRequested(item, $event)\"\r\n (lazyLoad)=\"onLazyLoad(item.key, $event)\"\r\n (rowClick)=\"onTableRowClick(item, $event)\"\r\n />\r\n } @else {\r\n <mt-client-list-cards-view\r\n [state]=\"item\"\r\n [rowActions]=\"rowActionsFor(item)\"\r\n [rowActionsLoadingFn]=\"rowActionsLoadingFnFor(item)\"\r\n (rowActionsRequested)=\"onRowActionsRequested(item, $event)\"\r\n (lazyLoad)=\"onLazyLoad(item.key, $event)\"\r\n (loadMore)=\"loadMoreCards(item.key)\"\r\n (cardClick)=\"onCardClick(item, $event)\"\r\n />\r\n }\r\n }\r\n </section>\r\n }\r\n</div>\r\n" }]
|
|
2691
|
+
}], ctorParameters: () => [], propDecorators: { configurations: [{ type: i0.Input, args: [{ isSignal: true, alias: "configurations", required: true }] }], defaultTake: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultTake", required: false }] }], loaded: [{ type: i0.Output, args: ["loaded"] }], errored: [{ type: i0.Output, args: ["errored"] }], itemClicked: [{ type: i0.Output, args: ["itemClicked"] }] } });
|
|
2979
2692
|
|
|
2980
2693
|
/**
|
|
2981
2694
|
* Generated bundle index. Do not edit.
|