@richardmcquiston01/dashboard-widgets-toolkit 0.2.0 → 0.4.1

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.
@@ -37,7 +37,7 @@ interface KpiWidgetData {
37
37
  readonly label: string;
38
38
  /** Short context under the value, e.g. "Incl. shipping and tax". */
39
39
  readonly hint?: string;
40
- /** Whether a rise is good news (default true). Colours the change. */
40
+ /** Whether a rise is good news (default true). Colors the change. */
41
41
  readonly higherIsBetter?: boolean;
42
42
  }
43
43
  /** A value against a maximum, drawn as a meter. */
@@ -155,48 +155,6 @@ declare function isWidgetEmptyState(payload: WidgetPayload): payload is WidgetEm
155
155
  */
156
156
  declare function emptyTextFor(payload: WidgetPayload): string | null;
157
157
 
158
- /** A size hint for grid layouts; renderers may map it to column spans. */
159
- declare const WIDGET_SIZES: readonly ["small", "medium", "large", "full"];
160
- type WidgetSize = (typeof WIDGET_SIZES)[number];
161
- /**
162
- * How a widget fills free space in a grid layout. `height` stretches it to
163
- * the height of its row; `width` widens it to take the columns left over in
164
- * its row; `both` does both. Absent, a widget is only as big as its content
165
- * and `defaultSize`.
166
- */
167
- declare const WIDGET_FILLS: readonly ["height", "width", "both"];
168
- type WidgetFill = (typeof WIDGET_FILLS)[number];
169
- /**
170
- * What a widget is, independent of its data. Mirrors Maker Toolkit's
171
- * `widget` table (`widget_key`, `title`, `description`, `view_type`,
172
- * `roles`, `sort_order`, `active`).
173
- */
174
- interface WidgetDefinition {
175
- /** Stable, unique id. Layouts and providers are keyed by it. */
176
- readonly key: string;
177
- readonly title: string;
178
- readonly description?: string;
179
- readonly kind: WidgetKind;
180
- /** Lower comes first. Default 0; ties keep definition order. */
181
- readonly sortOrder?: number;
182
- /** Roles allowed to see it. Absent or empty means everyone. */
183
- readonly roles?: readonly string[];
184
- /** False hides it from every user. Default true. */
185
- readonly active?: boolean;
186
- readonly defaultSize?: WidgetSize;
187
- /** Fill free space in the row (height, width or both). Default: none. */
188
- readonly fill?: WidgetFill;
189
- }
190
- /**
191
- * Identity helper that keeps the literal `kind` (so typed providers can be
192
- * checked against it) and gives a definition its type without a cast.
193
- */
194
- declare function defineWidget<const D extends WidgetDefinition>(definition: D): D;
195
- /** Definitions sorted by `sortOrder` (stable: ties keep their input order). */
196
- declare function sortDefinitions<D extends WidgetDefinition>(definitions: readonly D[]): D[];
197
- /** Whether a widget is active and visible to someone with these roles. */
198
- declare function isWidgetVisibleTo(definition: WidgetDefinition, roles: readonly string[] | undefined): boolean;
199
-
200
158
  /**
201
159
  * Number formatting via `Intl`, with an injectable locale (undefined uses
202
160
  * the runtime's default), plus KPI period-over-period deltas.
@@ -258,77 +216,6 @@ declare const DEFAULT_COMPARISON_LABEL = "previous period";
258
216
  */
259
217
  declare function kpiDelta(current: number, previous: number | null | undefined, options?: KpiDeltaOptions): KpiDelta | null;
260
218
 
