@masterteam/client-components 0.0.82 → 0.0.83
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.map +1 -1
- package/fesm2022/masterteam-client-components-client-list.mjs +294 -5
- package/fesm2022/masterteam-client-components-client-list.mjs.map +1 -1
- package/package.json +4 -4
- package/types/masterteam-client-components-client-list.d.ts +94 -2
|
@@ -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';\r\nimport { HttpClient } from '@angular/common/http';\r\nimport { map, Observable } from 'rxjs';\r\nimport {\r\n ClientInstancePreviewConfig,\r\n PreviewFetchRecordResponse,\r\n PreviewResponse,\r\n Response,\r\n} from './client-instance-preview.model';\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class ClientInstancePreviewApiService {\r\n private readonly http = inject(HttpClient);\r\n private readonly fetchBaseUrl = 'fetch';\r\n\r\n resolve(\r\n config: ClientInstancePreviewConfig,\r\n ): Observable<Response<PreviewResponse>> {\r\n const contextKey = config.contextKey;\r\n const areaKeys = config.displayAreas?.length\r\n ? config.displayAreas\r\n : ['card'];\r\n\r\n return this.http\r\n .get<Response<PreviewFetchRecordResponse>>(\r\n `${this.fetchBaseUrl}/records/${config.instanceId}`,\r\n {\r\n params: this.buildRecordPreviewParams(\r\n contextKey,\r\n areaKeys,\r\n config.processRequestId,\r\n ),\r\n },\r\n )\r\n .pipe(\r\n map((recordResponse) => ({\r\n endpoint: recordResponse.endpoint,\r\n status: recordResponse.status,\r\n code: recordResponse.code,\r\n locale: recordResponse.locale,\r\n message: recordResponse.message,\r\n errors: recordResponse.errors,\r\n cacheSession: recordResponse.cacheSession,\r\n data: {\r\n contextKey: recordResponse.data?.contextKey ?? contextKey,\r\n schemas: recordResponse.data?.schemas ?? [],\r\n catalog: recordResponse.data?.catalog ?? { properties: [] },\r\n record: recordResponse.data?.record ?? null,\r\n displayConfigurations:\r\n recordResponse.data?.projectionMeta?.displayOrder ?? [],\r\n },\r\n })),\r\n );\r\n }\r\n\r\n private buildRecordPreviewParams(\r\n contextKey: string,\r\n areaKeys: string[],\r\n processRequestId?: number,\r\n ): Record<string, string> {\r\n const params: Record<string, string> = {\r\n contextKey,\r\n Projection: 'Card',\r\n };\r\n\r\n if (processRequestId != null) {\r\n params['ProcessContext[RequestId]'] = String(processRequestId);\r\n }\r\n\r\n areaKeys.forEach((areaKey, index) => {\r\n params[`Display[areas][${index}]`] = areaKey;\r\n });\r\n\r\n return params;\r\n }\r\n}\r\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 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 mapPreviewToEntities(\n this.response(),\n this.config().displayAreas ?? ['card'],\n ),\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 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';\r\nimport { HttpClient } from '@angular/common/http';\r\nimport { map, Observable } from 'rxjs';\r\nimport {\r\n ClientInstancePreviewConfig,\r\n PreviewFetchRecordResponse,\r\n PreviewResponse,\r\n Response,\r\n} from './client-instance-preview.model';\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class ClientInstancePreviewApiService {\r\n private readonly http = inject(HttpClient);\r\n private readonly fetchBaseUrl = 'fetch';\r\n\r\n resolve(\r\n config: ClientInstancePreviewConfig,\r\n ): Observable<Response<PreviewResponse>> {\r\n const contextKey = config.contextKey;\r\n const areaKeys = config.displayAreas?.length\r\n ? config.displayAreas\r\n : ['card'];\r\n\r\n return this.http\r\n .get<Response<PreviewFetchRecordResponse>>(\r\n `${this.fetchBaseUrl}/records/${config.instanceId}`,\r\n {\r\n params: this.buildRecordPreviewParams(\r\n contextKey,\r\n areaKeys,\r\n config.processRequestId,\r\n ),\r\n },\r\n )\r\n .pipe(\r\n map((recordResponse) => ({\r\n endpoint: recordResponse.endpoint,\r\n status: recordResponse.status,\r\n code: recordResponse.code,\r\n locale: recordResponse.locale,\r\n message: recordResponse.message,\r\n errors: recordResponse.errors,\r\n cacheSession: recordResponse.cacheSession,\r\n data: {\r\n contextKey: recordResponse.data?.contextKey ?? contextKey,\r\n schemas: recordResponse.data?.schemas ?? [],\r\n catalog: recordResponse.data?.catalog ?? { properties: [] },\r\n record: recordResponse.data?.record ?? null,\r\n displayConfigurations:\r\n recordResponse.data?.projectionMeta?.displayOrder ?? [],\r\n },\r\n })),\r\n );\r\n }\r\n\r\n private buildRecordPreviewParams(\r\n contextKey: string,\r\n areaKeys: string[],\r\n processRequestId?: number,\r\n ): Record<string, string> {\r\n const params: Record<string, string> = {\r\n contextKey,\r\n Projection: 'Card',\r\n };\r\n\r\n if (processRequestId != null) {\r\n params['ProcessContext[RequestId]'] = String(processRequestId);\r\n }\r\n\r\n areaKeys.forEach((areaKey, index) => {\r\n params[`Display[areas][${index}]`] = areaKey;\r\n });\r\n\r\n return params;\r\n }\r\n}\r\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 EntityNestedPropertyMeta,\r\n EntitySectionLabel,\r\n isSectionKey,\r\n resolveLocalizedLabel,\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 readonly loading = signal(false);\r\n readonly error = signal<string | null>(null);\r\n readonly response = signal<PreviewResponse | null>(null);\r\n\r\n readonly entities = computed(() =>\r\n mapPreviewToEntities(\r\n this.response(),\r\n this.config().displayAreas ?? ['card'],\r\n ),\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 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","@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,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, Injectable, signal, computed, input, output, Component, viewChild, effect, untracked, ChangeDetectionStrategy } from '@angular/core';
|
|
2
|
+
import { inject, Injectable, signal, computed, input, output, Component, viewChild, effect, untracked, ChangeDetectionStrategy, ElementRef } 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,6 +22,9 @@ 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';
|
|
25
28
|
|
|
26
29
|
const DEFAULT_INCLUDE_STATE = ['escalation'];
|
|
27
30
|
class ClientListApiService {
|
|
@@ -1678,6 +1681,108 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1678
1681
|
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" }]
|
|
1679
1682
|
}], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }] } });
|
|
1680
1683
|
|
|
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
|
+
|
|
1681
1786
|
function mergeRuntimeFilters(configured = [], runtime = {}, columns = []) {
|
|
1682
1787
|
const filters = [...configured];
|
|
1683
1788
|
for (const [rawKey, rawValue] of Object.entries(runtime)) {
|
|
@@ -1772,21 +1877,86 @@ const DEFAULT_COLLAPSE_ICON = 'arrow.chevron-up';
|
|
|
1772
1877
|
const DEFAULT_EXPAND_ICON = 'arrow.chevron-down';
|
|
1773
1878
|
const DEFAULT_SIDE_CONTENT_SPAN = 3;
|
|
1774
1879
|
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:';
|
|
1775
1888
|
const ESCALATION_STATE_KEY = 'escalation';
|
|
1776
1889
|
const VIEW_ESCALATION_ACTION_KEY = 'viewEscalation';
|
|
1777
1890
|
const VIEW_ESCALATION_OPERATION_KEY = 'ViewEscalation';
|
|
1891
|
+
/** Makes each instance's `aria-controls` target unique on the page. */
|
|
1892
|
+
let panelIdCounter = 0;
|
|
1778
1893
|
class ClientList {
|
|
1779
1894
|
api = inject(ClientListApiService);
|
|
1780
1895
|
state = inject(ClientListStateService);
|
|
1781
1896
|
runtimeActions = inject(ClientListRuntimeActionsService);
|
|
1782
1897
|
runtimeRunner = inject(RuntimeActionRunner);
|
|
1783
1898
|
transloco = inject(TranslocoService);
|
|
1899
|
+
host = inject(ElementRef);
|
|
1784
1900
|
configurations = input.required(...(ngDevMode ? [{ debugName: "configurations" }] : /* istanbul ignore next */ []));
|
|
1785
1901
|
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 */ []));
|
|
1786
1908
|
loaded = output();
|
|
1787
1909
|
errored = output();
|
|
1788
1910
|
itemClicked = output();
|
|
1911
|
+
/** The list the navigator switched to. Never fires in `stacked`. */
|
|
1912
|
+
activeListChanged = output();
|
|
1789
1913
|
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 */ []));
|
|
1790
1960
|
subscriptions = new Map();
|
|
1791
1961
|
inFlightRequestSignatures = new Map();
|
|
1792
1962
|
fulfilledRequestSignatures = new Map();
|
|
@@ -1801,6 +1971,122 @@ class ClientList {
|
|
|
1801
1971
|
});
|
|
1802
1972
|
});
|
|
1803
1973
|
});
|
|
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
|
+
}
|
|
1804
2090
|
}
|
|
1805
2091
|
onLazyLoad(itemKey, event) {
|
|
1806
2092
|
const item = this.state.itemsByKey()[itemKey];
|
|
@@ -2145,6 +2431,8 @@ class ClientList {
|
|
|
2145
2431
|
: 'components.clientComponents.list.table');
|
|
2146
2432
|
}
|
|
2147
2433
|
ngOnDestroy() {
|
|
2434
|
+
this.resizeObserver?.disconnect();
|
|
2435
|
+
this.resizeObserver = null;
|
|
2148
2436
|
this.subscriptions.forEach((sub) => sub.unsubscribe());
|
|
2149
2437
|
this.subscriptions.clear();
|
|
2150
2438
|
this.rowActionsLoadingFnCache.clear();
|
|
@@ -2667,11 +2955,11 @@ class ClientList {
|
|
|
2667
2955
|
return mergeRuntimeFilters(config.filters, this.runtimeFilters.get(key), this.state.itemsByKey()[key]?.columns);
|
|
2668
2956
|
}
|
|
2669
2957
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2670
|
-
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: [
|
|
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 }, multiLayout: { classPropertyName: "multiLayout", publicName: "multiLayout", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { loaded: "loaded", errored: "errored", itemClicked: "itemClicked", activeListChanged: "activeListChanged" }, providers: [
|
|
2671
2959
|
ClientListStateService,
|
|
2672
2960
|
ClientListRuntimeActionsService,
|
|
2673
2961
|
ClientListToolbarService,
|
|
2674
|
-
], ngImport: i0, template: "<div class=\"flex flex-col gap-4\">\
|
|
2962
|
+
], ngImport: i0, template: "@if (useNavigator()) {\n <!--\n Master/detail: the rail lists every configured module, the pane renders\n only the selected one. Deliberately NOT a hidden-but-mounted pane per\n module \u2014 a seven-module workspace tab would otherwise mount seven tables,\n each with its own virtual scroller and paginator, to show one.\n -->\n <div class=\"mt-cl-shell\" [class.mt-cl-shell--rail]=\"isRailLayout()\">\n <mt-client-list-navigator\n class=\"mt-cl-shell__rail\"\n [class.mt-cl-nav--rail]=\"isRailLayout()\"\n [entries]=\"navigatorEntries()\"\n [activeKey]=\"activeKey()\"\n [panelId]=\"panelId\"\n (selected)=\"setActiveKey($event)\"\n />\n\n <div class=\"mt-cl-shell__pane\" role=\"tabpanel\" [id]=\"panelId\">\n @if (activeItem(); as item) {\n @if (item.config.showHeader) {\n <div class=\"mt-cl-pane__header\">\n <div class=\"mt-cl-pane__heading\">\n <h3 class=\"mt-cl-pane__title\">\n {{ item.title || item.moduleKey || defaultTitle(item) }}\n </h3>\n @if (paneMeta(item); as meta) {\n <p\n class=\"mt-cl-pane__meta\"\n [class.mt-cl-pane__meta--error]=\"!!item.error\"\n >\n {{ meta }}\n </p>\n }\n </div>\n\n @if (item.config.headerStart) {\n <div class=\"mt-cl-pane__header-start\">\n <ng-container\n [ngTemplateOutlet]=\"item.config.headerStart\"\n [ngTemplateOutletContext]=\"templateContext(item)\"\n />\n </div>\n }\n\n @if (item.config.headerEnd) {\n <div class=\"mt-cl-pane__actions\">\n <ng-container\n [ngTemplateOutlet]=\"item.config.headerEnd\"\n [ngTemplateOutletContext]=\"templateContext(item)\"\n />\n </div>\n }\n </div>\n }\n\n <ng-container\n [ngTemplateOutlet]=\"itemContent\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n }\n </div>\n </div>\n} @else {\n <div class=\"flex flex-col gap-4\">\n @for (item of items(); track item.key) {\n <section class=\"flex flex-col gap-4\">\n @if (item.config.showHeader) {\n <div class=\"flex w-full items-center gap-2\">\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\n @if (item.config.collapse.enabled) {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n [icon]=\"\n item.expanded\n ? item.config.collapse.collapseIcon\n : item.config.collapse.expandIcon\n \"\n (onClick)=\"toggleExpanded(item.key)\"\n />\n }\n <h3 class=\"m-0 text-lg font-semibold\">\n {{ item.title || item.moduleKey || defaultTitle(item) }}\n </h3>\n @if (item.config.headerStart) {\n <div class=\"flex items-center gap-2\">\n <ng-container\n [ngTemplateOutlet]=\"item.config.headerStart\"\n [ngTemplateOutletContext]=\"templateContext(item)\"\n />\n </div>\n }\n </div>\n @if (item.config.headerEnd) {\n <div class=\"ml-auto flex shrink-0 items-center gap-2\">\n <ng-container\n [ngTemplateOutlet]=\"item.config.headerEnd\"\n [ngTemplateOutletContext]=\"templateContext(item)\"\n />\n </div>\n }\n </div>\n }\n\n @if (item.expanded || !item.config.collapse.enabled) {\n <ng-container\n [ngTemplateOutlet]=\"itemContent\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n }\n </section>\n }\n </div>\n}\n\n<!--\n The one place a list's body is described. Both layouts render through it so\n the navigator pane and the stacked sections can never drift apart.\n-->\n<ng-template #itemContent let-rawItem>\n @let item = asItem(rawItem);\n\n @if (item.config.templateContent) {\n <ng-container\n [ngTemplateOutlet]=\"item.config.templateContent\"\n [ngTemplateOutletContext]=\"templateContext(item)\"\n />\n } @else if (item.type === \"informative\") {\n <mt-client-list-informative-view [state]=\"item\" />\n } @else if (item.areaType === \"table\") {\n <mt-client-list-table-view\n [state]=\"item\"\n [rowActions]=\"rowActionsFor(item)\"\n [actionShape]=\"item.config.actionShape\"\n [rowActionsLoadingFn]=\"rowActionsLoadingFnFor(item)\"\n (rowActionsRequested)=\"onRowActionsRequested(item, $event)\"\n (lazyLoad)=\"onLazyLoad(item.key, $event)\"\n (rowClick)=\"onTableRowClick(item, $event)\"\n />\n } @else {\n <mt-client-list-cards-view\n [state]=\"item\"\n [rowActions]=\"rowActionsFor(item)\"\n [rowActionsLoadingFn]=\"rowActionsLoadingFnFor(item)\"\n (rowActionsRequested)=\"onRowActionsRequested(item, $event)\"\n (lazyLoad)=\"onLazyLoad(item.key, $event)\"\n (loadMore)=\"loadMoreCards(item.key)\"\n (cardClick)=\"onCardClick(item, $event)\"\n />\n }\n</ng-template>\n", styles: [".mt-cl-shell{display:grid;grid-template-columns:minmax(0,1fr);gap:1rem;align-items:start}.mt-cl-shell--rail{grid-template-columns:16.5rem minmax(0,1fr);gap:1.5rem}.mt-cl-shell--rail .mt-cl-shell__rail{padding-inline-end:1.25rem;border-inline-end:1px solid var(--p-content-border-color, #e5e7eb)}.mt-cl-shell__pane{display:flex;flex-direction:column;gap:1rem;min-width:0}.mt-cl-pane__header{display:flex;flex-wrap:wrap;align-items:flex-start;gap:.75rem 1rem}.mt-cl-pane__heading{display:flex;flex-direction:column;gap:.125rem;min-width:0;flex:1 1 auto}.mt-cl-pane__title{margin:0;color:var(--p-text-color, #111827);font-size:1.25rem;font-weight:600;line-height:1.75rem;overflow-wrap:anywhere}.mt-cl-pane__meta{margin:0;color:var(--p-text-muted-color, #6b7280);font-size:.8125rem;line-height:1.25rem}.mt-cl-pane__meta--error{color:var(--p-red-500, #ef4444)}.mt-cl-pane__actions{display:flex;flex:0 0 auto;align-items:center;gap:.5rem;margin-inline-start:auto}.mt-cl-pane__header-start{display:flex;flex:0 0 auto;align-items:center;gap:.5rem}\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"] }, { kind: "component", type: ClientListNavigator, selector: "mt-client-list-navigator", inputs: ["entries", "activeKey", "panelId"], outputs: ["selected"] }] });
|
|
2675
2963
|
}
|
|
2676
2964
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientList, decorators: [{
|
|
2677
2965
|
type: Component,
|
|
@@ -2681,12 +2969,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
2681
2969
|
ClientListTableView,
|
|
2682
2970
|
ClientListCardsView,
|
|
2683
2971
|
ClientListInformativeView,
|
|
2972
|
+
ClientListNavigator,
|
|
2684
2973
|
], providers: [
|
|
2685
2974
|
ClientListStateService,
|
|
2686
2975
|
ClientListRuntimeActionsService,
|
|
2687
2976
|
ClientListToolbarService,
|
|
2688
|
-
], template: "<div class=\"flex flex-col gap-4\">\
|
|
2689
|
-
}], 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"] }] } });
|
|
2977
|
+
], template: "@if (useNavigator()) {\n <!--\n Master/detail: the rail lists every configured module, the pane renders\n only the selected one. Deliberately NOT a hidden-but-mounted pane per\n module \u2014 a seven-module workspace tab would otherwise mount seven tables,\n each with its own virtual scroller and paginator, to show one.\n -->\n <div class=\"mt-cl-shell\" [class.mt-cl-shell--rail]=\"isRailLayout()\">\n <mt-client-list-navigator\n class=\"mt-cl-shell__rail\"\n [class.mt-cl-nav--rail]=\"isRailLayout()\"\n [entries]=\"navigatorEntries()\"\n [activeKey]=\"activeKey()\"\n [panelId]=\"panelId\"\n (selected)=\"setActiveKey($event)\"\n />\n\n <div class=\"mt-cl-shell__pane\" role=\"tabpanel\" [id]=\"panelId\">\n @if (activeItem(); as item) {\n @if (item.config.showHeader) {\n <div class=\"mt-cl-pane__header\">\n <div class=\"mt-cl-pane__heading\">\n <h3 class=\"mt-cl-pane__title\">\n {{ item.title || item.moduleKey || defaultTitle(item) }}\n </h3>\n @if (paneMeta(item); as meta) {\n <p\n class=\"mt-cl-pane__meta\"\n [class.mt-cl-pane__meta--error]=\"!!item.error\"\n >\n {{ meta }}\n </p>\n }\n </div>\n\n @if (item.config.headerStart) {\n <div class=\"mt-cl-pane__header-start\">\n <ng-container\n [ngTemplateOutlet]=\"item.config.headerStart\"\n [ngTemplateOutletContext]=\"templateContext(item)\"\n />\n </div>\n }\n\n @if (item.config.headerEnd) {\n <div class=\"mt-cl-pane__actions\">\n <ng-container\n [ngTemplateOutlet]=\"item.config.headerEnd\"\n [ngTemplateOutletContext]=\"templateContext(item)\"\n />\n </div>\n }\n </div>\n }\n\n <ng-container\n [ngTemplateOutlet]=\"itemContent\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n }\n </div>\n </div>\n} @else {\n <div class=\"flex flex-col gap-4\">\n @for (item of items(); track item.key) {\n <section class=\"flex flex-col gap-4\">\n @if (item.config.showHeader) {\n <div class=\"flex w-full items-center gap-2\">\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\n @if (item.config.collapse.enabled) {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n [icon]=\"\n item.expanded\n ? item.config.collapse.collapseIcon\n : item.config.collapse.expandIcon\n \"\n (onClick)=\"toggleExpanded(item.key)\"\n />\n }\n <h3 class=\"m-0 text-lg font-semibold\">\n {{ item.title || item.moduleKey || defaultTitle(item) }}\n </h3>\n @if (item.config.headerStart) {\n <div class=\"flex items-center gap-2\">\n <ng-container\n [ngTemplateOutlet]=\"item.config.headerStart\"\n [ngTemplateOutletContext]=\"templateContext(item)\"\n />\n </div>\n }\n </div>\n @if (item.config.headerEnd) {\n <div class=\"ml-auto flex shrink-0 items-center gap-2\">\n <ng-container\n [ngTemplateOutlet]=\"item.config.headerEnd\"\n [ngTemplateOutletContext]=\"templateContext(item)\"\n />\n </div>\n }\n </div>\n }\n\n @if (item.expanded || !item.config.collapse.enabled) {\n <ng-container\n [ngTemplateOutlet]=\"itemContent\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n }\n </section>\n }\n </div>\n}\n\n<!--\n The one place a list's body is described. Both layouts render through it so\n the navigator pane and the stacked sections can never drift apart.\n-->\n<ng-template #itemContent let-rawItem>\n @let item = asItem(rawItem);\n\n @if (item.config.templateContent) {\n <ng-container\n [ngTemplateOutlet]=\"item.config.templateContent\"\n [ngTemplateOutletContext]=\"templateContext(item)\"\n />\n } @else if (item.type === \"informative\") {\n <mt-client-list-informative-view [state]=\"item\" />\n } @else if (item.areaType === \"table\") {\n <mt-client-list-table-view\n [state]=\"item\"\n [rowActions]=\"rowActionsFor(item)\"\n [actionShape]=\"item.config.actionShape\"\n [rowActionsLoadingFn]=\"rowActionsLoadingFnFor(item)\"\n (rowActionsRequested)=\"onRowActionsRequested(item, $event)\"\n (lazyLoad)=\"onLazyLoad(item.key, $event)\"\n (rowClick)=\"onTableRowClick(item, $event)\"\n />\n } @else {\n <mt-client-list-cards-view\n [state]=\"item\"\n [rowActions]=\"rowActionsFor(item)\"\n [rowActionsLoadingFn]=\"rowActionsLoadingFnFor(item)\"\n (rowActionsRequested)=\"onRowActionsRequested(item, $event)\"\n (lazyLoad)=\"onLazyLoad(item.key, $event)\"\n (loadMore)=\"loadMoreCards(item.key)\"\n (cardClick)=\"onCardClick(item, $event)\"\n />\n }\n</ng-template>\n", styles: [".mt-cl-shell{display:grid;grid-template-columns:minmax(0,1fr);gap:1rem;align-items:start}.mt-cl-shell--rail{grid-template-columns:16.5rem minmax(0,1fr);gap:1.5rem}.mt-cl-shell--rail .mt-cl-shell__rail{padding-inline-end:1.25rem;border-inline-end:1px solid var(--p-content-border-color, #e5e7eb)}.mt-cl-shell__pane{display:flex;flex-direction:column;gap:1rem;min-width:0}.mt-cl-pane__header{display:flex;flex-wrap:wrap;align-items:flex-start;gap:.75rem 1rem}.mt-cl-pane__heading{display:flex;flex-direction:column;gap:.125rem;min-width:0;flex:1 1 auto}.mt-cl-pane__title{margin:0;color:var(--p-text-color, #111827);font-size:1.25rem;font-weight:600;line-height:1.75rem;overflow-wrap:anywhere}.mt-cl-pane__meta{margin:0;color:var(--p-text-muted-color, #6b7280);font-size:.8125rem;line-height:1.25rem}.mt-cl-pane__meta--error{color:var(--p-red-500, #ef4444)}.mt-cl-pane__actions{display:flex;flex:0 0 auto;align-items:center;gap:.5rem;margin-inline-start:auto}.mt-cl-pane__header-start{display:flex;flex:0 0 auto;align-items:center;gap:.5rem}\n"] }]
|
|
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 }] }], multiLayout: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiLayout", required: false }] }], loaded: [{ type: i0.Output, args: ["loaded"] }], errored: [{ type: i0.Output, args: ["errored"] }], itemClicked: [{ type: i0.Output, args: ["itemClicked"] }], activeListChanged: [{ type: i0.Output, args: ["activeListChanged"] }] } });
|
|
2690
2979
|
|
|
2691
2980
|
/**
|
|
2692
2981
|
* Generated bundle index. Do not edit.
|