@mintplayer/ng-spark 22.23.0 → 22.25.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 (71) hide show
  1. package/README.md +196 -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 +310 -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-contributions.mjs +721 -0
  9. package/fesm2022/mintplayer-ng-spark-contributions.mjs.map +1 -0
  10. package/fesm2022/mintplayer-ng-spark-grid.mjs +2385 -1118
  11. package/fesm2022/mintplayer-ng-spark-grid.mjs.map +1 -1
  12. package/fesm2022/mintplayer-ng-spark-history.mjs +598 -0
  13. package/fesm2022/mintplayer-ng-spark-history.mjs.map +1 -0
  14. package/fesm2022/mintplayer-ng-spark-icon.mjs +64 -27
  15. package/fesm2022/mintplayer-ng-spark-icon.mjs.map +1 -1
  16. package/fesm2022/mintplayer-ng-spark-models.mjs +405 -762
  17. package/fesm2022/mintplayer-ng-spark-models.mjs.map +1 -1
  18. package/fesm2022/mintplayer-ng-spark-moderation.mjs +1212 -0
  19. package/fesm2022/mintplayer-ng-spark-moderation.mjs.map +1 -0
  20. package/fesm2022/mintplayer-ng-spark-panels.mjs +60 -0
  21. package/fesm2022/mintplayer-ng-spark-panels.mjs.map +1 -0
  22. package/fesm2022/mintplayer-ng-spark-pipes.mjs +1158 -765
  23. package/fesm2022/mintplayer-ng-spark-pipes.mjs.map +1 -1
  24. package/fesm2022/mintplayer-ng-spark-po-create.mjs +273 -169
  25. package/fesm2022/mintplayer-ng-spark-po-create.mjs.map +1 -1
  26. package/fesm2022/mintplayer-ng-spark-po-detail.mjs +795 -410
  27. package/fesm2022/mintplayer-ng-spark-po-detail.mjs.map +1 -1
  28. package/fesm2022/mintplayer-ng-spark-po-edit.mjs +1196 -280
  29. package/fesm2022/mintplayer-ng-spark-po-edit.mjs.map +1 -1
  30. package/fesm2022/mintplayer-ng-spark-po-form.mjs +2244 -1224
  31. package/fesm2022/mintplayer-ng-spark-po-form.mjs.map +1 -1
  32. package/fesm2022/mintplayer-ng-spark-query-list.mjs +571 -303
  33. package/fesm2022/mintplayer-ng-spark-query-list.mjs.map +1 -1
  34. package/fesm2022/mintplayer-ng-spark-renderers.mjs +44 -55
  35. package/fesm2022/mintplayer-ng-spark-renderers.mjs.map +1 -1
  36. package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs +216 -177
  37. package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs.map +1 -1
  38. package/fesm2022/mintplayer-ng-spark-routes.mjs +24 -28
  39. package/fesm2022/mintplayer-ng-spark-routes.mjs.map +1 -1
  40. package/fesm2022/mintplayer-ng-spark-services.mjs +625 -640
  41. package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
  42. package/fesm2022/mintplayer-ng-spark-shell.mjs +1031 -397
  43. package/fesm2022/mintplayer-ng-spark-shell.mjs.map +1 -1
  44. package/fesm2022/mintplayer-ng-spark-soft-delete.mjs +294 -0
  45. package/fesm2022/mintplayer-ng-spark-soft-delete.mjs.map +1 -0
  46. package/fesm2022/mintplayer-ng-spark.mjs +14 -48
  47. package/fesm2022/mintplayer-ng-spark.mjs.map +1 -1
  48. package/package.json +23 -2
  49. package/types/mintplayer-ng-spark-attribute-description.d.ts +25 -18
  50. package/types/mintplayer-ng-spark-client-operations.d.ts +139 -160
  51. package/types/mintplayer-ng-spark-column-filter.d.ts +73 -55
  52. package/types/mintplayer-ng-spark-contributions.d.ts +231 -0
  53. package/types/mintplayer-ng-spark-grid.d.ts +917 -448
  54. package/types/mintplayer-ng-spark-history.d.ts +216 -0
  55. package/types/mintplayer-ng-spark-icon.d.ts +15 -13
  56. package/types/mintplayer-ng-spark-models.d.ts +681 -573
  57. package/types/mintplayer-ng-spark-moderation.d.ts +402 -0
  58. package/types/mintplayer-ng-spark-panels.d.ts +143 -0
  59. package/types/mintplayer-ng-spark-pipes.d.ts +131 -149
  60. package/types/mintplayer-ng-spark-po-create.d.ts +46 -37
  61. package/types/mintplayer-ng-spark-po-detail.d.ts +180 -117
  62. package/types/mintplayer-ng-spark-po-edit.d.ts +367 -64
  63. package/types/mintplayer-ng-spark-po-form.d.ts +558 -373
  64. package/types/mintplayer-ng-spark-query-list.d.ts +143 -82
  65. package/types/mintplayer-ng-spark-renderers.d.ts +121 -85
  66. package/types/mintplayer-ng-spark-retry-action-modal.d.ts +31 -34
  67. package/types/mintplayer-ng-spark-routes.d.ts +7 -11
  68. package/types/mintplayer-ng-spark-services.d.ts +377 -261
  69. package/types/mintplayer-ng-spark-shell.d.ts +228 -132
  70. package/types/mintplayer-ng-spark-soft-delete.d.ts +90 -0
  71. package/types/mintplayer-ng-spark.d.ts +19 -12
