@lieselthuriot/widget-renderer 0.1.0 → 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/README.md +299 -244
- package/fesm2022/{lieselthuriot-widget-renderer-chart-accessibility-CXEeXwdH.mjs → lieselthuriot-widget-renderer-chart-accessibility-BzH2alxy.mjs} +23 -16
- package/fesm2022/lieselthuriot-widget-renderer-chart-accessibility-BzH2alxy.mjs.map +1 -0
- package/fesm2022/lieselthuriot-widget-renderer-chart-element-renderer-wVHdIYuJ.mjs +993 -0
- package/fesm2022/lieselthuriot-widget-renderer-chart-element-renderer-wVHdIYuJ.mjs.map +1 -0
- package/fesm2022/lieselthuriot-widget-renderer.mjs +299 -37
- package/fesm2022/lieselthuriot-widget-renderer.mjs.map +1 -1
- package/package.json +10 -8
- package/types/lieselthuriot-widget-renderer.d.ts +42 -8
- package/types/lieselthuriot-widget-renderer.d.ts.map +1 -1
- package/fesm2022/lieselthuriot-widget-renderer-chart-accessibility-CXEeXwdH.mjs.map +0 -1
- package/fesm2022/lieselthuriot-widget-renderer-chart-element-renderer-BO3V-jYo.mjs +0 -722
- package/fesm2022/lieselthuriot-widget-renderer-chart-element-renderer-BO3V-jYo.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,244 +1,299 @@
|
|
|
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
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
- `
|
|
184
|
-
- `
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
[
|
|
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)
|