@richardmcquiston01/dashboard-widgets-toolkit 0.15.0 → 0.17.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/dist/react.cjs CHANGED
@@ -830,6 +830,14 @@ var DEFAULT_LABELS = {
830
830
  menuMoveLater: "Move later",
831
831
  menuHide: "Hide",
832
832
  menuOptions: "Options\u2026",
833
+ menuDuplicate: "Duplicate",
834
+ duplicate: (title) => `Duplicate ${title}`,
835
+ duplicateBlocked: (title, reason) => `Duplicate ${title} (unavailable: ${reason})`,
836
+ duplicated: (title) => `Duplicated ${title}`,
837
+ optionsDelete: "Delete this widget",
838
+ confirmDelete: "Delete this widget? Its settings are lost.",
839
+ deleted: (title) => `Deleted ${title}`,
840
+ suggestedViewTitle: (title, viewLabel) => `${title} ${viewLabel}`,
833
841
  optionsFor: (title) => `Options for ${title}`,
834
842
  optionsTitleField: "Title",
835
843
  titleInUse: "Another widget already uses this title.",
@@ -1939,13 +1947,15 @@ var NOT_LOCKED = Object.freeze({
1939
1947
  move: false,
1940
1948
  hide: false,
1941
1949
  minimize: false,
1942
- options: false
1950
+ options: false,
1951
+ clone: false
1943
1952
  });
1944
1953
  var FULLY_LOCKED = Object.freeze({
1945
1954
  move: true,
1946
1955
  hide: true,
1947
1956
  minimize: true,
1948
- options: true
1957
+ options: true,
1958
+ clone: true
1949
1959
  });
1950
1960
  function resolveWidgetLock(definition) {
1951
1961
  const setting = definition.locked;
@@ -1955,7 +1965,8 @@ function resolveWidgetLock(definition) {
1955
1965
  move: setting.move === true,
1956
1966
  hide: setting.hide === true,
1957
1967
  minimize: setting.minimize === true,
1958
- options: setting.options === true
1968
+ options: setting.options === true,
1969
+ clone: setting.clone === true
1959
1970
  };
1960
1971
  }
1961
1972
  function sortDefinitions(definitions) {
@@ -2057,6 +2068,9 @@ function viewChoices(item) {
2057
2068
  }))
2058
2069
  ];
2059
2070
  }
2071
+ function isProviderView(view) {
2072
+ return view.convert === void 0;
2073
+ }
2060
2074
  function isKnownView(item, key) {
2061
2075
  return key !== void 0 && (key === BASE_VIEW_KEY || findView(item, key) !== void 0);
2062
2076
  }
@@ -2066,6 +2080,14 @@ function effectiveViewKey(item, chosen) {
2066
2080
  if (isKnownView(item, item.defaultView)) return item.defaultView;
2067
2081
  return BASE_VIEW_KEY;
2068
2082
  }