package/README.md ADDED
@@ -0,0 +1,196 @@
1
+ # @mintplayer/ng-spark
2
+
3
+ The Angular front end for [MintPlayer.Spark](https://github.com/MintPlayer/MintPlayer.Spark): routed
4
+ persistent-object pages (list, detail, create, edit) driven by the server's model, the query grid,
5
+ attribute renderers, client operations, the program-units shell, and the UI halves of the Spark add-on
6
+ packages (soft delete, history, moderation).
7
+
8
+ Sign-in, registration and the account pages are in the companion package
9
+ [`@mintplayer/ng-spark-auth`](https://github.com/MintPlayer/MintPlayer.Spark/blob/master/libs/node_packages/ng-spark-auth/README.md).
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @mintplayer/ng-spark @mintplayer/ng-bootstrap @mintplayer/pagination
15
+ ```
16
+
17
+ The major version follows Angular's: `22.x` targets Angular 22. Peer dependencies: `@angular/core`,
18
+ `common`, `router`, `forms`, `cdk` (^22), `@mintplayer/ng-bootstrap` (^22.19), `@mintplayer/pagination`,
19
+ `rxjs` (~7.8).
20
+
21
+ ## Minimal setup
22
+
23
+ ```ts
24
+ // app.config.ts
25
+ import { provideRouter } from '@angular/router';
26
+ import { provideHttpClient } from '@angular/common/http';
27
+ import { provideSpark } from '@mintplayer/ng-spark';
28
+ import { withSparkTimezone } from '@mintplayer/ng-spark/services';
29
+
30
+ export const appConfig: ApplicationConfig = {
31
+ providers: [
32
+ provideRouter(routes),
33
+ provideHttpClient(...withSparkTimezone()),
34
+ provideSpark(), // optional: { baseUrl: '/spark' } is the default
35
+ ],
36
+ };
37
+
38
+ // app.routes.ts
39
+ import { sparkRoutes } from '@mintplayer/ng-spark/routes';
40
+
41
+ export const routes: Routes = [
42
+ { path: 'home', loadComponent: () => import('./home.component') },
43
+ ...sparkRoutes(), // last: po/:type, po/:type/:id, query/:queryId, …
44
+ ];
45
+ ```
46
+
47
+ Routes with a parameterised first segment of your own must come **after** `sparkRoutes()`.
48
+
49
+ ## Entry points
50
+
51
+ Import from the entry point, not from the package root; each one is a separate chunk.
52
+
53
+ | Entry point | What it provides |
54
+ |---|---|
55
+ | `@mintplayer/ng-spark` | `provideSpark(config?)`, `SPARK_CONFIG` (`baseUrl`), `SPARK_AUTH_STATE` (the bridge `ng-spark-auth` fills) |
56
+ | `/routes` | `sparkRoutes()` — the routed persistent-object and query pages |
57
+ | `/services` | `SparkService`, `SparkStreamingService`, `SparkLanguageService`, `SparkQueryActionsService`, `RetryActionService`; the timezone feature `withSparkTimezone()` |
58
+ | `/models` | The wire types (persistent objects, attributes, queries, envelopes) |
59
+ | `/shell` | `<spark-shell>` with its slot directives, `<spark-program-units>`, `<spark-language-selector>` ([guide](https://github.com/MintPlayer/MintPlayer.Spark/blob/master/docs/guide-program-units.md)) |
60
+ | `/query-list` | `<spark-query-list>` — the routed query page |
61
+ | `/grid` | `<spark-query-grid>`, `<spark-query-card>`, `<spark-grid-cell>`, `<spark-search-box>` and the caption/icon/actions slot directives; `SPARK_GRID_PAGE_SIZES` |
62
+ | `/column-filter` | `<spark-column-filter-panel>` |
63
+ | `/po-detail`, `/po-create`, `/po-edit` | The routed detail, create and edit pages |
64
+ | `/po-form` | `<spark-po-form>`, `<spark-reference-picker>`, `<spark-lookup-picker>` |
65
+ | `/renderers` | `provideSparkAttributeRenderers([...])`, `SPARK_ATTRIBUTE_RENDERERS`, `withDeclaredInputs` ([guide](https://github.com/MintPlayer/MintPlayer.Spark/blob/master/docs/guide-custom-attribute-renderers.md)) |
66
+ | `/panels` | `provideSparkDetailPanels(...)`, `provideSparkDetailActions(...)`, `provideSparkQueryListActions(...)` — how add-ons put components on the routed pages |
67
+ | `/client-operations` | `provideSparkClientOperations()` — notify, navigate and refresh operations from server-side action code; the toast container |
68
+ | `/retry-action-modal` | The modal that answers a server's retry action ([guide](https://github.com/MintPlayer/MintPlayer.Spark/blob/master/docs/guide-manager-retry-actions.md)) |
69
+ | `/attribute-description` | The [i] tooltip for attribute help text ([guide](https://github.com/MintPlayer/MintPlayer.Spark/blob/master/docs/guide-attribute-descriptions.md)) |
70
+ | `/icon` | `<spark-icon>` |
71
+ | `/pipes` | The pipes the pages use (attribute values, references, lookups, translations, router links) |
72
+ | `/soft-delete` | `provideSparkSoftDelete()` — the Deleted toggle on query lists, the recycle bin, Restore / Purge |
73
+ | `/history` | `provideSparkHistory()` — `<spark-po-history>`: revision list, read-only view, diff, Revert |
74
+ | `/moderation` | `provideSparkModeration()`, `sparkModerationRoutes()`, `sparkModerationRenderers` — vote widget, flag button, review queue, reputation badge, moderator panel |
75
+
76
+ ### Timezone (`withSparkTimezone`)
77
+
78
+ ```ts
79
+ provideHttpClient(...withSparkTimezone({ cookieName: 'spark-timezone' }))
80
+ ```
81
+
82
+ Sends the browser's IANA zone as a header and writes it to a cookie (only when it changed) so a
83
+ server-side render can read it. The header is never sent on the server platform. Use the same cookie
84
+ name as the server's `Spark:TimeZone:CookieName`; `cookieName: false` writes no cookie. See
85
+ [guide-dates-and-sorting.md](https://github.com/MintPlayer/MintPlayer.Spark/blob/master/docs/guide-dates-and-sorting.md).
86
+
87
+ ### Panels and actions on the routed pages
88
+
89
+ ```ts
90
+ import { provideSparkDetailPanels, provideSparkDetailActions } from '@mintplayer/ng-spark/panels';
91
+
92
+ providers: [
93
+ provideSparkDetailPanels({ id: 'audit', component: AuditPanelComponent, order: 10 }),
94
+ provideSparkDetailActions({ id: 'export', component: ExportButtonComponent }),
95
+ ]
96
+ ```
97
+
98
+ Each component declares `context = input.required<SparkDetailContext>()` (query-list actions:
99
+ `SparkQueryListContext`) and does its own permission checks. A second registration with the same `id`
100
+ replaces the first.
101
+
102
+ ### Query toolbar, selection and sub-queries
103
+
104
+ The grid (`/grid`) owns one toolbar model, `SparkQueryGridComponent.toolbarActions()`. It holds the
105
+ server's default `New` and `Delete` (`isDefault` entries from `/spark/actions/list`) and the custom
106
+ actions, each enabled from the live selection count. The sub-query card's header renders it on the
107
+ right, with the overflow under the translated "More" label (`common.more`), and the query page's
108
+ action bar renders it too.
109
+
110
+ The grid also renders:
111
+ - an "N selected" chip while rows are selected. There is no select-all: with paged, lazy or
112
+ virtual-scrolled rows it could only tick the rows loaded so far. Deselect-all is the datatable's
113
+ own header checkbox, shown only while a row is selected. Single selection has no checkbox
114
+ column, so there the chip carries a ⊗ that clears the selection;
115
+ - a per-row `⋮` menu of the actions whose rule accepts one row. A menu item runs on that row only and
116
+ leaves the checkbox selection alone. The menu is a CDK connected overlay with ng-bootstrap's
117
+ `<bs-dropdown-menu>` inside, not `@mintplayer/ng-bootstrap/dropdown`, pending
118
+ [ng-bootstrap #419](https://github.com/MintPlayer/mintplayer-ng-bootstrap/issues/419) (the
119
+ dropdown fesm fails to initialise in unlinked JIT test runs).
120
+
121
+ - **Selection** follows `selectionMode` (`'auto' | 'none' | 'single' | 'multiple'`).
122
+ - It is set on the query, or on the parent type's `queries` entry, which `<spark-query-card
123
+ [selectionMode]>` and `<spark-query-grid [selectionModeSetting]>` take.
124
+ - `'auto'` derives it from the custom actions, as before.
125
+ - **Delete** posts the selection to `SparkService.deleteMany()` (`/spark/po/delete-many`). One
126
+ request deletes all the rows or none.
127
+ - **New** on a sub-query navigates to the create page with `parentId`, `parentType` and `queryId`.
128
+ The create page always asks `SparkService.newObject()` (`/spark/po/new`) for its blank object, so
129
+ the server's `OnNewAsync` sets the defaults. For a sub-query, its base fills the reference to the
130
+ parent.
131
+ - Read `EntityType.queries` with `subQueriesOf(type)`. An entry is a bare alias or
132
+ `{ query, selectionMode?, parentReference? }`.
133
+ - **Search.** The card's header has a search box on the right, after the actions, as Vidyano's
134
+ sub-query tabs do. It is `<spark-search-box [(term)]>`, the same component the query page uses: a
135
+ 300 ms debounce, a clear button, and Escape to clear. The term goes to the grid's `search`, so the
136
+ server searches the sub-query's rows (with `parentId`/`parentType`), and to the column filters'
137
+ value lists as `querySearch`. **A new term clears the selection.** The card's `search` input is the
138
+ starting term; `[searchable]="false"` hides the box, and a card over bound `data` has none.
139
+ - **Recycle bin.** While the grid lists `deleted: 'only'`, or sits under a deleted parent, its
140
+ toolbar and row menu are empty (no New, no Delete, no custom action): the recycle bin offers only
141
+ Restore and Purge. `<spark-query-card>` takes `[deleted]` (the rows' mode, forwarded to its grid)
142
+ and `[parentDeleted]` (the parent is a deleted row; the detail page sets it for a row opened with
143
+ `?deleted=only`). Under a deleted parent the grid sends `parentDeleted: 'include'` on execute and
144
+ distinct-values, which the server honours only for holders of `ViewDeleted/{ParentType}`.
145
+
146
+ ```html
147
+ <spark-search-box [(term)]="term" />
148
+ <spark-query-grid queryId="all-cars" [search]="term()" />
149
+ ```
150
+
151
+ See [guide-custom-actions.md](https://github.com/MintPlayer/MintPlayer.Spark/blob/master/docs/guide-custom-actions.md)
152
+ for the server side of this.
153
+
154
+ ### Soft delete, history and moderation
155
+
156
+ ```ts
157
+ import { provideSparkSoftDelete } from '@mintplayer/ng-spark/soft-delete';
158
+ import { provideSparkHistory } from '@mintplayer/ng-spark/history';
159
+ import { provideSparkModeration, sparkModerationRenderers, sparkModerationRoutes } from '@mintplayer/ng-spark/moderation';
160
+ import { provideSparkAttributeRenderers } from '@mintplayer/ng-spark/renderers';
161
+
162
+ providers: [
163
+ provideSparkSoftDelete(),
164
+ provideSparkHistory(),
165
+ provideSparkModeration(),
166
+ provideSparkAttributeRenderers([...sparkModerationRenderers]),
167
+ ]
168
+
169
+ // routes: ...sparkModerationRoutes() before ...sparkRoutes()
170
+ ```
171
+
172
+ Each needs its server package: [SoftDelete](https://github.com/MintPlayer/MintPlayer.Spark/blob/master/libs/soft_delete/MintPlayer.Spark.SoftDelete/README.md),
173
+ [History](https://github.com/MintPlayer/MintPlayer.Spark/blob/master/libs/history/MintPlayer.Spark.History/README.md),
174
+ [Moderation](https://github.com/MintPlayer/MintPlayer.Spark/blob/master/libs/moderation/MintPlayer.Spark.Moderation/README.md)
175
+ ([guide](https://github.com/MintPlayer/MintPlayer.Spark/blob/master/docs/guide-moderation.md)). `apps/QnA` uses all three.
176
+
177
+ ### Contributions (preview)
178
+
179
+ ```ts
180
+ import { provideSparkContributions, sparkContributionRenderers } from '@mintplayer/ng-spark/contributions';
181
+
182
+ providers: [
183
+ provideSparkContributions(), // "Revert to this version" (row menu + contribution page)
184
+ provideSparkAttributeRenderers([...sparkContributionRenderers]), // attribution row line, lineDiff
185
+ ]
186
+ ```
187
+
188
+ No routes of its own: the History link opens `query/:queryId` with `?parentId=&parentType=` and one
189
+ column filter per slot, which the query page reads for any query. Needs
190
+ [MintPlayer.Spark.Contributions](https://github.com/MintPlayer/MintPlayer.Spark/blob/master/libs/contributions/MintPlayer.Spark.Contributions/README.md).
191
+
192
+ ## More
193
+
194
+ - [Repository README](https://github.com/MintPlayer/MintPlayer.Spark/blob/master/README.md) — the developer guides
195
+ - [HTTP API specification](https://github.com/MintPlayer/MintPlayer.Spark/blob/master/docs/Spark-API-Specification.md)
196
+ - [Release notes 11.0.0-preview.91 / ng-spark 22.24.0](https://github.com/MintPlayer/MintPlayer.Spark/blob/master/docs/release-notes-preview-91.md)
@@ -1,37 +1,48 @@
1
- import * as i0 from '@angular/core';
2
- import { input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
3
- import { BsTooltipDirective } from '@mintplayer/ng-bootstrap/tooltip';
4
- import { SparkIconComponent } from '@mintplayer/ng-spark/icon';
5
- import { resolveTranslation, currentLanguage } from '@mintplayer/ng-spark/models';
6
-
7
- /**
8
- * The [i] beside an attribute label: a focusable button whose tooltip shows the attribute's
9
- * `description` (#348). Renders nothing when the model declares no description, so every label
10
- * site can include it unconditionally.
11
- *
12
- * Accessibility comes from `*bsTooltip`: it opens on hover AND focus, closes on Escape, and sets
13
- * `aria-describedby` on the button while open. The button's own name is the description text, so
14
- * a screen reader announces the help on focus without waiting for the tooltip.
15
- *
16
- * Clicks are stopped: the [i] lives inside sortable grid headers and `<label for>` elements, and
17
- * must neither toggle the sort nor move focus into the input.
18
- */
19
- class SparkAttributeDescriptionComponent {
20
- /** The attribute's `description`; `undefined` (the common case) renders nothing. */
21
- description = input(/* @ts-ignore */
22
- ...(ngDevMode ? [undefined, { debugName: "description" }] : /* istanbul ignore next */ []));
23
- /** Where the tooltip opens relative to the [i]. */
24
- position = input('top', /* @ts-ignore */
25
- ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
26
- /** Resolved for the current language; re-evaluates when the user switches language. */
27
- text = computed(() => resolveTranslation(this.description(), currentLanguage()).trim(), /* @ts-ignore */
28
- ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
29
- onClick(event) {
30
- event.preventDefault();
31
- event.stopPropagation();
32
- }
33
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkAttributeDescriptionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
34
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SparkAttributeDescriptionComponent, isStandalone: true, selector: "spark-attribute-description", inputs: { description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
1
+ import * as i0 from "@angular/core";
2
+ import { ChangeDetectionStrategy, Component, computed, input } from "@angular/core";
3
+ import { BsTooltipDirective } from "@mintplayer/ng-bootstrap/tooltip";
4
+ import { SparkIconComponent } from "@mintplayer/ng-spark/icon";
5
+ import { currentLanguage, resolveTranslation } from "@mintplayer/ng-spark/models";
6
+ var SparkAttributeDescriptionComponent = class SparkAttributeDescriptionComponent {
7
+ description = input(...ngDevMode ? [void 0, { debugName: "description" }] : /* istanbul ignore next */ []);
8
+ position = input("top", ...ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []);
9
+ text = computed(() => resolveTranslation(this.description(), currentLanguage()).trim(), ...ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []);
10
+ onClick(event) {
11
+ event.preventDefault();
12
+ event.stopPropagation();
13
+ }
14
+ static ɵfac = i0.ɵɵngDeclareFactory({
15
+ minVersion: "12.0.0",
16
+ version: "22.2.0",
17
+ ngImport: i0,
18
+ type: SparkAttributeDescriptionComponent,
19
+ deps: [],
20
+ target: i0.ɵɵFactoryTarget.Component
21
+ });
22
+ static ɵcmp = i0.ɵɵngDeclareComponent({
23
+ minVersion: "17.0.0",
24
+ version: "22.2.0",
25
+ type: SparkAttributeDescriptionComponent,
26
+ isStandalone: true,
27
+ selector: "spark-attribute-description",
28
+ inputs: {
29
+ description: {
30
+ classPropertyName: "description",
31
+ publicName: "description",
32
+ isSignal: true,
33
+ isRequired: false,
34
+ transformFunction: null
35
+ },
36
+ position: {
37
+ classPropertyName: "position",
38
+ publicName: "position",
39
+ isSignal: true,
40
+ isRequired: false,
41
+ transformFunction: null
42
+ }
43
+ },
44
+ ngImport: i0,
45
+ template: `
35
46
  @if (text(); as text) {
36
47
  <button
37
48
  type="button"
@@ -42,11 +53,36 @@ class SparkAttributeDescriptionComponent {
42
53
  <span *bsTooltip="position()" class="spark-attribute-description-text">{{ text }}</span>
43
54
  </button>
44
55
  }
45
- `, isInline: true, styles: [":host{display:inline-block;line-height:1}.spark-attribute-description-trigger{color:inherit;opacity:.6;font-size:.875em;vertical-align:baseline}.spark-attribute-description-trigger:hover,.spark-attribute-description-trigger:focus-visible{opacity:1}.spark-attribute-description-text{display:inline-block;white-space:pre-line;text-align:start;max-width:20rem}\n"], dependencies: [{ kind: "directive", type: BsTooltipDirective, selector: "*[bsTooltip]", inputs: ["bsTooltip"] }, { kind: "component", type: SparkIconComponent, selector: "spark-icon", inputs: ["name"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
46
- }
47
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkAttributeDescriptionComponent, decorators: [{
48
- type: Component,
49
- args: [{ selector: 'spark-attribute-description', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [BsTooltipDirective, SparkIconComponent], template: `
56
+ `,
57
+ isInline: true,
58
+ styles: [":host{display:inline-block;line-height:1}.spark-attribute-description-trigger{color:inherit;opacity:.6;font-size:.875em;vertical-align:baseline}.spark-attribute-description-trigger:hover,.spark-attribute-description-trigger:focus-visible{opacity:1}.spark-attribute-description-text{display:inline-block;white-space:pre-line;text-align:start;max-width:20rem}\n"],
59
+ dependencies: [{
60
+ kind: "directive",
61
+ type: BsTooltipDirective,
62
+ selector: "*[bsTooltip]",
63
+ inputs: ["bsTooltip"]
64
+ }, {
65
+ kind: "component",
66
+ type: SparkIconComponent,
67
+ selector: "spark-icon",
68
+ inputs: ["name"]
69
+ }],
70
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
71
+ });
72
+ };
73
+ i0.ɵɵngDeclareClassMetadata({
74
+ minVersion: "12.0.0",
75
+ version: "22.2.0",
76
+ ngImport: i0,
77
+ type: SparkAttributeDescriptionComponent,
78
+ decorators: [{
79
+ type: Component,
80
+ args: [{
81
+ selector: "spark-attribute-description",
82
+ standalone: true,
83
+ changeDetection: ChangeDetectionStrategy.OnPush,
84
+ imports: [BsTooltipDirective, SparkIconComponent],
85
+ template: `
50
86
  @if (text(); as text) {
51
87
  <button
52
88
  type="button"
@@ -57,12 +93,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
57
93
  <span *bsTooltip="position()" class="spark-attribute-description-text">{{ text }}</span>
58
94
  </button>
59
95
  }
60
- `, styles: [":host{display:inline-block;line-height:1}.spark-attribute-description-trigger{color:inherit;opacity:.6;font-size:.875em;vertical-align:baseline}.spark-attribute-description-trigger:hover,.spark-attribute-description-trigger:focus-visible{opacity:1}.spark-attribute-description-text{display:inline-block;white-space:pre-line;text-align:start;max-width:20rem}\n"] }]
61
- }], propDecorators: { description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }] } });
62
-
63
- /**
64
- * Generated bundle index. Do not edit.
65
- */
66
-
96
+ `,
97
+ styles: [":host{display:inline-block;line-height:1}.spark-attribute-description-trigger{color:inherit;opacity:.6;font-size:.875em;vertical-align:baseline}.spark-attribute-description-trigger:hover,.spark-attribute-description-trigger:focus-visible{opacity:1}.spark-attribute-description-text{display:inline-block;white-space:pre-line;text-align:start;max-width:20rem}\n"]
98
+ }]
99
+ }],
100
+ propDecorators: {
101
+ description: [{
102
+ type: i0.Input,
103
+ args: [{
104
+ isSignal: true,
105
+ alias: "description",
106
+ required: false
107
+ }]
108
+ }],
109
+ position: [{
110
+ type: i0.Input,
111
+ args: [{
112
+ isSignal: true,
113
+ alias: "position",
114
+ required: false
115
+ }]
116
+ }]
117
+ }
118
+ });
67
119
  export { SparkAttributeDescriptionComponent };
