@mintplayer/ng-spark 22.23.0 → 22.24.0

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.
Files changed (68) hide show
  1. package/README.md +181 -0
  2. package/fesm2022/mintplayer-ng-spark-attribute-description.mjs +100 -47
  3. package/fesm2022/mintplayer-ng-spark-attribute-description.mjs.map +1 -1
  4. package/fesm2022/mintplayer-ng-spark-client-operations.mjs +308 -329
  5. package/fesm2022/mintplayer-ng-spark-client-operations.mjs.map +1 -1
  6. package/fesm2022/mintplayer-ng-spark-column-filter.mjs +175 -135
  7. package/fesm2022/mintplayer-ng-spark-column-filter.mjs.map +1 -1
  8. package/fesm2022/mintplayer-ng-spark-grid.mjs +2259 -1119
  9. package/fesm2022/mintplayer-ng-spark-grid.mjs.map +1 -1
  10. package/fesm2022/mintplayer-ng-spark-history.mjs +611 -0
  11. package/fesm2022/mintplayer-ng-spark-history.mjs.map +1 -0
  12. package/fesm2022/mintplayer-ng-spark-icon.mjs +64 -27
  13. package/fesm2022/mintplayer-ng-spark-icon.mjs.map +1 -1
  14. package/fesm2022/mintplayer-ng-spark-models.mjs +366 -762
  15. package/fesm2022/mintplayer-ng-spark-models.mjs.map +1 -1
  16. package/fesm2022/mintplayer-ng-spark-moderation.mjs +1212 -0
  17. package/fesm2022/mintplayer-ng-spark-moderation.mjs.map +1 -0
  18. package/fesm2022/mintplayer-ng-spark-panels.mjs +52 -0
  19. package/fesm2022/mintplayer-ng-spark-panels.mjs.map +1 -0
  20. package/fesm2022/mintplayer-ng-spark-pipes.mjs +1122 -765
  21. package/fesm2022/mintplayer-ng-spark-pipes.mjs.map +1 -1
  22. package/fesm2022/mintplayer-ng-spark-po-create.mjs +273 -169
  23. package/fesm2022/mintplayer-ng-spark-po-create.mjs.map +1 -1
  24. package/fesm2022/mintplayer-ng-spark-po-detail.mjs +786 -410
  25. package/fesm2022/mintplayer-ng-spark-po-detail.mjs.map +1 -1
  26. package/fesm2022/mintplayer-ng-spark-po-edit.mjs +292 -284
  27. package/fesm2022/mintplayer-ng-spark-po-edit.mjs.map +1 -1
  28. package/fesm2022/mintplayer-ng-spark-po-form.mjs +2187 -1225
  29. package/fesm2022/mintplayer-ng-spark-po-form.mjs.map +1 -1
  30. package/fesm2022/mintplayer-ng-spark-query-list.mjs +516 -303
  31. package/fesm2022/mintplayer-ng-spark-query-list.mjs.map +1 -1
  32. package/fesm2022/mintplayer-ng-spark-renderers.mjs +17 -55
  33. package/fesm2022/mintplayer-ng-spark-renderers.mjs.map +1 -1
  34. package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs +216 -177
  35. package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs.map +1 -1
  36. package/fesm2022/mintplayer-ng-spark-routes.mjs +24 -28
  37. package/fesm2022/mintplayer-ng-spark-routes.mjs.map +1 -1
  38. package/fesm2022/mintplayer-ng-spark-services.mjs +625 -640
  39. package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
  40. package/fesm2022/mintplayer-ng-spark-shell.mjs +1013 -397
  41. package/fesm2022/mintplayer-ng-spark-shell.mjs.map +1 -1
  42. package/fesm2022/mintplayer-ng-spark-soft-delete.mjs +294 -0
  43. package/fesm2022/mintplayer-ng-spark-soft-delete.mjs.map +1 -0
  44. package/fesm2022/mintplayer-ng-spark.mjs +13 -50
  45. package/fesm2022/mintplayer-ng-spark.mjs.map +1 -1
  46. package/package.json +17 -1
  47. package/types/mintplayer-ng-spark-attribute-description.d.ts +25 -18
  48. package/types/mintplayer-ng-spark-client-operations.d.ts +133 -160
  49. package/types/mintplayer-ng-spark-column-filter.d.ts +73 -55
  50. package/types/mintplayer-ng-spark-grid.d.ts +885 -448
  51. package/types/mintplayer-ng-spark-history.d.ts +223 -0
  52. package/types/mintplayer-ng-spark-icon.d.ts +15 -13
  53. package/types/mintplayer-ng-spark-models.d.ts +624 -573
  54. package/types/mintplayer-ng-spark-moderation.d.ts +402 -0
  55. package/types/mintplayer-ng-spark-panels.d.ts +108 -0
  56. package/types/mintplayer-ng-spark-pipes.d.ts +120 -149
  57. package/types/mintplayer-ng-spark-po-create.d.ts +46 -37
  58. package/types/mintplayer-ng-spark-po-detail.d.ts +177 -117
  59. package/types/mintplayer-ng-spark-po-edit.d.ts +64 -65
  60. package/types/mintplayer-ng-spark-po-form.d.ts +507 -371
  61. package/types/mintplayer-ng-spark-query-list.d.ts +118 -82
  62. package/types/mintplayer-ng-spark-renderers.d.ts +79 -85
  63. package/types/mintplayer-ng-spark-retry-action-modal.d.ts +31 -34
  64. package/types/mintplayer-ng-spark-routes.d.ts +7 -11
  65. package/types/mintplayer-ng-spark-services.d.ts +377 -261
  66. package/types/mintplayer-ng-spark-shell.d.ts +209 -128
  67. package/types/mintplayer-ng-spark-soft-delete.d.ts +90 -0
  68. package/types/mintplayer-ng-spark.d.ts +7 -12
