@lieselthuriot/widget-renderer 0.0.0-stage → 0.1.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 +21 -0
- package/README.md +243 -2
- package/fesm2022/lieselthuriot-widget-renderer-chart-accessibility-CXEeXwdH.mjs +178 -0
- package/fesm2022/lieselthuriot-widget-renderer-chart-accessibility-CXEeXwdH.mjs.map +1 -0
- package/fesm2022/lieselthuriot-widget-renderer-chart-element-renderer-BO3V-jYo.mjs +722 -0
- package/fesm2022/lieselthuriot-widget-renderer-chart-element-renderer-BO3V-jYo.mjs.map +1 -0
- package/fesm2022/lieselthuriot-widget-renderer.mjs +1435 -0
- package/fesm2022/lieselthuriot-widget-renderer.mjs.map +1 -0
- package/package.json +51 -3
- package/types/lieselthuriot-widget-renderer.d.ts +363 -0
- package/types/lieselthuriot-widget-renderer.d.ts.map +1 -0
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,244 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Widget Renderer
|
|
2
2
|
|
|
3
|
-
|
|
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`, `@angular/core`, and `echarts` are peer dependencies. The package supports Angular 21 and 22 and ECharts 6.x. npm, pnpm, and Bun install missing peer dependencies automatically; with Yarn, also add `echarts`.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- Render `mini`, `compact`, `tall`, `square`, `wide`, `mega`, `banner`, and `hero` widgets from a version 2 contract
|
|
22
|
+
- Place elements with configurable rows, columns, spans, and anchors
|
|
23
|
+
- Support text, images, buttons, dividers, pie, donut, funnel, gauge, bar, line, and progress elements
|
|
24
|
+
- Render charts with ECharts using SVG output
|
|
25
|
+
- Validate incoming data with structured paths and messages
|
|
26
|
+
- Normalize optional contract fields such as button defaults and text padding
|
|
27
|
+
- Blur sensitive backgrounds and elements until explicitly revealed
|
|
28
|
+
- Emit refresh and button-action events without making network requests
|
|
29
|
+
- Provide accessible names and text summaries for charts and progress bars
|
|
30
|
+
- Respect reduced-motion preferences for loading and active progress animations
|
|
31
|
+
- Localize renderer-owned accessible labels, with built-in German, French, and Dutch locales
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
Import the standalone `WidgetRenderer` component into an Angular component and bind a `WidgetContract`:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { Component } from '@angular/core';
|
|
39
|
+
import { ButtonElement, WidgetContract, WidgetRenderer } from '@lieselthuriot/widget-renderer';
|
|
40
|
+
|
|
41
|
+
@Component({
|
|
42
|
+
selector: 'app-dashboard-card',
|
|
43
|
+
imports: [WidgetRenderer],
|
|
44
|
+
template: `
|
|
45
|
+
<widget-renderer
|
|
46
|
+
[widget]="widget"
|
|
47
|
+
[darkMode]="darkMode"
|
|
48
|
+
[loading]="loading"
|
|
49
|
+
(refreshRequested)="refreshWidget()"
|
|
50
|
+
(actionRequested)="handleAction($event)"
|
|
51
|
+
/>
|
|
52
|
+
`,
|
|
53
|
+
})
|
|
54
|
+
export class DashboardCard {
|
|
55
|
+
readonly darkMode = false;
|
|
56
|
+
readonly loading = false;
|
|
57
|
+
|
|
58
|
+
readonly widget: WidgetContract = {
|
|
59
|
+
version: 2,
|
|
60
|
+
id: 'hello-widget',
|
|
61
|
+
size: 'square',
|
|
62
|
+
rows: [{ unit: 'auto' }],
|
|
63
|
+
columns: [{ size: 1, unit: 'star' }],
|
|
64
|
+
background: {
|
|
65
|
+
color: '#172033',
|
|
66
|
+
sensitive: false,
|
|
67
|
+
repeat: false,
|
|
68
|
+
size: 'cover',
|
|
69
|
+
anchor: 'center',
|
|
70
|
+
},
|
|
71
|
+
refresh: { enabled: true, label: 'Refresh widget' },
|
|
72
|
+
elements: [
|
|
73
|
+
{
|
|
74
|
+
type: 'text',
|
|
75
|
+
grid: { row: 0, column: 0, rowSpan: 1, columnSpan: 1, anchor: 'center' },
|
|
76
|
+
sensitive: false,
|
|
77
|
+
text: 'Hello from Widget Renderer',
|
|
78
|
+
style: {
|
|
79
|
+
color: '#ffffff',
|
|
80
|
+
font: { size: 16, bold: true },
|
|
81
|
+
align: 'center',
|
|
82
|
+
wrap: true,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
refreshWidget(): void {
|
|
89
|
+
// Fetch updated data in the consuming application.
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
handleAction(event: { element: ButtonElement; event: Event }): void {
|
|
93
|
+
// Handle the configured button request in the consuming application.
|
|
94
|
+
console.log(event);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
> **Important:** `WidgetRenderer` emits `refreshRequested` and `actionRequested`. It does not call the configured button URL or perform refresh requests itself.
|
|
100
|
+
|
|
101
|
+
### Loading state
|
|
102
|
+
|
|
103
|
+
Set `loading` to `true` for the default square skeleton, or to a widget size such as `'wide'` for a matching skeleton:
|
|
104
|
+
|
|
105
|
+
```html
|
|
106
|
+
<widget-renderer [loading]="'wide'" [darkMode]="true" />
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Dark mode
|
|
110
|
+
|
|
111
|
+
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.
|
|
112
|
+
|
|
113
|
+
### Outputs
|
|
114
|
+
|
|
115
|
+
`refreshRequested` is emitted when the refresh control is clicked. `actionRequested` is emitted with the clicked `ButtonElement` and DOM event:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { ButtonElement } from '@lieselthuriot/widget-renderer';
|
|
119
|
+
|
|
120
|
+
handleAction(event: { element: ButtonElement; event: Event }): void {
|
|
121
|
+
event.event.preventDefault();
|
|
122
|
+
const { method, url, body, requestRefresh } = event.element;
|
|
123
|
+
// Perform the application-specific request here.
|
|
124
|
+
console.log(method, url, body, requestRefresh);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Localization
|
|
129
|
+
|
|
130
|
+
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.
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
import {
|
|
134
|
+
germanWidgetRendererLocale,
|
|
135
|
+
WidgetRendererLocaleOverrides,
|
|
136
|
+
} from '@lieselthuriot/widget-renderer';
|
|
137
|
+
|
|
138
|
+
readonly rendererLocale: WidgetRendererLocaleOverrides = {
|
|
139
|
+
...germanWidgetRendererLocale,
|
|
140
|
+
};
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Built-in `germanWidgetRendererLocale`, `frenchWidgetRendererLocale`, and `dutchWidgetRendererLocale` values are available. For a small customization, override only the required values:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import { WidgetRendererLocaleOverrides } from '@lieselthuriot/widget-renderer';
|
|
147
|
+
|
|
148
|
+
readonly rendererLocale: WidgetRendererLocaleOverrides = {
|
|
149
|
+
aria: {
|
|
150
|
+
showSensitiveData: 'Afficher les données privées',
|
|
151
|
+
chart: {
|
|
152
|
+
noDataSummary: (title) => `${title ? `${title} : ` : ''}aucune donnée.`,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
```html
|
|
159
|
+
<widget-renderer [widget]="widget" [locale]="rendererLocale" />
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The `aria` locale covers the loading status, toolbar, sensitive-content controls, image failure fallback, chart and progress accessible names, generated chart summaries, and progress segment labels. Locale changes update the rendered labels without changing widget-provided content.
|
|
163
|
+
|
|
164
|
+
## Widget contract
|
|
165
|
+
|
|
166
|
+
Every input must use `version: 2` and include:
|
|
167
|
+
|
|
168
|
+
| Field | Description |
|
|
169
|
+
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
170
|
+
| `id` | A stable identifier for the widget |
|
|
171
|
+
| `size` | `mini`, `compact`, `square`, `wide`, `mega`, `banner`, `hero`, or `tall`; dimensions include the 8px layout gap |
|
|
172
|
+
| `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 |
|
|
173
|
+
| `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 |
|
|
174
|
+
| `background` | Color, optional image, sizing, position, repeat, overlay, and sensitivity settings |
|
|
175
|
+
| `refresh` | Whether to show a refresh action and its accessible label |
|
|
176
|
+
| `elements` | An array of supported widget elements |
|
|
177
|
+
|
|
178
|
+
Element positions are zero-based. For example, `row: 0` and `column: 0` place an element in the first grid cell.
|
|
179
|
+
|
|
180
|
+
### Supported elements
|
|
181
|
+
|
|
182
|
+
- `text`: formatted text with alignment, wrapping, font styling, color, background, radius, and padding
|
|
183
|
+
- `image`: remote or local image with alternative text, fit mode, radius, and overlay color
|
|
184
|
+
- `button`: `get` or `post` action metadata, optional body, solid or outline styling, and hover behavior
|
|
185
|
+
- `divider`: horizontal or vertical separator
|
|
186
|
+
- `pie`, `donut`, and `funnel`: labeled data points with optional legends and per-point colors
|
|
187
|
+
- `gauge`: a value within a minimum and maximum range
|
|
188
|
+
- `bar` and `line`: labeled series data with axis and line styling
|
|
189
|
+
- `progress`: a range-based progress bar with label, suffix, legend, and active animation options
|
|
190
|
+
|
|
191
|
+
Editor-only `id` fields are not required. The renderer accepts `WidgetContract` data and normalizes optional button fields and text padding before rendering.
|
|
192
|
+
|
|
193
|
+
### Sensitive content
|
|
194
|
+
|
|
195
|
+
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.
|
|
196
|
+
|
|
197
|
+
> **Warning:** Sensitive-content handling is a display and privacy feature, not encryption. Sensitive values remain in the JavaScript object supplied by the consuming application.
|
|
198
|
+
|
|
199
|
+
## Validation and normalization
|
|
200
|
+
|
|
201
|
+
Use the exported helpers when data enters your application:
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
import {
|
|
205
|
+
normalizeWidgetInput,
|
|
206
|
+
parseWidgetInput,
|
|
207
|
+
safeParseWidgetInput,
|
|
208
|
+
} from '@lieselthuriot/widget-renderer';
|
|
209
|
+
|
|
210
|
+
const result = safeParseWidgetInput(payload);
|
|
211
|
+
|
|
212
|
+
if (!result.success) {
|
|
213
|
+
console.error(result.issues);
|
|
214
|
+
} else {
|
|
215
|
+
const widget = normalizeWidgetInput(result.data);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Throws WidgetValidationError when the payload is invalid.
|
|
219
|
+
const parsed = parseWidgetInput(payload);
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
`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.
|
|
223
|
+
|
|
224
|
+
## Public API
|
|
225
|
+
|
|
226
|
+
Import everything from `@lieselthuriot/widget-renderer`; internal file paths are not part of the public API. The entry point exports:
|
|
227
|
+
|
|
228
|
+
- `WidgetRenderer`, the standalone rendering component, and its `WidgetLoading` input type. Element and chart implementation components are internal.
|
|
229
|
+
- Widget model types, including `WidgetContract`, `WidgetInput`, `Widget`, and the element types
|
|
230
|
+
- `normalizeWidgetInput`
|
|
231
|
+
- `safeParseWidgetInput`, `parseWidgetInput`, `isWidgetContract`, `isWidget`, `WidgetValidationError`, and the validation result types
|
|
232
|
+
- Grid layout helpers and widget dimensions, such as `widgetWidth`, `widgetHeight`, `gridTemplate`, and `elementGridStyle`
|
|
233
|
+
- Locales: `defaultWidgetRendererLocale`, `germanWidgetRendererLocale`, `frenchWidgetRendererLocale`, `dutchWidgetRendererLocale`, `WidgetRendererLocale`, and `WidgetRendererLocaleOverrides`
|
|
234
|
+
- `chartAriaLabel` and `chartAccessibilitySummary`
|
|
235
|
+
|
|
236
|
+
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).
|
|
237
|
+
|
|
238
|
+
## Contributing
|
|
239
|
+
|
|
240
|
+
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).
|
|
241
|
+
|
|
242
|
+
## License
|
|
243
|
+
|
|
244
|
+
[MIT](https://github.com/LieselThuriot/widget-studio/blob/main/LICENSE)
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
import { Injectable, signal } 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
|
+
progressBreakdown: (label, start, end) => `${label}: ${start} to ${end}`
|
|
15
|
+
};
|
|
16
|
+
const defaultWidgetRendererLocale = { aria: {
|
|
17
|
+
toolbarActions: "Widget actions",
|
|
18
|
+
loading: "Widget loading",
|
|
19
|
+
showSensitiveData: "Show sensitive data",
|
|
20
|
+
hideSensitiveData: "Hide sensitive data",
|
|
21
|
+
imageUnavailable: "Image unavailable",
|
|
22
|
+
chart
|
|
23
|
+
} };
|
|
24
|
+
function locale(aria, chartLocale) {
|
|
25
|
+
return { aria: {
|
|
26
|
+
...aria,
|
|
27
|
+
chart: chartLocale
|
|
28
|
+
} };
|
|
29
|
+
}
|
|
30
|
+
const germanWidgetRendererLocale = locale({
|
|
31
|
+
toolbarActions: "Widget-Aktionen",
|
|
32
|
+
loading: "Widget wird geladen",
|
|
33
|
+
showSensitiveData: "Sensible Daten anzeigen",
|
|
34
|
+
hideSensitiveData: "Sensible Daten ausblenden",
|
|
35
|
+
imageUnavailable: "Bild nicht verfügbar"
|
|
36
|
+
}, {
|
|
37
|
+
...chart,
|
|
38
|
+
progressAriaLabel: (title, value, range) => `${title || "Fortschritt"}, ${value} von ${range}.`,
|
|
39
|
+
gaugeAriaLabel: (title, value, max) => `${title || "Messwert"}, ${value} von ${max}.`,
|
|
40
|
+
chartAriaLabel: (title, type, count) => `${title || `${type}-Diagramm`}, ${count} Datenpunkte.`,
|
|
41
|
+
progressSummary: (title, value, min, max, breakdown) => `${title || "Fortschritt"}: ${value} von ${max}; Bereich ${min} bis ${max}.${breakdown}`,
|
|
42
|
+
gaugeSummary: (title, value, max, percentage) => `${title || "Messwert"}: Wert ${value} von ${max}, ${percentage} Prozent.`,
|
|
43
|
+
noDataSummary: (title) => `${title ? `${title}: ` : ""}keine Daten.`,
|
|
44
|
+
noSegmentsSummary: " Keine Segmente.",
|
|
45
|
+
morePoints: (count) => `${count} weitere Datenpunkte.`,
|
|
46
|
+
progressSegmentLabel: (label, value) => `${label}: ${value}`,
|
|
47
|
+
progressBreakdown: (label, start, end) => `${label}: ${start} bis ${end}`
|
|
48
|
+
});
|
|
49
|
+
const frenchWidgetRendererLocale = locale({
|
|
50
|
+
toolbarActions: "Actions du widget",
|
|
51
|
+
loading: "Chargement du widget",
|
|
52
|
+
showSensitiveData: "Afficher les données sensibles",
|
|
53
|
+
hideSensitiveData: "Masquer les données sensibles",
|
|
54
|
+
imageUnavailable: "Image indisponible"
|
|
55
|
+
}, {
|
|
56
|
+
...chart,
|
|
57
|
+
progressAriaLabel: (title, value, range) => `${title || "Progression"}, ${value} sur ${range}.`,
|
|
58
|
+
gaugeAriaLabel: (title, value, max) => `${title || "Jauge"}, ${value} sur ${max}.`,
|
|
59
|
+
chartAriaLabel: (title, type, count) => `${title || `Graphique ${type}`}, ${count} points de données.`,
|
|
60
|
+
progressSummary: (title, value, min, max, breakdown) => `${title || "Progression"} : ${value} sur ${max} ; plage de ${min} à ${max}.${breakdown}`,
|
|
61
|
+
gaugeSummary: (title, value, max, percentage) => `${title || "Jauge"} : valeur ${value} sur ${max}, ${percentage} pour cent.`,
|
|
62
|
+
noDataSummary: (title) => `${title ? `${title} : ` : ""}aucune donnée.`,
|
|
63
|
+
noSegmentsSummary: " Aucun segment.",
|
|
64
|
+
morePoints: (count) => `${count} points de données supplémentaires.`,
|
|
65
|
+
progressSegmentLabel: (label, value) => `${label} : ${value}`,
|
|
66
|
+
progressBreakdown: (label, start, end) => `${label} : de ${start} à ${end}`
|
|
67
|
+
});
|
|
68
|
+
const dutchWidgetRendererLocale = locale({
|
|
69
|
+
toolbarActions: "Widgetacties",
|
|
70
|
+
loading: "Widget wordt geladen",
|
|
71
|
+
showSensitiveData: "Gevoelige gegevens tonen",
|
|
72
|
+
hideSensitiveData: "Gevoelige gegevens verbergen",
|
|
73
|
+
imageUnavailable: "Afbeelding niet beschikbaar"
|
|
74
|
+
}, {
|
|
75
|
+
...chart,
|
|
76
|
+
progressAriaLabel: (title, value, range) => `${title || "Voortgang"}, ${value} van ${range}.`,
|
|
77
|
+
gaugeAriaLabel: (title, value, max) => `${title || "Meter"}, ${value} van ${max}.`,
|
|
78
|
+
chartAriaLabel: (title, type, count) => `${title || `${type}-grafiek`}, ${count} gegevenspunten.`,
|
|
79
|
+
progressSummary: (title, value, min, max, breakdown) => `${title || "Voortgang"}: ${value} van ${max}; bereik ${min} tot ${max}.${breakdown}`,
|
|
80
|
+
gaugeSummary: (title, value, max, percentage) => `${title || "Meter"}: waarde ${value} van ${max}, ${percentage} procent.`,
|
|
81
|
+
noDataSummary: (title) => `${title ? `${title}: ` : ""}geen gegevens.`,
|
|
82
|
+
noSegmentsSummary: " Geen segmenten.",
|
|
83
|
+
morePoints: (count) => `${count} extra gegevenspunten.`,
|
|
84
|
+
progressSegmentLabel: (label, value) => `${label}: ${value}`,
|
|
85
|
+
progressBreakdown: (label, start, end) => `${label}: ${start} tot ${end}`
|
|
86
|
+
});
|
|
87
|
+
var WidgetTooltip = class WidgetTooltip {
|
|
88
|
+
progress = signal(null, ...ngDevMode ? [{ debugName: "progress" }] : /* istanbul ignore next */ []);
|
|
89
|
+
overlay = signal(null, ...ngDevMode ? [{ debugName: "overlay" }] : /* istanbul ignore next */ []);
|
|
90
|
+
progressOwner = null;
|
|
91
|
+
setOverlay(overlay) {
|
|
92
|
+
this.overlay.set(overlay);
|
|
93
|
+
}
|
|
94
|
+
overlayElement() {
|
|
95
|
+
return this.overlay();
|
|
96
|
+
}
|
|
97
|
+
showProgress(owner, state) {
|
|
98
|
+
this.progressOwner = owner;
|
|
99
|
+
this.progress.set(state);
|
|
100
|
+
}
|
|
101
|
+
hideProgress(owner) {
|
|
102
|
+
if (this.progressOwner !== owner) return;
|
|
103
|
+
this.progressOwner = null;
|
|
104
|
+
this.progress.set(null);
|
|
105
|
+
}
|
|
106
|
+
clear() {
|
|
107
|
+
this.progressOwner = null;
|
|
108
|
+
this.progress.set(null);
|
|
109
|
+
this.overlay.set(null);
|
|
110
|
+
}
|
|
111
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
112
|
+
minVersion: "12.0.0",
|
|
113
|
+
version: "22.2.3",
|
|
114
|
+
ngImport: i0,
|
|
115
|
+
type: WidgetTooltip,
|
|
116
|
+
deps: [],
|
|
117
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
118
|
+
});
|
|
119
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
120
|
+
minVersion: "12.0.0",
|
|
121
|
+
version: "22.2.3",
|
|
122
|
+
ngImport: i0,
|
|
123
|
+
type: WidgetTooltip
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
127
|
+
minVersion: "12.0.0",
|
|
128
|
+
version: "22.2.3",
|
|
129
|
+
ngImport: i0,
|
|
130
|
+
type: WidgetTooltip,
|
|
131
|
+
decorators: [{ type: Injectable }]
|
|
132
|
+
});
|
|
133
|
+
const MAX_POINTS = 20;
|
|
134
|
+
function number(value) {
|
|
135
|
+
return Number.isInteger(value) ? `${value}` : value.toFixed(2).replace(/0+$/, "").replace(/\.$/, "");
|
|
136
|
+
}
|
|
137
|
+
function points(data, locale) {
|
|
138
|
+
const visible = data.slice(0, MAX_POINTS).map((point) => `${point.label}: ${number(point.value)}`);
|
|
139
|
+
const remaining = data.length - visible.length;
|
|
140
|
+
return remaining > 0 ? `${visible.join("; ")}; ${locale.morePoints(remaining)}` : visible.join("; ");
|
|
141
|
+
}
|
|
142
|
+
function progressSegments(element) {
|
|
143
|
+
let position = element.data.min;
|
|
144
|
+
return element.data.items.flatMap((item) => {
|
|
145
|
+
const start = position;
|
|
146
|
+
position += item.value;
|
|
147
|
+
return item.kind === "segment" ? [{
|
|
148
|
+
segment: item,
|
|
149
|
+
start,
|
|
150
|
+
end: position
|
|
151
|
+
}] : [];
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
function progressValue(element) {
|
|
155
|
+
return element.data.items.reduce((total, item) => total + (item.kind === "segment" ? item.value : 0), 0);
|
|
156
|
+
}
|
|
157
|
+
function chartAriaLabel(element, locale = defaultWidgetRendererLocale.aria.chart) {
|
|
158
|
+
if (element.type === "progress") return locale.progressAriaLabel(element.title, number(progressValue(element)), number(element.data.max - element.data.min));
|
|
159
|
+
if (element.type === "gauge") return locale.gaugeAriaLabel(element.title, number(element.data.value), number(element.data.max));
|
|
160
|
+
return locale.chartAriaLabel(element.title, element.type, element.data.length);
|
|
161
|
+
}
|
|
162
|
+
function chartAccessibilitySummary(element, locale = defaultWidgetRendererLocale.aria.chart) {
|
|
163
|
+
if (element.type === "progress") {
|
|
164
|
+
const segments = progressSegments(element);
|
|
165
|
+
const breakdown = segments.length ? ` ${segments.map(({ segment, start, end }) => locale.progressBreakdown(segment.label, number(start), number(end))).join("; ")}.` : locale.noSegmentsSummary;
|
|
166
|
+
return locale.progressSummary(element.title, number(progressValue(element)), number(element.data.min), number(element.data.max), breakdown);
|
|
167
|
+
}
|
|
168
|
+
if (element.type === "gauge") {
|
|
169
|
+
const range = element.data.max - element.data.min;
|
|
170
|
+
const percentage = Math.max(0, Math.min(100, (element.data.value - element.data.min) / range * 100));
|
|
171
|
+
return locale.gaugeSummary(element.title, number(element.data.value), number(element.data.max), number(percentage));
|
|
172
|
+
}
|
|
173
|
+
if (!element.data.length) return locale.noDataSummary(element.title);
|
|
174
|
+
return locale.dataSummary(element.title, points(element.data, locale));
|
|
175
|
+
}
|
|
176
|
+
export { dutchWidgetRendererLocale as a, defaultWidgetRendererLocale as i, chartAriaLabel as n, frenchWidgetRendererLocale as o, WidgetTooltip as r, germanWidgetRendererLocale as s, chartAccessibilitySummary as t };
|
|
177
|
+
|
|
178
|
+
//# sourceMappingURL=lieselthuriot-widget-renderer-chart-accessibility-CXEeXwdH.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lieselthuriot-widget-renderer-chart-accessibility-CXEeXwdH.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 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 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 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 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 progressSegmentLabel: (label, value) => `${label}: ${value}`,\n progressBreakdown: (label, start, end) => `${label}: ${start} tot ${end}`,\n },\n);\n","import { Injectable, signal } from '@angular/core';\n\nexport interface ProgressTooltipState {\n x: number;\n y: number;\n label: string;\n value: number;\n color?: string;\n}\n\n@Injectable()\nexport class WidgetTooltip {\n readonly progress = signal<ProgressTooltipState | null>(null);\n private readonly overlay = signal<HTMLElement | null>(null);\n private progressOwner: object | null = null;\n\n setOverlay(overlay: HTMLElement): void {\n this.overlay.set(overlay);\n }\n\n overlayElement(): HTMLElement | null {\n return this.overlay();\n }\n\n showProgress(owner: object, state: ProgressTooltipState): void {\n this.progressOwner = owner;\n this.progress.set(state);\n }\n\n hideProgress(owner: object): void {\n if (this.progressOwner !== owner) return;\n this.progressOwner = null;\n this.progress.set(null);\n }\n\n clear(): void {\n this.progressOwner = null;\n this.progress.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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"}
|