68
- //# sourceMappingURL=mintplayer-ng-spark-attribute-description.mjs.map
120
+
121
+ //# sourceMappingURL=mintplayer-ng-spark-attribute-description.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mintplayer-ng-spark-attribute-description.mjs","sources":["../../attribute-description/src/spark-attribute-description.component.ts","../../attribute-description/mintplayer-ng-spark-attribute-description.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { BsTooltipDirective } from '@mintplayer/ng-bootstrap/tooltip';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\nimport { TranslatedString, currentLanguage, resolveTranslation } from '@mintplayer/ng-spark/models';\n\nexport type SparkAttributeDescriptionPosition = 'top' | 'bottom' | 'start' | 'end';\n\n/**\n * The [i] beside an attribute label: a focusable button whose tooltip shows the attribute's\n * `description` (#348). Renders nothing when the model declares no description, so every label\n * site can include it unconditionally.\n *\n * Accessibility comes from `*bsTooltip`: it opens on hover AND focus, closes on Escape, and sets\n * `aria-describedby` on the button while open. The button's own name is the description text, so\n * a screen reader announces the help on focus without waiting for the tooltip.\n *\n * Clicks are stopped: the [i] lives inside sortable grid headers and `<label for>` elements, and\n * must neither toggle the sort nor move focus into the input.\n */\n@Component({\n selector: 'spark-attribute-description',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [BsTooltipDirective, SparkIconComponent],\n template: `\n @if (text(); as text) {\n <button\n type=\"button\"\n class=\"btn btn-link p-0 ms-1 align-baseline spark-attribute-description-trigger\"\n [attr.aria-label]=\"text\"\n (click)=\"onClick($event)\">\n <spark-icon name=\"info-circle\" />\n <span *bsTooltip=\"position()\" class=\"spark-attribute-description-text\">{{ text }}</span>\n </button>\n }\n `,\n styles: [`\n :host {\n display: inline-block;\n line-height: 1;\n }\n .spark-attribute-description-trigger {\n color: inherit;\n opacity: 0.6;\n font-size: 0.875em;\n vertical-align: baseline;\n }\n .spark-attribute-description-trigger:hover,\n .spark-attribute-description-trigger:focus-visible {\n opacity: 1;\n }\n .spark-attribute-description-text {\n display: inline-block;\n white-space: pre-line;\n text-align: start;\n max-width: 20rem;\n }\n `],\n})\nexport class SparkAttributeDescriptionComponent {\n /** The attribute's `description`; `undefined` (the common case) renders nothing. */\n description = input<TranslatedString | undefined>();\n\n /** Where the tooltip opens relative to the [i]. */\n position = input<SparkAttributeDescriptionPosition>('top');\n\n /** Resolved for the current language; re-evaluates when the user switches language. */\n text = computed(() => resolveTranslation(this.description(), currentLanguage()).trim());\n\n onClick(event: MouseEvent): void {\n event.preventDefault();\n event.stopPropagation();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;AAOA;;;;;;;;;;;AAWG;MAyCU,kCAAkC,CAAA;;AAE7C,IAAA,WAAW,GAAG,KAAK;+FAAgC;;IAGnD,QAAQ,GAAG,KAAK,CAAoC,KAAK;iFAAC;;AAG1D,IAAA,IAAI,GAAG,QAAQ,CAAC,MAAM,kBAAkB,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,eAAe,EAAE,CAAC,CAAC,IAAI,EAAE;6EAAC;AAEvF,IAAA,OAAO,CAAC,KAAiB,EAAA;QACvB,KAAK,CAAC,cAAc,EAAE;QACtB,KAAK,CAAC,eAAe,EAAE;IACzB;uGAbW,kCAAkC,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAlC,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,kCAAkC,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,6BAAA,EAAA,MAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAnCnC;;;;;;;;;;;GAWT,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,yWAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAZS,kBAAkB,gFAAE,kBAAkB,EAAA,QAAA,EAAA,YAAA,EAAA,MAAA,EAAA,CAAA,MAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAoCrC,kCAAkC,EAAA,UAAA,EAAA,CAAA;kBAxC9C,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,6BAA6B,EAAA,UAAA,EAC3B,IAAI,EAAA,eAAA,EACC,uBAAuB,CAAC,MAAM,EAAA,OAAA,EACtC,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,EAAA,QAAA,EACvC;;;;;;;;;;;AAWT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,yWAAA,CAAA,EAAA;;;ACnCH;;AAEG;;;;"}
1
+ {"version":3,"file":"mintplayer-ng-spark-attribute-description.mjs","names":[],"sources":["../../attribute-description/src/spark-attribute-description.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { BsTooltipDirective } from '@mintplayer/ng-bootstrap/tooltip';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\nimport { TranslatedString, currentLanguage, resolveTranslation } from '@mintplayer/ng-spark/models';\n\nexport type SparkAttributeDescriptionPosition = 'top' | 'bottom' | 'start' | 'end';\n\n/**\n * The [i] beside an attribute label: a focusable button whose tooltip shows the attribute's\n * `description` (#348). Renders nothing when the model declares no description, so every label\n * site can include it unconditionally.\n *\n * Accessibility comes from `*bsTooltip`: it opens on hover AND focus, closes on Escape, and sets\n * `aria-describedby` on the button while open. The button's own name is the description text, so\n * a screen reader announces the help on focus without waiting for the tooltip.\n *\n * Clicks are stopped: the [i] lives inside sortable grid headers and `<label for>` elements, and\n * must neither toggle the sort nor move focus into the input.\n */\n@Component({\n selector: 'spark-attribute-description',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [BsTooltipDirective, SparkIconComponent],\n template: `\n @if (text(); as text) {\n <button\n type=\"button\"\n class=\"btn btn-link p-0 ms-1 align-baseline spark-attribute-description-trigger\"\n [attr.aria-label]=\"text\"\n (click)=\"onClick($event)\">\n <spark-icon name=\"info-circle\" />\n <span *bsTooltip=\"position()\" class=\"spark-attribute-description-text\">{{ text }}</span>\n </button>\n }\n `,\n styles: [`\n :host {\n display: inline-block;\n line-height: 1;\n }\n .spark-attribute-description-trigger {\n color: inherit;\n opacity: 0.6;\n font-size: 0.875em;\n vertical-align: baseline;\n }\n .spark-attribute-description-trigger:hover,\n .spark-attribute-description-trigger:focus-visible {\n opacity: 1;\n }\n .spark-attribute-description-text {\n display: inline-block;\n white-space: pre-line;\n text-align: start;\n max-width: 20rem;\n }\n `],\n})\nexport class SparkAttributeDescriptionComponent {\n /** The attribute's `description`; `undefined` (the common case) renders nothing. */\n description = input<TranslatedString | undefined>();\n\n /** Where the tooltip opens relative to the [i]. */\n position = input<SparkAttributeDescriptionPosition>('top');\n\n /** Resolved for the current language; re-evaluates when the user switches language. */\n text = computed(() => resolveTranslation(this.description(), currentLanguage()).trim());\n\n onClick(event: MouseEvent): void {\n event.preventDefault();\n event.stopPropagation();\n }\n}\n"],"mappings":";;;;;AA2DA,IAAa,qCAAb,MAAa,mCAAkC;CAE7C,cAAc,MAAqC,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAGnD,WAAW,MAAyC,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAG1D,OAAO,eAAe,mBAAmB,KAAK,YAAW,GAAI,gBAAe,CAAE,CAAC,CAAC,KAAI,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAEvF,QAAQ,OAAiB;EACvB,MAAM,eAAc;EACpB,MAAM,gBAAe;CACvB;CAbW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAkC,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAlC,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAkC,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,UAAA;EAAA,UAnCnC;;;;;;;;;;;;EAWT,UAAA;EAAA,QAAA,CAAA,yWAAA;EAAA,cAAA,CAAA;GAAA,MAAA;GAAA,MAZS;GAAkB,UAAA;GAAA,QAAA,CAAA,WAAA;EAAA,GAAA;GAAA,MAAA;GAAA,MAAE;GAAkB,UAAA;GAAA,QAAA,CAAA,MAAA;EAAA,CAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAoCrC,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAkC,YAAA,CAAA;EAxC9C,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAA6B,YAC3B;GAAI,iBACC,wBAAwB;GAAM,SACtC,CAAC,oBAAoB,kBAAkB;GAAC,UACvC;;;;;;;;;;;;GAWT,QAAA,CAAA,yWAAA;EAAA,CAAA"}