261
- /**
262
- * A viewer's dashboard customisation: order, hidden and minimised widgets.
263
- * Pure functions over a small JSON-serialisable object. Persist it however
264
- * you like (a settings table, localStorage); `parseLayout` tolerates
265
- * anything, so a corrupt saved value never breaks the dashboard.
266
- *
267
- * Generalised from Maker Toolkit's desktop `dashboard-layout.ts`.
268
- */
269
- /** What layout functions need from a widget definition. */
270
- interface LayoutItem {
271
- readonly key: string;
272
- readonly sortOrder?: number;
273
- }
274
- interface DashboardLayout {
275
- /** Widget keys in display order. Keys not listed follow, by sortOrder. */
276
- readonly order: readonly string[];
277
- /** Widget keys removed from the dashboard (restorable). */
278
- readonly hidden: readonly string[];
279
- /** Widget keys collapsed to their header. */
280
- readonly minimized: readonly string[];
281
- }
282
- declare const EMPTY_LAYOUT: DashboardLayout;
283
- /**
284
- * Reads a saved layout: a JSON string or an already-parsed object. Missing,
285
- * corrupt or wrongly typed values give the empty layout (or empty lists for
286
- * the fields that are wrong); non-string and duplicate keys are dropped.
287
- */
288
- declare function parseLayout(raw: unknown): DashboardLayout;
289
- /** The JSON string to persist. */
290
- declare function serializeLayout(layout: DashboardLayout): string;
291
- /**
292
- * The widgets to show, in order: saved keys first (when still present),
293
- * then widgets the layout doesn't know yet, by sortOrder. Hidden widgets
294
- * are left out.
295
- */
296
- declare function visibleWidgets<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout): T[];
297
- /** Hidden widgets, by sortOrder, for a "restore" menu. */
298
- declare function hiddenWidgets<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout): T[];
299
- /**
300
- * Moves `key` to `toIndex` within the current visible order. Returns the
301
- * same layout object when the key isn't visible or the index is out of
302
- * range, so callers can skip a save.
303
- */
304
- declare function moveWidget<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout, key: string, toIndex: number): DashboardLayout;
305
- /** Moves `key` by `offset` places (negative is earlier). Clamped at the ends. */
306
- declare function moveWidgetBy<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout, key: string, offset: number): DashboardLayout;
307
- /** Moves `key` one place earlier. */
308
- declare function moveWidgetUp<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout, key: string): DashboardLayout;
309
- /** Moves `key` one place later. */
310
- declare function moveWidgetDown<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout, key: string): DashboardLayout;
311
- declare function isHidden(layout: DashboardLayout, key: string): boolean;
312
- declare function isMinimized(layout: DashboardLayout, key: string): boolean;
313
- /** Hides `key`. No-op (same object) if already hidden. */
314
- declare function hideWidget(layout: DashboardLayout, key: string): DashboardLayout;
315
- /** Shows `key` again, expanded. No-op (same object) if not hidden. */
316
- declare function showWidget(layout: DashboardLayout, key: string): DashboardLayout;
317
- /** Hides or shows `key` (showing also clears its minimised state). */
318
- declare function toggleHidden(layout: DashboardLayout, key: string): DashboardLayout;
319
- /** Collapses `key` to its header. No-op (same object) if already minimised. */
320
- declare function minimizeWidget(layout: DashboardLayout, key: string): DashboardLayout;
321
- /** Expands `key`. No-op (same object) if not minimised. */
322
- declare function restoreWidget(layout: DashboardLayout, key: string): DashboardLayout;
323
- /** Minimises or restores `key`. */
324
- declare function toggleMinimized(layout: DashboardLayout, key: string): DashboardLayout;
325
- /**
326
- * Drops keys for widgets that no longer exist. Only pass the full list of
327
- * definitions (not a role-filtered one), or a viewer's settings for widgets
328
- * they temporarily can't see are lost.
329
- */
330
- declare function pruneLayout<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout): DashboardLayout;
331
-
332
219
  /**
333
220
  * Runs each widget's data provider and collects the outcome per widget, so
334
221
  * one failing provider never breaks the rest of the dashboard. The package
@@ -341,14 +228,31 @@ interface WidgetContext {
341
228
  /** The viewer's roles. Widgets with `roles` need at least one of them. */
342
229
  readonly roles?: readonly string[];
343
230
  }
344
- /** Loads one widget's payload. May be sync or async; may throw. */
345
- type WidgetProvider<C = WidgetContext> = (context: C, definition: WidgetDefinition) => WidgetPayload | Promise<WidgetPayload>;
231
+ /** What the resolver hands every provider, after the context and definition. */
232
+ interface ProviderOptions {
233
+ /**
234
+ * Aborted when the widget times out, is refreshed, or the dashboard is torn
235
+ * down. Pass it to `fetch` (or any cancellable call) to stop the work; a
236
+ * provider that ignores it is still abandoned, just not stopped.
237
+ */
238
+ readonly signal: AbortSignal;
239
+ }
240
+ /**
241
+ * Loads one widget's payload. May be sync or async; may throw. The third
242
+ * argument is optional to use: a provider that takes only `(context,
243
+ * definition)` keeps working.
244
+ */
245
+ type WidgetProvider<C = WidgetContext> = (context: C, definition: WidgetDefinition, options: ProviderOptions) => WidgetPayload | Promise<WidgetPayload>;
346
246
  /** Providers keyed by widget key. */
