@meshmakers/octo-meshboard 3.5.500 → 3.5.510
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
CHANGED
|
@@ -142,6 +142,80 @@ With `plain` the host is responsible for the gutter: a route that loads the view
|
|
|
142
142
|
an unpadded outlet should wrap it in a padded page instead. Exported helpers: `MeshBoardChrome`,
|
|
143
143
|
`MESHBOARD_CHROME_ROUTE_DATA`, `resolveMeshBoardChrome`.
|
|
144
144
|
|
|
145
|
+
### Period set by the host (`timeRange` / `timeFilterControl`, AB#7582)
|
|
146
|
+
|
|
147
|
+
A host page that has its own period control (one "from / to" for the whole page) passes the period
|
|
148
|
+
to the embedded board as **two exact instants** and switches the board's own time-range picker off,
|
|
149
|
+
so page and dashboard always show the same days, whatever time zone the browser is in.
|
|
150
|
+
|
|
151
|
+
| Input | Type | Effect |
|
|
152
|
+
|-------|------|--------|
|
|
153
|
+
| `timeRange` | `{ from: Date \| string; to: Date \| string } \| undefined` | Half-open period `[from, to)`. `Date` objects are taken as they are. Strings must be **RFC 3339 with an offset**: `YYYY-MM-DDTHH:MM:SS`, optional `.s`–`.sss`, then `Z` or `±HH:MM` (e.g. `2026-03-28T05:00:00Z`, `2026-03-28T06:00:00+01:00`). Rejected as invalid: no offset (the browser would read it in its own zone), no seconds, compact offsets (`+0100`), lowercase `t`/`z`, a space instead of `T`, more than 3 fraction digits (these forms parse differently across browsers, e.g. Safari), and dates or times that do not exist (`2026-02-30`, `24:00`, `23:60`, leap second `:60`). The instant is computed from the fields, not by `Date.parse`. |
|
|
154
|
+
| `timeFilterControl` | `'board' \| 'host'` (default `'board'`) | `'host'` hides the board's time-range picker and its reset button. Entity selectors and the toolbar actions stay, in `headerMode` `full` and `compact`. |
|
|
155
|
+
|
|
156
|
+
Rules while `timeRange` is set (and the board's time filter is enabled):
|
|
157
|
+
|
|
158
|
+
- `$timeRangeFrom` / `$timeRangeTo` and the stream-data window of every stream-data widget are
|
|
159
|
+
exactly the two instants — no snapping to 00:00, independent of `pickerConfig.showTime` and of
|
|
160
|
+
the board `timeZoneMode`.
|
|
161
|
+
- **Day buckets and labels still follow the board's `timeZoneMode`:** the date labels of the
|
|
162
|
+
widgets and the calendar rollup a line chart picks (`resolveStreamDataTimeZone()`) use the board's
|
|
163
|
+
zone, not the host's. A board in `'local'` mode opened in a New York browser cuts daily buckets
|
|
164
|
+
at New York midnight even though the window is exactly the host's instants. An embedded board
|
|
165
|
+
should therefore set `timeZoneMode` to the host's business zone (e.g. `Europe/Vienna`). Calendar
|
|
166
|
+
rollups are cut at 00:00 of that zone; they cannot represent a day that starts at 06:00 (gas
|
|
167
|
+
day) — a widget that must show gas days needs a query that does not use a daily calendar rollup.
|
|
168
|
+
- **The host wins:** `tf_*` query parameters and the stored/default selection are ignored, the view
|
|
169
|
+
writes no `tf_*` parameters, the stored selection is not touched and the board shows no unsaved
|
|
170
|
+
changes. The picker is hidden and its events are ignored, whatever the `timeFilterControl`
|
|
171
|
+
value. `tf_*` parameters that were in the URL when the view opened stay in the URL but are never
|
|
172
|
+
applied again once the host has owned the period (also not after `timeRange` goes back to
|
|
173
|
+
`undefined`).
|
|
174
|
+
- A new period reloads the widgets once (an equal period — same instants — reloads nothing); the
|
|
175
|
+
board definition is not reloaded. Setting `timeRange` back to `undefined` returns the board to
|
|
176
|
+
its own selection (and, with `'board'`, shows the picker again).
|
|
177
|
+
- An invalid period (unparseable value, or `from >= to`) sends **no stream-data query that is
|
|
178
|
+
bound to the time filter**: the widgets show their normal empty state (`resolveStreamDataScope`
|
|
179
|
+
returns `{ kind: 'empty', reason: 'invalidTimeRange' }`; the process widget's element bindings
|
|
180
|
+
get no value, a widget group over a stream-data query shows no items), `$timeRangeFrom` /
|
|
181
|
+
`$timeRangeTo` are removed, and the view logs one console error naming the input. A widget with
|
|
182
|
+
**`ignoreTimeFilter: true`** does not depend on the period and keeps querying (with its selector
|
|
183
|
+
scope). Custom widgets pass their `ignoreTimeFilter` as the third argument of
|
|
184
|
+
`resolveStreamDataScope`, or ask `isStreamDataBlockedByHostTimeRange(ignoreTimeFilter)`.
|
|
185
|
+
**Limitation:** widgets backed by a *runtime* query or aggregation whose filter uses
|
|
186
|
+
`$timeRangeFrom` / `$timeRangeTo` are not gated; they run with the placeholder unresolved. An
|
|
187
|
+
invalid range is a host bug — fix it there.
|
|
188
|
+
- A board whose time filter is disabled ignores `timeRange` and logs nothing.
|
|
189
|
+
- With `timeRange` undefined and `timeFilterControl='board'` the view behaves exactly as before.
|
|
190
|
+
- With `timeFilterControl='host'` and `timeRange` undefined (e.g. while the host page is still
|
|
191
|
+
computing its period) the board keeps working on its own selection, with the picker hidden; it
|
|
192
|
+
writes no `tf_*` parameters, because the URL belongs to the host.
|
|
193
|
+
|
|
194
|
+
Day rules belong to the host: the library has no "day start" offset. A host whose day starts at
|
|
195
|
+
06:00 (e.g. a gas day) computes the instants itself, in its business time zone:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
import { MeshBoardHostTimeRange } from '@meshmakers/octo-meshboard';
|
|
199
|
+
|
|
200
|
+
/** Gas day `day` (yyyy-mm-dd) = 06:00 Vienna to 06:00 Vienna the next day (23/24/25 h on DST days). */
|
|
201
|
+
function gasDay(day: string): MeshBoardHostTimeRange {
|
|
202
|
+
return { from: viennaInstant(day, 6), to: viennaInstant(addDays(day, 1), 6) };
|
|
203
|
+
}
|
|
204
|
+
// viennaInstant(day, hour) returns the UTC instant of that Vienna wall-clock time, e.g.
|
|
205
|
+
// viennaInstant('2026-03-28', 6) = '2026-03-28T05:00:00Z', viennaInstant('2026-03-29', 6) = '2026-03-29T04:00:00Z'.
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
```html
|
|
209
|
+
<mm-meshboard-view headerMode="compact" timeFilterControl="host" [timeRange]="period()"></mm-meshboard-view>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The period is held transiently by `MeshBoardStateService` (`hostTimeRange`, `setHostTimeRange(state,
|
|
213
|
+
owner)`, `clearHostTimeRange(owner)`) and never persisted. The service is root-provided, so the view
|
|
214
|
+
registers itself as the owner: it clears the period when it is destroyed or drops its `timeRange`,
|
|
215
|
+
but never a period another view set in the meantime, and a view destroyed while its board is still
|
|
216
|
+
loading publishes nothing. Exported helpers: `MeshBoardHostTimeRange`, `MeshBoardTimeFilterControl`,
|
|
217
|
+
`parseHostTimeRange`, `HostTimeRangeState`.
|
|
218
|
+
|
|
145
219
|
### URL Sync (`meshBoardSyncUrl`)
|
|
146
220
|
|
|
147
221
|
After the initial load and after every post-init board switch (e.g. via the
|
|
@@ -316,6 +390,95 @@ is logged once and ignored. Tables offer string cells of text columns (persisten
|
|
|
316
390
|
only when their value type is `ENUM`); pie / donut charts offer every category — the resolved text
|
|
317
391
|
is the slice name in data labels, legend and tooltips, the status colour still follows the raw
|
|
318
392
|
value. A resolver that reads a signal re-labels the charts when it changes.
|
|
393
|
+
**Numeric enum cells (AB#7655):** a cell that holds a number in a persistent-query column whose
|
|
394
|
+
value type is an enum (`valueType` matches `ENUM`) is offered too, with `value` = the number as
|
|
395
|
+
string (`2` → `'2'`) and `valueType`; numbers of other columns are not. A column's `valueLabels`
|
|
396
|
+
(below) wins over the resolver — such a column never calls it.
|
|
397
|
+
|
|
398
|
+
**Column titles per language and value labels (AB#7655).** Board authors configure both in the
|
|
399
|
+
board JSON/YAML (the table config dialog does not edit them):
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
interface TableColumn {
|
|
403
|
+
field: string;
|
|
404
|
+
title: string; // fallback header
|
|
405
|
+
titles?: Record<string, string>; // header per culture code
|
|
406
|
+
valueLabels?: Record<string, TableColumnValueLabel>; // raw value (as string) -> label
|
|
407
|
+
width?: number; dataType?: string; format?: string; statusMapping?: ...;
|
|
408
|
+
}
|
|
409
|
+
interface TableColumnValueLabel {
|
|
410
|
+
label: string | Record<string, string>; // one text, or a text per culture code
|
|
411
|
+
color?: string; // optional CSS text colour
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
- **Host language.** The library does not know the host's UI language and never derives it from
|
|
416
|
+
`LOCALE_ID` (static in many apps). A host that wants per-language texts hands it over once — a
|
|
417
|
+
culture code or a `Signal<string | null>`; the widgets follow a signal without reloading the board
|
|
418
|
+
or refetching data:
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
providers: [
|
|
422
|
+
provideMeshBoard({ language: languageService.currentCulture }), // Signal<string>, e.g. 'de-AT'
|
|
423
|
+
// or, when the widgets are registered lazily (no provideMeshBoard()):
|
|
424
|
+
provideMeshBoardLanguage(languageService.currentCulture),
|
|
425
|
+
// or directly: { provide: MESHBOARD_LANGUAGE, useValue: 'en-GB' }
|
|
426
|
+
]
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
No language = every column shows `title` and per-language labels show the raw value, exactly as
|
|
430
|
+
before. `pickLocalizedText(texts, language)` is exported for hosts that need the same lookup.
|
|
431
|
+
- **`titles`** — lookup order: the exact culture or language (`de-AT`, `de`; case-insensitive);
|
|
432
|
+
for a culture (`de-AT`) without an exact key, a key equal to its language part (`de`); for a bare
|
|
433
|
+
language (`de`, e.g. from a simple language switcher) without an exact key, the **first authored
|
|
434
|
+
key of that language** (`de-AT` in `{ "en-GB": …, "de-AT": …, "de-DE": … }`, in the order the keys
|
|
435
|
+
are written); then `title`. A culture never picks a sibling culture (`de-DE` does not take `de-AT`).
|
|
436
|
+
Columns derived from a persistent query (no explicit `columns`) keep their generated titles.
|
|
437
|
+
- **`valueLabels`** — keys are the raw value as string; a number `2` and a string `"2"` both match
|
|
438
|
+
`"2"`. The cell shows the label as **plain text** like every other cell (no badge); `color`, when
|
|
439
|
+
set, is the text colour — use a theme token such as `var(--kendo-color-error)`; without it the
|
|
440
|
+
theme text colour applies. A per-language `label` follows the host language like `titles`. A value
|
|
441
|
+
without a label (unmapped, e.g. `4`, or a per-language label without a match) keeps the column's
|
|
442
|
+
rendering: `numeric` with `format` like the `number` pipe, `iso8601` with `format` like the `date`
|
|
443
|
+
pipe, plain text through the board's date-time formatter; `null` / missing is an empty cell. When
|
|
444
|
+
no label applies for the current language at all (e.g. only `de-AT` labels and the host language is
|
|
445
|
+
`en-GB`), the column renders exactly as without `valueLabels`, including `MESHBOARD_LABEL_RESOLVER`.
|
|
446
|
+
Otherwise `valueLabels` win over `MESHBOARD_LABEL_RESOLVER`. **`valueLabels` together with
|
|
447
|
+
`statusMapping` (status icons) is not supported:** the labels are shown, the status icons are not,
|
|
448
|
+
and the widget logs a console warning.
|
|
449
|
+
- **Configuration errors** — a culture key that is not a culture code (`german`, `de_AT`), a
|
|
450
|
+
malformed `titles` entry (no object, non-string or empty text) or a malformed `valueLabels` entry
|
|
451
|
+
(no object, no usable `label`, non-string `color`) is ignored; the widget logs **one** console
|
|
452
|
+
warning per widget, column and set of problems naming them (a different problem after a config
|
|
453
|
+
edit is reported again), and the table still renders.
|
|
454
|
+
- **Refinery Studio** — the table config dialog keeps hand-authored columns: saving a
|
|
455
|
+
persistent-query table without changing the query keeps its explicit `columns` that the query still
|
|
456
|
+
returns (unchanged, in their order), drops those whose field the query no longer returns and adds the
|
|
457
|
+
query's other columns with a generated title, like derived columns (when the query's columns cannot
|
|
458
|
+
be loaded, the explicit columns stay unchanged; a new query starts without explicit columns); for
|
|
459
|
+
runtime-entity tables, columns that stay selected on the same CK type keep all their properties
|
|
460
|
+
(`titles`, `valueLabels`, `width`, `format`, `statusMapping`, ...), newly selected ones get a
|
|
461
|
+
generated title. A new `config` on the same widget instance re-renders headers and labels.
|
|
462
|
+
|
|
463
|
+
Example — the daily quality 1/2/3 shown as L1/L2/L3 with German and English headers:
|
|
464
|
+
|
|
465
|
+
```json
|
|
466
|
+
{
|
|
467
|
+
"columns": [
|
|
468
|
+
{ "field": "day", "title": "Day", "titles": { "de-AT": "Tag", "en-GB": "Day" } },
|
|
469
|
+
{
|
|
470
|
+
"field": "dataquality_max",
|
|
471
|
+
"title": "Dataquality",
|
|
472
|
+
"titles": { "de-AT": "Datenqualität", "en-GB": "Data quality" },
|
|
473
|
+
"valueLabels": {
|
|
474
|
+
"1": { "label": "L1", "color": "var(--kendo-color-success)" },
|
|
475
|
+
"2": { "label": "L2", "color": "var(--kendo-color-warning)" },
|
|
476
|
+
"3": { "label": "L3", "color": "var(--kendo-color-error)" }
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
]
|
|
480
|
+
}
|
|
481
|
+
```
|
|
319
482
|
|
|
320
483
|
### Pie Chart Widget
|
|
321
484
|
Displays data as pie or donut chart.
|