@@ -1 +1 @@
1
- {"version":3,"file":"mintplayer-ng-spark-query-list.mjs","sources":["../../query-list/src/spark-query-list.component.ts","../../query-list/src/spark-query-list.component.html","../../query-list/mintplayer-ng-spark-query-list.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, DestroyRef, computed, effect, inject, input, output, signal, viewChild, TemplateRef } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { Subscription } from 'rxjs';\nimport { CommonModule, NgTemplateOutlet } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { ActivatedRoute, ParamMap, Router } from '@angular/router';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsBadgeComponent } from '@mintplayer/ng-bootstrap/badge';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';\nimport { BsGridComponent, BsGridRowDirective, BsGridColumnDirective } from '@mintplayer/ng-bootstrap/grid';\nimport { BsInputGroupComponent } from '@mintplayer/ng-bootstrap/input-group';\nimport { BsPriorityNavComponent, BsPriorityNavItemDirective } from '@mintplayer/ng-bootstrap/priority-nav';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { SparkService, SparkStreamingService, SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport { TranslateKeyPipe, ResolveTranslationPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\nimport { SparkQueryGridComponent } from '@mintplayer/ng-spark/grid';\nimport {\n CustomActionDefinition,\n StreamingMessage,\n QueryColumn,\n QueryResultItem,\n} from '@mintplayer/ng-spark/models';\n\n/**\n * The routed query page: chrome around one {@link SparkQueryGridComponent}.\n *\n * It owns what is genuinely page-shaped and route-shaped, and nothing else:\n *\n * - **Route resolution.** It has no `queryId` input; it reads `paramMap`, and it serves two\n * routes — `query/:queryId`, and `po/:type`, which resolves an entity type to a query. That\n * second one is type-to-query resolution, not query rendering, and is why this component still\n * exists rather than the router pointing at the grid.\n * - **Streaming.** The websocket lives here so it stays out of every PO detail page's bundle;\n * the snapshot is filtered and sorted client-side and handed to the grid as `[data]`.\n * - The action bar, the caption, the LIVE badge, the search box and the New button.\n *\n * The grid itself — columns, cells, paging, the row link, selection, custom-action execution —\n * is the shared component. This page previously wrote out `<bs-datatable>` twice, once per\n * transport, with a shared row template between them; both are gone.\n */\n@Component({\n selector: 'spark-query-list',\n imports: [BsBadgeComponent, CommonModule, NgTemplateOutlet, FormsModule, BsAlertComponent, BsFormComponent, BsFormControlDirective, BsGridComponent, BsGridRowDirective, BsGridColumnDirective, BsInputGroupComponent, BsPriorityNavComponent, BsPriorityNavItemDirective, BsSpinnerComponent, SparkIconComponent, SparkQueryGridComponent, ResolveTranslationPipe, TranslateKeyPipe],\n templateUrl: './spark-query-list.component.html',\n styleUrl: './spark-query-list.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class.virtual-scrolling]': 'isVirtualScrolling()'\n }\n})\nexport class SparkQueryListComponent {\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n private readonly sparkService = inject(SparkService);\n private readonly streamingService = inject(SparkStreamingService);\n private readonly destroyRef = inject(DestroyRef);\n protected readonly lang = inject(SparkLanguageService);\n\n extraActionsTemplate = input<TemplateRef<void> | null>(null);\n showCustomActions = input(true);\n\n /**\n * Forwarded to the grid, so a query PAGE can replace its row links — the same reason the card\n * forwards it. Without this the escape hatch existed only for a directly-embedded grid.\n */\n rowRoute = input<((row: QueryResultItem) => unknown[] | null) | null>(null);\n\n rowClicked = output<QueryResultItem>();\n createClicked = output<void>();\n customActionExecuted = output<{ action: CustomActionDefinition }>();\n\n colors = Color;\n\n /** The query the grid should render, resolved from the route. Null until it is known. */\n queryId = signal<string | null>(null);\n errorMessage = signal<string | null>(null);\n searchTerm = signal('');\n\n private readonly grid = viewChild(SparkQueryGridComponent);\n\n /**\n * Grid state, surfaced for this page's chrome.\n *\n * Optional `viewChild`, read defensively: the action bar and caption render above the grid, so\n * on the first change-detection pass the query has not resolved yet.\n */\n protected readonly query = computed(() => this.grid()?.query() ?? null);\n protected readonly entityType = computed(() => this.grid()?.entityType() ?? null);\n protected readonly customActions = computed(() => this.grid()?.customActions() ?? []);\n protected readonly canCreate = computed(() => this.grid()?.canCreate() ?? false);\n protected readonly resultCount = computed(() => this.grid()?.resultCount() ?? null);\n protected readonly isVirtualScrolling = computed(() => this.grid()?.isVirtualScrolling() ?? false);\n protected readonly gridError = computed(() => this.grid()?.errorMessage() ?? null);\n\n /** Whether an action's selection rule is satisfied. Delegated: the grid holds the selection. */\n /** @see SparkPoDetailComponent.customActionClass — same allow-list, same default. */\n protected customActionClass(action: CustomActionDefinition): string {\n const variant = action.variant?.toLowerCase();\n switch (variant) {\n case 'danger':\n case 'warning':\n case 'primary':\n case 'secondary':\n case 'success':\n return `btn btn-${variant}`;\n default:\n return 'btn btn-outline-primary';\n }\n }\n\n protected isActionEnabled(action: CustomActionDefinition): boolean {\n return this.grid()?.isActionEnabled(action) ?? false;\n }\n\n // --- streaming ---------------------------------------------------------------\n\n isStreaming = signal(false);\n private streamingSub: Subscription | null = null;\n /** Columns as sent with the stream's snapshot; empty until it arrives. */\n protected readonly streamColumns = signal<QueryColumn[]>([]);\n private readonly allItems = signal<QueryResultItem[]>([]);\n private readonly streamItems = signal<QueryResultItem[]>([]);\n\n /**\n * Rows handed to the grid, or `null` to let it fetch for itself.\n *\n * Null for a normal query — an empty array would read as \"here are no rows\" and suppress the\n * fetch entirely.\n */\n protected readonly gridData = computed(() =>\n this.query()?.isStreamingQuery ? this.streamItems() : null);\n\n constructor() {\n this.route.paramMap.pipe(takeUntilDestroyed()).subscribe(params => {\n // The handler is async and this is a subscribe, so a rejection lands nowhere: the metadata\n // load would reject, the query would stay null, and the template would render a spinner\n // FOREVER — while this component has had an errorMessage surface all along that only the\n // fetch path ever reached.\n this.onParamsChange(params).catch((e: unknown) => this.reportLoadFailure(e as HttpErrorResponse));\n });\n\n this.destroyRef.onDestroy(() => this.disconnectStreaming());\n\n // Connect the socket once the grid has resolved a streaming query, and disconnect whenever it\n // resolves anything else. The grid knows not to fetch for a streaming query, so there is no\n // window in which both transports are live.\n effect(() => {\n const q = this.query();\n if (q?.isStreamingQuery) {\n this.connectStreaming(q.id);\n } else {\n this.disconnectStreaming();\n }\n });\n\n // Client-side filter and sort for the streaming snapshot. The server never sees these: there\n // is no request to attach them to.\n effect(() => {\n this.searchTerm();\n this.grid()?.settings();\n this.allItems();\n if (this.isStreaming()) this.applyFilter();\n });\n }\n\n private async onParamsChange(params: ParamMap): Promise<void> {\n this.errorMessage.set(null);\n this.queryId.set(null);\n this.allItems.set([]);\n this.streamItems.set([]);\n this.streamColumns.set([]);\n this.disconnectStreaming();\n\n const queryId = params.get('queryId');\n const typeParam = params.get('type');\n\n if (queryId) {\n this.queryId.set(queryId);\n return;\n }\n\n if (!typeParam) return;\n\n // `po/:type` — the route names an entity type, so find the query that lists it. The grid takes\n // a query, and this translation is the only reason it cannot take the route directly.\n const [entityTypes, queries] = await Promise.all([\n this.sparkService.getEntityTypes(),\n this.sparkService.getQueries(),\n ]);\n const entityType = entityTypes.find(t => t.id === typeParam || t.alias === typeParam);\n if (!entityType) {\n this.reportLoadFailure({ status: 404 } as HttpErrorResponse);\n return;\n }\n\n const singularName = entityType.name;\n const match = queries.find(q => {\n if (q.entityType === singularName) return true;\n const sourceName = q.source.includes('.') ? q.source.substring(q.source.indexOf('.') + 1) : q.source;\n return sourceName === singularName || sourceName === singularName + 's';\n });\n\n if (match) this.queryId.set(match.alias || match.id);\n else this.reportLoadFailure({ status: 404 } as HttpErrorResponse);\n }\n\n /**\n * A load failure has to render, not just be swallowed: a denied query answers 404 (audit M-3, so\n * existence is not leaked), which is indistinguishable from a missing one — hence a deliberately\n * generic message rather than a guess at which it was.\n */\n private reportLoadFailure(err: HttpErrorResponse): void {\n this.errorMessage.set(\n err?.status === 404\n ? (this.lang.t('spark.query.unavailable') || 'This list is not available.')\n : (err?.error?.error || err?.message || 'An unexpected error occurred'));\n }\n\n protected async onCustomAction(action: CustomActionDefinition): Promise<void> {\n await this.grid()?.onCustomAction(action);\n }\n\n protected onCreate(): void {\n this.createClicked.emit();\n const et = this.entityType();\n if (et) this.router.navigate(['/po', et.alias || et.id, 'new']);\n }\n\n protected clearSearch(): void {\n this.searchTerm.set('');\n }\n\n private connectStreaming(queryId: string): void {\n if (this.streamingSub) return;\n this.isStreaming.set(true);\n\n this.streamingSub = this.streamingService.connectToStreamingQuery(queryId).subscribe({\n next: (message) => this.handleStreamingMessage(message),\n error: (err) => {\n this.errorMessage.set(err?.message || 'Streaming connection failed');\n this.isStreaming.set(false);\n },\n complete: () => this.isStreaming.set(false),\n });\n }\n\n private disconnectStreaming(): void {\n if (this.streamingSub) {\n this.streamingSub.unsubscribe();\n this.streamingSub = null;\n }\n this.isStreaming.set(false);\n }\n\n private handleStreamingMessage(message: StreamingMessage): void {\n switch (message.type) {\n case 'snapshot':\n this.errorMessage.set(null);\n // Columns come with the snapshot and never change for the life of the stream, so they are\n // stored once and handed to the grid alongside the rows — a projection cannot describe\n // itself, and the grid has no entity-type metadata to fall back on any more.\n this.streamColumns.set(message.columns);\n this.allItems.set(message.data);\n break;\n\n case 'patch':\n if (message.updated.length > 0) {\n this.allItems.update(items => items.map(item => {\n const patch = message.updated.find(u => u.id === item.id);\n if (!patch) return item;\n return {\n ...item,\n values: item.values.map(v =>\n v.key in patch.values ? { ...v, value: patch.values[v.key] } : v),\n };\n }));\n }\n break;\n\n case 'error':\n this.errorMessage.set(message.message);\n break;\n }\n }\n\n private applyFilter(): void {\n let items = this.allItems();\n\n const term = this.searchTerm().toLowerCase();\n if (term) {\n items = items.filter(item =>\n item.values.some(v => String(v.value ?? '').toLowerCase().includes(term)));\n }\n\n // The datatable in `[data]` mode also sorts on header clicks, but the sort must survive a\n // patch: re-deriving from `allItems` without re-applying it would silently reorder the grid\n // under the user on every update.\n const sortCols = this.grid()?.settings().sortColumns ?? [];\n if (sortCols.length > 0) {\n items = [...items].sort((a, b) => {\n for (const col of sortCols) {\n const aVal = a.values.find(v => v.key === col.property)?.value ?? '';\n const bVal = b.values.find(v => v.key === col.property)?.value ?? '';\n const cmp = String(aVal).localeCompare(String(bVal));\n if (cmp !== 0) return col.direction === 'descending' ? -cmp : cmp;\n }\n return 0;\n });\n }\n\n this.streamItems.set(items);\n }\n}\n","<div class=\"d-flex flex-column h-100\">\n <div class=\"spark-actionbar py-2 flex-shrink-0\" [class.spark-actionbar-wide]=\"isVirtualScrolling()\">\n <bs-priority-nav [moreLabel]=\"lang.t('common.more')\" [collapseAt]=\"'sm'\">\n @if (canCreate()) {\n <button *bsPriorityNavItem=\"1\" class=\"btn btn-primary\" (click)=\"onCreate()\">\n <spark-icon name=\"plus-lg\" /> {{ 'common.new' | t }}\n </button>\n }\n @if (showCustomActions()) {\n @for (action of customActions(); track action.name) {\n <!-- Disabled, not hidden: hiding makes the affordance undiscoverable and reflows the\n priority nav on every selection change. The server enforces the same rule. -->\n <button *bsPriorityNavItem=\"10 + action.offset\" [class]=\"customActionClass(action)\"\n [disabled]=\"!isActionEnabled(action)\" (click)=\"onCustomAction(action)\">\n {{ action.displayName | resolveTranslation }}\n </button>\n }\n }\n @if (extraActionsTemplate(); as extraActionsTpl) {\n <ng-container *bsPriorityNavItem=\"50\">\n <ng-container *ngTemplateOutlet=\"extraActionsTpl\"></ng-container>\n </ng-container>\n }\n </bs-priority-nav>\n </div>\n\n <h2 class=\"mb-4 flex-shrink-0\" [class.px-4]=\"isVirtualScrolling()\">\n {{ (query()?.description | resolveTranslation) || query()?.name || ('common.loading' | t) }}\n @if (isStreaming()) {\n <bs-badge [type]=\"colors.success\" class=\"ms-2 align-middle spark-live-badge\">LIVE</bs-badge>\n }\n </h2>\n\n @if (errorMessage(); as err) {\n <!-- The route could not be resolved to a query at all. A failure INSIDE the grid renders in\n the grid; this is the one it cannot report, because it never got a query id. -->\n <bs-alert [type]=\"colors.danger\" class=\"m-3 d-block\">\n {{ err }}\n </bs-alert>\n } @else if (queryId(); as qId) {\n <div class=\"flex-shrink-0\" [class.px-4]=\"isVirtualScrolling()\">\n <bs-form>\n <bs-grid>\n <div bsRow class=\"mb-3\">\n <div [md]=\"12\">\n <bs-input-group>\n <span class=\"addon\">\n <spark-icon name=\"search\" />\n </span>\n <input\n type=\"text\"\n [placeholder]=\"'common.search' | t\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\">\n @if (searchTerm()) {\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"clearSearch()\">\n <spark-icon name=\"x-lg\" />\n </button>\n }\n </bs-input-group>\n </div>\n <!-- Its own full-width row rather than a column beside the box: the count is\n occasional, and reserving 8 columns for it shrank the search permanently. The\n @if moved out of the column so nothing reserves vertical space when absent. -->\n @if (searchTerm() && resultCount() !== null) {\n <div [md]=\"12\" class=\"text-end mt-1\">\n <span class=\"text-muted\">\n {{ resultCount() }} {{ resultCount() === 1 ? ('common.resultFound' | t) : ('common.resultsFound' | t) }}\n </span>\n </div>\n }\n </div>\n </bs-grid>\n </bs-form>\n </div>\n\n <!--\n One grid, both transports. `data` is null for a normal query, so the grid fetches and pages\n server-side; for a streaming query this page owns the socket and hands the filtered snapshot\n in. This used to be two near-identical <bs-datatable> blocks with a shared row template.\n -->\n <spark-query-grid class=\"flex-grow-1\"\n [queryId]=\"qId\"\n [data]=\"gridData()\"\n [columns]=\"gridData() ? streamColumns() : null\"\n [search]=\"searchTerm()\"\n [rowRoute]=\"rowRoute()\"\n (rowClicked)=\"rowClicked.emit($event)\"\n (customActionExecuted)=\"customActionExecuted.emit($event)\" />\n } @else {\n <div class=\"text-center p-5\">\n <bs-spinner />\n </div>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mintplayer-ng-spark-query-list.mjs","names":[],"sources":["../../query-list/src/spark-query-list.component.ts","../../query-list/src/spark-query-list.component.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, DestroyRef, computed, effect, inject, input, output, signal, viewChild, TemplateRef } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { Subscription } from 'rxjs';\nimport { CommonModule, NgTemplateOutlet } from '@angular/common';\nimport { ActivatedRoute, ParamMap, Router } from '@angular/router';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsBadgeComponent } from '@mintplayer/ng-bootstrap/badge';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsGridComponent, BsGridRowDirective, BsGridColumnDirective } from '@mintplayer/ng-bootstrap/grid';\nimport { BsPriorityNavComponent, BsPriorityNavItemDirective } from '@mintplayer/ng-bootstrap/priority-nav';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { SparkService, SparkStreamingService, SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport { TranslateKeyPipe, ResolveTranslationPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\nimport { SparkQueryGridComponent, SparkQueryToolbarAction, SparkSearchBoxComponent, sparkActionClass } from '@mintplayer/ng-spark/grid';\nimport {\n CustomActionDefinition,\n StreamingMessage,\n QueryColumn,\n QueryResultItem,\n SparkDeletedFilter,\n} from '@mintplayer/ng-spark/models';\nimport { NgComponentOutlet } from '@angular/common';\nimport {\n SPARK_QUERY_LIST_ACTIONS,\n SparkQueryListContext,\n orderSparkExtensions,\n parseSparkDeletedParam,\n} from '@mintplayer/ng-spark/panels';\n\n/**\n * The routed query page: chrome around one {@link SparkQueryGridComponent}.\n *\n * It owns what is genuinely page-shaped and route-shaped, and nothing else:\n *\n * - **Route resolution.** It has no `queryId` input; it reads `paramMap`, and it serves two\n * routes — `query/:queryId`, and `po/:type`, which resolves an entity type to a query. That\n * second one is type-to-query resolution, not query rendering, and is why this component still\n * exists rather than the router pointing at the grid.\n * - **Streaming.** The websocket lives here so it stays out of every PO detail page's bundle;\n * the snapshot is filtered and sorted client-side and handed to the grid as `[data]`.\n * - The action bar, the caption, the LIVE badge, the search box and the New button.\n *\n * The grid itself — columns, cells, paging, the row link, selection, custom-action execution —\n * is the shared component. This page previously wrote out `<bs-datatable>` twice, once per\n * transport, with a shared row template between them; both are gone.\n */\n@Component({\n selector: 'spark-query-list',\n imports: [BsBadgeComponent, CommonModule, NgTemplateOutlet, NgComponentOutlet, BsAlertComponent, BsGridComponent, BsGridRowDirective, BsGridColumnDirective, BsPriorityNavComponent, BsPriorityNavItemDirective, BsSpinnerComponent, SparkIconComponent, SparkQueryGridComponent, SparkSearchBoxComponent, ResolveTranslationPipe, TranslateKeyPipe],\n templateUrl: './spark-query-list.component.html',\n styleUrl: './spark-query-list.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class.virtual-scrolling]': 'isVirtualScrolling()'\n }\n})\nexport class SparkQueryListComponent {\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n private readonly sparkService = inject(SparkService);\n private readonly streamingService = inject(SparkStreamingService);\n private readonly destroyRef = inject(DestroyRef);\n protected readonly lang = inject(SparkLanguageService);\n\n extraActionsTemplate = input<TemplateRef<void> | null>(null);\n showCustomActions = input(true);\n\n /**\n * Forwarded to the grid, so a query PAGE can replace its row links — the same reason the card\n * forwards it. Without this the escape hatch existed only for a directly-embedded grid.\n */\n rowRoute = input<((row: QueryResultItem) => unknown[] | null) | null>(null);\n\n rowClicked = output<QueryResultItem>();\n createClicked = output<void>();\n customActionExecuted = output<{ action: CustomActionDefinition }>();\n\n colors = Color;\n\n /** The query the grid should render, resolved from the route. Null until it is known. */\n queryId = signal<string | null>(null);\n errorMessage = signal<string | null>(null);\n searchTerm = signal('');\n\n private readonly grid = viewChild(SparkQueryGridComponent);\n\n /**\n * Grid state, surfaced for this page's chrome.\n *\n * Optional `viewChild`, read defensively: the action bar and caption render above the grid, so\n * on the first change-detection pass the query has not resolved yet.\n */\n protected readonly query = computed(() => this.grid()?.query() ?? null);\n protected readonly entityType = computed(() => this.grid()?.entityType() ?? null);\n protected readonly customActions = computed(() => this.grid()?.customActions() ?? []);\n protected readonly canCreate = computed(() => this.grid()?.offersCreate() ?? false);\n protected readonly resultCount = computed(() => this.grid()?.resultCount() ?? null);\n protected readonly isVirtualScrolling = computed(() => this.grid()?.isVirtualScrolling() ?? false);\n protected readonly gridError = computed(() => this.grid()?.errorMessage() ?? null);\n protected readonly permissions = computed(() => this.grid()?.permissions() ?? null);\n\n /** Add-on buttons for the action bar (#460, `SPARK_QUERY_LIST_ACTIONS`), e.g. the Deleted toggle. */\n protected readonly listActions = orderSparkExtensions(inject(SPARK_QUERY_LIST_ACTIONS, { optional: true }), a => a.priority ?? 60);\n\n /**\n * The soft-deletion mode (#460, T2), read from the route's `?deleted=` so the recycle bin survives\n * a reload and the back button from a row opened in it. `exclude` unless the route says otherwise.\n */\n protected readonly deletedMode = signal<SparkDeletedFilter>('exclude');\n\n /** Bumped by `context.reload()`; the grid treats any new value as \"re-run the query\". */\n private readonly reloadToken = signal(0);\n\n protected readonly listContext = computed((): SparkQueryListContext | null => {\n const query = this.query();\n if (!query) return null;\n return {\n query,\n entityType: this.entityType(),\n permissions: this.permissions(),\n deleted: this.deletedMode(),\n setDeleted: (mode: SparkDeletedFilter) => this.setDeleted(mode),\n reload: () => this.reloadToken.update(n => n + 1),\n };\n });\n\n protected readonly gridReloadToken = this.reloadToken.asReadonly();\n\n private setDeleted(mode: SparkDeletedFilter): void {\n this.router.navigate([], {\n relativeTo: this.route,\n queryParams: { deleted: mode === 'exclude' ? null : mode },\n queryParamsHandling: 'merge',\n replaceUrl: true,\n });\n }\n\n /** Whether an action's selection rule is satisfied. Delegated: the grid holds the selection. */\n /** @see SparkPoDetailComponent.customActionClass — same allow-list, same default. */\n protected customActionClass(action: CustomActionDefinition): string {\n const variant = action.variant?.toLowerCase();\n switch (variant) {\n case 'danger':\n case 'warning':\n case 'primary':\n case 'secondary':\n case 'success':\n return `btn btn-${variant}`;\n default:\n return 'btn btn-outline-primary';\n }\n }\n\n /** The grid's toolbar model (#460, M15), the same list the sub-query card renders. */\n protected readonly toolbarActions = computed(() => this.grid()?.toolbarActions() ?? []);\n\n protected toolbarActionClass(action: SparkQueryToolbarAction): string {\n // New keeps its solid primary look; the rest follow their variant.\n return action.kind === 'new' ? 'btn btn-primary' : sparkActionClass(action.definition);\n }\n\n protected isToolbarActionEnabled(action: SparkQueryToolbarAction): boolean {\n return this.grid()?.isToolbarActionEnabled(action) ?? false;\n }\n\n protected runToolbarAction(action: SparkQueryToolbarAction): void {\n if (action.kind === 'new') {\n // Through this page's own onCreate, so `createClicked` fires exactly as it always has.\n this.onCreate();\n return;\n }\n void this.grid()?.runToolbarAction(action);\n }\n\n protected isActionEnabled(action: CustomActionDefinition): boolean {\n return this.grid()?.isActionEnabled(action) ?? false;\n }\n\n // --- streaming ---------------------------------------------------------------\n\n isStreaming = signal(false);\n private streamingSub: Subscription | null = null;\n /** Columns as sent with the stream's snapshot; empty until it arrives. */\n protected readonly streamColumns = signal<QueryColumn[]>([]);\n private readonly allItems = signal<QueryResultItem[]>([]);\n private readonly streamItems = signal<QueryResultItem[]>([]);\n\n /**\n * Rows handed to the grid, or `null` to let it fetch for itself.\n *\n * Null for a normal query — an empty array would read as \"here are no rows\" and suppress the\n * fetch entirely.\n */\n protected readonly gridData = computed(() =>\n this.query()?.isStreamingQuery ? this.streamItems() : null);\n\n constructor() {\n this.route.paramMap.pipe(takeUntilDestroyed()).subscribe(params => {\n // The handler is async and this is a subscribe, so a rejection lands nowhere: the metadata\n // load would reject, the query would stay null, and the template would render a spinner\n // FOREVER — while this component has had an errorMessage surface all along that only the\n // fetch path ever reached.\n this.onParamsChange(params).catch((e: unknown) => this.reportLoadFailure(e as HttpErrorResponse));\n });\n\n // Separate from paramMap: switching the recycle bin on changes only the query string, and must\n // not re-resolve the query (which would reset page, sort and filters for nothing).\n this.route.queryParamMap.pipe(takeUntilDestroyed()).subscribe(query =>\n this.deletedMode.set(parseSparkDeletedParam(query.get('deleted')) ?? 'exclude'));\n\n this.destroyRef.onDestroy(() => this.disconnectStreaming());\n\n // Connect the socket once the grid has resolved a streaming query, and disconnect whenever it\n // resolves anything else. The grid knows not to fetch for a streaming query, so there is no\n // window in which both transports are live.\n effect(() => {\n const q = this.query();\n if (q?.isStreamingQuery) {\n this.connectStreaming(q.id);\n } else {\n this.disconnectStreaming();\n }\n });\n\n // Client-side filter and sort for the streaming snapshot. The server never sees these: there\n // is no request to attach them to.\n effect(() => {\n this.searchTerm();\n this.grid()?.settings();\n this.allItems();\n if (this.isStreaming()) this.applyFilter();\n });\n }\n\n private async onParamsChange(params: ParamMap): Promise<void> {\n this.errorMessage.set(null);\n this.queryId.set(null);\n this.allItems.set([]);\n this.streamItems.set([]);\n this.streamColumns.set([]);\n this.disconnectStreaming();\n\n const queryId = params.get('queryId');\n const typeParam = params.get('type');\n\n if (queryId) {\n this.queryId.set(queryId);\n return;\n }\n\n if (!typeParam) return;\n\n // `po/:type` — the route names an entity type, so find the query that lists it. The grid takes\n // a query, and this translation is the only reason it cannot take the route directly.\n const [entityTypes, queries] = await Promise.all([\n this.sparkService.getEntityTypes(),\n this.sparkService.getQueries(),\n ]);\n const entityType = entityTypes.find(t => t.id === typeParam || t.alias === typeParam);\n if (!entityType) {\n this.reportLoadFailure({ status: 404 } as HttpErrorResponse);\n return;\n }\n\n const singularName = entityType.name;\n const match = queries.find(q => {\n if (q.entityType === singularName) return true;\n const sourceName = q.source.includes('.') ? q.source.substring(q.source.indexOf('.') + 1) : q.source;\n return sourceName === singularName || sourceName === singularName + 's';\n });\n\n if (match) this.queryId.set(match.alias || match.id);\n else this.reportLoadFailure({ status: 404 } as HttpErrorResponse);\n }\n\n /**\n * A load failure has to render, not just be swallowed: a denied query answers 404 (audit M-3, so\n * existence is not leaked), which is indistinguishable from a missing one — hence a deliberately\n * generic message rather than a guess at which it was.\n */\n private reportLoadFailure(err: HttpErrorResponse): void {\n this.errorMessage.set(\n err?.status === 404\n ? (this.lang.t('spark.query.unavailable') || 'This list is not available.')\n : (err?.error?.error || err?.message || 'An unexpected error occurred'));\n }\n\n protected async onCustomAction(action: CustomActionDefinition): Promise<void> {\n await this.grid()?.onCustomAction(action);\n }\n\n protected onCreate(): void {\n this.createClicked.emit();\n const et = this.entityType();\n if (et) this.router.navigate(['/po', et.alias || et.id, 'new']);\n }\n\n protected clearSearch(): void {\n this.searchTerm.set('');\n }\n\n private connectStreaming(queryId: string): void {\n if (this.streamingSub) return;\n this.isStreaming.set(true);\n\n this.streamingSub = this.streamingService.connectToStreamingQuery(queryId).subscribe({\n next: (message) => this.handleStreamingMessage(message),\n error: (err) => {\n this.errorMessage.set(err?.message || 'Streaming connection failed');\n this.isStreaming.set(false);\n },\n complete: () => this.isStreaming.set(false),\n });\n }\n\n private disconnectStreaming(): void {\n if (this.streamingSub) {\n this.streamingSub.unsubscribe();\n this.streamingSub = null;\n }\n this.isStreaming.set(false);\n }\n\n private handleStreamingMessage(message: StreamingMessage): void {\n switch (message.type) {\n case 'snapshot':\n this.errorMessage.set(null);\n // Columns come with the snapshot and never change for the life of the stream, so they are\n // stored once and handed to the grid alongside the rows — a projection cannot describe\n // itself, and the grid has no entity-type metadata to fall back on any more.\n this.streamColumns.set(message.columns);\n this.allItems.set(message.data);\n break;\n\n case 'patch':\n if (message.updated.length > 0) {\n this.allItems.update(items => items.map(item => {\n const patch = message.updated.find(u => u.id === item.id);\n if (!patch) return item;\n return {\n ...item,\n values: item.values.map(v =>\n v.key in patch.values ? { ...v, value: patch.values[v.key] } : v),\n };\n }));\n }\n break;\n\n case 'error':\n this.errorMessage.set(message.message);\n break;\n }\n }\n\n private applyFilter(): void {\n let items = this.allItems();\n\n const term = this.searchTerm().toLowerCase();\n if (term) {\n items = items.filter(item =>\n item.values.some(v => String(v.value ?? '').toLowerCase().includes(term)));\n }\n\n // The datatable in `[data]` mode also sorts on header clicks, but the sort must survive a\n // patch: re-deriving from `allItems` without re-applying it would silently reorder the grid\n // under the user on every update.\n const sortCols = this.grid()?.settings().sortColumns ?? [];\n if (sortCols.length > 0) {\n items = [...items].sort((a, b) => {\n for (const col of sortCols) {\n const aVal = a.values.find(v => v.key === col.property)?.value ?? '';\n const bVal = b.values.find(v => v.key === col.property)?.value ?? '';\n const cmp = String(aVal).localeCompare(String(bVal));\n if (cmp !== 0) return col.direction === 'descending' ? -cmp : cmp;\n }\n return 0;\n });\n }\n\n this.streamItems.set(items);\n }\n}\n","<div class=\"d-flex flex-column h-100\">\n <div class=\"spark-actionbar py-2 flex-shrink-0\" [class.spark-actionbar-wide]=\"isVirtualScrolling()\">\n <bs-priority-nav [moreLabel]=\"lang.t('common.more')\" [collapseAt]=\"'sm'\">\n <!-- The grid's toolbar model (#460, M15): New, Delete and the custom actions — the list the\n sub-query card renders too. Disabled, not hidden: hiding makes the affordance\n undiscoverable and reflows the priority nav on every selection change. The server\n enforces the same rules. -->\n @for (action of toolbarActions(); track action.name) {\n @if (action.kind !== 'custom' || showCustomActions()) {\n <button *bsPriorityNavItem=\"action.priority\" type=\"button\" [class]=\"toolbarActionClass(action)\"\n [attr.data-action]=\"action.name\"\n [disabled]=\"!isToolbarActionEnabled(action)\" (click)=\"runToolbarAction(action)\">\n @if (action.kind === 'new') {\n <spark-icon name=\"plus-lg\" /> {{ 'common.new' | t }}\n } @else {\n @if (action.definition.icon && action.kind === 'delete') {\n <spark-icon [name]=\"action.definition.icon\" />\n }\n {{ action.definition.displayName | resolveTranslation }}\n }\n </button>\n }\n }\n @if (extraActionsTemplate(); as extraActionsTpl) {\n <ng-container *bsPriorityNavItem=\"50\">\n <ng-container *ngTemplateOutlet=\"extraActionsTpl\"></ng-container>\n </ng-container>\n }\n <!-- Add-on actions (SPARK_QUERY_LIST_ACTIONS), e.g. the soft-delete Deleted toggle. -->\n @if (listContext(); as ctx) {\n @for (action of listActions; track action.id) {\n <ng-container *bsPriorityNavItem=\"action.priority ?? 60\">\n <ng-container *ngComponentOutlet=\"action.component; inputs: { context: ctx }\" />\n </ng-container>\n }\n }\n </bs-priority-nav>\n </div>\n\n <h2 class=\"mb-4 flex-shrink-0\" [class.px-4]=\"isVirtualScrolling()\">\n {{ (query()?.description | resolveTranslation) || query()?.name || ('common.loading' | t) }}\n @if (isStreaming()) {\n <bs-badge [type]=\"colors.success\" class=\"ms-2 align-middle spark-live-badge\">LIVE</bs-badge>\n }\n @if (deletedMode() === 'only') {\n <bs-badge [type]=\"colors.secondary\" class=\"ms-2 align-middle spark-deleted-badge\">{{ 'softDelete.deleted' | t }}</bs-badge>\n }\n </h2>\n\n @if (errorMessage(); as err) {\n <!-- The route could not be resolved to a query at all. A failure INSIDE the grid renders in\n the grid; this is the one it cannot report, because it never got a query id. -->\n <bs-alert [type]=\"colors.danger\" class=\"m-3 d-block\">\n {{ err }}\n </bs-alert>\n } @else if (queryId(); as qId) {\n <div class=\"flex-shrink-0\" [class.px-4]=\"isVirtualScrolling()\">\n <bs-grid>\n <div bsRow class=\"mb-3\">\n <div [md]=\"12\">\n <!-- The same box as the sub-query card's header (#460 M15): debounce, clear button. -->\n <spark-search-box [(term)]=\"searchTerm\" />\n </div>\n <!-- Its own full-width row rather than a column beside the box: the count is\n occasional, and reserving 8 columns for it shrank the search permanently. The\n @if moved out of the column so nothing reserves vertical space when absent. -->\n @if (searchTerm() && resultCount() !== null) {\n <div [md]=\"12\" class=\"text-end mt-1\">\n <span class=\"text-muted\">\n {{ resultCount() }} {{ resultCount() === 1 ? ('common.resultFound' | t) : ('common.resultsFound' | t) }}\n </span>\n </div>\n }\n </div>\n </bs-grid>\n </div>\n\n <!--\n One grid, both transports. `data` is null for a normal query, so the grid fetches and pages\n server-side; for a streaming query this page owns the socket and hands the filtered snapshot\n in. This used to be two near-identical <bs-datatable> blocks with a shared row template.\n -->\n <spark-query-grid class=\"flex-grow-1\"\n [queryId]=\"qId\"\n [data]=\"gridData()\"\n [columns]=\"gridData() ? streamColumns() : null\"\n [search]=\"searchTerm()\"\n [deleted]=\"deletedMode()\"\n [reloadToken]=\"gridReloadToken()\"\n [rowRoute]=\"rowRoute()\"\n (rowClicked)=\"rowClicked.emit($event)\"\n (customActionExecuted)=\"customActionExecuted.emit($event)\" />\n } @else {\n <div class=\"text-center p-5\">\n <bs-spinner />\n </div>\n }\n</div>\n"],"mappings":"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"}
@@ -1,64 +1,26 @@
1
- import { reflectComponentType, InjectionToken } from '@angular/core';
2
-
3
- // Reflection result cached per component type: the input builders are template
4
- // expressions re-evaluated every CD pass (and query-list virtual-scrolls).
5
- const declaredInputs = new Map();
6
- /**
7
- * Drops entries the component doesn't declare, so every contract member is
8
- * genuinely optional — NgComponentOutlet throws on an undeclared input.
9
- */
1
+ import { InjectionToken, reflectComponentType } from "@angular/core";
2
+ const declaredInputs = /* @__PURE__ */ new Map();
10
3
  function withDeclaredInputs(component, inputs) {
11
- let declared = declaredInputs.get(component);
12
- if (!declared) {
13
- declared = new Set(reflectComponentType(component)?.inputs.map(i => i.templateName) ?? []);
14
- declaredInputs.set(component, declared);
15
- }
16
- return Object.fromEntries(Object.entries(inputs).filter(([k]) => declared.has(k)));
4
+ let declared = declaredInputs.get(component);
5
+ if (!declared) {
6
+ declared = new Set(reflectComponentType(component)?.inputs.map((i) => i.templateName) ?? []);
7
+ declaredInputs.set(component, declared);
8
+ }
9
+ return Object.fromEntries(Object.entries(inputs).filter(([k]) => declared.has(k)));
17
10
  }
18
- /**
19
- * The renderer-facing value of an attribute: the flat value, or for AsDetail
20
- * attributes (whose flat value the server nulls on purpose) the nested
21
- * PersistentObject (single) / PersistentObject[] (array).
22
- *
23
- * Used by the detail and edit paths, which still work in attributes.
24
- */
25
11
  function rendererValue(attr) {
26
- return attr?.value ?? attr?.object ?? attr?.objects;
12
+ return attr?.value ?? attr?.object ?? attr?.objects;
27
13
  }
28
- /**
29
- * The renderer-facing value of a query-result cell.
30
- *
31
- * A cell is a single channel: whatever a renderer needs is already on `value` — for a single-child
32
- * AsDetail column that is the nested PersistentObject itself, put there by the server so this
33
- * matches what {@link rendererValue} falls through to on a detail page. There is nothing to fall
34
- * back to, which is why this is deliberately not the same function: keeping them separate is what
35
- * stops a renderer silently receiving `undefined` because it was written against the attribute
36
- * shape's `object` / `objects` fields, which a row does not carry.
37
- */
38
14
  function cellValue(value) {
39
- return value?.value;
15
+ return value?.value;
40
16
  }
41
-
42
- const SPARK_ATTRIBUTE_RENDERERS = new InjectionToken('SparkAttributeRenderers', { factory: () => [] });
43
- /**
44
- * Register custom attribute renderers globally.
45
- *
46
- * @example
47
- * provideSparkAttributeRenderers([
48
- * { name: 'video-player', detailComponent: VideoDetailComponent, columnComponent: VideoColumnComponent },
49
- * { name: 'color-swatch', detailComponent: ColorDetailComponent, columnComponent: ColorColumnComponent },
50
- * ])
51
- */
17
+ const SPARK_ATTRIBUTE_RENDERERS = new InjectionToken("SparkAttributeRenderers", { factory: () => [] });
52
18
  function provideSparkAttributeRenderers(renderers) {
53
- return {
54
- provide: SPARK_ATTRIBUTE_RENDERERS,
55
- useValue: renderers,
56
- };
19
+ return {
20
+ provide: SPARK_ATTRIBUTE_RENDERERS,
21
+ useValue: renderers
22
+ };
57
23
  }
58
-
59
- /**
60
- * Generated bundle index. Do not edit.
61
- */
62
-
63
24
  export { SPARK_ATTRIBUTE_RENDERERS, cellValue, provideSparkAttributeRenderers, rendererValue, withDeclaredInputs };
64
- //# sourceMappingURL=mintplayer-ng-spark-renderers.mjs.map
25
+
26
+ //# sourceMappingURL=mintplayer-ng-spark-renderers.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mintplayer-ng-spark-renderers.mjs","sources":["../../renderers/src/renderer-inputs.ts","../../renderers/src/spark-attribute-renderer-registry.ts","../../renderers/mintplayer-ng-spark-renderers.ts"],"sourcesContent":["import { reflectComponentType, Type } from '@angular/core';\nimport { PersistentObjectAttribute, QueryResultItemValue } from '@mintplayer/ng-spark/models';\n\n// Reflection result cached per component type: the input builders are template\n// expressions re-evaluated every CD pass (and query-list virtual-scrolls).\nconst declaredInputs = new Map<Type<any>, Set<string>>();\n\n/**\n * Drops entries the component doesn't declare, so every contract member is\n * genuinely optional — NgComponentOutlet throws on an undeclared input.\n */\nexport function withDeclaredInputs(component: Type<any>, inputs: Record<string, any>): Record<string, any> {\n let declared = declaredInputs.get(component);\n if (!declared) {\n declared = new Set(reflectComponentType(component)?.inputs.map(i => i.templateName) ?? []);\n declaredInputs.set(component, declared);\n }\n return Object.fromEntries(Object.entries(inputs).filter(([k]) => declared!.has(k)));\n}\n\n/**\n * The renderer-facing value of an attribute: the flat value, or for AsDetail\n * attributes (whose flat value the server nulls on purpose) the nested\n * PersistentObject (single) / PersistentObject[] (array).\n *\n * Used by the detail and edit paths, which still work in attributes.\n */\nexport function rendererValue(attr: PersistentObjectAttribute | undefined): any {\n return attr?.value ?? attr?.object ?? attr?.objects;\n}\n\n/**\n * The renderer-facing value of a query-result cell.\n *\n * A cell is a single channel: whatever a renderer needs is already on `value` — for a single-child\n * AsDetail column that is the nested PersistentObject itself, put there by the server so this\n * matches what {@link rendererValue} falls through to on a detail page. There is nothing to fall\n * back to, which is why this is deliberately not the same function: keeping them separate is what\n * stops a renderer silently receiving `undefined` because it was written against the attribute\n * shape's `object` / `objects` fields, which a row does not carry.\n */\nexport function cellValue(value: QueryResultItemValue | undefined): any {\n return value?.value;\n}\n","import { InjectionToken, Provider, Type } from '@angular/core';\n\nexport interface SparkAttributeRendererRegistration {\n /** The renderer name (must match attr.renderer in model JSON) */\n name: string;\n /** Component for the PO detail page. Must implement SparkAttributeDetailRenderer. Omit for a column/edit-only renderer. */\n detailComponent?: Type<any> | null;\n /** Component for query-list column cells. Must implement SparkAttributeColumnRenderer. Omit for a detail/edit-only renderer. */\n columnComponent?: Type<any> | null;\n /** Optional component for create/edit forms. Must implement SparkAttributeEditRenderer. When omitted, the default input is used. */\n editComponent?: Type<any>;\n /**\n * Relabels this column's values in a filter panel (#431).\n *\n * A renderer is a component that paints arbitrary DOM, and a distinct value has no row, so it\n * cannot be instantiated to produce text for a filter list. The label therefore defaults to the\n * server-computed cell text: a status rendered as a coloured pill lists `Active`, a rating\n * rendered as stars lists `3`. This is the escape for when that is too raw — a value stored as\n * `2` whose cell reads \"High priority\".\n *\n * ⚠️ A **pure function**: no DOM, no component instantiation, no row context. There is no row to\n * give it, which is the whole reason the renderer itself cannot be used here.\n */\n filterLabel?: (value: unknown) => string;\n}\n\nexport const SPARK_ATTRIBUTE_RENDERERS = new InjectionToken<SparkAttributeRendererRegistration[]>(\n 'SparkAttributeRenderers',\n { factory: () => [] }\n);\n\n/**\n * Register custom attribute renderers globally.\n *\n * @example\n * provideSparkAttributeRenderers([\n * { name: 'video-player', detailComponent: VideoDetailComponent, columnComponent: VideoColumnComponent },\n * { name: 'color-swatch', detailComponent: ColorDetailComponent, columnComponent: ColorColumnComponent },\n * ])\n */\nexport function provideSparkAttributeRenderers(\n renderers: SparkAttributeRendererRegistration[]\n): Provider {\n return {\n provide: SPARK_ATTRIBUTE_RENDERERS,\n useValue: renderers,\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;AAGA;AACA;AACA,MAAM,cAAc,GAAG,IAAI,GAAG,EAA0B;AAExD;;;AAGG;AACG,SAAU,kBAAkB,CAAC,SAAoB,EAAE,MAA2B,EAAA;IAClF,IAAI,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC;IAC5C,IAAI,CAAC,QAAQ,EAAE;QACb,QAAQ,GAAG,IAAI,GAAG,CAAC,oBAAoB,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;AAC1F,QAAA,cAAc,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC;IACzC;AACA,IAAA,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,QAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACrF;AAEA;;;;;;AAMG;AACG,SAAU,aAAa,CAAC,IAA2C,EAAA;IACvE,OAAO,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,MAAM,IAAI,IAAI,EAAE,OAAO;AACrD;AAEA;;;;;;;;;AASG;AACG,SAAU,SAAS,CAAC,KAAuC,EAAA;IAC/D,OAAO,KAAK,EAAE,KAAK;AACrB;;ACjBO,MAAM,yBAAyB,GAAG,IAAI,cAAc,CACzD,yBAAyB,EACzB,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;AAGvB;;;;;;;;AAQG;AACG,SAAU,8BAA8B,CAC5C,SAA+C,EAAA;IAE/C,OAAO;AACL,QAAA,OAAO,EAAE,yBAAyB;AAClC,QAAA,QAAQ,EAAE,SAAS;KACpB;AACH;;AC/CA;;AAEG;;;;"}
1
+ {"version":3,"file":"mintplayer-ng-spark-renderers.mjs","names":[],"sources":["../../renderers/src/renderer-inputs.ts","../../renderers/src/spark-attribute-renderer-registry.ts"],"sourcesContent":["import { reflectComponentType, Type } from '@angular/core';\nimport { PersistentObjectAttribute, QueryResultItemValue } from '@mintplayer/ng-spark/models';\n\n// Reflection result cached per component type: the input builders are template\n// expressions re-evaluated every CD pass (and query-list virtual-scrolls).\nconst declaredInputs = new Map<Type<any>, Set<string>>();\n\n/**\n * Drops entries the component doesn't declare, so every contract member is\n * genuinely optional — NgComponentOutlet throws on an undeclared input.\n */\nexport function withDeclaredInputs(component: Type<any>, inputs: Record<string, any>): Record<string, any> {\n let declared = declaredInputs.get(component);\n if (!declared) {\n declared = new Set(reflectComponentType(component)?.inputs.map(i => i.templateName) ?? []);\n declaredInputs.set(component, declared);\n }\n return Object.fromEntries(Object.entries(inputs).filter(([k]) => declared!.has(k)));\n}\n\n/**\n * The renderer-facing value of an attribute: the flat value, or for AsDetail\n * attributes (whose flat value the server nulls on purpose) the nested\n * PersistentObject (single) / PersistentObject[] (array).\n *\n * Used by the detail and edit paths, which still work in attributes.\n */\nexport function rendererValue(attr: PersistentObjectAttribute | undefined): any {\n return attr?.value ?? attr?.object ?? attr?.objects;\n}\n\n/**\n * The renderer-facing value of a query-result cell.\n *\n * A cell is a single channel: whatever a renderer needs is already on `value` — for a single-child\n * AsDetail column that is the nested PersistentObject itself, put there by the server so this\n * matches what {@link rendererValue} falls through to on a detail page. There is nothing to fall\n * back to, which is why this is deliberately not the same function: keeping them separate is what\n * stops a renderer silently receiving `undefined` because it was written against the attribute\n * shape's `object` / `objects` fields, which a row does not carry.\n */\nexport function cellValue(value: QueryResultItemValue | undefined): any {\n return value?.value;\n}\n","import { InjectionToken, Provider, Type } from '@angular/core';\n\nexport interface SparkAttributeRendererRegistration {\n /** The renderer name (must match attr.renderer in model JSON) */\n name: string;\n /** Component for the PO detail page. Must implement SparkAttributeDetailRenderer. Omit for a column/edit-only renderer. */\n detailComponent?: Type<any> | null;\n /** Component for query-list column cells. Must implement SparkAttributeColumnRenderer. Omit for a detail/edit-only renderer. */\n columnComponent?: Type<any> | null;\n /** Optional component for create/edit forms. Must implement SparkAttributeEditRenderer. When omitted, the default input is used. */\n editComponent?: Type<any>;\n /**\n * Relabels this column's values in a filter panel (#431).\n *\n * A renderer is a component that paints arbitrary DOM, and a distinct value has no row, so it\n * cannot be instantiated to produce text for a filter list. The label therefore defaults to the\n * server-computed cell text: a status rendered as a coloured pill lists `Active`, a rating\n * rendered as stars lists `3`. This is the escape for when that is too raw — a value stored as\n * `2` whose cell reads \"High priority\".\n *\n * ⚠️ A **pure function**: no DOM, no component instantiation, no row context. There is no row to\n * give it, which is the whole reason the renderer itself cannot be used here.\n */\n filterLabel?: (value: unknown) => string;\n}\n\nexport const SPARK_ATTRIBUTE_RENDERERS = new InjectionToken<SparkAttributeRendererRegistration[]>(\n 'SparkAttributeRenderers',\n { factory: () => [] }\n);\n\n/**\n * Register custom attribute renderers globally.\n *\n * @example\n * provideSparkAttributeRenderers([\n * { name: 'video-player', detailComponent: VideoDetailComponent, columnComponent: VideoColumnComponent },\n * { name: 'color-swatch', detailComponent: ColorDetailComponent, columnComponent: ColorColumnComponent },\n * ])\n */\nexport function provideSparkAttributeRenderers(\n renderers: SparkAttributeRendererRegistration[]\n): Provider {\n return {\n provide: SPARK_ATTRIBUTE_RENDERERS,\n useValue: renderers,\n };\n}\n"],"mappings":";AAKA,MAAM,iCAAiB,IAAI,IAAG;AAM9B,SAAgB,mBAAmB,WAAsB,QAA2B;CAClF,IAAI,WAAW,eAAe,IAAI,SAAS;CAC3C,IAAI,CAAC,UAAU;EACb,WAAW,IAAI,IAAI,qBAAqB,SAAS,CAAC,EAAE,OAAO,KAAI,MAAK,EAAE,YAAY,KAAK,CAAA,CAAE;EACzF,eAAe,IAAI,WAAW,QAAQ;CACxC;CACA,OAAO,OAAO,YAAY,OAAO,QAAQ,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,SAAU,IAAI,CAAC,CAAC,CAAC;AACpF;AASA,SAAgB,cAAc,MAA2C;CACvE,OAAO,MAAM,SAAS,MAAM,UAAU,MAAM;AAC9C;AAYA,SAAgB,UAAU,OAAuC;CAC/D,OAAO,OAAO;AAChB;ACjBA,MAAa,4BAA4B,IAAI,eAC3C,2BACA,EAAE,eAAe,CAAA,EAAE,CAAE;AAYvB,SAAgB,+BACd,WAA+C;CAE/C,OAAO;EACL,SAAS;EACT,UAAU;CACX;AACH"}