@lieselthuriot/widget-renderer 0.0.0-stage → 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Liesel Thuriot
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,299 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # Widget Renderer
2
+
3
+ Reusable Angular components for rendering compact, grid-based widgets with text, imagery, actions, charts, progress indicators, loading states, and sensitive-content controls.
4
+
5
+ The package is designed to receive a plain `WidgetContract` from an API or application. Consumers do not need to add editor-only element IDs or hydrate the data before rendering.
6
+
7
+ - **Live editor:** [widget-studio.liesel.dev](https://widget-studio.liesel.dev). Design a widget and copy its JSON.
8
+ - **Dashboard grid:** [`@lieselthuriot/widget-landscape`](https://www.npmjs.com/package/@lieselthuriot/widget-landscape)
9
+ - **C# contracts:** [`WidgetStudio.Contracts`](https://www.nuget.org/packages/WidgetStudio.Contracts) produces JSON in the same format from .NET.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @lieselthuriot/widget-renderer
15
+ ```
16
+
17
+ `@angular/common` and `@angular/core` are peer dependencies. The package supports Angular 21 and 22. Charts are drawn as SVG by the package itself, with [`d3-array`](https://www.npmjs.com/package/d3-array) and [`d3-shape`](https://www.npmjs.com/package/d3-shape) installed automatically as regular dependencies for the chart geometry.
18
+
19
+ ### Upgrading from 0.1
20
+
21
+ Version 0.2 no longer uses ECharts. Remove `echarts` from your dependencies if nothing else in your application needs it. The widget contract is unchanged, but charts look slightly different:
22
+
23
+ - Pie, donut, gauge, and funnel charts stack their title, chart, and legend, so the chart uses the available space and never overlaps the legend.
24
+ - Bar and line axes, ticks, and labels are laid out by the renderer; crowded category labels are thinned out automatically.
25
+ - Clicking a legend entry no longer hides the matching data point.
26
+ - Every chart can be focused with the keyboard. Arrow keys, Home, and End move between data points, Escape hides the tooltip, and the selected point is announced to screen readers.
27
+ - The keyboard focus ring can be themed with the `--widget-renderer-focus-ring` and `--widget-renderer-focus-ring-contrast` CSS custom properties.
28
+ - Custom locales can set the new `dataPointLabel` and `gaugeValueLabel` chart entries; English values are used when they are omitted from overrides.
29
+
30
+ ## Features
31
+
32
+ - Render `mini`, `compact`, `tall`, `square`, `wide`, `mega`, `banner`, and `hero` widgets from a version 2 contract
33
+ - Place elements with configurable rows, columns, spans, and anchors
34
+ - Support text, images, buttons, dividers, pie, donut, funnel, gauge, bar, line, and progress elements
35
+ - Render charts as lightweight, keyboard-accessible SVG without a charting library
36
+ - Validate incoming data with structured paths and messages
37
+ - Normalize optional contract fields such as button defaults and text padding
38
+ - Blur sensitive backgrounds and elements until explicitly revealed
39
+ - Emit refresh and button-action events without making network requests
40
+ - Provide accessible names and text summaries for charts and progress bars
41
+ - Respect reduced-motion preferences for loading and active progress animations
42
+ - Localize renderer-owned accessible labels, with built-in German, French, and Dutch locales
43
+
44
+ ## Usage
45
+
46
+ Import the standalone `WidgetRenderer` component into an Angular component and bind a `WidgetContract`:
47
+
48
+ ```ts
49
+ import { Component } from '@angular/core';
50
+ import { ButtonElement, WidgetContract, WidgetRenderer } from '@lieselthuriot/widget-renderer';
51
+
52
+ @Component({
53
+ selector: 'app-dashboard-card',
54
+ imports: [WidgetRenderer],
55
+ template: `
56
+ <widget-renderer
57
+ [widget]="widget"
58
+ [darkMode]="darkMode"
59
+ [loading]="loading"
60
+ (refreshRequested)="refreshWidget()"
61
+ (actionRequested)="handleAction($event)"
62
+ />
63
+ `,
64
+ })
65
+ export class DashboardCard {
66
+ readonly darkMode = false;
67
+ readonly loading = false;
68
+
69
+ readonly widget: WidgetContract = {
70
+ version: 2,
71
+ id: 'hello-widget',
72
+ size: 'square',
73
+ rows: [{ unit: 'auto' }],
74
+ columns: [{ size: 1, unit: 'star' }],
75
+ background: {
76
+ color: '#172033',
77
+ sensitive: false,
78
+ repeat: false,
79
+ size: 'cover',
80
+ anchor: 'center',
81
+ },
82
+ refresh: { enabled: true, label: 'Refresh widget' },
83
+ elements: [
84
+ {
85
+ type: 'text',
86
+ grid: { row: 0, column: 0, rowSpan: 1, columnSpan: 1, anchor: 'center' },
87
+ sensitive: false,
88
+ text: 'Hello from Widget Renderer',
89
+ style: {
90
+ color: '#ffffff',
91
+ font: { size: 16, bold: true },
92
+ align: 'center',
93
+ wrap: true,
94
+ },
95
+ },
96
+ ],
97
+ };
98
+
99
+ refreshWidget(): void {
100
+ // Fetch updated data in the consuming application.
101
+ }
102
+
103
+ handleAction(event: { element: ButtonElement; event: Event }): void {
104
+ // Handle the configured button request in the consuming application.
105
+ console.log(event);
106
+ }
107
+ }
108
+ ```
109
+
110
+ > **Important:** `WidgetRenderer` emits `refreshRequested` and `actionRequested`. It does not call the configured button URL or perform refresh requests itself.
111
+
112
+ ### Loading state
113
+
114
+ Set `loading` to `true` for the default square skeleton, or to a widget size such as `'wide'` for a matching skeleton:
115
+
116
+ ```html
117
+ <widget-renderer [loading]="'wide'" [darkMode]="true" />
118
+ ```
119
+
120
+ ### Dark mode
121
+
122
+ Pass `darkMode` to style the loading state for a dark surface. The rendered widget itself uses the colors provided by its contract, so applications can control the surrounding theme and widget palette independently.
123
+
124
+ ### Outputs
125
+
126
+ `refreshRequested` is emitted when the refresh control is clicked. `actionRequested` is emitted with the clicked `ButtonElement` and DOM event:
127
+
128
+ ```ts
129
+ import { ButtonElement } from '@lieselthuriot/widget-renderer';
130
+
131
+ handleAction(event: { element: ButtonElement; event: Event }): void {
132
+ event.event.preventDefault();
133
+ const { method, url, body, requestRefresh } = event.element;
134
+ // Perform the application-specific request here.
135
+ console.log(method, url, body, requestRefresh);
136
+ }
137
+ ```
138
+
139
+ ### Localization
140
+
141
+ Renderer-owned accessible labels use English defaults and can be localized with the optional `locale` input. The input accepts partial nested overrides, so you can provide a complete locale or change only the values you need. Widget content, labels, image alternative text, refresh labels, and validation diagnostics remain consumer-provided.
142
+
143
+ ```ts
144
+ import {
145
+ germanWidgetRendererLocale,
146
+ WidgetRendererLocaleOverrides,
147
+ } from '@lieselthuriot/widget-renderer';
148
+
149
+ readonly rendererLocale: WidgetRendererLocaleOverrides = {
150
+ ...germanWidgetRendererLocale,
151
+ };
152
+ ```
153
+
154
+ Built-in `germanWidgetRendererLocale`, `frenchWidgetRendererLocale`, and `dutchWidgetRendererLocale` values are available. For a small customization, override only the required values:
155
+
156
+ ```ts
157
+ import { WidgetRendererLocaleOverrides } from '@lieselthuriot/widget-renderer';
158
+
159
+ readonly rendererLocale: WidgetRendererLocaleOverrides = {
160
+ aria: {
161
+ showSensitiveData: 'Afficher les données privées',
162
+ chart: {
163
+ noDataSummary: (title) => `${title ? `${title} : ` : ''}aucune donnée.`,
164
+ },
165
+ },
166
+ };
167
+ ```
168
+
169
+ ```html
170
+ <widget-renderer [widget]="widget" [locale]="rendererLocale" />
171
+ ```
172
+
173
+ The `aria` locale covers the loading status, toolbar, sensitive-content controls, image failure fallback, chart and progress accessible names, generated chart summaries, chart data point announcements, and progress segment labels. Locale changes update the rendered labels without changing widget-provided content.
174
+
175
+ ## Widget contract
176
+
177
+ Every input must use `version: 2` and include:
178
+
179
+ | Field | Description |
180
+ | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
181
+ | `id` | A stable identifier for the widget |
182
+ | `size` | `mini`, `compact`, `square`, `wide`, `mega`, `banner`, `hero`, or `tall`; dimensions include the 8px layout gap |
183
+ | `rows` | Non-empty grid tracks using `pixel`, `auto`, or `star` units; `size` is required for `pixel` and `star` tracks and must be omitted for `auto` tracks |
184
+ | `columns` | Non-empty grid tracks using `pixel`, `auto`, or `star` units; `size` is required for `pixel` and `star` tracks and must be omitted for `auto` tracks |
185
+ | `background` | Color, optional image, sizing, position, repeat, overlay, and sensitivity settings |
186
+ | `refresh` | Whether to show a refresh action and its accessible label |
187
+ | `elements` | An array of supported widget elements |
188
+
189
+ Element positions are zero-based. For example, `row: 0` and `column: 0` place an element in the first grid cell.
190
+
191
+ ### Supported elements
192
+
193
+ - `text`: formatted text with alignment, wrapping, font styling, color, background, radius, and padding
194
+ - `image`: remote or local image with alternative text, fit mode, radius, and overlay color
195
+ - `button`: `get` or `post` action metadata, optional body, solid or outline styling, and hover behavior
196
+ - `divider`: horizontal or vertical separator
197
+ - `pie`, `donut`, and `funnel`: labeled data points with optional legends and per-point colors
198
+ - `gauge`: a value within a minimum and maximum range
199
+ - `bar` and `line`: labeled series data with axis and line styling
200
+ - `progress`: a range-based progress bar with label, suffix, legend, and active animation options
201
+
202
+ Editor-only `id` fields are not required. The renderer accepts `WidgetContract` data and normalizes optional button fields and text padding before rendering.
203
+
204
+ ### Sensitive content
205
+
206
+ Set `sensitive: true` on the background or an element to blur it and remove hidden content from the accessible tree. The renderer provides a local show/hide control when sensitive content is present.
207
+
208
+ > **Warning:** Sensitive-content handling is a display and privacy feature, not encryption. Sensitive values remain in the JavaScript object supplied by the consuming application.
209
+
210
+ ## Validation and normalization
211
+
212
+ Use the exported helpers when data enters your application:
213
+
214
+ ```ts
215
+ import {
216
+ normalizeWidgetInput,
217
+ parseWidgetInput,
218
+ safeParseWidgetInput,
219
+ } from '@lieselthuriot/widget-renderer';
220
+
221
+ const result = safeParseWidgetInput(payload);
222
+
223
+ if (!result.success) {
224
+ console.error(result.issues);
225
+ } else {
226
+ const widget = normalizeWidgetInput(result.data);
227
+ }
228
+
229
+ // Throws WidgetValidationError when the payload is invalid.
230
+ const parsed = parseWidgetInput(payload);
231
+ ```
232
+
233
+ `WidgetValidationIssue` entries contain a `path` such as `elements[0].style.font.size` and a human-readable `message`. Invalid input supplied to `WidgetRenderer` produces a safe empty widget and exposes issues through `validationIssues`. `isWidgetContract` and `isWidget` are also available as type guards.
234
+
235
+ ### External validation rules
236
+
237
+ Add your own checks with `validationRules`. Rules run only after the built-in validation passes and receive the typed widget. Each rule returns `WidgetValidationIssue` entries; return an empty array when the widget is allowed.
238
+
239
+ ```ts
240
+ import { WidgetValidationRuleInput } from '@lieselthuriot/widget-renderer';
241
+
242
+ const allowedColors = new Set(['#ffffff', '#1f2937']);
243
+
244
+ export const rules: WidgetValidationRuleInput[] = [
245
+ // A plain function is a blocking rule.
246
+ (widget) =>
247
+ widget.background.image
248
+ ? [{ path: 'background.image', message: 'Background images are not allowed' }]
249
+ : [],
250
+ // Report-only rules let the widget render and only expose their issues.
251
+ {
252
+ mode: 'report',
253
+ validate: (widget) =>
254
+ widget.background.color && !allowedColors.has(widget.background.color.toLowerCase())
255
+ ? [{ path: 'background.color', message: 'Color is not allowed' }]
256
+ : [],
257
+ },
258
+ ];
259
+ ```
260
+
261
+ ```html
262
+ <widget-renderer [widget]="widget" [validationRules]="rules" />
263
+ ```
264
+
265
+ - `mode: 'block'` (the default): the renderer shows the safe empty widget and the validation overlay.
266
+ - `mode: 'report'`: the widget renders normally; the issue is only exposed through `validationIssues`.
267
+ - Rule issues get a `severity` of `block` or `report`. A rule that throws produces one issue with its own mode.
268
+ - The same rules work outside the component: `safeParseWidgetInput(payload, { rules })` fails only on blocking issues and returns report-only issues in `result.issues`. `parseWidgetInput(payload, { rules })` throws only on blocking issues.
269
+
270
+ ## Layout fingerprints
271
+
272
+ `computeWidgetFingerprint(payload)` returns a stable ID of the widget's look, such as `wfp:1:3f9c…`, or `null` for invalid input. Use it to approve a layout once and then accept any widget with the same fingerprint.
273
+
274
+ The fingerprint covers the size, grid tracks, background (including its image), element types, positions, order, styles, image sources, and button method and behavior. It ignores content that can differ per visitor: the widget `id`, text, button labels, image alt text, chart titles, chart and progress data, button URLs and bodies, and the refresh label. Only whether text, labels, alt text, and titles are empty is included. Hex colors are compared case-insensitively, and omitted defaults match their explicit values.
275
+
276
+ `WidgetStudio.Contracts` computes the same value with `WidgetFingerprint.Compute`, so a .NET backend can verify fingerprints itself. The version number after `wfp:` (`WIDGET_FINGERPRINT_VERSION`) changes whenever the rules change.
277
+
278
+ ## Public API
279
+
280
+ Import everything from `@lieselthuriot/widget-renderer`; internal file paths are not part of the public API. The entry point exports:
281
+
282
+ - `WidgetRenderer`, the standalone rendering component, and its `WidgetLoading` input type. Element and chart implementation components are internal.
283
+ - Widget model types, including `WidgetContract`, `WidgetInput`, `Widget`, and the element types
284
+ - `normalizeWidgetInput`
285
+ - `safeParseWidgetInput`, `parseWidgetInput`, `isWidgetContract`, `isWidget`, `WidgetValidationError`, and the validation result types
286
+ - `computeWidgetFingerprint` and `WIDGET_FINGERPRINT_VERSION`
287
+ - Grid layout helpers and widget dimensions, such as `widgetWidth`, `widgetHeight`, `gridTemplate`, and `elementGridStyle`
288
+ - Locales: `defaultWidgetRendererLocale`, `germanWidgetRendererLocale`, `frenchWidgetRendererLocale`, `dutchWidgetRendererLocale`, `WidgetRendererLocale`, and `WidgetRendererLocaleOverrides`
289
+ - `chartAriaLabel` and `chartAccessibilitySummary`
290
+
291
+ The complete export list is maintained in [`src/public-api.ts`](https://github.com/LieselThuriot/widget-studio/blob/main/projects/widget-renderer/src/public-api.ts).
292
+
293
+ ## Contributing
294
+
295
+ The package is developed in the [widget-studio repository](https://github.com/LieselThuriot/widget-studio), together with the editor, the landscape package, and the C# contracts. See the repository README for build, test, and release instructions. Report problems in the [issue tracker](https://github.com/LieselThuriot/widget-studio/issues).
296
+
297
+ ## License
298
+
299
+ [MIT](https://github.com/LieselThuriot/widget-studio/blob/main/LICENSE)
@@ -0,0 +1,185 @@
1
+ import * as i0 from "@angular/core";
2
+ import { Injectable, signal, untracked } from "@angular/core";
3
+ const chart = {
4
+ progressAriaLabel: (title, value, range) => `${title || "Progress"}, ${value} of ${range}.`,
5
+ gaugeAriaLabel: (title, value, max) => `${title || "Gauge"}, ${value} of ${max}.`,
6
+ chartAriaLabel: (title, chartType, pointCount) => `${title || chartType} chart, ${pointCount} data points.`,
7
+ progressSummary: (title, value, min, max, breakdown) => `${title || "Progress"}: ${value} of ${max}; range ${min} to ${max}.${breakdown}`,
8
+ gaugeSummary: (title, value, max, percentage) => `${title || "Gauge"}: value ${value} of ${max}, ${percentage} percent.`,
9
+ dataSummary: (title, points) => `${title ? `${title}: ` : ""}${points}.`,
10
+ noDataSummary: (title) => `${title ? `${title}: ` : ""}no data.`,
11
+ noSegmentsSummary: " No segments.",
12
+ morePoints: (count) => `${count} more points.`,
13
+ progressSegmentLabel: (label, value) => `${label}: ${value}`,
14
+ dataPointLabel: (label, value) => `${label}: ${value}`,
15
+ gaugeValueLabel: "Value",
16
+ progressBreakdown: (label, start, end) => `${label}: ${start} to ${end}`
17
+ };
18
+ const defaultWidgetRendererLocale = { aria: {
19
+ toolbarActions: "Widget actions",
20
+ loading: "Widget loading",
21
+ showSensitiveData: "Show sensitive data",
22
+ hideSensitiveData: "Hide sensitive data",
23
+ imageUnavailable: "Image unavailable",
24
+ chart
25
+ } };
26
+ function locale(aria, chartLocale) {
27
+ return { aria: {
28
+ ...aria,
29
+ chart: chartLocale
30
+ } };
31
+ }
32
+ const germanWidgetRendererLocale = locale({
33
+ toolbarActions: "Widget-Aktionen",
34
+ loading: "Widget wird geladen",
35
+ showSensitiveData: "Sensible Daten anzeigen",
36
+ hideSensitiveData: "Sensible Daten ausblenden",
37
+ imageUnavailable: "Bild nicht verfügbar"
38
+ }, {
39
+ ...chart,
40
+ progressAriaLabel: (title, value, range) => `${title || "Fortschritt"}, ${value} von ${range}.`,
41
+ gaugeAriaLabel: (title, value, max) => `${title || "Messwert"}, ${value} von ${max}.`,
42
+ chartAriaLabel: (title, type, count) => `${title || `${type}-Diagramm`}, ${count} Datenpunkte.`,
43
+ progressSummary: (title, value, min, max, breakdown) => `${title || "Fortschritt"}: ${value} von ${max}; Bereich ${min} bis ${max}.${breakdown}`,
44
+ gaugeSummary: (title, value, max, percentage) => `${title || "Messwert"}: Wert ${value} von ${max}, ${percentage} Prozent.`,
45
+ noDataSummary: (title) => `${title ? `${title}: ` : ""}keine Daten.`,
46
+ noSegmentsSummary: " Keine Segmente.",
47
+ morePoints: (count) => `${count} weitere Datenpunkte.`,
48
+ gaugeValueLabel: "Wert",
49
+ progressSegmentLabel: (label, value) => `${label}: ${value}`,
50
+ progressBreakdown: (label, start, end) => `${label}: ${start} bis ${end}`
51
+ });
52
+ const frenchWidgetRendererLocale = locale({
53
+ toolbarActions: "Actions du widget",
54
+ loading: "Chargement du widget",
55
+ showSensitiveData: "Afficher les données sensibles",
56
+ hideSensitiveData: "Masquer les données sensibles",
57
+ imageUnavailable: "Image indisponible"
58
+ }, {
59
+ ...chart,
60
+ progressAriaLabel: (title, value, range) => `${title || "Progression"}, ${value} sur ${range}.`,
61
+ gaugeAriaLabel: (title, value, max) => `${title || "Jauge"}, ${value} sur ${max}.`,
62
+ chartAriaLabel: (title, type, count) => `${title || `Graphique ${type}`}, ${count} points de données.`,
63
+ progressSummary: (title, value, min, max, breakdown) => `${title || "Progression"} : ${value} sur ${max} ; plage de ${min} à ${max}.${breakdown}`,
64
+ gaugeSummary: (title, value, max, percentage) => `${title || "Jauge"} : valeur ${value} sur ${max}, ${percentage} pour cent.`,
65
+ noDataSummary: (title) => `${title ? `${title} : ` : ""}aucune donnée.`,
66
+ noSegmentsSummary: " Aucun segment.",
67
+ morePoints: (count) => `${count} points de données supplémentaires.`,
68
+ progressSegmentLabel: (label, value) => `${label} : ${value}`,
69
+ dataPointLabel: (label, value) => `${label} : ${value}`,
70
+ gaugeValueLabel: "Valeur",
71
+ progressBreakdown: (label, start, end) => `${label} : de ${start} à ${end}`
72
+ });
73
+ const dutchWidgetRendererLocale = locale({
74
+ toolbarActions: "Widgetacties",
75
+ loading: "Widget wordt geladen",
76
+ showSensitiveData: "Gevoelige gegevens tonen",
77
+ hideSensitiveData: "Gevoelige gegevens verbergen",
78
+ imageUnavailable: "Afbeelding niet beschikbaar"
79
+ }, {
80
+ ...chart,
81
+ progressAriaLabel: (title, value, range) => `${title || "Voortgang"}, ${value} van ${range}.`,
82
+ gaugeAriaLabel: (title, value, max) => `${title || "Meter"}, ${value} van ${max}.`,
83
+ chartAriaLabel: (title, type, count) => `${title || `${type}-grafiek`}, ${count} gegevenspunten.`,
84
+ progressSummary: (title, value, min, max, breakdown) => `${title || "Voortgang"}: ${value} van ${max}; bereik ${min} tot ${max}.${breakdown}`,
85
+ gaugeSummary: (title, value, max, percentage) => `${title || "Meter"}: waarde ${value} van ${max}, ${percentage} procent.`,
86
+ noDataSummary: (title) => `${title ? `${title}: ` : ""}geen gegevens.`,
87
+ noSegmentsSummary: " Geen segmenten.",
88
+ morePoints: (count) => `${count} extra gegevenspunten.`,
89
+ gaugeValueLabel: "Waarde",
90
+ progressSegmentLabel: (label, value) => `${label}: ${value}`,
91
+ progressBreakdown: (label, start, end) => `${label}: ${start} tot ${end}`
92
+ });
93
+ var WidgetTooltip = class WidgetTooltip {
94
+ state = signal(null, ...ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []);
95
+ overlay = signal(null, ...ngDevMode ? [{ debugName: "overlay" }] : /* istanbul ignore next */ []);
96
+ currentOwner = signal(null, ...ngDevMode ? [{ debugName: "currentOwner" }] : /* istanbul ignore next */ []);
97
+ owner = this.currentOwner.asReadonly();
98
+ setOverlay(overlay) {
99
+ this.overlay.set(overlay);
100
+ }
101
+ overlayElement() {
102
+ return this.overlay();
103
+ }
104
+ show(owner, state) {
105
+ this.currentOwner.set(owner);
106
+ this.state.set(state);
107
+ }
108
+ hide(owner) {
109
+ if (untracked(this.currentOwner) !== owner) return;
110
+ this.currentOwner.set(null);
111
+ this.state.set(null);
112
+ }
113
+ clear() {
114
+ this.currentOwner.set(null);
115
+ this.state.set(null);
116
+ this.overlay.set(null);
117
+ }
118
+ static ɵfac = i0.ɵɵngDeclareFactory({
119
+ minVersion: "12.0.0",
120
+ version: "22.2.4",
121
+ ngImport: i0,
122
+ type: WidgetTooltip,
123
+ deps: [],
124
+ target: i0.ɵɵFactoryTarget.Injectable
125
+ });
126
+ static ɵprov = i0.ɵɵngDeclareInjectable({
127
+ minVersion: "12.0.0",
128
+ version: "22.2.4",
129
+ ngImport: i0,
130
+ type: WidgetTooltip
131
+ });
132
+ };
133
+ i0.ɵɵngDeclareClassMetadata({
134
+ minVersion: "12.0.0",
135
+ version: "22.2.4",
136
+ ngImport: i0,
137
+ type: WidgetTooltip,
138
+ decorators: [{ type: Injectable }]
139
+ });
140
+ const MAX_POINTS = 20;
141
+ function number(value) {
142
+ return Number.isInteger(value) ? `${value}` : value.toFixed(2).replace(/0+$/, "").replace(/\.$/, "");
143
+ }
144
+ function points(data, locale) {
145
+ const visible = data.slice(0, MAX_POINTS).map((point) => `${point.label}: ${number(point.value)}`);
146
+ const remaining = data.length - visible.length;
147
+ return remaining > 0 ? `${visible.join("; ")}; ${locale.morePoints(remaining)}` : visible.join("; ");
148
+ }
149
+ function progressSegments(element) {
150
+ let position = element.data.min;
151
+ return element.data.items.flatMap((item) => {
152
+ const start = position;
153
+ position += item.value;
154
+ return item.kind === "segment" ? [{
155
+ segment: item,
156
+ start,
157
+ end: position
158
+ }] : [];
159
+ });
160
+ }
161
+ function progressValue(element) {
162
+ return element.data.items.reduce((total, item) => total + (item.kind === "segment" ? item.value : 0), 0);
163
+ }
164
+ function chartAriaLabel(element, locale = defaultWidgetRendererLocale.aria.chart) {
165
+ if (element.type === "progress") return locale.progressAriaLabel(element.title, number(progressValue(element)), number(element.data.max - element.data.min));
166
+ if (element.type === "gauge") return locale.gaugeAriaLabel(element.title, number(element.data.value), number(element.data.max));
167
+ return locale.chartAriaLabel(element.title, element.type, element.data.length);
168
+ }
169
+ function chartAccessibilitySummary(element, locale = defaultWidgetRendererLocale.aria.chart) {
170
+ if (element.type === "progress") {
171
+ const segments = progressSegments(element);
172
+ const breakdown = segments.length ? ` ${segments.map(({ segment, start, end }) => locale.progressBreakdown(segment.label, number(start), number(end))).join("; ")}.` : locale.noSegmentsSummary;
173
+ return locale.progressSummary(element.title, number(progressValue(element)), number(element.data.min), number(element.data.max), breakdown);
174
+ }
175
+ if (element.type === "gauge") {
176
+ const range = element.data.max - element.data.min;
177
+ const percentage = Math.max(0, Math.min(100, (element.data.value - element.data.min) / range * 100));
178
+ return locale.gaugeSummary(element.title, number(element.data.value), number(element.data.max), number(percentage));
179
+ }
180
+ if (!element.data.length) return locale.noDataSummary(element.title);
181
+ return locale.dataSummary(element.title, points(element.data, locale));
182
+ }
183
+ export { dutchWidgetRendererLocale as a, defaultWidgetRendererLocale as i, chartAriaLabel as n, frenchWidgetRendererLocale as o, WidgetTooltip as r, germanWidgetRendererLocale as s, chartAccessibilitySummary as t };
184
+
185
+ //# sourceMappingURL=lieselthuriot-widget-renderer-chart-accessibility-BzH2alxy.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lieselthuriot-widget-renderer-chart-accessibility-BzH2alxy.mjs","names":[],"sources":["../../../projects/widget-renderer/src/lib/widget-locale.ts","../../../projects/widget-renderer/src/lib/widget-tooltip.ts","../../../projects/widget-renderer/src/lib/chart-accessibility.ts"],"sourcesContent":["export interface WidgetRendererChartLocale {\n progressAriaLabel: (title: string | undefined, value: string, range: string) => string;\n gaugeAriaLabel: (title: string | undefined, value: string, max: string) => string;\n chartAriaLabel: (title: string | undefined, chartType: string, pointCount: number) => string;\n progressSummary: (\n title: string | undefined,\n value: string,\n min: string,\n max: string,\n breakdown: string,\n ) => string;\n gaugeSummary: (\n title: string | undefined,\n value: string,\n max: string,\n percentage: string,\n ) => string;\n dataSummary: (title: string | undefined, points: string) => string;\n noDataSummary: (title: string | undefined) => string;\n noSegmentsSummary: string;\n morePoints: (count: number) => string;\n progressSegmentLabel: (label: string, value: string) => string;\n dataPointLabel: (label: string, value: string) => string;\n gaugeValueLabel: string;\n progressBreakdown: (label: string, start: string, end: string) => string;\n}\n\nexport interface WidgetRendererAriaLocale {\n toolbarActions: string;\n loading: string;\n showSensitiveData: string;\n hideSensitiveData: string;\n imageUnavailable: string;\n chart: WidgetRendererChartLocale;\n}\n\nexport interface WidgetRendererLocale {\n aria: WidgetRendererAriaLocale;\n}\n\nexport interface WidgetRendererLocaleOverrides {\n aria?: Partial<Omit<WidgetRendererAriaLocale, 'chart'>> & {\n chart?: Partial<WidgetRendererChartLocale>;\n };\n}\n\nconst chart = {\n progressAriaLabel: (title: string | undefined, value: string, range: string) =>\n `${title || 'Progress'}, ${value} of ${range}.`,\n gaugeAriaLabel: (title: string | undefined, value: string, max: string) =>\n `${title || 'Gauge'}, ${value} of ${max}.`,\n chartAriaLabel: (title: string | undefined, chartType: string, pointCount: number) =>\n `${title || chartType} chart, ${pointCount} data points.`,\n progressSummary: (\n title: string | undefined,\n value: string,\n min: string,\n max: string,\n breakdown: string,\n ) => `${title || 'Progress'}: ${value} of ${max}; range ${min} to ${max}.${breakdown}`,\n gaugeSummary: (title: string | undefined, value: string, max: string, percentage: string) =>\n `${title || 'Gauge'}: value ${value} of ${max}, ${percentage} percent.`,\n dataSummary: (title: string | undefined, points: string) =>\n `${title ? `${title}: ` : ''}${points}.`,\n noDataSummary: (title: string | undefined) => `${title ? `${title}: ` : ''}no data.`,\n noSegmentsSummary: ' No segments.',\n morePoints: (count: number) => `${count} more points.`,\n progressSegmentLabel: (label: string, value: string) => `${label}: ${value}`,\n dataPointLabel: (label: string, value: string) => `${label}: ${value}`,\n gaugeValueLabel: 'Value',\n progressBreakdown: (label: string, start: string, end: string) => `${label}: ${start} to ${end}`,\n} satisfies WidgetRendererChartLocale;\n\nexport const defaultWidgetRendererLocale: WidgetRendererLocale = {\n aria: {\n toolbarActions: 'Widget actions',\n loading: 'Widget loading',\n showSensitiveData: 'Show sensitive data',\n hideSensitiveData: 'Hide sensitive data',\n imageUnavailable: 'Image unavailable',\n chart,\n },\n};\n\nfunction locale(\n aria: Omit<WidgetRendererAriaLocale, 'chart'>,\n chartLocale: WidgetRendererChartLocale,\n): WidgetRendererLocale {\n return { aria: { ...aria, chart: chartLocale } };\n}\n\nexport const germanWidgetRendererLocale = locale(\n {\n toolbarActions: 'Widget-Aktionen',\n loading: 'Widget wird geladen',\n showSensitiveData: 'Sensible Daten anzeigen',\n hideSensitiveData: 'Sensible Daten ausblenden',\n imageUnavailable: 'Bild nicht verfügbar',\n },\n {\n ...chart,\n progressAriaLabel: (title, value, range) => `${title || 'Fortschritt'}, ${value} von ${range}.`,\n gaugeAriaLabel: (title, value, max) => `${title || 'Messwert'}, ${value} von ${max}.`,\n chartAriaLabel: (title, type, count) => `${title || `${type}-Diagramm`}, ${count} Datenpunkte.`,\n progressSummary: (title, value, min, max, breakdown) =>\n `${title || 'Fortschritt'}: ${value} von ${max}; Bereich ${min} bis ${max}.${breakdown}`,\n gaugeSummary: (title, value, max, percentage) =>\n `${title || 'Messwert'}: Wert ${value} von ${max}, ${percentage} Prozent.`,\n noDataSummary: (title) => `${title ? `${title}: ` : ''}keine Daten.`,\n noSegmentsSummary: ' Keine Segmente.',\n morePoints: (count) => `${count} weitere Datenpunkte.`,\n gaugeValueLabel: 'Wert',\n progressSegmentLabel: (label, value) => `${label}: ${value}`,\n progressBreakdown: (label, start, end) => `${label}: ${start} bis ${end}`,\n },\n);\n\nexport const frenchWidgetRendererLocale = locale(\n {\n toolbarActions: 'Actions du widget',\n loading: 'Chargement du widget',\n showSensitiveData: 'Afficher les données sensibles',\n hideSensitiveData: 'Masquer les données sensibles',\n imageUnavailable: 'Image indisponible',\n },\n {\n ...chart,\n progressAriaLabel: (title, value, range) => `${title || 'Progression'}, ${value} sur ${range}.`,\n gaugeAriaLabel: (title, value, max) => `${title || 'Jauge'}, ${value} sur ${max}.`,\n chartAriaLabel: (title, type, count) =>\n `${title || `Graphique ${type}`}, ${count} points de données.`,\n progressSummary: (title, value, min, max, breakdown) =>\n `${title || 'Progression'} : ${value} sur ${max} ; plage de ${min} à ${max}.${breakdown}`,\n gaugeSummary: (title, value, max, percentage) =>\n `${title || 'Jauge'} : valeur ${value} sur ${max}, ${percentage} pour cent.`,\n noDataSummary: (title) => `${title ? `${title} : ` : ''}aucune donnée.`,\n noSegmentsSummary: ' Aucun segment.',\n morePoints: (count) => `${count} points de données supplémentaires.`,\n progressSegmentLabel: (label, value) => `${label} : ${value}`,\n dataPointLabel: (label, value) => `${label} : ${value}`,\n gaugeValueLabel: 'Valeur',\n progressBreakdown: (label, start, end) => `${label} : de ${start} à ${end}`,\n },\n);\n\nexport const dutchWidgetRendererLocale = locale(\n {\n toolbarActions: 'Widgetacties',\n loading: 'Widget wordt geladen',\n showSensitiveData: 'Gevoelige gegevens tonen',\n hideSensitiveData: 'Gevoelige gegevens verbergen',\n imageUnavailable: 'Afbeelding niet beschikbaar',\n },\n {\n ...chart,\n progressAriaLabel: (title, value, range) => `${title || 'Voortgang'}, ${value} van ${range}.`,\n gaugeAriaLabel: (title, value, max) => `${title || 'Meter'}, ${value} van ${max}.`,\n chartAriaLabel: (title, type, count) =>\n `${title || `${type}-grafiek`}, ${count} gegevenspunten.`,\n progressSummary: (title, value, min, max, breakdown) =>\n `${title || 'Voortgang'}: ${value} van ${max}; bereik ${min} tot ${max}.${breakdown}`,\n gaugeSummary: (title, value, max, percentage) =>\n `${title || 'Meter'}: waarde ${value} van ${max}, ${percentage} procent.`,\n noDataSummary: (title) => `${title ? `${title}: ` : ''}geen gegevens.`,\n noSegmentsSummary: ' Geen segmenten.',\n morePoints: (count) => `${count} extra gegevenspunten.`,\n gaugeValueLabel: 'Waarde',\n progressSegmentLabel: (label, value) => `${label}: ${value}`,\n progressBreakdown: (label, start, end) => `${label}: ${start} tot ${end}`,\n },\n);\n","import { Injectable, signal, untracked } from '@angular/core';\n\nexport interface ChartTooltipState {\n x: number;\n y: number;\n label: string;\n value: number;\n color?: string;\n}\n\n@Injectable()\nexport class WidgetTooltip {\n readonly state = signal<ChartTooltipState | null>(null);\n private readonly overlay = signal<HTMLElement | null>(null);\n private readonly currentOwner = signal<object | null>(null);\n readonly owner = this.currentOwner.asReadonly();\n\n setOverlay(overlay: HTMLElement): void {\n this.overlay.set(overlay);\n }\n\n overlayElement(): HTMLElement | null {\n return this.overlay();\n }\n\n show(owner: object, state: ChartTooltipState): void {\n this.currentOwner.set(owner);\n this.state.set(state);\n }\n\n hide(owner: object): void {\n if (untracked(this.currentOwner) !== owner) return;\n this.currentOwner.set(null);\n this.state.set(null);\n }\n\n clear(): void {\n this.currentOwner.set(null);\n this.state.set(null);\n this.overlay.set(null);\n }\n}\n","import { ChartElement, ProgressElement, ProgressSegment } from './widget.model';\nimport { defaultWidgetRendererLocale, WidgetRendererChartLocale } from './widget-locale';\n\nconst MAX_POINTS = 20;\n\nfunction number(value: number): string {\n return Number.isInteger(value)\n ? `${value}`\n : value.toFixed(2).replace(/0+$/, '').replace(/\\.$/, '');\n}\n\nfunction points(\n data: { label: string; value: number }[],\n locale: WidgetRendererChartLocale,\n): string {\n const visible = data\n .slice(0, MAX_POINTS)\n .map((point) => `${point.label}: ${number(point.value)}`);\n const remaining = data.length - visible.length;\n return remaining > 0\n ? `${visible.join('; ')}; ${locale.morePoints(remaining)}`\n : visible.join('; ');\n}\n\nfunction progressSegments(\n element: ProgressElement,\n): { segment: ProgressSegment; start: number; end: number }[] {\n let position = element.data.min;\n return element.data.items.flatMap((item) => {\n const start = position;\n position += item.value;\n return item.kind === 'segment' ? [{ segment: item, start, end: position }] : [];\n });\n}\n\nfunction progressValue(element: ProgressElement): number {\n return element.data.items.reduce(\n (total, item) => total + (item.kind === 'segment' ? item.value : 0),\n 0,\n );\n}\n\nexport function chartAriaLabel(\n element: ChartElement | ProgressElement,\n locale = defaultWidgetRendererLocale.aria.chart,\n): string {\n if (element.type === 'progress') {\n return locale.progressAriaLabel(\n element.title,\n number(progressValue(element)),\n number(element.data.max - element.data.min),\n );\n }\n if (element.type === 'gauge') {\n return locale.gaugeAriaLabel(\n element.title,\n number(element.data.value),\n number(element.data.max),\n );\n }\n return locale.chartAriaLabel(element.title, element.type, element.data.length);\n}\n\nexport function chartAccessibilitySummary(\n element: ChartElement | ProgressElement,\n locale = defaultWidgetRendererLocale.aria.chart,\n): string {\n if (element.type === 'progress') {\n const segments = progressSegments(element);\n const breakdown = segments.length\n ? ` ${segments.map(({ segment, start, end }) => locale.progressBreakdown(segment.label, number(start), number(end))).join('; ')}.`\n : locale.noSegmentsSummary;\n return locale.progressSummary(\n element.title,\n number(progressValue(element)),\n number(element.data.min),\n number(element.data.max),\n breakdown,\n );\n }\n if (element.type === 'gauge') {\n const range = element.data.max - element.data.min;\n const percentage = Math.max(\n 0,\n Math.min(100, ((element.data.value - element.data.min) / range) * 100),\n );\n return locale.gaugeSummary(\n element.title,\n number(element.data.value),\n number(element.data.max),\n number(percentage),\n );\n }\n if (!element.data.length) return locale.noDataSummary(element.title);\n return locale.dataSummary(element.title, points(element.data, locale));\n}\n"],"mappings":"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"}