@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.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
|
-
|
|
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
|
|
2236
|
-
if (
|
|
2237
|
-
|
|
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) =>
|
|
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 {
|
|
4492
|
-
|
|
4493
|
-
|
|
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:
|
|
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 {
|
|
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
|
-
|
|
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 {
|
|
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
|
|
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;
|