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