347
247
  type WidgetProviders<C = WidgetContext> = Readonly<Record<string, WidgetProvider<C>>>;
348
248
  interface OkWidget {
349
249
  readonly definition: WidgetDefinition;
350
250
  readonly status: 'ok';
351
251
  readonly data: WidgetData;
252
+ /** When the data was produced (epoch ms), set when a provider resolved it. */
253
+ readonly updatedAt?: number;
254
+ /** True while cached data is shown and a fresh load is still running. */
255
+ readonly stale?: boolean;
352
256
  }
353
257
  interface EmptyWidget {
354
258
  readonly definition: WidgetDefinition;
@@ -372,6 +276,20 @@ interface LoadingWidget {
372
276
  /** Anything a dashboard can render: resolved, or still loading. */
373
277
  type DashboardWidget = ResolvedWidget | LoadingWidget;
374
278
  type WidgetStatus = DashboardWidget['status'];
279
+ /** A payload the cache holds, with when it was stored (epoch ms). */
280
+ interface CachedPayload {
281
+ readonly payload: unknown;
282
+ readonly storedAt: number;
283
+ }
284
+ /**
285
+ * Consumer-supplied storage for widget payloads (memory, localStorage, a
286
+ * database). The package never stores anything itself. Payloads read back
287
+ * are validated again, so a corrupt entry cannot break a widget.
288
+ */
289
+ interface WidgetCache {
290
+ get(key: string): CachedPayload | undefined | Promise<CachedPayload | undefined>;
291
+ set(key: string, entry: CachedPayload): void | Promise<void>;
292
+ }
375
293
  interface ResolveOptions {
376
294
  /**
377
295
  * Validate each provider's payload with `validateWidgetData` (and check
@@ -379,6 +297,17 @@ interface ResolveOptions {
379
297
  * trusted, typed providers.
380
298
  */
381
299
  readonly validate?: boolean;
300
+ /**
301
+ * Give each provider this many milliseconds. A timeout aborts the
302
+ * provider's signal and yields an `error` widget naming the key.
303
+ */
304
+ readonly timeoutMs?: number;
305
+ /** Cancels loading; affected widgets resolve as `error` (canceled). */
306
+ readonly signal?: AbortSignal;
307
+ /** Where to store successful payloads (see `WidgetCache`). */
308
+ readonly cache?: WidgetCache;
309
+ /** Cache key for a widget. Default: the widget's `key`. */
310
+ readonly cacheKey?: (definition: WidgetDefinition) => string;
382
311
  }
383
312
  /**
384
313
  * Turns a payload (typically JSON from an API) into a resolved widget:
@@ -390,7 +319,12 @@ declare function resolvePayload(definition: WidgetDefinition, payload: unknown,
390
319
  declare function failedWidget(definition: WidgetDefinition, error: string): ErrorWidget;
391
320
  /** Loading placeholders for definitions, before their data arrives. */
392
321
  declare function loadingWidgets(definitions: readonly WidgetDefinition[]): LoadingWidget[];
393
- /** Runs one widget's provider, capturing a throw or bad payload as an error. */
322
+ /** The cache key for a widget under these options. */
323
+ declare function cacheKeyFor(definition: WidgetDefinition, options: ResolveOptions): string;
324
+ /**
325
+ * Runs one widget's provider, capturing a throw, a timeout, a cancellation or
326
+ * a bad payload as an error widget. Never rejects.
327
+ */
394
328
  declare function resolveWidget<C extends WidgetContext>(definition: WidgetDefinition, provider: WidgetProvider<C> | undefined, context: C, options?: ResolveOptions): Promise<ResolvedWidget>;
395
329
  /**
396
330
  * The widgets a viewer can see: active, allowed for `roles`, in sort order.
@@ -404,6 +338,281 @@ declare function widgetsFor(definitions: readonly WidgetDefinition[], roles: rea
404
338
  */
405
339
  declare function resolveWidgets<C extends WidgetContext>(definitions: readonly WidgetDefinition[], providers: WidgetProviders<C>, context: C, options?: ResolveOptions): Promise<ResolvedWidget[]>;
406
340
 
341
+ /**
342
+ * A small Result type. Functions in this package return a Result rather than
343
+ * throwing, so the caller has to look at the outcome. `throw` is reserved for
344
+ * programmer error.
345
+ */
346
+ interface Ok<T> {
347
+ readonly ok: true;
348
+ readonly value: T;
349
+ }
350
+ interface Err {
351
+ readonly ok: false;
352
+ /** Specific enough to act on: names the field, the widget and the problem. */
353
+ readonly error: string;
354
+ }
355
+ type Result<T> = Ok<T> | Err;
356
+ declare function ok<T>(value: T): Ok<T>;
357
+ declare function err(error: string): Err;
358
+ /** Turns an unknown thrown value into a readable message. */
359
+ declare function describeUnknownError(cause: unknown): string;
360
+
361
+ /**
362
+ * Detail view, core part: the data and query types, sorting, filtering and
363
+ * paging, query (de)serialisation for URLs, and validation. Pure functions;
364
+ * nothing here renders, fetches or reads `location`. See
365
+ * `docs/design/detail-view.md`.
366
+ */
367
+
368
+ /** `client`: the provider returns every row; `server`: it honors the query. */
369
+ declare const DETAIL_MODES: readonly ["client", "server"];
370
+ type DetailMode = (typeof DETAIL_MODES)[number];
371
+ declare const DEFAULT_DETAIL_PAGE_SIZE = 25;
372
+ declare const MAX_DETAIL_PAGE_SIZE = 200;
373
+ /** The `detail` setting on a widget definition. `true` means all defaults. */
374
+ interface WidgetDetailOptions {
375
+ /** Heading of the detail view. Default: the widget's title. */
376
+ readonly title?: string;
377
+ /** Rows per page, 1 to 200. Default 25. */
378
+ readonly pageSize?: number;
379
+ /** Default `client`. */
380
+ readonly mode?: DetailMode;
381
+ }
382
+ /** `WidgetDetailOptions` with every default filled in. */
383
+ interface ResolvedDetailOptions {
384
+ readonly title: string;
385
+ readonly pageSize: number;
386
+ readonly mode: DetailMode;
387
+ }
388
+ interface DetailColumn {
389
+ /** Stable id used by sort, filters and URLs. Unique within the data. */
390
+ readonly key: string;
391
+ readonly label: string;
392
+ /** Right-aligned, and sorted as numbers when cell values are numbers. */
393
+ readonly numeric?: boolean;
394
+ /** Default true. */
395
+ readonly sortable?: boolean;
396
+ /** Default false: gets its own filter input when true. */
397
+ readonly filterable?: boolean;
398
+ }
399
+ interface DetailCell {
400
+ /** What is shown. */
401
+ readonly text: string;
402
+ /** What sorts and filters. Default: `text`. */
403
+ readonly value?: string | number;
404
+ /** Renders the cell as a link (http, https, mailto or relative only). */
405
+ readonly href?: string;
406
+ }
407
+ interface DetailData {
408
+ readonly columns: readonly DetailColumn[];
409
+ /** One array of cells per row, each as long as `columns`. */
410
+ readonly rows: readonly (readonly DetailCell[])[];
411
+ /** Server mode: rows matching the query across all pages. */
412
+ readonly totalRows?: number;
413
+ }
414
+ type SortDirection = 'asc' | 'desc';
415
+ interface DetailSort {
416
+ readonly column: string;
417
+ readonly direction: SortDirection;
418
+ }
419
+ interface DetailQuery {
420
+ /** Free text, matched against every column. */
421
+ readonly search?: string;
422
+ /** Per-column text, keyed by column key. Blank entries are ignored. */
423
+ readonly filters?: Readonly<Record<string, string>>;
424
+ readonly sort?: DetailSort;
425
+ /** 1-based. */
426
+ readonly page: number;
427
+ readonly pageSize: number;
428
+ }
429
+ /** What a detail provider receives beyond the usual arguments. */
430
+ interface DetailProviderOptions extends ProviderOptions {
431
+ readonly query: DetailQuery;
432
+ }
433
+ /**
434
+ * Loads the full data for one widget's detail view. In client mode it may
435
+ * ignore `options.query`; in server mode it returns one page and `totalRows`.
436
+ */
437
+ type DetailProvider<C = WidgetContext> = (context: C, definition: WidgetDefinition, options: DetailProviderOptions) => DetailData | Promise<DetailData>;
438
+ /** Detail providers keyed by widget key. */
439
+ type DetailProviders<C = WidgetContext> = Readonly<Record<string, DetailProvider<C>>>;
440
+ interface DetailPage {
441
+ /** The rows of the requested page (after filtering and sorting). */
442
+ readonly rows: readonly (readonly DetailCell[])[];
443
+ /** Rows matching the query, across all pages. */
444
+ readonly totalRows: number;
445
+ /** The page shown: the requested one, clamped into range. */
446
+ readonly page: number;
447
+ readonly pageCount: number;
448
+ }
449
+ /** Reads `definition.detail` with defaults; undefined when there is none. */
450
+ declare function resolveDetailOptions(definition: Pick<WidgetDefinition, 'title' | 'detail'>): ResolvedDetailOptions | undefined;
451
+ /** The first page, unsorted and unfiltered. */
452
+ declare function defaultDetailQuery(pageSize?: number): DetailQuery;
453
+ /**
454
+ * Filters, sorts and pages `data` in memory (client mode). Matching is
455
+ * case- and accent-insensitive substring matching on each cell's `value`
456
+ * (else `text`). Sorting is stable, locale-aware (`locale`, default the
457
+ * runtime's) and numeric for number values. `page` is clamped into range.
458
+ * Fails, naming the column, when the query names a column that does not
459
+ * exist or cannot be sorted.
460
+ */
461
+ declare function queryRows(data: DetailData, query: DetailQuery, locale?: string): Result<DetailPage>;
462
+ /**
463
+ * The query as a URL query string without the leading `?`: `q` (search),
464
+ * `sort` (`column:asc|desc`), `page` and `f.<column>` filters. `pageSize` is
465
+ * not included: the widget definition owns it. Defaults are left out.
466
+ */
467
+ declare function serializeDetailQuery(query: DetailQuery): string;
468
+ /**
469
+ * Reads a query string (with or without the leading `?`) written by
470
+ * `serializeDetailQuery`. Unknown parameters are ignored, so it can share a
471
+ * URL with the rest of an app. A malformed value fails with a message naming
472
+ * the parameter. When `data` is given, column keys are checked against it.
473
+ */
474
+ declare function parseDetailQuery(queryString: string, pageSize?: number, data?: Pick<DetailData, 'columns'>): Result<DetailQuery>;
475
+ /**
476
+ * Checks data from a detail provider: unique non-empty column keys, labels,
477
+ * every row as long as `columns`, cell `text` strings, `value` a string or
478
+ * finite number, safe `href`s, and `totalRows` a non-negative integer.
479
+ * Reports every problem, naming the column or row and cell.
480
+ */
481
+ declare function validateDetailData(data: unknown, subject?: string): Result<DetailData>;
482
+ /**
483
+ * The detail data for a widget whose card payload is already complete, so a
484
+ * detail view needs no provider: a TABLE without a `footer` (a footer means
485
+ * rows were left out) and a BAR_LIST. Returns undefined for everything else
486
+ * (alert lists, gauges, KPIs, text and charts): those need a detail provider.
487
+ */
488
+ declare function deriveDetailData(data: WidgetData, locale?: Locale): DetailData | undefined;
489
+
490
+ /** A size hint for grid layouts; renderers may map it to column spans. */
491
+ declare const WIDGET_SIZES: readonly ["small", "medium", "large", "full"];
492
+ type WidgetSize = (typeof WIDGET_SIZES)[number];
493
+ /**
494
+ * How a widget fills free space in a grid layout. `height` stretches it to
495
+ * the height of its row; `width` widens it to take the columns left over in
496
+ * its row; `both` does both. Absent, a widget is only as big as its content
497
+ * and `defaultSize`.
498
+ */
499
+ declare const WIDGET_FILLS: readonly ["height", "width", "both"];
500
+ type WidgetFill = (typeof WIDGET_FILLS)[number];
501
+ /**
502
+ * What a widget is, independent of its data. Mirrors Maker Toolkit's
503
+ * `widget` table (`widget_key`, `title`, `description`, `view_type`,
504
+ * `roles`, `sort_order`, `active`).
505
+ */
506
+ interface WidgetDefinition {
507
+ /** Stable, unique id. Layouts and providers are keyed by it. */
508
+ readonly key: string;
509
+ readonly title: string;
510
+ readonly description?: string;
511
+ readonly kind: WidgetKind;
512
+ /** Lower comes first. Default 0; ties keep definition order. */
513
+ readonly sortOrder?: number;
514
+ /** Roles allowed to see it. Absent or empty means everyone. */
515
+ readonly roles?: readonly string[];
516
+ /** False hides it from every user. Default true. */
517
+ readonly active?: boolean;
518
+ readonly defaultSize?: WidgetSize;
519
+ /**
520
+ * Share of the row the widget takes, in twelfths: an integer from 2 to 12
521
+ * (6 is half the row, 12 the whole row). When any widget in a grid sets
522
+ * it, the grid becomes 12 columns wide and widgets without a `width` use
523
+ * the one their `defaultSize` implies (see `widthForSize`). Narrow grids
524
+ * give widgets more room automatically.
525
+ */
526
+ readonly width?: number;
527
+ /**
528
+ * Gives the widget a detail view: a full list with sorting, filtering and
529
+ * paging. `true` uses defaults. The data comes from a detail provider.
530
+ */
531
+ readonly detail?: boolean | WidgetDetailOptions;
532
+ /** Fill free space in the row (height, width or both). Default: none. */
533
+ readonly fill?: WidgetFill;
534
+ }
535
+ /**
536
+ * Identity helper that keeps the literal `kind` (so typed providers can be
537
+ * checked against it) and gives a definition its type without a cast.
538
+ */
539
+ declare function defineWidget<const D extends WidgetDefinition>(definition: D): D;
540
+ /** Definitions sorted by `sortOrder` (stable: ties keep their input order). */
541
+ declare function sortDefinitions<D extends WidgetDefinition>(definitions: readonly D[]): D[];
542
+ /** Whether a widget is active and visible to someone with these roles. */
543
+ declare function isWidgetVisibleTo(definition: WidgetDefinition, roles: readonly string[] | undefined): boolean;
544
+
545
+ /**
546
+ * A viewer's dashboard customization: order, hidden and minimized widgets.
547
+ * Pure functions over a small JSON-serialisable object. Persist it however
548
+ * you like (a settings table, localStorage); `parseLayout` tolerates
549
+ * anything, so a corrupt saved value never breaks the dashboard.
550
+ *
551
+ * Generalised from Maker Toolkit's desktop `dashboard-layout.ts`.
552
+ */
553
+ /** What layout functions need from a widget definition. */
554
+ interface LayoutItem {
555
+ readonly key: string;
556
+ readonly sortOrder?: number;
557
+ }
558
+ interface DashboardLayout {
559
+ /** Widget keys in display order. Keys not listed follow, by sortOrder. */
560
+ readonly order: readonly string[];
561
+ /** Widget keys removed from the dashboard (restorable). */
562
+ readonly hidden: readonly string[];
563
+ /** Widget keys collapsed to their header. */
564
+ readonly minimized: readonly string[];
565
+ }
566
+ declare const EMPTY_LAYOUT: DashboardLayout;
567
+ /**
568
+ * Reads a saved layout: a JSON string or an already-parsed object. Missing,
569
+ * corrupt or wrongly typed values give the empty layout (or empty lists for
570
+ * the fields that are wrong); non-string and duplicate keys are dropped.
571
+ */
572
+ declare function parseLayout(raw: unknown): DashboardLayout;
573
+ /** The JSON string to persist. */
574
+ declare function serializeLayout(layout: DashboardLayout): string;
575
+ /**
576
+ * The widgets to show, in order: saved keys first (when still present),
577
+ * then widgets the layout doesn't know yet, by sortOrder. Hidden widgets
578
+ * are left out.
579
+ */
580
+ declare function visibleWidgets<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout): T[];
581
+ /** Hidden widgets, by sortOrder, for a "restore" menu. */
582
+ declare function hiddenWidgets<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout): T[];
583
+ /**
584
+ * Moves `key` to `toIndex` within the current visible order. Returns the
585
+ * same layout object when the key isn't visible or the index is out of
586
+ * range, so callers can skip a save.
587
+ */
588
+ declare function moveWidget<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout, key: string, toIndex: number): DashboardLayout;
589
+ /** Moves `key` by `offset` places (negative is earlier). Clamped at the ends. */
590
+ declare function moveWidgetBy<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout, key: string, offset: number): DashboardLayout;
591
+ /** Moves `key` one place earlier. */
592
+ declare function moveWidgetUp<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout, key: string): DashboardLayout;
593
+ /** Moves `key` one place later. */
594
+ declare function moveWidgetDown<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout, key: string): DashboardLayout;
595
+ declare function isHidden(layout: DashboardLayout, key: string): boolean;
596
+ declare function isMinimized(layout: DashboardLayout, key: string): boolean;
597
+ /** Hides `key`. No-op (same object) if already hidden. */
598
+ declare function hideWidget(layout: DashboardLayout, key: string): DashboardLayout;
599
+ /** Shows `key` again, expanded. No-op (same object) if not hidden. */
600
+ declare function showWidget(layout: DashboardLayout, key: string): DashboardLayout;
601
+ /** Hides or shows `key` (showing also clears its minimized state). */
602
+ declare function toggleHidden(layout: DashboardLayout, key: string): DashboardLayout;
603
+ /** Collapses `key` to its header. No-op (same object) if already minimized. */
604
+ declare function minimizeWidget(layout: DashboardLayout, key: string): DashboardLayout;
605
+ /** Expands `key`. No-op (same object) if not minimized. */
606
+ declare function restoreWidget(layout: DashboardLayout, key: string): DashboardLayout;
607
+ /** Minimizes or restores `key`. */
608
+ declare function toggleMinimized(layout: DashboardLayout, key: string): DashboardLayout;
609
+ /**
610
+ * Drops keys for widgets that no longer exist. Only pass the full list of
611
+ * definitions (not a role-filtered one), or a viewer's settings for widgets
612
+ * they temporarily can't see are lost.
613
+ */
614
+ declare function pruneLayout<T extends LayoutItem>(definitions: readonly T[], layout: DashboardLayout): DashboardLayout;
615
+
407
616
  /** Axis maths for charts: clean domains and tick values. Pure. */