2083
+ function loadViewKey(item, chosen) {
2084
+ const key = effectiveViewKey(item, chosen);
2085
+ const view = findView(item, key);
2086
+ return view !== void 0 && isProviderView(view) ? key : BASE_VIEW_KEY;
2087
+ }
2088
+ function loadViewKind(item, chosen) {
2089
+ return findView(item, loadViewKey(item, chosen))?.kind ?? item.kind;
2090
+ }
2069
2091
  function cellValue2(cell) {
2070
2092
  const value = cell?.value;
2071
2093
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
@@ -2200,6 +2222,11 @@ function convertView(widgetKey, view, data, options = {}) {
2200
2222
  where: `Widget ${JSON.stringify(widgetKey)} view ${JSON.stringify(view.key)}`,
2201
2223
  ...options.locale === void 0 ? {} : { locale: options.locale }
2202
2224
  };
2225
+ if (conversion === void 0) {
2226
+ return err(
2227
+ `${context.where}: the provider supplies this view, so there is nothing to convert.`
2228
+ );
2229
+ }
2203
2230
  const expected = VIEW_CONVERSIONS[conversion.type]?.from;
2204
2231
  if (expected === void 0) {
2205
2232
  return err(
@@ -2227,14 +2254,29 @@ function convertView(widgetKey, view, data, options = {}) {
2227
2254
  function applyView(widget, chosen, options = {}) {
2228
2255
  const key = effectiveViewKey(widget.definition, chosen);
2229
2256
  const view = findView(widget.definition, key);
2230
- if (view === void 0) return widget;
2257
+ const wanted = loadViewKey(widget.definition, chosen);
2258
+ const loaded = widget.status === "ok" ? widget.view ?? BASE_VIEW_KEY : void 0;
2259
+ const mismatched = loaded !== void 0 && loaded !== wanted;
2260
+ if (view === void 0) {
2261
+ return mismatched ? {
2262
+ definition: widget.definition,
2263
+ status: "loading",
2264
+ ...widget.options === void 0 ? {} : { options: widget.options }
2265
+ } : widget;
2266
+ }
2231
2267
  const definition = {
2232
2268
  ...widget.definition,
2233
2269
  kind: view.kind
2234
2270
  };
2235
- if (widget.status === "loading") return { ...widget, definition };
2236
- if (widget.status === "empty") return { ...widget, definition };
2237
- if (widget.status === "error") return { ...widget, definition };
2271
+ if (widget.status !== "ok") return { ...widget, definition };
2272
+ if (mismatched) {
2273
+ return {
2274
+ definition,
2275
+ status: "loading",
2276
+ ...widget.options === void 0 ? {} : { options: widget.options }
2277
+ };
2278
+ }
2279
+ if (isProviderView(view)) return { ...widget, definition };
2238
2280
  const converted = convertView(
2239
2281
  widget.definition.key,
2240
2282
  view,
@@ -2405,6 +2447,62 @@ function isTitleInUse(definitions, layout, key, title) {
2405
2447
  );
2406
2448
  }
2407
2449
 
2450
+ // src/core/clones.ts
2451
+ function cloneNumber(key) {
2452
+ const match = /#(\d+)$/.exec(key);
2453
+ return match === null ? void 0 : Number(match[1]);
2454
+ }
2455
+ function cleanClones(definitions, clones, options = {}) {
2456
+ if (clones === void 0) return void 0;
2457
+ const byKey = new Map(definitions.map((d) => [d.key, d]));
2458
+ const seen = /* @__PURE__ */ new Set();
2459
+ const kept = [];
2460
+ for (const clone of clones) {
2461
+ if (seen.has(clone.key)) continue;
2462
+ const original = byKey.get(clone.from);
2463
+ if (original === void 0 || original.sourceKey !== void 0) continue;
2464
+ const existing = byKey.get(clone.key);
2465
+ if (existing !== void 0 && existing.sourceKey !== clone.from) continue;
2466
+ if (options.overrideLocks !== true && resolveWidgetLock(original).clone) {
2467
+ continue;
2468
+ }
2469
+ seen.add(clone.key);
2470
+ kept.push(clone);
2471
+ }
2472
+ if (kept.length === 0) return void 0;
2473
+ return kept.length === clones.length && kept.every((c, i) => c === clones[i]) ? clones : kept;
2474
+ }
2475
+ function defaultCloneTitle(original, clone, index) {
2476
+ const base = original.title ?? original.key;
2477
+ const suffix = ` (#${cloneNumber(clone.key) ?? index + 1})`;
2478
+ return `${base.slice(0, MAX_WIDGET_TITLE_LENGTH - suffix.length)}${suffix}`;
2479
+ }
2480
+ function withClones(definitions, layout, options = {}) {
2481
+ const valid = cleanClones(
2482
+ definitions,
2483
+ layout.clones,
2484
+ options
2485
+ );
2486
+ if (valid === void 0) return definitions;
2487
+ const present = new Set(definitions.map((d) => d.key));
2488
+ const toAdd = valid.filter((c) => !present.has(c.key));
2489
+ if (toAdd.length === 0) return definitions;
2490
+ const out = [];
2491
+ for (const definition of definitions) {
2492
+ out.push(definition);
2493
+ toAdd.forEach((clone, index) => {
2494
+ if (clone.from !== definition.key) return;
2495
+ const copy = { ...definition };
2496
+ copy.key = clone.key;
2497
+ copy.sourceKey = definition.key;
2498
+ copy.title = defaultCloneTitle(definition, clone, index);
2499
+ delete copy.locked;
2500
+ out.push(copy);
2501
+ });
2502
+ }
2503
+ return out;
2504
+ }
2505
+
2408
2506
  // src/core/layout.ts
2409
2507
  var MAX_PAGE_TITLE_LENGTH = 30;
2410
2508
  var MAX_PARSED_PAGES = 50;
@@ -2882,6 +2980,114 @@ function moveWidgetToPage(definitions, layout, widgetKey, targetKey, options = {
2882
2980
  }
2883
2981
  return ok(moved);
2884
2982
  }
2983
+
2984
+ // src/core/duplicate.ts
2985
+ function canDuplicate(definitions, key, options = {}) {
2986
+ const definition = definitions.find(
2987
+ (d) => d.key === key
2988
+ );
2989
+ if (definition === void 0) return false;
2990
+ if (options.overrideLocks === true) return true;
2991
+ const sourceKey = definition.sourceKey ?? definition.key;
2992
+ const original = definitions.find(
2993
+ (d) => d.key === sourceKey
2994
+ );
2995
+ return original !== void 0 && !resolveWidgetLock(original).clone;
2996
+ }
2997
+ function nextCloneKey(definitions, layout, fromKey) {
2998
+ const taken = /* @__PURE__ */ new Set([
2999
+ ...definitions.map((d) => d.key),
3000
+ ...(layout.clones ?? []).map((c) => c.key)
3001
+ ]);
3002
+ let highest = 0;
3003
+ for (const clone of layout.clones ?? []) {
3004
+ if (clone.from === fromKey) {
3005
+ highest = Math.max(highest, cloneNumber(clone.key) ?? 0);
3006
+ }
3007
+ }
3008
+ let n = highest + 1;
3009
+ while (taken.has(`${fromKey}#${n}`)) n += 1;
3010
+ return `${fromKey}#${n}`;
3011
+ }
3012
+ function duplicateWidget(definitions, layout, key, options = {}) {
3013
+ const widget = definitions.find((d) => d.key === key);
3014
+ if (widget === void 0) {
3015
+ return err(`Widget ${JSON.stringify(key)} isn't on this dashboard.`);
3016
+ }
3017
+ const originalKey = widget.sourceKey ?? widget.key;
3018
+ if (!canDuplicate(definitions, key, options)) {
3019
+ return err(
3020
+ `Widget ${JSON.stringify(originalKey)} is locked against duplicating.`
3021
+ );
3022
+ }
3023
+ const cloneKey = nextCloneKey(definitions, layout, originalKey);
3024
+ const copiedSettings = (() => {
3025
+ const mine = layout.settings?.[key];
3026
+ if (mine === void 0) return void 0;
3027
+ const { title: _title, ...rest } = mine;
3028
+ return Object.keys(rest).length === 0 ? void 0 : rest;
3029
+ })();
3030
+ const settings = copiedSettings === void 0 ? layout.settings : { ...layout.settings, [cloneKey]: copiedSettings };
3031
+ let next = {
3032
+ ...layout,
3033
+ clones: [...layout.clones ?? [], { key: cloneKey, from: originalKey }],
3034
+ ...settings === void 0 ? {} : { settings }
3035
+ };
3036
+ const all = withClones(definitions, next, options);
3037
+ const assigned = assignPages(all, next);
3038
+ const pageKey = [...assigned].find(
3039
+ ([, widgets]) => widgets.some((w) => w.key === key)
3040
+ )?.[0];
3041
+ if (pageKey !== void 0) {
3042
+ const view = pageLayout(next, pageKey);
3043
+ const onPage = assigned.get(pageKey) ?? [];
3044
+ if (view !== void 0) {
3045
+ const shown = visibleWidgets(onPage, view).filter((d) => !resolveWidgetLock(d).move && d.key !== cloneKey).map((d) => d.key);
3046
+ const at = shown.indexOf(key);
3047
+ const order = at === -1 ? [...shown, cloneKey] : [...shown.slice(0, at + 1), cloneKey, ...shown.slice(at + 1)];
3048
+ next = withPageLayout(next, pageKey, { ...view, order });
3049
+ }
3050
+ }
3051
+ if (hasPages(next) && pageKey !== void 0) {
3052
+ const effective = effectiveDefinitions(all, next, options);
3053
+ const room = pageRoom(effective, next, pageKey, options);
3054
+ if (room !== void 0 && room.rowsUsed > room.maxRows) {
3055
+ const page = next.pages?.find(
3056
+ (p) => p.key === pageKey
3057
+ );
3058
+ const width = effective.find(
3059
+ (d) => d.key === cloneKey
3060
+ )?.width;
3061
+ return err(
3062
+ `Page "${page?.title ?? pageKey}" has no room for a widget${width === void 0 ? "" : ` ${width} wide`} (it holds ${room.maxRows} rows).`
3063
+ );
3064
+ }
3065
+ }
3066
+ return ok({ layout: next, key: cloneKey });
3067
+ }
3068
+ function deleteClone(layout, key) {
3069
+ if (layout.clones?.some((clone) => clone.key === key) !== true) return layout;
3070
+ const without2 = (keys) => keys.includes(key) ? keys.filter((k) => k !== key) : keys;
3071
+ const { clones, settings, ...rest } = layout;
3072
+ const remaining = (clones ?? []).filter((c) => c.key !== key);
3073
+ const { [key]: _gone, ...otherSettings } = settings ?? {};
3074
+ return {
3075
+ ...rest,
3076
+ order: without2(layout.order),
3077
+ hidden: without2(layout.hidden),
3078
+ minimized: without2(layout.minimized),
3079
+ ...layout.pages === void 0 ? {} : {
3080
+ pages: layout.pages.map((page) => ({
3081
+ ...page,
3082
+ order: without2(page.order),
3083
+ hidden: without2(page.hidden),
3084
+ minimized: without2(page.minimized)
3085
+ }))
3086
+ },
3087
+ ...Object.keys(otherSettings).length === 0 ? {} : { settings: otherSettings },
3088
+ ...remaining.length === 0 ? {} : { clones: remaining }
3089
+ };
3090
+ }
2885
3091
  function InlineConfirm({
2886
3092
  message,
2887
3093
  onAnswer
@@ -3132,6 +3338,8 @@ function WidgetOptionsDialog({
3132
3338
  settings,
3133
3339
  baseline,
3134
3340
  isTitleInUse: isTitleInUse2,
3341
+ sourceTitle,
3342
+ onDelete,
3135
3343
  onApply,
3136
3344
  onClose
3137
3345
  }) {
@@ -3144,6 +3352,7 @@ function WidgetOptionsDialog({
3144
3352
  const [confirming, setConfirming] = react.useState(
3145
3353
  void 0
3146
3354
  );
3355
+ const autoTitle = react.useRef(void 0);
3147
3356
  react.useEffect(() => {
3148
3357
  const dialog = dialogRef.current;
3149
3358
  if (dialog === null) return void 0;
@@ -3186,6 +3395,9 @@ function WidgetOptionsDialog({
3186
3395
  } else if (which === "reset") {
3187
3396
  onApply(EMPTY_PATCH);
3188
3397
  onClose();
3398
+ } else if (which === "delete") {
3399
+ onDelete?.();
3400
+ onClose();
3189
3401
  } else if (baseline !== void 0) {
3190
3402
  onApply(patchFrom(definition, draftFrom(baseline.settings)));
3191
3403
  onClose();
@@ -3201,7 +3413,8 @@ function WidgetOptionsDialog({
3201
3413
  apply: labels.confirmApply,
3202
3414
  discard: labels.confirmDiscard,
3203
3415
  reset: labels.confirmResetOptions,
3204
- revert: labels.confirmRevertOptions
3416
+ revert: labels.confirmRevertOptions,
3417
+ delete: labels.confirmDelete
3205
3418
  };
3206
3419
  const widths = [];
3207
3420
  for (let width = range.min; width <= range.max; width += 1)
@@ -3298,7 +3511,32 @@ function WidgetOptionsDialog({
3298
3511
  value: effectiveViewKey(definition, draft.view),
3299
3512
  onChange: (event) => {
3300
3513
  const value = event.target.value;
3301
- setDraft((current) => ({ ...current, view: value }));
3514
+ setDraft((current) => {
3515
+ const next = { ...current, view: value };
3516
+ if (sourceTitle === void 0 || current.title.trim() !== "" && current.title !== autoTitle.current) {
3517
+ return next;
3518
+ }
3519
+ const label = views.find(
3520
+ (view) => view.key === value
3521
+ )?.label;
3522
+ const startView = effectiveViewKey(
3523
+ definition,
3524
+ settings?.view
3525
+ );
3526
+ if (label === void 0 || value === startView) {
3527
+ autoTitle.current = void 0;
3528
+ return { ...next, title: "" };
3529
+ }
3530
+ let suggestion = labels.suggestedViewTitle(
3531
+ sourceTitle,
3532
+ label
3533
+ );
3534
+ for (let n = 2; isTitleInUse2?.(suggestion) === true && n < 50; n += 1) {
3535
+ suggestion = `${labels.suggestedViewTitle(sourceTitle, label)} (#${n})`;
3536
+ }
3537
+ autoTitle.current = suggestion;
3538
+ return { ...next, title: suggestion };
3539
+ });
3302
3540
  },
3303
3541
  children: views.map((view) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: view.key, children: view.key === effectiveViewKey(definition, void 0) ? labels.viewDefault(view.label) : view.label }, view.key))
3304
3542
  }
@@ -3355,6 +3593,15 @@ function WidgetOptionsDialog({
3355
3593
  onClick: () => setConfirming("revert"),
3356
3594
  children: labels.revertToBeforeEditing
3357
3595
  }
3596
+ ),
3597
+ onDelete !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
3598
+ "button",
3599
+ {
3600
+ type: "button",
3601
+ className: slot("button", "dwt-button"),
3602
+ onClick: () => setConfirming("delete"),
3603
+ children: labels.optionsDelete
3604
+ }
3358
3605
  )
3359
3606
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(
3360
3607
  InlineConfirm,
@@ -4359,6 +4606,29 @@ function DashboardInner({
4359
4606
  setPageError(void 0);
4360
4607
  change(withPageLayout(layout, activeKey, next));
4361
4608
  }
4609
+ function duplicateItems(definition, key, title) {
4610
+ const sources = [...authorDefinitions.values()];
4611
+ if (!canDuplicate(sources, key, { overrideLocks })) return [];
4612
+ const trial = duplicateWidget(sources, layout, key, {
4613
+ overrideLocks,
4614
+ ...roomOptions
4615
+ });
4616
+ return [
4617
+ {
4618
+ kind: "action",
4619
+ key: "duplicate",
4620
+ label: labels.menuDuplicate,
4621
+ ariaLabel: trial.ok ? labels.duplicate(title) : labels.duplicateBlocked(title, trial.error),
4622
+ disabled: !trial.ok,
4623
+ onSelect: () => {
4624
+ if (!trial.ok) return;
4625
+ change(trial.value.layout);
4626
+ setOptionsKey(trial.value.key);
4627
+ setAnnouncement(labels.duplicated(title));
4628
+ }
4629
+ }
4630
+ ];
4631
+ }
4362
4632
  function selectPage(pageKey) {
4363
4633
  const index = pages.findIndex((page) => page.key === pageKey);
4364
4634
  if (index === -1) return;
@@ -4488,10 +4758,16 @@ function DashboardInner({
4488
4758
  definitions,
4489
4759
  defaultLayout ?? EMPTY_LAYOUT
4490
4760
  );
4491
- const { settings: _defaultSettings, ...arrangement } = arranged;
4492
- change(
4493
- layout.settings === void 0 ? arrangement : { ...arrangement, settings: layout.settings }
4494
- );
4761
+ const {
4762
+ settings: _defaultSettings,
4763
+ clones: _defaultClones,
4764
+ ...arrangement
4765
+ } = arranged;
4766
+ change({
4767
+ ...arrangement,
4768
+ ...layout.settings === void 0 ? {} : { settings: layout.settings },
4769
+ ...layout.clones === void 0 ? {} : { clones: layout.clones }
4770
+ });
4495
4771
  } else if (yes && which === "revert" && snapshot !== void 0) {
4496
4772
  change(snapshot);
4497
4773
  }
@@ -4797,6 +5073,7 @@ function DashboardInner({
4797
5073
  onSelect: () => setOptionsKey(key)
4798
5074
  }
4799
5075
  ],
5076
+ ...duplicateItems(definition, key, title),
4800
5077
  ...pageTargets,
4801
5078
  ...lock.hide ? [] : [
4802
5079
  {
@@ -4891,6 +5168,21 @@ function DashboardInner({
4891
5168
  definition: authorDefinitions.get(optionsKey),
4892
5169
  settings: layout.settings?.[optionsKey],
4893
5170
  ...snapshot === void 0 ? {} : { baseline: { settings: snapshot.settings?.[optionsKey] } },
5171
+ ...authorDefinitions.get(optionsKey)?.sourceKey === void 0 ? {} : {
5172
+ sourceTitle: effectiveWidgets.find(
5173
+ (w) => w.definition.key === authorDefinitions.get(optionsKey)?.sourceKey
5174
+ )?.definition.title ?? authorDefinitions.get(optionsKey)?.sourceKey ?? "",
5175
+ onDelete: () => {
5176
+ change(deleteClone(layout, optionsKey));
5177
+ setAnnouncement(
5178
+ labels.deleted(
5179
+ effectiveWidgets.find(
5180
+ (w) => w.definition.key === optionsKey
5181
+ )?.definition.title ?? optionsKey
5182
+ )
5183
+ );
5184
+ }
5185
+ },
4894
5186
  isTitleInUse: (title) => isTitleInUse(
4895
5187
  widgets.map((w) => w.definition),
4896
5188
  layout,
@@ -5418,7 +5710,10 @@ function resolvePayload(definition, payload, options = {}) {
5418
5710
  } else {
5419
5711
  const result = validateWidgetData(payload, {
5420
5712
  widgetKey: definition.key,
5421
- expectedKind: definition.kind
5713
+ expectedKind: loadViewKind(
5714
+ definition,
5715
+ options.viewValues?.[definition.key]
5716
+ )
5422
5717
  });
5423
5718
  if (!result.ok) {
5424
5719
  return { definition, status: "error", error: result.error };
@@ -5429,7 +5724,14 @@ function resolvePayload(definition, payload, options = {}) {
5429
5724
  if (emptyText !== null) {
5430
5725
  return { definition, status: "empty", emptyText };
5431
5726
  }
5432
- return { definition, status: "ok", data: checked };
5727
+ return {
5728
+ definition,
5729
+ status: "ok",
5730
+ data: checked,
5731
+ ...hasViews(definition) ? {
5732
+ view: loadViewKey(definition, options.viewValues?.[definition.key])
5733
+ } : {}
5734
+ };
5433
5735
  }
5434
5736
  function failedWidget(definition, error) {
5435
5737
  return { definition, status: "error", error };
@@ -5438,8 +5740,15 @@ function loadingWidgets(definitions) {
5438
5740
  return definitions.map((definition) => ({ definition, status: "loading" }));
5439
5741
  }
5440
5742
  function cacheKeyFor(definition, options, values) {
5441
- const base = options.cacheKey === void 0 ? definition.key : options.cacheKey(definition);
5442
- return values === void 0 ? base : `${base}${optionsCacheSuffix(definition, values, options)}`;
5743
+ const base = options.cacheKey === void 0 ? definition.sourceKey ?? definition.key : options.cacheKey(definition);
5744
+ const view = loadViewKey(
5745
+ definition,
5746
+ options.viewValues?.[definition.key]
5747
+ );
5748
+ const viewPart = view === BASE_VIEW_KEY ? "" : `view=${encodeURIComponent(view)}`;
5749
+ const optionPart = values === void 0 ? "" : optionsCacheSuffix(definition, values, options);
5750
+ if (viewPart === "") return `${base}${optionPart}`;
5751
+ return optionPart === "" ? `${base}?${viewPart}` : `${base}?${viewPart}&${optionPart.slice(1)}`;
5443
5752
  }
5444
5753
  function optionValuesFor(definition, options) {
5445
5754
  return resolveOptionValues(
@@ -5450,6 +5759,9 @@ function optionValuesFor(definition, options) {
5450
5759
  }
5451
5760
  function finalizeWidget(widget, values) {
5452
5761
  if (Object.keys(values).length === 0) return widget;
5762
+ if (widget.status === "ok" && widget.view !== void 0 && widget.view !== BASE_VIEW_KEY) {
5763
+ return { ...widget, options: values };
5764
+ }
5453
5765
  if (widget.status === "ok") {
5454
5766
  const data = applyClientOptions(
5455
5767
  widget.definition,
@@ -5500,7 +5812,8 @@ async function resolveWidgetUnapplied(definition, provider, context, options = {
5500
5812
  if (controller.signal.aborted) throw new LoadInterruption();
5501
5813
  return provider(context, definition, {
5502
5814
  signal: controller.signal,
5503
- options: values
5815
+ options: values,
5816
+ view: loadViewKey(definition, options.viewValues?.[definition.key])
5504
5817
  });
5505
5818
  }),
5506
5819
  interrupted
@@ -5548,9 +5861,18 @@ function createWidgetLoader(options) {
5548
5861
  ([key, values]) => values === void 0 ? [] : [[key, values]]
5549
5862
  )
5550
5863
  );
5864
+ const chosenViews = new Map(
5865
+ Object.entries(baseOptions.viewValues ?? {}).flatMap(
5866
+ ([key, view]) => view === void 0 ? [] : [[key, view]]
5867
+ )
5868
+ );
5551
5869
  const resolveOptions = baseOptions;
5552
5870
  function currentOptions() {
5553
- return { ...resolveOptions, optionValues: Object.fromEntries(chosen) };
5871
+ return {
5872
+ ...resolveOptions,
5873
+ optionValues: Object.fromEntries(chosen),
5874
+ viewValues: Object.fromEntries(chosenViews)
5875
+ };
5554
5876
  }
5555
5877
  const unapplied = /* @__PURE__ */ new Map();
5556
5878
  const visible = widgetsFor(definitions, context.roles);
@@ -5612,7 +5934,11 @@ function createWidgetLoader(options) {
5612
5934
  }
5613
5935
  }
5614
5936
  if (!isCurrent()) return;
5615
- const provider = Object.prototype.hasOwnProperty.call(providers, key) ? providers[key] : void 0;
5937
+ const providerKey = definition.sourceKey ?? key;
5938
+ const provider = Object.prototype.hasOwnProperty.call(
5939
+ providers,
5940
+ providerKey
5941
+ ) ? providers[providerKey] : void 0;
5616
5942
  const result = await resolveWidgetUnapplied(definition, provider, context, {
5617
5943
  ...runOptions,
5618
5944
  signal: controller.signal
@@ -5679,6 +6005,23 @@ function createWidgetLoader(options) {
5679
6005
  }
5680
6006
  start(definition, true);
5681
6007
  }
6008
+ function setView(key, view) {
6009
+ if (disposed) return;
6010
+ const definition = definitionFor(key);
6011
+ if (definition === void 0) return;
6012
+ const before = loadViewKey(definition, chosenViews.get(key));
6013
+ if (view === void 0 || view === "") chosenViews.delete(key);
6014
+ else chosenViews.set(key, view);
6015
+ const after = loadViewKey(definition, chosenViews.get(key));
6016
+ if (before === after || !started.has(key)) return;
6017
+ const current = snapshot.find(
6018
+ (widget) => widget.definition.key === key
6019
+ );
6020
+ if (current !== void 0 && current.status !== "loading") {
6021
+ replace(key, { definition, status: "loading" });
6022
+ }
6023
+ start(definition, true);
6024
+ }
5682
6025
  function dispose() {
5683
6026
  disposed = true;
5684
6027
  abortAll(new Error("The widget loader was disposed."));
@@ -5697,6 +6040,7 @@ function createWidgetLoader(options) {
5697
6040
  loadAll,
5698
6041
  refresh,
5699
6042
  setOptions,
6043
+ setView,
5700
6044
  dispose
5701
6045
  };
5702
6046
  }
@@ -5712,7 +6056,8 @@ function useWidgets(definitions, providers, context, options = {}) {
5712
6056
  signal,
5713
6057
  cache,
5714
6058
  cacheKey,
5715
- optionValues
6059
+ optionValues,
6060
+ viewValues
5716
6061
  } = options;
5717
6062
  const placeholders = react.useMemo(
5718
6063
  () => loadingWidgets(widgetsFor(definitions, context.roles)),
@@ -5724,6 +6069,8 @@ function useWidgets(definitions, providers, context, options = {}) {
5724
6069
  const gridRef = react.useRef(null);
5725
6070
  const optionValuesRef = react.useRef(optionValues);
5726
6071
  optionValuesRef.current = optionValues;
6072
+ const viewValuesRef = react.useRef(viewValues);
6073
+ viewValuesRef.current = viewValues;
5727
6074
  react.useEffect(() => {
5728
6075
  const created = createWidgetLoader({
5729
6076
  definitions,
@@ -5734,7 +6081,8 @@ function useWidgets(definitions, providers, context, options = {}) {
5734
6081
  ...signal === void 0 ? {} : { signal },
5735
6082
  ...cache === void 0 ? {} : { cache },
5736
6083
  ...cacheKey === void 0 ? {} : { cacheKey },
5737
- ...optionValuesRef.current === void 0 ? {} : { optionValues: optionValuesRef.current }
6084
+ ...optionValuesRef.current === void 0 ? {} : { optionValues: optionValuesRef.current },
6085
+ ...viewValuesRef.current === void 0 ? {} : { viewValues: viewValuesRef.current }
5738
6086
  });
5739
6087
  loaderRef.current = created;
5740
6088
  setWidgets(created.getSnapshot());
@@ -5813,6 +6161,16 @@ function useWidgets(definitions, providers, context, options = {}) {
5813
6161
  if (chosen !== void 0) loader.setOptions(key, chosen);
5814
6162
  }
5815
6163
  }, [loader, optionSignature]);
6164
+ const viewSignature = JSON.stringify(viewValues ?? {});
6165
+ react.useEffect(() => {
6166
+ if (loader === null) return;
6167
+ for (const [key, view] of Object.entries(viewValuesRef.current ?? {})) {
6168
+ loader.setView(key, view);
6169
+ }
6170
+ }, [loader, viewSignature]);
6171
+ const setView = react.useCallback((key, view) => {
6172
+ loaderRef.current?.setView(key, view);
6173
+ }, []);
5816
6174
  const setOptions = react.useCallback(
5817
6175
  (key, chosen) => {
5818
6176
  loaderRef.current?.setOptions(key, chosen);
@@ -5822,7 +6180,7 @@ function useWidgets(definitions, providers, context, options = {}) {
5822
6180
  const refresh = react.useCallback((key) => {
5823
6181
  loaderRef.current?.refresh(key);
5824
6182
  }, []);
5825
- return { widgets, refresh, setOptions, gridRef };
6183
+ return { widgets, refresh, setOptions, setView, gridRef };
5826
6184
  }
5827
6185
 
5828
6186
  exports.AlertListWidget = AlertListWidget;