@mintplayer/ng-spark 22.29.0 → 22.31.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.
- package/README.md +3 -3
- package/auth/README.md +216 -0
- package/fesm2022/mintplayer-ng-spark-auth-account.mjs +2023 -0
- package/fesm2022/mintplayer-ng-spark-auth-account.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs +96 -0
- package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-confirm-email.mjs +200 -0
- package/fesm2022/mintplayer-ng-spark-auth-confirm-email.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-core.mjs +452 -0
- package/fesm2022/mintplayer-ng-spark-auth-core.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-external-login.mjs +201 -0
- package/fesm2022/mintplayer-ng-spark-auth-external-login.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-forgot-password.mjs +196 -0
- package/fesm2022/mintplayer-ng-spark-auth-forgot-password.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-guards.mjs +16 -0
- package/fesm2022/mintplayer-ng-spark-auth-guards.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-interceptors.mjs +18 -0
- package/fesm2022/mintplayer-ng-spark-auth-interceptors.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-login.mjs +282 -0
- package/fesm2022/mintplayer-ng-spark-auth-login.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-models.mjs +114 -0
- package/fesm2022/mintplayer-ng-spark-auth-models.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-pipes.mjs +43 -0
- package/fesm2022/mintplayer-ng-spark-auth-pipes.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-register.mjs +216 -0
- package/fesm2022/mintplayer-ng-spark-auth-register.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-reset-password.mjs +223 -0
- package/fesm2022/mintplayer-ng-spark-auth-reset-password.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-routes.mjs +194 -0
- package/fesm2022/mintplayer-ng-spark-auth-routes.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-sign-in.mjs +210 -0
- package/fesm2022/mintplayer-ng-spark-auth-sign-in.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-two-factor.mjs +218 -0
- package/fesm2022/mintplayer-ng-spark-auth-two-factor.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth.mjs +48 -0
- package/fesm2022/mintplayer-ng-spark-auth.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-client-operations.mjs +368 -2
- package/fesm2022/mintplayer-ng-spark-client-operations.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-grid.mjs +22 -9
- package/fesm2022/mintplayer-ng-spark-grid.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-identity-provider-connected-applications.mjs +389 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider-connected-applications.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider-core.mjs +204 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider-core.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider-developers.mjs +486 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider-developers.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider-management.mjs +350 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider-management.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider.mjs +66 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-models.mjs +2 -1
- package/fesm2022/mintplayer-ng-spark-models.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-detail.mjs +13 -3
- package/fesm2022/mintplayer-ng-spark-po-detail.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-form.mjs +6 -3
- package/fesm2022/mintplayer-ng-spark-po-form.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-pwa.mjs +162 -0
- package/fesm2022/mintplayer-ng-spark-pwa.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-query-list.mjs +5 -2
- package/fesm2022/mintplayer-ng-spark-query-list.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-renderers.mjs +109 -4
- package/fesm2022/mintplayer-ng-spark-renderers.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs +36 -6
- package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-services.mjs +48 -6
- package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-shell.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark.mjs.map +1 -1
- package/package.json +101 -3
- package/types/mintplayer-ng-spark-auth-account.d.ts +255 -0
- package/types/mintplayer-ng-spark-auth-auth-bar.d.ts +22 -0
- package/types/mintplayer-ng-spark-auth-confirm-email.d.ts +21 -0
- package/types/mintplayer-ng-spark-auth-core.d.ts +197 -0
- package/types/mintplayer-ng-spark-auth-external-login.d.ts +132 -0
- package/types/mintplayer-ng-spark-auth-forgot-password.d.ts +18 -0
- package/types/mintplayer-ng-spark-auth-guards.d.ts +15 -0
- package/types/mintplayer-ng-spark-auth-interceptors.d.ts +2 -0
- package/types/mintplayer-ng-spark-auth-login.d.ts +46 -0
- package/types/mintplayer-ng-spark-auth-models.d.ts +489 -0
- package/types/mintplayer-ng-spark-auth-pipes.d.ts +8 -0
- package/types/mintplayer-ng-spark-auth-register.d.ts +21 -0
- package/types/mintplayer-ng-spark-auth-reset-password.d.ts +25 -0
- package/types/mintplayer-ng-spark-auth-routes.d.ts +154 -0
- package/types/mintplayer-ng-spark-auth-sign-in.d.ts +84 -0
- package/types/mintplayer-ng-spark-auth-two-factor.d.ts +23 -0
- package/types/mintplayer-ng-spark-auth.d.ts +24 -0
- package/types/mintplayer-ng-spark-client-operations.d.ts +148 -3
- package/types/mintplayer-ng-spark-grid.d.ts +19 -1
- package/types/mintplayer-ng-spark-identity-provider-connected-applications.d.ts +35 -0
- package/types/mintplayer-ng-spark-identity-provider-core.d.ts +144 -0
- package/types/mintplayer-ng-spark-identity-provider-developers.d.ts +55 -0
- package/types/mintplayer-ng-spark-identity-provider-management.d.ts +33 -0
- package/types/mintplayer-ng-spark-identity-provider.d.ts +64 -0
- package/types/mintplayer-ng-spark-models.d.ts +28 -0
- package/types/mintplayer-ng-spark-po-detail.d.ts +8 -0
- package/types/mintplayer-ng-spark-po-form.d.ts +2 -0
- package/types/mintplayer-ng-spark-pwa.d.ts +89 -0
- package/types/mintplayer-ng-spark-query-list.d.ts +2 -0
- package/types/mintplayer-ng-spark-renderers.d.ts +47 -0
- package/types/mintplayer-ng-spark-retry-action-modal.d.ts +1 -0
- package/types/mintplayer-ng-spark-services.d.ts +37 -1
- package/types/mintplayer-ng-spark-shell.d.ts +2 -2
- package/types/mintplayer-ng-spark.d.ts +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mintplayer-ng-spark-renderers.mjs","names":[],"sources":["../../renderers/src/renderer-inputs.ts","../../renderers/src/spark-attribute-renderer-registry.ts","../../renderers/src/row-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 * A **row** renderer for AsDetail tables (the PO detail page and the edit form). Attributes of a row\n * type that carry this renderer are not drawn as columns; instead the component is drawn ONCE per\n * row, under the row's first cell, given all of them. Must implement {@link SparkAttributeRowRenderer}.\n *\n * For facts about a row rather than columns of it — contributions' \"by Alice · 3 days ago ·\n * History (4)\" line collapses three read-only attributes into one. `rendererOptions` are taken from\n * the first such attribute (a library writes the same options on each).\n */\n rowComponent?: 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","import { Type } from '@angular/core';\nimport { EntityAttributeDefinition, EntityType, hasShowedOnFlag, ShowedOn } from '@mintplayer/ng-spark/models';\nimport { SparkAttributeRendererRegistration } from './spark-attribute-renderer-registry';\nimport { withDeclaredInputs } from './renderer-inputs';\n\n/** The row renderer of an AsDetail row type, resolved once per type (see `rowComponent`). */\nexport interface SparkResolvedRowRenderer {\n component: Type<any>;\n /** The row-type attributes carrying the renderer, in model order. */\n attributes: EntityAttributeDefinition[];\n /** `rendererOptions` of the first of them. */\n options: Record<string, any> | undefined;\n}\n\n/** Whether `attribute` is drawn by a row renderer (and therefore not as a column). */\nexport function isRowRendered(attribute: EntityAttributeDefinition, registry: readonly SparkAttributeRendererRegistration[] | null | undefined): boolean {\n if (!attribute.renderer || !registry?.length) return false;\n return registry.some(r => r.name === attribute.renderer && !!r.rowComponent);\n}\n\n/**\n * The first row renderer the row type's visible attributes name, or null. One per row: a row type\n * whose attributes name two different row renderers gets the first in model order.\n */\nexport function resolveRowRenderer(\n rowType: EntityType | null | undefined,\n registry: readonly SparkAttributeRendererRegistration[] | null | undefined,\n): SparkResolvedRowRenderer | null {\n if (!rowType || !registry?.length) return null;\n const ordered = [...(rowType.attributes ?? [])]\n .filter(a => hasShowedOnFlag(a.showedOn, ShowedOn.Query) || hasShowedOnFlag(a.showedOn, ShowedOn.PersistentObject))\n .sort((a, b) => a.order - b.order);\n for (const attribute of ordered) {\n const registration = attribute.renderer ? registry.find(r => r.name === attribute.renderer && r.rowComponent) : undefined;\n if (!registration) continue;\n const attributes = ordered.filter(a => a.renderer === registration.name);\n return { component: registration.rowComponent!, attributes, options: attributes[0]?.rendererOptions };\n }\n return null;\n}\n\n/** The input bag for a row renderer, filtered to what the component declares. */\nexport function rowRendererInputs(renderer: SparkResolvedRowRenderer, row: Record<string, any>, ownerId: string | undefined): Record<string, any> {\n return withDeclaredInputs(renderer.component, {\n row,\n attributes: renderer.attributes,\n options: renderer.options,\n ownerId,\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;ACPA,MAAa,4BAA4B,IAAI,eAC3C,2BACA,EAAE,eAAe,CAAA,EAAE,CAAE;AAYvB,SAAgB,+BACd,WAA+C;CAE/C,OAAO;EACL,SAAS;EACT,UAAU;CACX;AACH;AC1CA,SAAgB,cAAc,WAAsC,UAA0E;CAC5I,IAAI,CAAC,UAAU,YAAY,CAAC,UAAU,QAAQ,OAAO;CACrD,OAAO,SAAS,MAAK,MAAK,EAAE,SAAS,UAAU,YAAY,CAAC,CAAC,EAAE,YAAY;AAC7E;AAMA,SAAgB,mBACd,SACA,UAA0E;CAE1E,IAAI,CAAC,WAAW,CAAC,UAAU,QAAQ,OAAO;CAC1C,MAAM,UAAU,CAAC,GAAI,QAAQ,cAAc,CAAA,CAAG,CAAC,CAC5C,QAAO,MAAK,gBAAgB,EAAE,UAAU,SAAS,KAAK,KAAK,gBAAgB,EAAE,UAAU,SAAS,gBAAgB,CAAC,CAAC,CAClH,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;CACnC,KAAK,MAAM,aAAa,SAAS;EAC/B,MAAM,eAAe,UAAU,WAAW,SAAS,MAAK,MAAK,EAAE,SAAS,UAAU,YAAY,EAAE,YAAY,IAAI;EAChH,IAAI,CAAC,cAAc;EACnB,MAAM,aAAa,QAAQ,QAAO,MAAK,EAAE,aAAa,aAAa,IAAI;EACvE,OAAO;GAAE,WAAW,aAAa;GAAe;GAAY,SAAS,WAAW,EAAE,EAAE;EAAe;CACrG;CACA,OAAO;AACT;AAGA,SAAgB,kBAAkB,UAAoC,KAA0B,SAA2B;CACzH,OAAO,mBAAmB,SAAS,WAAW;EAC5C;EACA,YAAY,SAAS;EACrB,SAAS,SAAS;EAClB;CACD,CAAA;AACH"}
|
|
1
|
+
{"version":3,"file":"mintplayer-ng-spark-renderers.mjs","names":[],"sources":["../../renderers/src/renderer-inputs.ts","../../renderers/src/paragraph-renderer.component.ts","../../renderers/src/spark-attribute-renderer-registry.ts","../../renderers/src/row-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 { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { SparkAttributeDetailRenderer } from './spark-attribute-renderer';\n\n/**\n * The core `paragraph` renderer (generic passkeys page, D5): a read-only string shown as body text,\n * full width and without a label — an explanatory text block on a persistent object, filled from\n * translations by the actions class. Pair it with `showedOn: PersistentObject` so it never becomes a\n * grid column.\n *\n * **Escaped by default.** The value is interpolated, so markup in it is shown as text. A blank line\n * starts a new paragraph and a single newline is a line break.\n *\n * `\"rendererOptions\": { \"sanitize\": false }` renders the value as HTML instead, through Angular's\n * `[innerHTML]`, whose sanitizer still strips scripts, event handlers and `javascript:` URLs. There is\n * deliberately no `bypassSecurityTrustHtml` anywhere on this path: the opt-out is \"allow markup\",\n * never \"allow script\".\n */\n@Component({\n selector: 'spark-paragraph-renderer',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (asHtml()) {\n <div class=\"spark-paragraph\" [innerHTML]=\"text()\"></div>\n } @else {\n @for (paragraph of paragraphs(); track $index) {\n <p class=\"spark-paragraph\">@for (line of paragraph; track $index) {@if (!$first) {<br />}{{ line }}}</p>\n }\n }\n `,\n styles: [`\n :host { display: block; }\n .spark-paragraph:last-child { margin-bottom: 0; }\n `],\n})\nexport class SparkParagraphRendererComponent implements SparkAttributeDetailRenderer {\n readonly value = input<any>();\n readonly options = input<Record<string, any> | undefined>();\n\n protected readonly text = computed(() => {\n const value = this.value();\n return value === null || value === undefined ? '' : String(value);\n });\n\n /** Only an explicit `false` opts out of escaping; anything else, absent included, escapes. */\n protected readonly asHtml = computed(() => this.options()?.['sanitize'] === false);\n\n /** Paragraphs split on blank lines, each a list of its lines. */\n protected readonly paragraphs = computed(() => {\n const text = this.text().replace(/\\r\\n?/g, '\\n').trim();\n if (!text) return [];\n return text.split(/\\n[ \\t]*\\n+/).map(paragraph => paragraph.split('\\n'));\n });\n}\n","import { InjectionToken, Provider, Type } from '@angular/core';\nimport { SparkParagraphRendererComponent } from './paragraph-renderer.component';\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 * A **row** renderer for AsDetail tables (the PO detail page and the edit form). Attributes of a row\n * type that carry this renderer are not drawn as columns; instead the component is drawn ONCE per\n * row, under the row's first cell, given all of them. Must implement {@link SparkAttributeRowRenderer}.\n *\n * For facts about a row rather than columns of it — contributions' \"by Alice · 3 days ago ·\n * History (4)\" line collapses three read-only attributes into one. `rendererOptions` are taken from\n * the first such attribute (a library writes the same options on each).\n */\n rowComponent?: 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 * The detail page draws the renderer across the whole row, with no label: for text blocks such\n * as the core `paragraph` renderer, which are body text rather than a labelled value.\n */\n fullWidth?: boolean;\n}\n\n/**\n * The renderers ng-spark ships itself, available in every app without registration. An app's own\n * registration of the same name wins (it comes first in the list).\n */\nexport const sparkCoreRenderers: readonly SparkAttributeRendererRegistration[] = [\n { name: 'paragraph', detailComponent: SparkParagraphRendererComponent, fullWidth: true },\n];\n\nexport const SPARK_ATTRIBUTE_RENDERERS = new InjectionToken<SparkAttributeRendererRegistration[]>(\n 'SparkAttributeRenderers',\n { factory: () => [...sparkCoreRenderers] }\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 // The app's first, so `find` by name lets it replace a core renderer.\n useValue: [...renderers, ...sparkCoreRenderers],\n };\n}\n","import { Type } from '@angular/core';\nimport { EntityAttributeDefinition, EntityType, hasShowedOnFlag, ShowedOn } from '@mintplayer/ng-spark/models';\nimport { SparkAttributeRendererRegistration } from './spark-attribute-renderer-registry';\nimport { withDeclaredInputs } from './renderer-inputs';\n\n/** The row renderer of an AsDetail row type, resolved once per type (see `rowComponent`). */\nexport interface SparkResolvedRowRenderer {\n component: Type<any>;\n /** The row-type attributes carrying the renderer, in model order. */\n attributes: EntityAttributeDefinition[];\n /** `rendererOptions` of the first of them. */\n options: Record<string, any> | undefined;\n}\n\n/** Whether `attribute` is drawn by a row renderer (and therefore not as a column). */\nexport function isRowRendered(attribute: EntityAttributeDefinition, registry: readonly SparkAttributeRendererRegistration[] | null | undefined): boolean {\n if (!attribute.renderer || !registry?.length) return false;\n return registry.some(r => r.name === attribute.renderer && !!r.rowComponent);\n}\n\n/**\n * The first row renderer the row type's visible attributes name, or null. One per row: a row type\n * whose attributes name two different row renderers gets the first in model order.\n */\nexport function resolveRowRenderer(\n rowType: EntityType | null | undefined,\n registry: readonly SparkAttributeRendererRegistration[] | null | undefined,\n): SparkResolvedRowRenderer | null {\n if (!rowType || !registry?.length) return null;\n const ordered = [...(rowType.attributes ?? [])]\n .filter(a => hasShowedOnFlag(a.showedOn, ShowedOn.Query) || hasShowedOnFlag(a.showedOn, ShowedOn.PersistentObject))\n .sort((a, b) => a.order - b.order);\n for (const attribute of ordered) {\n const registration = attribute.renderer ? registry.find(r => r.name === attribute.renderer && r.rowComponent) : undefined;\n if (!registration) continue;\n const attributes = ordered.filter(a => a.renderer === registration.name);\n return { component: registration.rowComponent!, attributes, options: attributes[0]?.rendererOptions };\n }\n return null;\n}\n\n/** The input bag for a row renderer, filtered to what the component declares. */\nexport function rowRendererInputs(renderer: SparkResolvedRowRenderer, row: Record<string, any>, ownerId: string | undefined): Record<string, any> {\n return withDeclaredInputs(renderer.component, {\n row,\n attributes: renderer.attributes,\n options: renderer.options,\n ownerId,\n });\n}\n"],"mappings":"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"}
|
|
@@ -6,11 +6,13 @@ import { BsModalBodyDirective, BsModalDirective, BsModalFooterDirective, BsModal
|
|
|
6
6
|
import { BsButtonTypeDirective } from "@mintplayer/ng-bootstrap/button-type";
|
|
7
7
|
import { RetryActionService, SparkService } from "@mintplayer/ng-spark/services";
|
|
8
8
|
import { SparkPoFormComponent } from "@mintplayer/ng-spark/po-form";
|
|
9
|
-
import {
|
|
9
|
+
import { TranslateKeyPipe } from "@mintplayer/ng-spark/pipes";
|
|
10
|
+
import { SPARK_RETRY_CANCEL, dictToNestedPo, nestedPoToDict } from "@mintplayer/ng-spark/models";
|
|
10
11
|
var SparkRetryActionModalComponent = class SparkRetryActionModalComponent {
|
|
11
12
|
retryActionService = inject(RetryActionService);
|
|
12
13
|
sparkService = inject(SparkService);
|
|
13
14
|
colors = Color;
|
|
15
|
+
cancel = SPARK_RETRY_CANCEL;
|
|
14
16
|
isOpen = computed(() => this.retryActionService.payload() !== null, ...ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []);
|
|
15
17
|
entityType = signal(null, ...ngDevMode ? [{ debugName: "entityType" }] : /* istanbul ignore next */ []);
|
|
16
18
|
formData = signal({}, ...ngDevMode ? [{ debugName: "formData" }] : /* istanbul ignore next */ []);
|
|
@@ -72,7 +74,7 @@ var SparkRetryActionModalComponent = class SparkRetryActionModalComponent {
|
|
|
72
74
|
selector: "spark-retry-action-modal",
|
|
73
75
|
ngImport: i0,
|
|
74
76
|
template: `
|
|
75
|
-
<bs-modal [isOpen]="isOpen()" (isOpenChange)="!$event && onOption(
|
|
77
|
+
<bs-modal [isOpen]="isOpen()" (isOpenChange)="!$event && onOption(cancel)">
|
|
76
78
|
<div *bsModal>
|
|
77
79
|
<div bsModalHeader>
|
|
78
80
|
<h5 class="modal-title">{{ retryActionService.payload()?.title }}</h5>
|
|
@@ -93,11 +95,22 @@ var SparkRetryActionModalComponent = class SparkRetryActionModalComponent {
|
|
|
93
95
|
@for (option of retryActionService.payload()?.options; track option) {
|
|
94
96
|
<button
|
|
95
97
|
type="button"
|
|
96
|
-
[color]="
|
|
98
|
+
[color]="colors.primary"
|
|
97
99
|
(click)="onOption(option)">
|
|
98
100
|
{{ option }}
|
|
99
101
|
</button>
|
|
100
102
|
}
|
|
103
|
+
<!-- The prompt's Cancel is the client's, in the user's language; it answers the identifier
|
|
104
|
+
'Cancel' whatever it reads, which is why the server asks for it with a flag. -->
|
|
105
|
+
@if (retryActionService.payload()?.cancellable) {
|
|
106
|
+
<button
|
|
107
|
+
type="button"
|
|
108
|
+
class="spark-retry-cancel"
|
|
109
|
+
[color]="colors.secondary"
|
|
110
|
+
(click)="onOption(cancel)">
|
|
111
|
+
{{ 'common.cancel' | t }}
|
|
112
|
+
</button>
|
|
113
|
+
}
|
|
101
114
|
</div>
|
|
102
115
|
</div>
|
|
103
116
|
</bs-modal>
|
|
@@ -172,6 +185,11 @@ var SparkRetryActionModalComponent = class SparkRetryActionModalComponent {
|
|
|
172
185
|
"nestedTriggerBlurred",
|
|
173
186
|
"refreshOverlayChange"
|
|
174
187
|
]
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
kind: "pipe",
|
|
191
|
+
type: TranslateKeyPipe,
|
|
192
|
+
name: "t"
|
|
175
193
|
}
|
|
176
194
|
],
|
|
177
195
|
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
@@ -194,10 +212,11 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
194
212
|
BsModalBodyDirective,
|
|
195
213
|
BsModalFooterDirective,
|
|
196
214
|
BsButtonTypeDirective,
|
|
197
|
-
SparkPoFormComponent
|
|
215
|
+
SparkPoFormComponent,
|
|
216
|
+
TranslateKeyPipe
|
|
198
217
|
],
|
|
199
218
|
template: `
|
|
200
|
-
<bs-modal [isOpen]="isOpen()" (isOpenChange)="!$event && onOption(
|
|
219
|
+
<bs-modal [isOpen]="isOpen()" (isOpenChange)="!$event && onOption(cancel)">
|
|
201
220
|
<div *bsModal>
|
|
202
221
|
<div bsModalHeader>
|
|
203
222
|
<h5 class="modal-title">{{ retryActionService.payload()?.title }}</h5>
|
|
@@ -218,11 +237,22 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
218
237
|
@for (option of retryActionService.payload()?.options; track option) {
|
|
219
238
|
<button
|
|
220
239
|
type="button"
|
|
221
|
-
[color]="
|
|
240
|
+
[color]="colors.primary"
|
|
222
241
|
(click)="onOption(option)">
|
|
223
242
|
{{ option }}
|
|
224
243
|
</button>
|
|
225
244
|
}
|
|
245
|
+
<!-- The prompt's Cancel is the client's, in the user's language; it answers the identifier
|
|
246
|
+
'Cancel' whatever it reads, which is why the server asks for it with a flag. -->
|
|
247
|
+
@if (retryActionService.payload()?.cancellable) {
|
|
248
|
+
<button
|
|
249
|
+
type="button"
|
|
250
|
+
class="spark-retry-cancel"
|
|
251
|
+
[color]="colors.secondary"
|
|
252
|
+
(click)="onOption(cancel)">
|
|
253
|
+
{{ 'common.cancel' | t }}
|
|
254
|
+
</button>
|
|
255
|
+
}
|
|
226
256
|
</div>
|
|
227
257
|
</div>
|
|
228
258
|
</bs-modal>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mintplayer-ng-spark-retry-action-modal.mjs","names":[],"sources":["../../retry-action-modal/src/spark-retry-action-modal.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, effect, inject, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective } from '@mintplayer/ng-bootstrap/modal';\nimport { BsButtonTypeDirective } from '@mintplayer/ng-bootstrap/button-type';\nimport { RetryActionService, SparkService } from '@mintplayer/ng-spark/services';\nimport { SparkPoFormComponent } from '@mintplayer/ng-spark/po-form';\nimport {\n dictToNestedPo,\n EntityAttributeDefinition,\n EntityType,\n EntityTypeResolver,\n nestedPoToDict,\n PersistentObject,\n PersistentObjectAttribute,\n} from '@mintplayer/ng-spark/models';\n\n/**\n * Renders a retry-action popup. Before PRD §3 this component rendered title / message /\n * option buttons only and silently forwarded the incoming <c>persistentObject</c> back to\n * the server on submit — meaning any <c>Retry.Action(..., persistentObject)</c> flow had\n * no UI to actually edit the PO. This component now embeds the shared PO form so every\n * scalar / Reference / AsDetail attribute on the scaffolded Virtual PO is a real form\n * field, and the values the user fills in flow back to the server via\n * <c>RetryResult.PersistentObject</c>.\n */\n@Component({\n selector: 'spark-retry-action-modal',\n imports: [CommonModule, BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective, BsButtonTypeDirective, SparkPoFormComponent],\n template: `\n <bs-modal [isOpen]=\"isOpen()\" (isOpenChange)=\"!$event && onOption('Cancel')\">\n <div *bsModal>\n <div bsModalHeader>\n <h5 class=\"modal-title\">{{ retryActionService.payload()?.title }}</h5>\n </div>\n <div bsModalBody>\n @if (retryActionService.payload()?.message; as message) {\n <p>{{ message }}</p>\n }\n @if (entityType(); as et) {\n <spark-po-form\n [entityType]=\"et\"\n [(formData)]=\"formData\"\n [showButtons]=\"false\">\n </spark-po-form>\n }\n </div>\n <div bsModalFooter>\n @for (option of retryActionService.payload()?.options; track option) {\n <button\n type=\"button\"\n [color]=\"option === 'Cancel' ? colors.secondary : colors.primary\"\n (click)=\"onOption(option)\">\n {{ option }}\n </button>\n }\n </div>\n </div>\n </bs-modal>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class SparkRetryActionModalComponent {\n protected readonly retryActionService = inject(RetryActionService);\n private readonly sparkService = inject(SparkService);\n\n colors = Color;\n isOpen = computed(() => this.retryActionService.payload() !== null);\n\n /**\n * EntityType definition for the incoming PO — fetched lazily via SparkService so the\n * form knows which attributes are editable, their labels, rules, renderers, etc.\n * `null` when the payload has no persistentObject or its objectTypeId doesn't match\n * any registered entity type (renders the modal as a simple option picker).\n */\n entityType = signal<EntityType | null>(null);\n formData = signal<Record<string, any>>({});\n private allEntityTypes: EntityType[] = [];\n\n constructor() {\n // Reseed form state every time the retry service opens/closes the modal. Effect\n // cleanup isn't needed since `payload` is a signal and the component's own lifetime\n // is root-scoped.\n effect(() => {\n const payload = this.retryActionService.payload();\n if (!payload?.persistentObject) {\n this.entityType.set(null);\n this.formData.set({});\n return;\n }\n void this.seedForm(payload.persistentObject);\n });\n }\n\n private async seedForm(po: PersistentObject): Promise<void> {\n // Virtual POs used for retry prompts (e.g. ConfirmDeleteCar) typically have no\n // security.json grant — so `getEntityTypes()` filters them out for the current user\n // and the lookup-by-id would return null, leaving the form blank. The scaffolded PO\n // already carries full attribute metadata (label / dataType / rules / renderer / etc.),\n // so we synthesize an EntityType from the attributes directly and skip the HTTP\n // lookup altogether. `getEntityTypes()` is still fetched because the embedded\n // spark-po-form needs the full list to resolve nested AsDetail / Reference types the\n // retry PO might point at. The guard below is a COMPONENT-local cache and is the only\n // one there is: the service does not cache, so every caller that skips such a guard\n // issues another request.\n if (this.allEntityTypes.length === 0) {\n try { this.allEntityTypes = await this.sparkService.getEntityTypes(); }\n catch { this.allEntityTypes = []; }\n }\n this.entityType.set(entityTypeFromPo(po));\n // Flatten the nested PO into the Record<string, any> shape the shared form uses\n // throughout the rest of ng-spark — same transformation po-edit applies.\n this.formData.set(nestedPoToDict(po));\n }\n\n onOption(option: string): void {\n const payload = this.retryActionService.payload();\n if (!payload) return;\n\n const populated = this.populatedPersistentObject(payload.persistentObject);\n this.retryActionService.respond({\n step: payload.step,\n option,\n persistentObject: populated,\n });\n }\n\n /**\n * Builds the PO the server sees under <c>Retry.Result.PersistentObject</c>. If the\n * form resolved an EntityType, rebuild from the schema + formData (identical to the\n * po-edit save path — AsDetail recursion included). Otherwise forward the incoming\n * PO unmodified so pre-§3 flows without editable attributes keep working.\n */\n private populatedPersistentObject(incoming: PersistentObject | undefined): PersistentObject | undefined {\n if (!incoming) return undefined;\n const type = this.entityType();\n if (!type) return incoming;\n\n const resolver: EntityTypeResolver = (clrName) => this.allEntityTypes.find(t => t.clrType === clrName);\n const rebuilt = dictToNestedPo(this.formData(), type, resolver);\n const populated: PersistentObject = {\n ...incoming,\n attributes: mergeAttributeMetadata(incoming.attributes ?? [], rebuilt.attributes),\n };\n return populated;\n }\n}\n\n/**\n * Builds a synthetic <see cref=\"EntityType\"/> from the PO's own scaffolded attributes so\n * the embedded spark-po-form can render without having to locate the matching server-side\n * EntityType registration. Used for Virtual POs that are schema-registered but not\n * security-granted (retry-action popups).\n */\nfunction entityTypeFromPo(po: PersistentObject): EntityType {\n return {\n id: po.objectTypeId,\n name: po.name,\n clrType: '', // Not needed by the form's rendering path; the PO's attributes carry the schema.\n breadcrumb: undefined,\n tabs: [],\n groups: [],\n attributes: (po.attributes ?? []).map(attrToDefinition),\n queries: [],\n };\n}\n\nfunction attrToDefinition(attr: PersistentObjectAttribute): EntityAttributeDefinition {\n return {\n id: attr.id ?? '',\n name: attr.name,\n label: attr.label,\n dataType: attr.dataType,\n isArray: attr.isArray,\n isRequired: attr.isRequired,\n isReadOnly: attr.isReadOnly,\n order: attr.order,\n query: attr.query,\n asDetailType: attr.asDetailType,\n showedOn: attr.showedOn,\n rules: attr.rules ?? [],\n group: attr.group,\n renderer: attr.renderer,\n rendererOptions: attr.rendererOptions,\n };\n}\n\n/**\n * Keeps the server-issued id + metadata on each attribute while overlaying the user's\n * values from the rebuilt PO. Prevents the modal from accidentally dropping server-only\n * fields (e.g. rules, renderer options) that the form didn't need to know about.\n */\nfunction mergeAttributeMetadata(\n incoming: PersistentObjectAttribute[],\n rebuilt: PersistentObjectAttribute[],\n): PersistentObjectAttribute[] {\n const byName = new Map(rebuilt.map(a => [a.name, a]));\n return incoming.map(source => {\n const updated = byName.get(source.name);\n if (!updated) return source;\n return {\n ...source,\n value: updated.value,\n object: updated.object,\n objects: updated.objects,\n asDetailType: updated.asDetailType ?? source.asDetailType,\n isValueChanged: true,\n };\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mintplayer-ng-spark-retry-action-modal.mjs","names":[],"sources":["../../retry-action-modal/src/spark-retry-action-modal.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, effect, inject, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective } from '@mintplayer/ng-bootstrap/modal';\nimport { BsButtonTypeDirective } from '@mintplayer/ng-bootstrap/button-type';\nimport { RetryActionService, SparkService } from '@mintplayer/ng-spark/services';\nimport { SparkPoFormComponent } from '@mintplayer/ng-spark/po-form';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark/pipes';\nimport {\n dictToNestedPo,\n EntityAttributeDefinition,\n EntityType,\n EntityTypeResolver,\n nestedPoToDict,\n PersistentObject,\n PersistentObjectAttribute,\n SPARK_RETRY_CANCEL,\n} from '@mintplayer/ng-spark/models';\n\n/**\n * Renders a retry-action popup. Before PRD §3 this component rendered title / message /\n * option buttons only and silently forwarded the incoming <c>persistentObject</c> back to\n * the server on submit — meaning any <c>Retry.Action(..., persistentObject)</c> flow had\n * no UI to actually edit the PO. This component now embeds the shared PO form so every\n * scalar / Reference / AsDetail attribute on the scaffolded Virtual PO is a real form\n * field, and the values the user fills in flow back to the server via\n * <c>RetryResult.PersistentObject</c>.\n */\n@Component({\n selector: 'spark-retry-action-modal',\n imports: [CommonModule, BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective, BsButtonTypeDirective, SparkPoFormComponent, TranslateKeyPipe],\n template: `\n <bs-modal [isOpen]=\"isOpen()\" (isOpenChange)=\"!$event && onOption(cancel)\">\n <div *bsModal>\n <div bsModalHeader>\n <h5 class=\"modal-title\">{{ retryActionService.payload()?.title }}</h5>\n </div>\n <div bsModalBody>\n @if (retryActionService.payload()?.message; as message) {\n <p>{{ message }}</p>\n }\n @if (entityType(); as et) {\n <spark-po-form\n [entityType]=\"et\"\n [(formData)]=\"formData\"\n [showButtons]=\"false\">\n </spark-po-form>\n }\n </div>\n <div bsModalFooter>\n @for (option of retryActionService.payload()?.options; track option) {\n <button\n type=\"button\"\n [color]=\"colors.primary\"\n (click)=\"onOption(option)\">\n {{ option }}\n </button>\n }\n <!-- The prompt's Cancel is the client's, in the user's language; it answers the identifier\n 'Cancel' whatever it reads, which is why the server asks for it with a flag. -->\n @if (retryActionService.payload()?.cancellable) {\n <button\n type=\"button\"\n class=\"spark-retry-cancel\"\n [color]=\"colors.secondary\"\n (click)=\"onOption(cancel)\">\n {{ 'common.cancel' | t }}\n </button>\n }\n </div>\n </div>\n </bs-modal>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class SparkRetryActionModalComponent {\n protected readonly retryActionService = inject(RetryActionService);\n private readonly sparkService = inject(SparkService);\n\n colors = Color;\n protected readonly cancel = SPARK_RETRY_CANCEL;\n isOpen = computed(() => this.retryActionService.payload() !== null);\n\n /**\n * EntityType definition for the incoming PO — fetched lazily via SparkService so the\n * form knows which attributes are editable, their labels, rules, renderers, etc.\n * `null` when the payload has no persistentObject or its objectTypeId doesn't match\n * any registered entity type (renders the modal as a simple option picker).\n */\n entityType = signal<EntityType | null>(null);\n formData = signal<Record<string, any>>({});\n private allEntityTypes: EntityType[] = [];\n\n constructor() {\n // Reseed form state every time the retry service opens/closes the modal. Effect\n // cleanup isn't needed since `payload` is a signal and the component's own lifetime\n // is root-scoped.\n effect(() => {\n const payload = this.retryActionService.payload();\n if (!payload?.persistentObject) {\n this.entityType.set(null);\n this.formData.set({});\n return;\n }\n void this.seedForm(payload.persistentObject);\n });\n }\n\n private async seedForm(po: PersistentObject): Promise<void> {\n // Virtual POs used for retry prompts (e.g. ConfirmDeleteCar) typically have no\n // security.json grant — so `getEntityTypes()` filters them out for the current user\n // and the lookup-by-id would return null, leaving the form blank. The scaffolded PO\n // already carries full attribute metadata (label / dataType / rules / renderer / etc.),\n // so we synthesize an EntityType from the attributes directly and skip the HTTP\n // lookup altogether. `getEntityTypes()` is still fetched because the embedded\n // spark-po-form needs the full list to resolve nested AsDetail / Reference types the\n // retry PO might point at. The guard below is a COMPONENT-local cache and is the only\n // one there is: the service does not cache, so every caller that skips such a guard\n // issues another request.\n if (this.allEntityTypes.length === 0) {\n try { this.allEntityTypes = await this.sparkService.getEntityTypes(); }\n catch { this.allEntityTypes = []; }\n }\n this.entityType.set(entityTypeFromPo(po));\n // Flatten the nested PO into the Record<string, any> shape the shared form uses\n // throughout the rest of ng-spark — same transformation po-edit applies.\n this.formData.set(nestedPoToDict(po));\n }\n\n onOption(option: string): void {\n const payload = this.retryActionService.payload();\n if (!payload) return;\n\n const populated = this.populatedPersistentObject(payload.persistentObject);\n this.retryActionService.respond({\n step: payload.step,\n option,\n persistentObject: populated,\n });\n }\n\n /**\n * Builds the PO the server sees under <c>Retry.Result.PersistentObject</c>. If the\n * form resolved an EntityType, rebuild from the schema + formData (identical to the\n * po-edit save path — AsDetail recursion included). Otherwise forward the incoming\n * PO unmodified so pre-§3 flows without editable attributes keep working.\n */\n private populatedPersistentObject(incoming: PersistentObject | undefined): PersistentObject | undefined {\n if (!incoming) return undefined;\n const type = this.entityType();\n if (!type) return incoming;\n\n const resolver: EntityTypeResolver = (clrName) => this.allEntityTypes.find(t => t.clrType === clrName);\n const rebuilt = dictToNestedPo(this.formData(), type, resolver);\n const populated: PersistentObject = {\n ...incoming,\n attributes: mergeAttributeMetadata(incoming.attributes ?? [], rebuilt.attributes),\n };\n return populated;\n }\n}\n\n/**\n * Builds a synthetic <see cref=\"EntityType\"/> from the PO's own scaffolded attributes so\n * the embedded spark-po-form can render without having to locate the matching server-side\n * EntityType registration. Used for Virtual POs that are schema-registered but not\n * security-granted (retry-action popups).\n */\nfunction entityTypeFromPo(po: PersistentObject): EntityType {\n return {\n id: po.objectTypeId,\n name: po.name,\n clrType: '', // Not needed by the form's rendering path; the PO's attributes carry the schema.\n breadcrumb: undefined,\n tabs: [],\n groups: [],\n attributes: (po.attributes ?? []).map(attrToDefinition),\n queries: [],\n };\n}\n\nfunction attrToDefinition(attr: PersistentObjectAttribute): EntityAttributeDefinition {\n return {\n id: attr.id ?? '',\n name: attr.name,\n label: attr.label,\n dataType: attr.dataType,\n isArray: attr.isArray,\n isRequired: attr.isRequired,\n isReadOnly: attr.isReadOnly,\n order: attr.order,\n query: attr.query,\n asDetailType: attr.asDetailType,\n showedOn: attr.showedOn,\n rules: attr.rules ?? [],\n group: attr.group,\n renderer: attr.renderer,\n rendererOptions: attr.rendererOptions,\n };\n}\n\n/**\n * Keeps the server-issued id + metadata on each attribute while overlaying the user's\n * values from the rebuilt PO. Prevents the modal from accidentally dropping server-only\n * fields (e.g. rules, renderer options) that the form didn't need to know about.\n */\nfunction mergeAttributeMetadata(\n incoming: PersistentObjectAttribute[],\n rebuilt: PersistentObjectAttribute[],\n): PersistentObjectAttribute[] {\n const byName = new Map(rebuilt.map(a => [a.name, a]));\n return incoming.map(source => {\n const updated = byName.get(source.name);\n if (!updated) return source;\n return {\n ...source,\n value: updated.value,\n object: updated.object,\n objects: updated.objects,\n asDetailType: updated.asDetailType ?? source.asDetailType,\n isValueChanged: true,\n };\n });\n}\n"],"mappings":"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"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
2
|
import { Injectable, NgZone, PLATFORM_ID, inject, signal } from "@angular/core";
|
|
3
|
+
import { DOCUMENT, isPlatformBrowser } from "@angular/common";
|
|
3
4
|
import { HttpClient, withInterceptors } from "@angular/common/http";
|
|
4
5
|
import { Observable, firstValueFrom } from "rxjs";
|
|
5
|
-
import { currentLanguage, filterQueryActions } from "@mintplayer/ng-spark/models";
|
|
6
|
+
import { SPARK_RETRY_CANCEL, currentLanguage, filterQueryActions } from "@mintplayer/ng-spark/models";
|
|
6
7
|
import { SPARK_CONFIG } from "@mintplayer/ng-spark";
|
|
7
|
-
import { SparkClientOperationDispatcher } from "@mintplayer/ng-spark/client-operations";
|
|
8
|
+
import { SparkClientMethodRegistry, SparkClientOperationDispatcher } from "@mintplayer/ng-spark/client-operations";
|
|
8
9
|
import { DomSanitizer } from "@angular/platform-browser";
|
|
9
|
-
import { DOCUMENT, isPlatformBrowser } from "@angular/common";
|
|
10
10
|
import { Router } from "@angular/router";
|
|
11
11
|
var RetryActionService = class RetryActionService {
|
|
12
12
|
resolveRetry = null;
|
|
@@ -48,9 +48,11 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
48
48
|
args: [{ providedIn: "root" }]
|
|
49
49
|
}]
|
|
50
50
|
});
|
|
51
|
+
const LANGUAGE_COOKIE_MAX_AGE = 31536e3;
|
|
51
52
|
var SparkLanguageService = class SparkLanguageService {
|
|
52
53
|
http = inject(HttpClient);
|
|
53
54
|
config = inject(SPARK_CONFIG, { optional: true });
|
|
55
|
+
document = isPlatformBrowser(inject(PLATFORM_ID)) ? inject(DOCUMENT) : null;
|
|
54
56
|
baseUrl = this.config?.baseUrl ?? "/spark";
|
|
55
57
|
currentLang = signal("en", ...ngDevMode ? [{ debugName: "currentLang" }] : /* istanbul ignore next */ []);
|
|
56
58
|
translationsMap = signal({}, ...ngDevMode ? [{ debugName: "translationsMap" }] : /* istanbul ignore next */ []);
|
|
@@ -66,6 +68,13 @@ var SparkLanguageService = class SparkLanguageService {
|
|
|
66
68
|
const lang = localStorage.getItem("spark-lang") ?? config.defaultLanguage;
|
|
67
69
|
this.currentLang.set(lang);
|
|
68
70
|
currentLanguage.set(lang);
|
|
71
|
+
this.writeCookie(lang);
|
|
72
|
+
}
|
|
73
|
+
writeCookie(lang) {
|
|
74
|
+
const document = this.document;
|
|
75
|
+
if (!document || !lang) return;
|
|
76
|
+
const secure = document.location?.protocol === "https:" ? "; Secure" : "";
|
|
77
|
+
document.cookie = `spark-lang=${encodeURIComponent(lang)}; Path=/; Max-Age=${LANGUAGE_COOKIE_MAX_AGE}; SameSite=Lax${secure}`;
|
|
69
78
|
}
|
|
70
79
|
async loadTranslations() {
|
|
71
80
|
const t = await firstValueFrom(this.http.get(`${this.baseUrl}/translations`));
|
|
@@ -75,6 +84,7 @@ var SparkLanguageService = class SparkLanguageService {
|
|
|
75
84
|
this.currentLang.set(lang);
|
|
76
85
|
currentLanguage.set(lang);
|
|
77
86
|
localStorage.setItem("spark-lang", lang);
|
|
87
|
+
this.writeCookie(lang);
|
|
78
88
|
}
|
|
79
89
|
resolve(ts) {
|
|
80
90
|
if (!ts) return "";
|
|
@@ -198,6 +208,7 @@ var SparkService = class SparkService {
|
|
|
198
208
|
retryActionService = inject(RetryActionService);
|
|
199
209
|
static MAX_RETRY_DEPTH = 16;
|
|
200
210
|
dispatcher = inject(SparkClientOperationDispatcher);
|
|
211
|
+
clientMethods = inject(SparkClientMethodRegistry);
|
|
201
212
|
async getEntityTypes() {
|
|
202
213
|
return firstValueFrom(this.http.get(`${this.baseUrl}/types`));
|
|
203
214
|
}
|
|
@@ -382,6 +393,19 @@ var SparkService = class SparkService {
|
|
|
382
393
|
const retryOp = envelope.operations.find((o) => o.type === "retry");
|
|
383
394
|
if (!retryOp) throw error;
|
|
384
395
|
if ((body.retryResults?.length ?? 0) >= SparkService.MAX_RETRY_DEPTH) throw new Error(`Gave up after answering ${SparkService.MAX_RETRY_DEPTH} retry prompts; the last was step ${retryOp.step} ("${retryOp.title}"). A hook that raises a retry without checking Retry.Result first will do this — it re-raises on every resubmission.`);
|
|
396
|
+
if (retryOp.clientMethod) {
|
|
397
|
+
const outcome = await this.clientMethods.invoke(retryOp.clientMethod, retryOp.arguments);
|
|
398
|
+
const answer = outcome.ok ? {
|
|
399
|
+
step: retryOp.step,
|
|
400
|
+
option: "OK",
|
|
401
|
+
value: outcome.value
|
|
402
|
+
} : {
|
|
403
|
+
step: retryOp.step,
|
|
404
|
+
option: SPARK_RETRY_CANCEL
|
|
405
|
+
};
|
|
406
|
+
body.retryResults = [...body.retryResults || [], answer];
|
|
407
|
+
return retryFn();
|
|
408
|
+
}
|
|
385
409
|
const payload = {
|
|
386
410
|
type: "retry-action",
|
|
387
411
|
step: retryOp.step,
|
|
@@ -389,10 +413,11 @@ var SparkService = class SparkService {
|
|
|
389
413
|
options: retryOp.options,
|
|
390
414
|
defaultOption: retryOp.defaultOption ?? void 0,
|
|
391
415
|
persistentObject: retryOp.persistentObject ?? void 0,
|
|
392
|
-
message: retryOp.message ?? void 0
|
|
416
|
+
message: retryOp.message ?? void 0,
|
|
417
|
+
cancellable: retryOp.cancellable === true
|
|
393
418
|
};
|
|
394
419
|
const result = await this.retryActionService.show(payload);
|
|
395
|
-
if (result.option ===
|
|
420
|
+
if (result.option === SPARK_RETRY_CANCEL && !payload.cancellable) throw error;
|
|
396
421
|
body.retryResults = [...body.retryResults || [], result];
|
|
397
422
|
return retryFn();
|
|
398
423
|
}
|
|
@@ -631,6 +656,23 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
631
656
|
args: [{ providedIn: "root" }]
|
|
632
657
|
}]
|
|
633
658
|
});
|
|
634
|
-
|
|
659
|
+
function sparkDatatableLabels(lang) {
|
|
660
|
+
const column = (key) => {
|
|
661
|
+
const template = lang.t(key);
|
|
662
|
+
return (name) => template.replace("{column}", name);
|
|
663
|
+
};
|
|
664
|
+
const columnWidth = lang.t("common.columnWidth");
|
|
665
|
+
return {
|
|
666
|
+
resizeColumn: column("common.resizeColumn"),
|
|
667
|
+
resizeColumnHint: lang.t("common.resizeColumnHint"),
|
|
668
|
+
resizeColumnOptions: column("common.resizeColumnOptions"),
|
|
669
|
+
narrowerColumn: column("common.narrowerColumn"),
|
|
670
|
+
widerColumn: column("common.widerColumn"),
|
|
671
|
+
columnWidth: (px) => columnWidth.replace("{px}", String(px)),
|
|
672
|
+
fitColumn: lang.t("common.fitColumn"),
|
|
673
|
+
resetColumn: lang.t("common.resetColumn")
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
export { RetryActionService, SPARK_RETURN_URL_STATE_KEY, SPARK_TIMEZONE_COOKIE, SPARK_TIMEZONE_HEADER, SparkIconRegistry, SparkLanguageService, SparkQueryActionsService, SparkReturnNavigationService, SparkService, SparkStreamingService, createSparkTimezoneInterceptor, resolveBrowserTimeZone, sparkDatatableLabels, sparkTimezoneInterceptor, withSparkTimezone };
|
|
635
677
|
|
|
636
678
|
//# sourceMappingURL=mintplayer-ng-spark-services.mjs.map
|