408
617
  /** The smallest 1, 2 or 5 × 10^n that is at least `value` (1 for 0 or less). */
409
618
  declare function niceStep(value: number): number;
@@ -423,4 +632,4 @@ declare function niceDomain(values: readonly number[], tickCount?: number): Nice
423
632
  */
424
633
  declare function labelStride(count: number, maxLabels?: number): number;
425
634
 
426
- export { emptyWidget as $, type AlertListWidgetData as A, type BarListWidgetData as B, type ValueFormat as C, type DashboardWidget as D, EMPTY_LAYOUT as E, type FormatOptions as F, type GraphWidgetData as G, WIDGET_FILLS as H, WIDGET_KINDS as I, WIDGET_SIZES as J, type KpiWidgetData as K, type Locale as L, MAX_GRAPH_SERIES as M, type NiceDomain as N, type OkWidget as O, type WidgetContext as P, type WidgetDataOf as Q, type ResolveOptions as R, type WidgetEmptyState as S, type TableWidgetData as T, type WidgetProvider as U, VALUE_FORMATS as V, type WidgetSize as W, type WidgetProviders as X, type WidgetStatus as Y, defineWidget as Z, emptyTextFor as _, type WidgetFill as a, failedWidget as a0, formatCount as a1, formatPercentChange as a2, formatValue as a3, hiddenWidgets as a4, hideWidget as a5, isHidden as a6, isMinimized as a7, isWidgetEmptyState as a8, isWidgetKind as a9, isWidgetVisibleTo as aa, kpiDelta as ab, labelStride as ac, loadingWidgets as ad, minimizeWidget as ae, moveWidget as af, moveWidgetBy as ag, moveWidgetDown as ah, moveWidgetUp as ai, niceDomain as aj, niceStep as ak, parseLayout as al, percentChange as am, pruneLayout as an, resolvePayload as ao, resolveWidget as ap, resolveWidgets as aq, restoreWidget as ar, serializeLayout as as, shareOf as at, showWidget as au, sortDefinitions as av, toggleHidden as aw, toggleMinimized as ax, visibleWidgets as ay, widgetsFor as az, type DashboardLayout as b, type GaugeWidgetData as c, type TextWidgetData as d, type WidgetData as e, type WidgetDefinition as f, type WidgetKind as g, type WidgetPayload as h, type AlertListItem as i, type BarListItem as j, DEFAULT_COMPARISON_LABEL as k, DEFAULT_CURRENCY as l, DEFAULT_EMPTY_TEXT as m, type DeltaDirection as n, type DeltaSentiment as o, type EmptyWidget as p, type ErrorWidget as q, type GraphPoint as r, type GraphSeries as s, type KpiDelta as t, type KpiDeltaOptions as u, type LayoutItem as v, type LoadingWidget as w, type ResolvedWidget as x, type TableCell as y, type TableColumn as z };
635
+ export { MAX_GRAPH_SERIES as $, type AlertListWidgetData as A, type BarListWidgetData as B, type CachedPayload as C, type DashboardWidget as D, type DetailPage as E, type DetailProvider as F, type GraphWidgetData as G, type DetailProviders as H, type DetailSort as I, EMPTY_LAYOUT as J, type KpiWidgetData as K, type Locale as L, type EmptyWidget as M, type NiceDomain as N, type Err as O, type ErrorWidget as P, type FormatOptions as Q, type ResolveOptions as R, type GraphPoint as S, type TableWidgetData as T, type GraphSeries as U, type KpiDelta as V, type WidgetSize as W, type KpiDeltaOptions as X, type LayoutItem as Y, type LoadingWidget as Z, MAX_DETAIL_PAGE_SIZE as _, type WidgetFill as a, sortDefinitions as a$, type Ok as a0, type OkWidget as a1, type ProviderOptions as a2, type ResolvedDetailOptions as a3, type ResolvedWidget as a4, type SortDirection as a5, type TableCell as a6, type TableColumn as a7, VALUE_FORMATS as a8, type ValueFormat as a9, isWidgetKind as aA, isWidgetVisibleTo as aB, kpiDelta as aC, labelStride as aD, loadingWidgets as aE, minimizeWidget as aF, moveWidget as aG, moveWidgetBy as aH, moveWidgetDown as aI, moveWidgetUp as aJ, niceDomain as aK, niceStep as aL, ok as aM, parseDetailQuery as aN, parseLayout as aO, percentChange as aP, pruneLayout as aQ, queryRows as aR, resolveDetailOptions as aS, resolvePayload as aT, resolveWidget as aU, resolveWidgets as aV, restoreWidget as aW, serializeDetailQuery as aX, serializeLayout as aY, shareOf as aZ, showWidget as a_, WIDGET_FILLS as aa, WIDGET_KINDS as ab, WIDGET_SIZES as ac, type WidgetCache as ad, type WidgetDataOf as ae, type WidgetDetailOptions as af, type WidgetEmptyState as ag, type WidgetProvider as ah, type WidgetStatus as ai, cacheKeyFor as aj, defaultDetailQuery as ak, defineWidget as al, deriveDetailData as am, describeUnknownError as an, emptyTextFor as ao, emptyWidget as ap, err as aq, failedWidget as ar, formatCount as as, formatPercentChange as at, formatValue as au, hiddenWidgets as av, hideWidget as aw, isHidden as ax, isMinimized as ay, isWidgetEmptyState as az, type WidgetDefinition as b, toggleHidden as b0, toggleMinimized as b1, validateDetailData as b2, visibleWidgets as b3, widgetsFor as b4, type DetailProviderOptions as c, type DetailData as d, type DetailQuery as e, type DashboardLayout as f, type GaugeWidgetData as g, type TextWidgetData as h, type WidgetData as i, type WidgetContext as j, type WidgetProviders as k, type WidgetKind as l, type Result as m, type WidgetPayload as n, type AlertListItem as o, type BarListItem as p, DEFAULT_COMPARISON_LABEL as q, DEFAULT_CURRENCY as r, DEFAULT_DETAIL_PAGE_SIZE as s, DEFAULT_EMPTY_TEXT as t, DETAIL_MODES as u, type DeltaDirection as v, type DeltaSentiment as w, type DetailCell as x, type DetailColumn as y, type DetailMode as z };