@masterteam/client-components 0.0.94 → 0.0.95
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.
- package/fesm2022/masterteam-client-components-client-instance-preview.mjs +37 -3
- package/fesm2022/masterteam-client-components-client-instance-preview.mjs.map +1 -1
- package/fesm2022/masterteam-client-components-client-list.mjs +1 -1
- package/fesm2022/masterteam-client-components-client-list.mjs.map +1 -1
- package/package.json +5 -5
- package/types/masterteam-client-components-client-instance-preview.d.ts +21 -2
|
@@ -69,9 +69,34 @@ class ClientInstancePreview {
|
|
|
69
69
|
* honored instead. The hard ≤280px overflow stack always applies regardless.
|
|
70
70
|
*/
|
|
71
71
|
stackOnNarrow = input(true, ...(ngDevMode ? [{ debugName: "stackOnNarrow" }] : /* istanbul ignore next */ []));
|
|
72
|
+
/**
|
|
73
|
+
* Level-data row the previewed record belongs to.
|
|
74
|
+
*
|
|
75
|
+
* Supplying it renders every `EntityList` property as the full read-only
|
|
76
|
+
* table the form shows, instead of a count and a drawer. That table loads
|
|
77
|
+
* itself from `Properties/{propertyId}/entitylist/table-metadata` and
|
|
78
|
+
* `fetch/query`, which need the owning level *and* its level-data scope —
|
|
79
|
+
* and the fetch Card payload carries neither, so the host passes this from
|
|
80
|
+
* the route while the level is parsed from `contextKey`.
|
|
81
|
+
*
|
|
82
|
+
* Left null, EntityList keeps the compact renderer: without the scope the
|
|
83
|
+
* table would draw its columns and never a row.
|
|
84
|
+
*/
|
|
85
|
+
levelDataId = input(null, ...(ngDevMode ? [{ debugName: "levelDataId" }] : /* istanbul ignore next */ []));
|
|
72
86
|
loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
73
87
|
error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
74
88
|
response = signal(null, ...(ngDevMode ? [{ debugName: "response" }] : /* istanbul ignore next */ []));
|
|
89
|
+
/**
|
|
90
|
+
* Runtime scope handed to each `EntityList` entity, or null when this host
|
|
91
|
+
* did not supply a level-data row. See {@link levelDataId}.
|
|
92
|
+
*/
|
|
93
|
+
entityListContext = computed(() => {
|
|
94
|
+
const levelDataId = this.levelDataId();
|
|
95
|
+
if (levelDataId == null || !(Number(levelDataId) > 0))
|
|
96
|
+
return null;
|
|
97
|
+
const levelId = readLevelId(this.config().contextKey);
|
|
98
|
+
return levelId > 0 ? { levelId, levelDataId } : null;
|
|
99
|
+
}, ...(ngDevMode ? [{ debugName: "entityListContext" }] : /* istanbul ignore next */ []));
|
|
75
100
|
entities = computed(() => {
|
|
76
101
|
const response = this.response();
|
|
77
102
|
const config = this.config();
|
|
@@ -136,12 +161,12 @@ class ClientInstancePreview {
|
|
|
136
161
|
this.loadSub?.unsubscribe();
|
|
137
162
|
}
|
|
138
163
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientInstancePreview, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
139
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ClientInstancePreview, isStandalone: true, selector: "mt-client-instance-preview", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, hideEmptyState: { classPropertyName: "hideEmptyState", publicName: "hideEmptyState", isSignal: true, isRequired: false, transformFunction: null }, stackOnNarrow: { classPropertyName: "stackOnNarrow", publicName: "stackOnNarrow", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (loading()) {\r\n <div class=\"grid grid-cols-1 gap-3 md:grid-cols-2\">\r\n @for (item of [1, 2, 3, 4]; track item) {\r\n <div class=\"rounded-lg border border-surface-200 bg-surface-50 p-4\">\r\n <div class=\"mb-3 h-4 w-28 animate-pulse rounded bg-surface-200\"></div>\r\n <div class=\"h-5 w-40 animate-pulse rounded bg-surface-200\"></div>\r\n </div>\r\n }\r\n </div>\r\n} @else if (error()) {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-surface-500\">\r\n {{ error() }}\r\n </p>\r\n </div>\r\n} @else if (entities().length) {\r\n <mt-entities-preview\r\n [entities]=\"entities()\"\r\n [stackOnNarrow]=\"stackOnNarrow()\"\r\n />\r\n} @else if (!hideEmptyState()) {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-surface-500\">\r\n {{\r\n \"components.clientComponents.instancePreview.noPreviewData\" | transloco\r\n }}\r\n </p>\r\n </div>\r\n}\r\n", dependencies: [{ kind: "component", type: EntitiesPreview, selector: "mt-entities-preview", inputs: ["entities", "attachmentShape", "stackOnNarrow", "clickableKeys", "activeKeys", "clickableTooltip", "activeTooltip"], outputs: ["entityClick"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
164
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ClientInstancePreview, isStandalone: true, selector: "mt-client-instance-preview", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, hideEmptyState: { classPropertyName: "hideEmptyState", publicName: "hideEmptyState", isSignal: true, isRequired: false, transformFunction: null }, stackOnNarrow: { classPropertyName: "stackOnNarrow", publicName: "stackOnNarrow", isSignal: true, isRequired: false, transformFunction: null }, levelDataId: { classPropertyName: "levelDataId", publicName: "levelDataId", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (loading()) {\r\n <div class=\"grid grid-cols-1 gap-3 md:grid-cols-2\">\r\n @for (item of [1, 2, 3, 4]; track item) {\r\n <div class=\"rounded-lg border border-surface-200 bg-surface-50 p-4\">\r\n <div class=\"mb-3 h-4 w-28 animate-pulse rounded bg-surface-200\"></div>\r\n <div class=\"h-5 w-40 animate-pulse rounded bg-surface-200\"></div>\r\n </div>\r\n }\r\n </div>\r\n} @else if (error()) {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-surface-500\">\r\n {{ error() }}\r\n </p>\r\n </div>\r\n} @else if (entities().length) {\r\n <mt-entities-preview\r\n [entities]=\"entities()\"\r\n [stackOnNarrow]=\"stackOnNarrow()\"\r\n [entityListContext]=\"entityListContext()\"\r\n />\r\n} @else if (!hideEmptyState()) {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-surface-500\">\r\n {{\r\n \"components.clientComponents.instancePreview.noPreviewData\" | transloco\r\n }}\r\n </p>\r\n </div>\r\n}\r\n", dependencies: [{ kind: "component", type: EntitiesPreview, selector: "mt-entities-preview", inputs: ["entities", "attachmentShape", "entityListContext", "stackOnNarrow", "clickableKeys", "activeKeys", "clickableTooltip", "activeTooltip"], outputs: ["entityClick"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
140
165
|
}
|
|
141
166
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientInstancePreview, decorators: [{
|
|
142
167
|
type: Component,
|
|
143
|
-
args: [{ selector: 'mt-client-instance-preview', standalone: true, imports: [EntitiesPreview, TranslocoPipe], template: "@if (loading()) {\r\n <div class=\"grid grid-cols-1 gap-3 md:grid-cols-2\">\r\n @for (item of [1, 2, 3, 4]; track item) {\r\n <div class=\"rounded-lg border border-surface-200 bg-surface-50 p-4\">\r\n <div class=\"mb-3 h-4 w-28 animate-pulse rounded bg-surface-200\"></div>\r\n <div class=\"h-5 w-40 animate-pulse rounded bg-surface-200\"></div>\r\n </div>\r\n }\r\n </div>\r\n} @else if (error()) {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-surface-500\">\r\n {{ error() }}\r\n </p>\r\n </div>\r\n} @else if (entities().length) {\r\n <mt-entities-preview\r\n [entities]=\"entities()\"\r\n [stackOnNarrow]=\"stackOnNarrow()\"\r\n />\r\n} @else if (!hideEmptyState()) {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-surface-500\">\r\n {{\r\n \"components.clientComponents.instancePreview.noPreviewData\" | transloco\r\n }}\r\n </p>\r\n </div>\r\n}\r\n" }]
|
|
144
|
-
}], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], hideEmptyState: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideEmptyState", required: false }] }], stackOnNarrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "stackOnNarrow", required: false }] }] } });
|
|
168
|
+
args: [{ selector: 'mt-client-instance-preview', standalone: true, imports: [EntitiesPreview, TranslocoPipe], template: "@if (loading()) {\r\n <div class=\"grid grid-cols-1 gap-3 md:grid-cols-2\">\r\n @for (item of [1, 2, 3, 4]; track item) {\r\n <div class=\"rounded-lg border border-surface-200 bg-surface-50 p-4\">\r\n <div class=\"mb-3 h-4 w-28 animate-pulse rounded bg-surface-200\"></div>\r\n <div class=\"h-5 w-40 animate-pulse rounded bg-surface-200\"></div>\r\n </div>\r\n }\r\n </div>\r\n} @else if (error()) {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-surface-500\">\r\n {{ error() }}\r\n </p>\r\n </div>\r\n} @else if (entities().length) {\r\n <mt-entities-preview\r\n [entities]=\"entities()\"\r\n [stackOnNarrow]=\"stackOnNarrow()\"\r\n [entityListContext]=\"entityListContext()\"\r\n />\r\n} @else if (!hideEmptyState()) {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-surface-500\">\r\n {{\r\n \"components.clientComponents.instancePreview.noPreviewData\" | transloco\r\n }}\r\n </p>\r\n </div>\r\n}\r\n" }]
|
|
169
|
+
}], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], hideEmptyState: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideEmptyState", required: false }] }], stackOnNarrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "stackOnNarrow", required: false }] }], levelDataId: [{ type: i0.Input, args: [{ isSignal: true, alias: "levelDataId", required: false }] }] } });
|
|
145
170
|
/**
|
|
146
171
|
* "This row exists in the draft but has not been published yet."
|
|
147
172
|
*
|
|
@@ -249,6 +274,15 @@ function toNestedPropertyMeta(meta) {
|
|
|
249
274
|
function toEntityValue(cell) {
|
|
250
275
|
return (cell?.value ?? '');
|
|
251
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* The owning level of a preview context key — `level:14/module:97` and a bare
|
|
279
|
+
* `level:14` both yield 14. `0` when the context names no level, which is what
|
|
280
|
+
* keeps a standalone module preview on the compact EntityList renderer.
|
|
281
|
+
*/
|
|
282
|
+
function readLevelId(contextKey) {
|
|
283
|
+
const match = /level:(\d+)/i.exec(contextKey ?? '');
|
|
284
|
+
return match ? Number(match[1]) : 0;
|
|
285
|
+
}
|
|
252
286
|
|
|
253
287
|
/**
|
|
254
288
|
* Generated bundle index. Do not edit.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"masterteam-client-components-client-instance-preview.mjs","sources":["../../../../packages/masterteam/client-components/client-instance-preview/client-instance-preview-api.service.ts","../../../../packages/masterteam/client-components/client-instance-preview/client-instance-preview.ts","../../../../packages/masterteam/client-components/client-instance-preview/client-instance-preview.html","../../../../packages/masterteam/client-components/client-instance-preview/masterteam-client-components-client-instance-preview.ts"],"sourcesContent":["import { Injectable, inject } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { map, Observable } from 'rxjs';\nimport {\n ClientInstancePreviewConfig,\n PreviewFetchRecordResponse,\n PreviewResponse,\n Response,\n} from './client-instance-preview.model';\n\n@Injectable({ providedIn: 'root' })\nexport class ClientInstancePreviewApiService {\n private readonly http = inject(HttpClient);\n private readonly fetchBaseUrl = 'fetch';\n\n resolve(\n config: ClientInstancePreviewConfig,\n ): Observable<Response<PreviewResponse>> {\n const contextKey = config.contextKey;\n const areaKeys = config.displayAreas?.length\n ? config.displayAreas\n : ['card'];\n\n return this.http\n .get<Response<PreviewFetchRecordResponse>>(\n `${this.fetchBaseUrl}/records/${config.instanceId}`,\n {\n params: this.buildRecordPreviewParams(\n contextKey,\n areaKeys,\n config.processRequestId,\n ),\n },\n )\n .pipe(\n map((recordResponse) => ({\n endpoint: recordResponse.endpoint,\n status: recordResponse.status,\n code: recordResponse.code,\n locale: recordResponse.locale,\n message: recordResponse.message,\n errors: recordResponse.errors,\n cacheSession: recordResponse.cacheSession,\n data: {\n contextKey: recordResponse.data?.contextKey ?? contextKey,\n schemas: recordResponse.data?.schemas ?? [],\n catalog: recordResponse.data?.catalog ?? { properties: [] },\n record: recordResponse.data?.record ?? null,\n displayConfigurations:\n recordResponse.data?.projectionMeta?.displayOrder ?? [],\n },\n })),\n );\n }\n\n private buildRecordPreviewParams(\n contextKey: string,\n areaKeys: string[],\n processRequestId?: number,\n ): Record<string, string> {\n const params: Record<string, string> = {\n contextKey,\n Projection: 'Card',\n };\n\n if (processRequestId != null) {\n params['ProcessContext[RequestId]'] = String(processRequestId);\n }\n\n areaKeys.forEach((areaKey, index) => {\n params[`Display[areas][${index}]`] = areaKey;\n });\n\n return params;\n }\n}\n","import {\n Component,\n computed,\n effect,\n inject,\n input,\n OnDestroy,\n signal,\n untracked,\n} from '@angular/core';\nimport { TranslocoPipe, TranslocoService } from '@jsverse/transloco';\nimport { Subscription } from 'rxjs';\n\nimport {\n EntitiesPreview,\n EntityData,\n EntityNestedPropertyMeta,\n EntitySectionLabel,\n isSectionKey,\n resolveLocalizedLabel,\n readEntityValueColor,\n serializeEntityRawValue,\n} from '@masterteam/components/entities';\n\nimport { ClientInstancePreviewApiService } from './client-instance-preview-api.service';\nimport {\n ClientInstancePreviewConfig,\n DisplayConfiguration,\n PreviewFetchPropertyMeta,\n PreviewFetchValueCell,\n PreviewResponse,\n} from './client-instance-preview.model';\n\n@Component({\n selector: 'mt-client-instance-preview',\n standalone: true,\n imports: [EntitiesPreview, TranslocoPipe],\n templateUrl: './client-instance-preview.html',\n})\nexport class ClientInstancePreview implements OnDestroy {\n private readonly clientInstancePreviewApiService = inject(\n ClientInstancePreviewApiService,\n );\n private readonly transloco = inject(TranslocoService);\n private loadSub?: Subscription;\n\n readonly config = input.required<ClientInstancePreviewConfig>();\n /** When true, suppresses the \"No preview data\" empty-state placeholder so the host can collapse. */\n readonly hideEmptyState = input<boolean>(false);\n /**\n * Forwarded to the inner `mt-entities-preview`. When `true` (default) every\n * field collapses to full width below a ~400px container width; pass `false`\n * from a host wide enough that each entity's `size` configuration should be\n * honored instead. The hard ≤280px overflow stack always applies regardless.\n */\n readonly stackOnNarrow = input<boolean>(true);\n readonly loading = signal(false);\n readonly error = signal<string | null>(null);\n readonly response = signal<PreviewResponse | null>(null);\n\n readonly entities = computed(() => {\n const response = this.response();\n const config = this.config();\n const entities = mapPreviewToEntities(\n response,\n config.displayAreas ?? ['card'],\n );\n\n return response && config.transformEntities\n ? config.transformEntities(entities, response)\n : entities;\n });\n /**\n * Name of the schema the loaded record belongs to — the level name for a\n * `level:{id}` context, the module name for a `.../module:{id}` one. Hosts\n * read it to title the preview (e.g. \"{Level Name} Details\"). Null until the\n * fetch resolves.\n */\n readonly schemaName = computed(() => {\n const response = this.response();\n const schemas = response?.schemas ?? [];\n const schemaId = response?.record?.schemaId;\n const schema =\n schemas.find((candidate) => candidate.id === schemaId) ?? schemas[0];\n\n return schema?.name || null;\n });\n\n constructor() {\n effect(() => {\n const config = this.config();\n if (!config) {\n return;\n }\n\n untracked(() => this.load(config));\n });\n }\n\n load(config: ClientInstancePreviewConfig): void {\n this.loadSub?.unsubscribe();\n this.loading.set(true);\n this.error.set(null);\n\n this.loadSub = this.clientInstancePreviewApiService\n .resolve(config)\n .subscribe({\n next: (response) => {\n this.loading.set(false);\n this.response.set(response.data);\n },\n error: (error) => {\n this.loading.set(false);\n this.response.set(null);\n\n // A row that exists in a Publish-mode project's DRAFT but has not\n // been published yet is a defined state, not a failure: the module\n // tabs read the published plan, so the record legitimately is not\n // there. Rendering the generic \"failed to load\" for it reads as data\n // loss to someone who can see the row in the schedule.\n if (isRecordNotPublished(error)) {\n this.error.set(\n this.transloco.translate(\n 'components.clientComponents.instancePreview.notPublishedYet',\n ),\n );\n return;\n }\n\n const message =\n error?.error?.message ??\n error?.message ??\n this.transloco.translate(\n 'components.clientComponents.instancePreview.loadFailed',\n );\n this.error.set(message);\n },\n });\n }\n\n ngOnDestroy(): void {\n this.loadSub?.unsubscribe();\n }\n}\n\n/**\n * \"This row exists in the draft but has not been published yet.\"\n *\n * Matched on `errors.details.reason`, NOT on the code or the message. The\n * envelope's `errors.code` is a fixed shared set — every not-found is `NF_001` —\n * and the message is localized, so neither can carry a distinction a client is\n * meant to branch on. The reason token is stable and culture-independent.\n */\nconst RECORD_NOT_PUBLISHED_REASON = 'schedule.record_not_published';\n\nfunction isRecordNotPublished(error: unknown): boolean {\n const body = (error as { error?: unknown } | null)?.error as\n | { errors?: { details?: { reason?: unknown } } }\n | null\n | undefined;\n\n return body?.errors?.details?.reason === RECORD_NOT_PUBLISHED_REASON;\n}\n\nfunction mapPreviewToEntities(\n response: PreviewResponse | null,\n areaKeys: string[],\n): EntityData[] {\n const record = response?.record;\n if (!record) {\n return [];\n }\n\n const properties = response?.catalog?.properties ?? [];\n const nestedProperties = response?.catalog?.nestedProperties ?? {};\n const propertyByKey = new Map(\n properties.map((property) => [property.key, property]),\n );\n const displayConfigurationsByPropertyKey =\n buildDisplayConfigurationsByPropertyKey(\n response?.displayConfigurations ?? [],\n areaKeys,\n );\n const lang = resolveActiveLang();\n\n // Iterate the display configs (not the catalog) so section markers — which\n // have no backing catalog property — are emitted too.\n const entities: EntityData[] = [];\n for (const [\n propertyKey,\n configuration,\n ] of displayConfigurationsByPropertyKey) {\n if (isSectionKey(propertyKey)) {\n entities.push(toSectionEntity(configuration, record.id, lang));\n continue;\n }\n const property = propertyByKey.get(propertyKey);\n if (!property) {\n continue;\n }\n entities.push(\n toEntityData(\n property,\n record.values?.[property.key],\n configuration,\n record.id,\n nestedProperties[property.key],\n lang,\n ),\n );\n }\n return entities;\n}\n\nfunction toSectionEntity(\n configuration: DisplayConfiguration,\n recordId: number,\n lang: string,\n): EntityData {\n const label = configuration.configuration?.['label'] as\n | EntitySectionLabel\n | undefined;\n return {\n id: recordId,\n key: configuration.propertyKey,\n normalizedKey: configuration.propertyKey,\n name: resolveLocalizedLabel(label, lang),\n value: '',\n viewType: 'Section',\n order: configuration.order,\n configuration: configuration.configuration as EntityData['configuration'],\n };\n}\n\nfunction resolveActiveLang(): string {\n if (typeof document === 'undefined') return 'en';\n return document.documentElement.lang || 'en';\n}\n\nfunction buildDisplayConfigurationsByPropertyKey(\n displayConfigurations: DisplayConfiguration[],\n areaKeys: string[],\n): Map<string, DisplayConfiguration> {\n const result = new Map<string, DisplayConfiguration>();\n const filteredConfigurations = displayConfigurations.filter((configuration) =>\n areaKeys.includes(configuration.areaKey),\n );\n const source =\n filteredConfigurations.length > 0\n ? filteredConfigurations\n : displayConfigurations;\n\n for (const configuration of source) {\n if (!configuration.propertyKey || result.has(configuration.propertyKey)) {\n continue;\n }\n\n result.set(configuration.propertyKey, configuration);\n }\n\n return result;\n}\n\nfunction toEntityData(\n meta: PreviewFetchPropertyMeta,\n cell: PreviewFetchValueCell | undefined,\n displayConfiguration: DisplayConfiguration | undefined,\n recordId: number,\n nestedProperties: PreviewFetchPropertyMeta[] | undefined,\n lang: string,\n): EntityData {\n const viewType = meta.viewType ?? 'Text';\n // Custom label override saved on the display config wins over the catalog label.\n const customLabel = resolveLocalizedLabel(\n displayConfiguration?.configuration?.['label'] as\n | EntitySectionLabel\n | undefined,\n lang,\n );\n\n return {\n id: recordId,\n propertyId: meta?.id,\n key: meta.key,\n normalizedKey: meta.normalizedKey,\n name: customLabel || meta.label,\n rawValue: serializeEntityRawValue(cell?.raw),\n color: readEntityValueColor(cell?.color),\n value: toEntityValue(cell),\n viewType,\n type: meta?.source,\n order: displayConfiguration?.order ?? meta.order,\n configuration: displayConfiguration?.configuration ?? undefined,\n propertyConfiguration: meta.configuration ?? undefined,\n nestedProperties: nestedProperties?.map(toNestedPropertyMeta),\n comparison: cell?.comparison,\n };\n}\n\nfunction toNestedPropertyMeta(\n meta: PreviewFetchPropertyMeta,\n): EntityNestedPropertyMeta {\n return {\n id: meta.id,\n key: meta.key,\n normalizedKey: meta.normalizedKey,\n label: meta.label,\n viewType: meta.viewType ?? 'Text',\n configuration: meta.configuration ?? undefined,\n order: meta.order,\n };\n}\n\nfunction toEntityValue(\n cell: PreviewFetchValueCell | undefined,\n): EntityData['value'] {\n return (cell?.value ?? '') as EntityData['value'];\n}\n","@if (loading()) {\r\n <div class=\"grid grid-cols-1 gap-3 md:grid-cols-2\">\r\n @for (item of [1, 2, 3, 4]; track item) {\r\n <div class=\"rounded-lg border border-surface-200 bg-surface-50 p-4\">\r\n <div class=\"mb-3 h-4 w-28 animate-pulse rounded bg-surface-200\"></div>\r\n <div class=\"h-5 w-40 animate-pulse rounded bg-surface-200\"></div>\r\n </div>\r\n }\r\n </div>\r\n} @else if (error()) {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-surface-500\">\r\n {{ error() }}\r\n </p>\r\n </div>\r\n} @else if (entities().length) {\r\n <mt-entities-preview\r\n [entities]=\"entities()\"\r\n [stackOnNarrow]=\"stackOnNarrow()\"\r\n />\r\n} @else if (!hideEmptyState()) {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-surface-500\">\r\n {{\r\n \"components.clientComponents.instancePreview.noPreviewData\" | transloco\r\n }}\r\n </p>\r\n </div>\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"masterteam-client-components-client-instance-preview.mjs","sources":["../../../../packages/masterteam/client-components/client-instance-preview/client-instance-preview-api.service.ts","../../../../packages/masterteam/client-components/client-instance-preview/client-instance-preview.ts","../../../../packages/masterteam/client-components/client-instance-preview/client-instance-preview.html","../../../../packages/masterteam/client-components/client-instance-preview/masterteam-client-components-client-instance-preview.ts"],"sourcesContent":["import { Injectable, inject } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { map, Observable } from 'rxjs';\nimport {\n ClientInstancePreviewConfig,\n PreviewFetchRecordResponse,\n PreviewResponse,\n Response,\n} from './client-instance-preview.model';\n\n@Injectable({ providedIn: 'root' })\nexport class ClientInstancePreviewApiService {\n private readonly http = inject(HttpClient);\n private readonly fetchBaseUrl = 'fetch';\n\n resolve(\n config: ClientInstancePreviewConfig,\n ): Observable<Response<PreviewResponse>> {\n const contextKey = config.contextKey;\n const areaKeys = config.displayAreas?.length\n ? config.displayAreas\n : ['card'];\n\n return this.http\n .get<Response<PreviewFetchRecordResponse>>(\n `${this.fetchBaseUrl}/records/${config.instanceId}`,\n {\n params: this.buildRecordPreviewParams(\n contextKey,\n areaKeys,\n config.processRequestId,\n ),\n },\n )\n .pipe(\n map((recordResponse) => ({\n endpoint: recordResponse.endpoint,\n status: recordResponse.status,\n code: recordResponse.code,\n locale: recordResponse.locale,\n message: recordResponse.message,\n errors: recordResponse.errors,\n cacheSession: recordResponse.cacheSession,\n data: {\n contextKey: recordResponse.data?.contextKey ?? contextKey,\n schemas: recordResponse.data?.schemas ?? [],\n catalog: recordResponse.data?.catalog ?? { properties: [] },\n record: recordResponse.data?.record ?? null,\n displayConfigurations:\n recordResponse.data?.projectionMeta?.displayOrder ?? [],\n },\n })),\n );\n }\n\n private buildRecordPreviewParams(\n contextKey: string,\n areaKeys: string[],\n processRequestId?: number,\n ): Record<string, string> {\n const params: Record<string, string> = {\n contextKey,\n Projection: 'Card',\n };\n\n if (processRequestId != null) {\n params['ProcessContext[RequestId]'] = String(processRequestId);\n }\n\n areaKeys.forEach((areaKey, index) => {\n params[`Display[areas][${index}]`] = areaKey;\n });\n\n return params;\n }\n}\n","import {\r\n Component,\r\n computed,\r\n effect,\r\n inject,\r\n input,\r\n OnDestroy,\r\n signal,\r\n untracked,\r\n} from '@angular/core';\r\nimport { TranslocoPipe, TranslocoService } from '@jsverse/transloco';\r\nimport { Subscription } from 'rxjs';\r\n\r\nimport {\r\n EntitiesPreview,\r\n EntityData,\r\n EntityListPreviewContext,\r\n EntityNestedPropertyMeta,\r\n EntitySectionLabel,\r\n isSectionKey,\r\n resolveLocalizedLabel,\r\n readEntityValueColor,\r\n serializeEntityRawValue,\r\n} from '@masterteam/components/entities';\r\n\r\nimport { ClientInstancePreviewApiService } from './client-instance-preview-api.service';\r\nimport {\r\n ClientInstancePreviewConfig,\r\n DisplayConfiguration,\r\n PreviewFetchPropertyMeta,\r\n PreviewFetchValueCell,\r\n PreviewResponse,\r\n} from './client-instance-preview.model';\r\n\r\n@Component({\r\n selector: 'mt-client-instance-preview',\r\n standalone: true,\r\n imports: [EntitiesPreview, TranslocoPipe],\r\n templateUrl: './client-instance-preview.html',\r\n})\r\nexport class ClientInstancePreview implements OnDestroy {\r\n private readonly clientInstancePreviewApiService = inject(\r\n ClientInstancePreviewApiService,\r\n );\r\n private readonly transloco = inject(TranslocoService);\r\n private loadSub?: Subscription;\r\n\r\n readonly config = input.required<ClientInstancePreviewConfig>();\r\n /** When true, suppresses the \"No preview data\" empty-state placeholder so the host can collapse. */\r\n readonly hideEmptyState = input<boolean>(false);\r\n /**\r\n * Forwarded to the inner `mt-entities-preview`. When `true` (default) every\r\n * field collapses to full width below a ~400px container width; pass `false`\r\n * from a host wide enough that each entity's `size` configuration should be\r\n * honored instead. The hard ≤280px overflow stack always applies regardless.\r\n */\r\n readonly stackOnNarrow = input<boolean>(true);\r\n /**\r\n * Level-data row the previewed record belongs to.\r\n *\r\n * Supplying it renders every `EntityList` property as the full read-only\r\n * table the form shows, instead of a count and a drawer. That table loads\r\n * itself from `Properties/{propertyId}/entitylist/table-metadata` and\r\n * `fetch/query`, which need the owning level *and* its level-data scope —\r\n * and the fetch Card payload carries neither, so the host passes this from\r\n * the route while the level is parsed from `contextKey`.\r\n *\r\n * Left null, EntityList keeps the compact renderer: without the scope the\r\n * table would draw its columns and never a row.\r\n */\r\n readonly levelDataId = input<number | string | null>(null);\r\n readonly loading = signal(false);\r\n readonly error = signal<string | null>(null);\r\n readonly response = signal<PreviewResponse | null>(null);\r\n\r\n /**\r\n * Runtime scope handed to each `EntityList` entity, or null when this host\r\n * did not supply a level-data row. See {@link levelDataId}.\r\n */\r\n protected readonly entityListContext =\r\n computed<EntityListPreviewContext | null>(() => {\r\n const levelDataId = this.levelDataId();\r\n if (levelDataId == null || !(Number(levelDataId) > 0)) return null;\r\n\r\n const levelId = readLevelId(this.config().contextKey);\r\n return levelId > 0 ? { levelId, levelDataId } : null;\r\n });\r\n\r\n readonly entities = computed(() => {\r\n const response = this.response();\r\n const config = this.config();\r\n const entities = mapPreviewToEntities(\r\n response,\r\n config.displayAreas ?? ['card'],\r\n );\r\n\r\n return response && config.transformEntities\r\n ? config.transformEntities(entities, response)\r\n : entities;\r\n });\r\n /**\r\n * Name of the schema the loaded record belongs to — the level name for a\r\n * `level:{id}` context, the module name for a `.../module:{id}` one. Hosts\r\n * read it to title the preview (e.g. \"{Level Name} Details\"). Null until the\r\n * fetch resolves.\r\n */\r\n readonly schemaName = computed(() => {\r\n const response = this.response();\r\n const schemas = response?.schemas ?? [];\r\n const schemaId = response?.record?.schemaId;\r\n const schema =\r\n schemas.find((candidate) => candidate.id === schemaId) ?? schemas[0];\r\n\r\n return schema?.name || null;\r\n });\r\n\r\n constructor() {\r\n effect(() => {\r\n const config = this.config();\r\n if (!config) {\r\n return;\r\n }\r\n\r\n untracked(() => this.load(config));\r\n });\r\n }\r\n\r\n load(config: ClientInstancePreviewConfig): void {\r\n this.loadSub?.unsubscribe();\r\n this.loading.set(true);\r\n this.error.set(null);\r\n\r\n this.loadSub = this.clientInstancePreviewApiService\r\n .resolve(config)\r\n .subscribe({\r\n next: (response) => {\r\n this.loading.set(false);\r\n this.response.set(response.data);\r\n },\r\n error: (error) => {\r\n this.loading.set(false);\r\n this.response.set(null);\r\n\r\n // A row that exists in a Publish-mode project's DRAFT but has not\r\n // been published yet is a defined state, not a failure: the module\r\n // tabs read the published plan, so the record legitimately is not\r\n // there. Rendering the generic \"failed to load\" for it reads as data\r\n // loss to someone who can see the row in the schedule.\r\n if (isRecordNotPublished(error)) {\r\n this.error.set(\r\n this.transloco.translate(\r\n 'components.clientComponents.instancePreview.notPublishedYet',\r\n ),\r\n );\r\n return;\r\n }\r\n\r\n const message =\r\n error?.error?.message ??\r\n error?.message ??\r\n this.transloco.translate(\r\n 'components.clientComponents.instancePreview.loadFailed',\r\n );\r\n this.error.set(message);\r\n },\r\n });\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this.loadSub?.unsubscribe();\r\n }\r\n}\r\n\r\n/**\r\n * \"This row exists in the draft but has not been published yet.\"\r\n *\r\n * Matched on `errors.details.reason`, NOT on the code or the message. The\r\n * envelope's `errors.code` is a fixed shared set — every not-found is `NF_001` —\r\n * and the message is localized, so neither can carry a distinction a client is\r\n * meant to branch on. The reason token is stable and culture-independent.\r\n */\r\nconst RECORD_NOT_PUBLISHED_REASON = 'schedule.record_not_published';\r\n\r\nfunction isRecordNotPublished(error: unknown): boolean {\r\n const body = (error as { error?: unknown } | null)?.error as\r\n | { errors?: { details?: { reason?: unknown } } }\r\n | null\r\n | undefined;\r\n\r\n return body?.errors?.details?.reason === RECORD_NOT_PUBLISHED_REASON;\r\n}\r\n\r\nfunction mapPreviewToEntities(\r\n response: PreviewResponse | null,\r\n areaKeys: string[],\r\n): EntityData[] {\r\n const record = response?.record;\r\n if (!record) {\r\n return [];\r\n }\r\n\r\n const properties = response?.catalog?.properties ?? [];\r\n const nestedProperties = response?.catalog?.nestedProperties ?? {};\r\n const propertyByKey = new Map(\r\n properties.map((property) => [property.key, property]),\r\n );\r\n const displayConfigurationsByPropertyKey =\r\n buildDisplayConfigurationsByPropertyKey(\r\n response?.displayConfigurations ?? [],\r\n areaKeys,\r\n );\r\n const lang = resolveActiveLang();\r\n\r\n // Iterate the display configs (not the catalog) so section markers — which\r\n // have no backing catalog property — are emitted too.\r\n const entities: EntityData[] = [];\r\n for (const [\r\n propertyKey,\r\n configuration,\r\n ] of displayConfigurationsByPropertyKey) {\r\n if (isSectionKey(propertyKey)) {\r\n entities.push(toSectionEntity(configuration, record.id, lang));\r\n continue;\r\n }\r\n const property = propertyByKey.get(propertyKey);\r\n if (!property) {\r\n continue;\r\n }\r\n entities.push(\r\n toEntityData(\r\n property,\r\n record.values?.[property.key],\r\n configuration,\r\n record.id,\r\n nestedProperties[property.key],\r\n lang,\r\n ),\r\n );\r\n }\r\n return entities;\r\n}\r\n\r\nfunction toSectionEntity(\r\n configuration: DisplayConfiguration,\r\n recordId: number,\r\n lang: string,\r\n): EntityData {\r\n const label = configuration.configuration?.['label'] as\r\n | EntitySectionLabel\r\n | undefined;\r\n return {\r\n id: recordId,\r\n key: configuration.propertyKey,\r\n normalizedKey: configuration.propertyKey,\r\n name: resolveLocalizedLabel(label, lang),\r\n value: '',\r\n viewType: 'Section',\r\n order: configuration.order,\r\n configuration: configuration.configuration as EntityData['configuration'],\r\n };\r\n}\r\n\r\nfunction resolveActiveLang(): string {\r\n if (typeof document === 'undefined') return 'en';\r\n return document.documentElement.lang || 'en';\r\n}\r\n\r\nfunction buildDisplayConfigurationsByPropertyKey(\r\n displayConfigurations: DisplayConfiguration[],\r\n areaKeys: string[],\r\n): Map<string, DisplayConfiguration> {\r\n const result = new Map<string, DisplayConfiguration>();\r\n const filteredConfigurations = displayConfigurations.filter((configuration) =>\r\n areaKeys.includes(configuration.areaKey),\r\n );\r\n const source =\r\n filteredConfigurations.length > 0\r\n ? filteredConfigurations\r\n : displayConfigurations;\r\n\r\n for (const configuration of source) {\r\n if (!configuration.propertyKey || result.has(configuration.propertyKey)) {\r\n continue;\r\n }\r\n\r\n result.set(configuration.propertyKey, configuration);\r\n }\r\n\r\n return result;\r\n}\r\n\r\nfunction toEntityData(\r\n meta: PreviewFetchPropertyMeta,\r\n cell: PreviewFetchValueCell | undefined,\r\n displayConfiguration: DisplayConfiguration | undefined,\r\n recordId: number,\r\n nestedProperties: PreviewFetchPropertyMeta[] | undefined,\r\n lang: string,\r\n): EntityData {\r\n const viewType = meta.viewType ?? 'Text';\r\n // Custom label override saved on the display config wins over the catalog label.\r\n const customLabel = resolveLocalizedLabel(\r\n displayConfiguration?.configuration?.['label'] as\r\n | EntitySectionLabel\r\n | undefined,\r\n lang,\r\n );\r\n\r\n return {\r\n id: recordId,\r\n propertyId: meta?.id,\r\n key: meta.key,\r\n normalizedKey: meta.normalizedKey,\r\n name: customLabel || meta.label,\r\n rawValue: serializeEntityRawValue(cell?.raw),\r\n color: readEntityValueColor(cell?.color),\r\n value: toEntityValue(cell),\r\n viewType,\r\n type: meta?.source,\r\n order: displayConfiguration?.order ?? meta.order,\r\n configuration: displayConfiguration?.configuration ?? undefined,\r\n propertyConfiguration: meta.configuration ?? undefined,\r\n nestedProperties: nestedProperties?.map(toNestedPropertyMeta),\r\n comparison: cell?.comparison,\r\n };\r\n}\r\n\r\nfunction toNestedPropertyMeta(\r\n meta: PreviewFetchPropertyMeta,\r\n): EntityNestedPropertyMeta {\r\n return {\r\n id: meta.id,\r\n key: meta.key,\r\n normalizedKey: meta.normalizedKey,\r\n label: meta.label,\r\n viewType: meta.viewType ?? 'Text',\r\n configuration: meta.configuration ?? undefined,\r\n order: meta.order,\r\n };\r\n}\r\n\r\nfunction toEntityValue(\r\n cell: PreviewFetchValueCell | undefined,\r\n): EntityData['value'] {\r\n return (cell?.value ?? '') as EntityData['value'];\r\n}\r\n\r\n/**\r\n * The owning level of a preview context key — `level:14/module:97` and a bare\r\n * `level:14` both yield 14. `0` when the context names no level, which is what\r\n * keeps a standalone module preview on the compact EntityList renderer.\r\n */\r\nfunction readLevelId(contextKey: string | undefined): number {\r\n const match = /level:(\\d+)/i.exec(contextKey ?? '');\r\n return match ? Number(match[1]) : 0;\r\n}\r\n","@if (loading()) {\r\n <div class=\"grid grid-cols-1 gap-3 md:grid-cols-2\">\r\n @for (item of [1, 2, 3, 4]; track item) {\r\n <div class=\"rounded-lg border border-surface-200 bg-surface-50 p-4\">\r\n <div class=\"mb-3 h-4 w-28 animate-pulse rounded bg-surface-200\"></div>\r\n <div class=\"h-5 w-40 animate-pulse rounded bg-surface-200\"></div>\r\n </div>\r\n }\r\n </div>\r\n} @else if (error()) {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-surface-500\">\r\n {{ error() }}\r\n </p>\r\n </div>\r\n} @else if (entities().length) {\r\n <mt-entities-preview\r\n [entities]=\"entities()\"\r\n [stackOnNarrow]=\"stackOnNarrow()\"\r\n [entityListContext]=\"entityListContext()\"\r\n />\r\n} @else if (!hideEmptyState()) {\r\n <div\r\n class=\"flex min-h-[22rem] items-center justify-center rounded-lg border border-dashed border-surface-300 bg-surface-50 p-6\"\r\n >\r\n <p class=\"max-w-md text-center text-sm text-surface-500\">\r\n {{\r\n \"components.clientComponents.instancePreview.noPreviewData\" | transloco\r\n }}\r\n </p>\r\n </div>\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -2001,7 +2001,7 @@ class ClientListCardsView {
|
|
|
2001
2001
|
});
|
|
2002
2002
|
}
|
|
2003
2003
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientListCardsView, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2004
|
-
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: 1rem;--mt-cards-min: 335px;--mt-cards-track: max( var(--mt-cards-min), (100% - 3 * var(--mt-cards-gap)) / 4 );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,
|
|
2004
|
+
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: 1rem;--mt-cards-min: 335px;--mt-cards-track: max( var(--mt-cards-min), (100% - 3 * var(--mt-cards-gap)) / 4 );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", "entityListContext", "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,
|
|
2005
2005
|
EntitiesPreview]] });
|
|
2006
2006
|
}
|
|
2007
2007
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientListCardsView, decorators: [{
|