@richardmcquiston01/dashboard-widgets-toolkit 0.14.0 → 0.16.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 +2232 -1587
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +49 -3
- package/dist/core.d.ts +49 -3
- package/dist/core.js +2210 -1588
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +2232 -1587
- 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 +2210 -1588
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +600 -59
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +25 -2
- package/dist/react.d.ts +25 -2
- package/dist/react.js +600 -59
- package/dist/react.js.map +1 -1
- package/dist/{storage-_Eb8Y98-.d.cts → storage-oivwB7al.d.cts} +247 -56
- package/dist/{storage-_Eb8Y98-.d.ts → storage-oivwB7al.d.ts} +247 -56
- 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.",
|
|
@@ -837,6 +845,8 @@ var DEFAULT_LABELS = {
|
|
|
837
845
|
widthDefault: "Default",
|
|
838
846
|
widthOption: (width, share) => `${width} of 12 (${share})`,
|
|
839
847
|
optionsFlexible: "Grow to fill empty space in the row",
|
|
848
|
+
optionsViewField: "View",
|
|
849
|
+
viewDefault: (viewLabel) => `${viewLabel} (default)`,
|
|
840
850
|
optionsDeclared: "Data",
|
|
841
851
|
apply: "Apply",
|
|
842
852
|
cancel: "Cancel",
|
|
@@ -1937,13 +1947,15 @@ var NOT_LOCKED = Object.freeze({
|
|
|
1937
1947
|
move: false,
|
|
1938
1948
|
hide: false,
|
|
1939
1949
|
minimize: false,
|
|
1940
|
-
options: false
|
|
1950
|
+
options: false,
|
|
1951
|
+
clone: false
|
|
1941
1952
|
});
|
|
1942
1953
|
var FULLY_LOCKED = Object.freeze({
|
|
1943
1954
|
move: true,
|
|
1944
1955
|
hide: true,
|
|
1945
1956
|
minimize: true,
|
|
1946
|
-
options: true
|
|
1957
|
+
options: true,
|
|
1958
|
+
clone: true
|
|
1947
1959
|
});
|
|
1948
1960
|
function resolveWidgetLock(definition) {
|
|
1949
1961
|
const setting = definition.locked;
|
|
@@ -1953,7 +1965,8 @@ function resolveWidgetLock(definition) {
|
|
|
1953
1965
|
move: setting.move === true,
|
|
1954
1966
|
hide: setting.hide === true,
|
|
1955
1967
|
minimize: setting.minimize === true,
|
|
1956
|
-
options: setting.options === true
|
|
1968
|
+
options: setting.options === true,
|
|
1969
|
+
clone: setting.clone === true
|
|
1957
1970
|
};
|
|
1958
1971
|
}
|
|
1959
1972
|
function sortDefinitions(definitions) {
|
|
@@ -1974,6 +1987,282 @@ function isWidgetVisibleTo(definition, roles) {
|
|
|
1974
1987
|
return definition.roles.some((role) => roles.includes(role));
|
|
1975
1988
|
}
|
|
1976
1989
|
|
|
1990
|
+
// src/core/payload.ts
|
|
1991
|
+
var WIDGET_KINDS = [
|
|
1992
|
+
"TEXT",
|
|
1993
|
+
"KPI",
|
|
1994
|
+
"GAUGE",
|
|
1995
|
+
"TABLE",
|
|
1996
|
+
"BAR_LIST",
|
|
1997
|
+
"ALERT_LIST",
|
|
1998
|
+
"GRAPH"
|
|
1999
|
+
];
|
|
2000
|
+
function isWidgetKind(value) {
|
|
2001
|
+
return typeof value === "string" && WIDGET_KINDS.includes(value);
|
|
2002
|
+
}
|
|
2003
|
+
var VALUE_FORMATS = ["number", "currency", "percent"];
|
|
2004
|
+
var MAX_GRAPH_SERIES = 8;
|
|
2005
|
+
var DEFAULT_EMPTY_TEXT = "Nothing to show yet.";
|
|
2006
|
+
function isWidgetEmptyState(payload) {
|
|
2007
|
+
return "empty" in payload && payload.empty === true;
|
|
2008
|
+
}
|
|
2009
|
+
function emptyTextFor(payload) {
|
|
2010
|
+
if (isWidgetEmptyState(payload)) {
|
|
2011
|
+
return payload.text;
|
|
2012
|
+
}
|
|
2013
|
+
switch (payload.kind) {
|
|
2014
|
+
case "TABLE":
|
|
2015
|
+
return payload.rows.length === 0 ? DEFAULT_EMPTY_TEXT : null;
|
|
2016
|
+
case "BAR_LIST":
|
|
2017
|
+
return payload.items.length === 0 ? DEFAULT_EMPTY_TEXT : null;
|
|
2018
|
+
case "ALERT_LIST":
|
|
2019
|
+
return payload.items.length === 0 ? payload.emptyText : null;
|
|
2020
|
+
case "GRAPH":
|
|
2021
|
+
return payload.series.every((series) => series.points.length === 0) ? DEFAULT_EMPTY_TEXT : null;
|
|
2022
|
+
case "TEXT":
|
|
2023
|
+
case "KPI":
|
|
2024
|
+
case "GAUGE":
|
|
2025
|
+
return null;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
// src/core/views.ts
|
|
2030
|
+
var BASE_VIEW_KEY = "base";
|
|
2031
|
+
var VIEW_CONVERSIONS = {
|
|
2032
|
+
tableToBarList: { from: "TABLE", to: "BAR_LIST" },
|
|
2033
|
+
tableToGraph: { from: "TABLE", to: "GRAPH" },
|
|
2034
|
+
barListToTable: { from: "BAR_LIST", to: "TABLE" },
|
|
2035
|
+
graphToTable: { from: "GRAPH", to: "TABLE" },
|
|
2036
|
+
barListToGraph: { from: "BAR_LIST", to: "GRAPH" }
|
|
2037
|
+
};
|
|
2038
|
+
var KIND_LABELS = {
|
|
2039
|
+
TEXT: "Text",
|
|
2040
|
+
KPI: "Number",
|
|
2041
|
+
GAUGE: "Gauge",
|
|
2042
|
+
TABLE: "Table",
|
|
2043
|
+
BAR_LIST: "Bars",
|
|
2044
|
+
ALERT_LIST: "List",
|
|
2045
|
+
GRAPH: "Chart"
|
|
2046
|
+
};
|
|
2047
|
+
function kindLabel(kind) {
|
|
2048
|
+
return KIND_LABELS[kind];
|
|
2049
|
+
}
|
|
2050
|
+
function hasViews(item) {
|
|
2051
|
+
return item.views !== void 0 && item.views.length > 0;
|
|
2052
|
+
}
|
|
2053
|
+
function findView(item, key) {
|
|
2054
|
+
return item.views?.find((view) => view.key === key);
|
|
2055
|
+
}
|
|
2056
|
+
function viewChoices(item) {
|
|
2057
|
+
if (!hasViews(item)) return [];
|
|
2058
|
+
return [
|
|
2059
|
+
{
|
|
2060
|
+
key: BASE_VIEW_KEY,
|
|
2061
|
+
label: item.baseViewLabel ?? kindLabel(item.kind),
|
|
2062
|
+
kind: item.kind
|
|
2063
|
+
},
|
|
2064
|
+
...(item.views ?? []).map(({ key, label, kind }) => ({
|
|
2065
|
+
key,
|
|
2066
|
+
label,
|
|
2067
|
+
kind
|
|
2068
|
+
}))
|
|
2069
|
+
];
|
|
2070
|
+
}
|
|
2071
|
+
function isKnownView(item, key) {
|
|
2072
|
+
return key !== void 0 && (key === BASE_VIEW_KEY || findView(item, key) !== void 0);
|
|
2073
|
+
}
|
|
2074
|
+
function effectiveViewKey(item, chosen) {
|
|
2075
|
+
if (!hasViews(item)) return BASE_VIEW_KEY;
|
|
2076
|
+
if (isKnownView(item, chosen)) return chosen;
|
|
2077
|
+
if (isKnownView(item, item.defaultView)) return item.defaultView;
|
|
2078
|
+
return BASE_VIEW_KEY;
|
|
2079
|
+
}
|
|
2080
|
+
function cellValue2(cell) {
|
|
2081
|
+
const value = cell?.value;
|
|
2082
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
2083
|
+
}
|
|
2084
|
+
function columnName(table, index) {
|
|
2085
|
+
return table.columns[index]?.label ?? `#${index + 1}`;
|
|
2086
|
+
}
|
|
2087
|
+
function checkColumn(table, index, field, context) {
|
|
2088
|
+
if (!Number.isInteger(index) || index < 0 || index >= table.columns.length) {
|
|
2089
|
+
return `${context.where}: ${field} column ${index} does not exist (the table has ${table.columns.length} columns, numbered from 0).`;
|
|
2090
|
+
}
|
|
2091
|
+
return void 0;
|
|
2092
|
+
}
|
|
2093
|
+
function numericColumn(table, index, context) {
|
|
2094
|
+
const numbers = [];
|
|
2095
|
+
for (const [rowIndex, row] of table.rows.entries()) {
|
|
2096
|
+
const value = cellValue2(row[index]);
|
|
2097
|
+
if (value === void 0) {
|
|
2098
|
+
return err(
|
|
2099
|
+
`${context.where}: row ${rowIndex + 1}, column ${index + 1} ("${columnName(table, index)}") has no numeric value. Add value to the cells.`
|
|
2100
|
+
);
|
|
2101
|
+
}
|
|
2102
|
+
numbers.push(value);
|
|
2103
|
+
}
|
|
2104
|
+
return ok(numbers);
|
|
2105
|
+
}
|
|
2106
|
+
function tableToBarList(table, conversion, context) {
|
|
2107
|
+
const bad = checkColumn(table, conversion.label, "label", context) ?? checkColumn(table, conversion.value, "value", context);
|
|
2108
|
+
if (bad !== void 0) return err(bad);
|
|
2109
|
+
const numbers = numericColumn(table, conversion.value, context);
|
|
2110
|
+
if (!numbers.ok) return numbers;
|
|
2111
|
+
const items = table.rows.map((row, index) => ({
|
|
2112
|
+
label: row[conversion.label]?.text ?? "",
|
|
2113
|
+
value: numbers.value[index],
|
|
2114
|
+
display: row[conversion.value]?.text ?? String(numbers.value[index])
|
|
2115
|
+
}));
|
|
2116
|
+
return ok({ kind: "BAR_LIST", items });
|
|
2117
|
+
}
|
|
2118
|
+
function tableToGraph(table, conversion, context) {
|
|
2119
|
+
if (conversion.values.length === 0 || conversion.values.length > MAX_GRAPH_SERIES) {
|
|
2120
|
+
return err(
|
|
2121
|
+
`${context.where}: a chart needs 1 to ${MAX_GRAPH_SERIES} series columns, got ${conversion.values.length}.`
|
|
2122
|
+
);
|
|
2123
|
+
}
|
|
2124
|
+
let bad = checkColumn(
|
|
2125
|
+
table,
|
|
2126
|
+
conversion.label,
|
|
2127
|
+
"label",
|
|
2128
|
+
context
|
|
2129
|
+
);
|
|
2130
|
+
for (const index of conversion.values) {
|
|
2131
|
+
bad ??= checkColumn(table, index, "series", context);
|
|
2132
|
+
}
|
|
2133
|
+
if (bad !== void 0) return err(bad);
|
|
2134
|
+
const series = [];
|
|
2135
|
+
for (const index of conversion.values) {
|
|
2136
|
+
const numbers = numericColumn(table, index, context);
|
|
2137
|
+
if (!numbers.ok) return numbers;
|
|
2138
|
+
series.push({
|
|
2139
|
+
name: columnName(table, index),
|
|
2140
|
+
points: table.rows.map((row, rowIndex) => ({
|
|
2141
|
+
label: row[conversion.label]?.text ?? "",
|
|
2142
|
+
value: numbers.value[rowIndex]
|
|
2143
|
+
}))
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
2146
|
+
return ok({
|
|
2147
|
+
kind: "GRAPH",
|
|
2148
|
+
series,
|
|
2149
|
+
chartType: conversion.chartType,
|
|
2150
|
+
valueFormat: conversion.valueFormat ?? "number",
|
|
2151
|
+
...conversion.currency === void 0 ? {} : { currency: conversion.currency },
|
|
2152
|
+
xLabel: columnName(table, conversion.label)
|
|
2153
|
+
});
|
|
2154
|
+
}
|
|
2155
|
+
function barListToTable(list, conversion) {
|
|
2156
|
+
return ok({
|
|
2157
|
+
kind: "TABLE",
|
|
2158
|
+
columns: [
|
|
2159
|
+
{ label: conversion.labelHeading ?? "Name" },
|
|
2160
|
+
{ label: conversion.valueHeading ?? "Value", numeric: true }
|
|
2161
|
+
],
|
|
2162
|
+
rows: list.items.map((item) => [
|
|
2163
|
+
{ text: item.label },
|
|
2164
|
+
{ text: item.display ?? String(item.value), value: item.value }
|
|
2165
|
+
])
|
|
2166
|
+
});
|
|
2167
|
+
}
|
|
2168
|
+
function graphToTable(graph, context) {
|
|
2169
|
+
const first = graph.series[0];
|
|
2170
|
+
if (first === void 0)
|
|
2171
|
+
return err(`${context.where}: the chart has no series.`);
|
|
2172
|
+
const format = {
|
|
2173
|
+
...graph.currency === void 0 ? {} : { currency: graph.currency },
|
|
2174
|
+
...context.locale === void 0 ? {} : { locale: context.locale }
|
|
2175
|
+
};
|
|
2176
|
+
return ok({
|
|
2177
|
+
kind: "TABLE",
|
|
2178
|
+
columns: [
|
|
2179
|
+
{ label: graph.xLabel ?? "Label" },
|
|
2180
|
+
...graph.series.map((series) => ({ label: series.name, numeric: true }))
|
|
2181
|
+
],
|
|
2182
|
+
rows: first.points.map((point, index) => [
|
|
2183
|
+
{ text: point.label },
|
|
2184
|
+
...graph.series.map((series) => {
|
|
2185
|
+
const value = series.points[index]?.value;
|
|
2186
|
+
return value === void 0 ? { text: "\u2014" } : { text: formatValue(value, graph.valueFormat, format), value };
|
|
2187
|
+
})
|
|
2188
|
+
])
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
2191
|
+
function barListToGraph(list, conversion) {
|
|
2192
|
+
return ok({
|
|
2193
|
+
kind: "GRAPH",
|
|
2194
|
+
series: [
|
|
2195
|
+
{
|
|
2196
|
+
name: conversion.seriesName ?? "Value",
|
|
2197
|
+
points: list.items.map((item) => ({
|
|
2198
|
+
label: item.label,
|
|
2199
|
+
value: item.value
|
|
2200
|
+
}))
|
|
2201
|
+
}
|
|
2202
|
+
],
|
|
2203
|
+
chartType: conversion.chartType,
|
|
2204
|
+
valueFormat: conversion.valueFormat ?? "number",
|
|
2205
|
+
...conversion.currency === void 0 ? {} : { currency: conversion.currency }
|
|
2206
|
+
});
|
|
2207
|
+
}
|
|
2208
|
+
function convertView(widgetKey, view, data, options = {}) {
|
|
2209
|
+
const conversion = view.convert;
|
|
2210
|
+
const context = {
|
|
2211
|
+
where: `Widget ${JSON.stringify(widgetKey)} view ${JSON.stringify(view.key)}`,
|
|
2212
|
+
...options.locale === void 0 ? {} : { locale: options.locale }
|
|
2213
|
+
};
|
|
2214
|
+
const expected = VIEW_CONVERSIONS[conversion.type]?.from;
|
|
2215
|
+
if (expected === void 0) {
|
|
2216
|
+
return err(
|
|
2217
|
+
`${context.where}: unknown conversion type ${JSON.stringify(conversion.type)}.`
|
|
2218
|
+
);
|
|
2219
|
+
}
|
|
2220
|
+
if (data.kind !== expected) {
|
|
2221
|
+
return err(
|
|
2222
|
+
`${context.where}: ${conversion.type} reads a ${expected} payload, but the widget returned ${data.kind}.`
|
|
2223
|
+
);
|
|
2224
|
+
}
|
|
2225
|
+
switch (conversion.type) {
|
|
2226
|
+
case "tableToBarList":
|
|
2227
|
+
return tableToBarList(data, conversion, context);
|
|
2228
|
+
case "tableToGraph":
|
|
2229
|
+
return tableToGraph(data, conversion, context);
|
|
2230
|
+
case "barListToTable":
|
|
2231
|
+
return barListToTable(data, conversion);
|
|
2232
|
+
case "graphToTable":
|
|
2233
|
+
return graphToTable(data, context);
|
|
2234
|
+
case "barListToGraph":
|
|
2235
|
+
return barListToGraph(data, conversion);
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
function applyView(widget, chosen, options = {}) {
|
|
2239
|
+
const key = effectiveViewKey(widget.definition, chosen);
|
|
2240
|
+
const view = findView(widget.definition, key);
|
|
2241
|
+
if (view === void 0) return widget;
|
|
2242
|
+
const definition = {
|
|
2243
|
+
...widget.definition,
|
|
2244
|
+
kind: view.kind
|
|
2245
|
+
};
|
|
2246
|
+
if (widget.status === "loading") return { ...widget, definition };
|
|
2247
|
+
if (widget.status === "empty") return { ...widget, definition };
|
|
2248
|
+
if (widget.status === "error") return { ...widget, definition };
|
|
2249
|
+
const converted = convertView(
|
|
2250
|
+
widget.definition.key,
|
|
2251
|
+
view,
|
|
2252
|
+
widget.data,
|
|
2253
|
+
options
|
|
2254
|
+
);
|
|
2255
|
+
if (!converted.ok) {
|
|
2256
|
+
return {
|
|
2257
|
+
definition,
|
|
2258
|
+
status: "error",
|
|
2259
|
+
error: converted.error,
|
|
2260
|
+
...widget.options === void 0 ? {} : { options: widget.options }
|
|
2261
|
+
};
|
|
2262
|
+
}
|
|
2263
|
+
return { ...widget, definition, data: converted.value };
|
|
2264
|
+
}
|
|
2265
|
+
|
|
1977
2266
|
// src/core/settings.ts
|
|
1978
2267
|
var MAX_WIDGET_TITLE_LENGTH = 80;
|
|
1979
2268
|
var MAX_COLUMN_LIST = 100;
|
|
@@ -2001,6 +2290,7 @@ function parseOne(raw) {
|
|
|
2001
2290
|
const width = raw["width"];
|
|
2002
2291
|
const flexible = raw["flexible"];
|
|
2003
2292
|
const options = raw["options"];
|
|
2293
|
+
const view = raw["view"];
|
|
2004
2294
|
const cleanTitle = typeof title === "string" ? title.trim().slice(0, MAX_WIDGET_TITLE_LENGTH) : "";
|
|
2005
2295
|
const chosen = {};
|
|
2006
2296
|
if (isRecord(options)) {
|
|
@@ -2013,7 +2303,8 @@ function parseOne(raw) {
|
|
|
2013
2303
|
...cleanTitle === "" ? {} : { title: cleanTitle },
|
|
2014
2304
|
...isWidgetWidth(width) ? { width } : {},
|
|
2015
2305
|
...typeof flexible === "boolean" ? { flexible } : {},
|
|
2016
|
-
...Object.keys(chosen).length === 0 ? {} : { options: chosen }
|
|
2306
|
+
...Object.keys(chosen).length === 0 ? {} : { options: chosen },
|
|
2307
|
+
...typeof view === "string" && view !== "" && view.length <= 80 ? { view } : {}
|
|
2017
2308
|
};
|
|
2018
2309
|
return Object.keys(settings).length === 0 ? void 0 : settings;
|
|
2019
2310
|
}
|
|
@@ -2081,12 +2372,18 @@ function effectiveDefinitions(definitions, layout, options = {}) {
|
|
|
2081
2372
|
}
|
|
2082
2373
|
function sameSettings(a, b) {
|
|
2083
2374
|
if (a === b) return true;
|
|
2084
|
-
return a?.title === b?.title && a?.width === b?.width && a?.flexible === b?.flexible && sameOptionValues(a?.options ?? {}, b?.options ?? {});
|
|
2375
|
+
return a?.title === b?.title && a?.width === b?.width && a?.flexible === b?.flexible && a?.view === b?.view && sameOptionValues(a?.options ?? {}, b?.options ?? {});
|
|
2085
2376
|
}
|
|
2086
2377
|
function setWidgetSettings(layout, key, patch) {
|
|
2087
2378
|
const before = layout.settings?.[key];
|
|
2088
2379
|
const draft = { ...before };
|
|
2089
|
-
for (const field of [
|
|
2380
|
+
for (const field of [
|
|
2381
|
+
"title",
|
|
2382
|
+
"width",
|
|
2383
|
+
"flexible",
|
|
2384
|
+
"options",
|
|
2385
|
+
"view"
|
|
2386
|
+
]) {
|
|
2090
2387
|
if (!(field in patch)) continue;
|
|
2091
2388
|
const value = patch[field];
|
|
2092
2389
|
if (value === void 0) {
|
|
@@ -2119,6 +2416,62 @@ function isTitleInUse(definitions, layout, key, title) {
|
|
|
2119
2416
|
);
|
|
2120
2417
|
}
|
|
2121
2418
|
|
|
2419
|
+
// src/core/clones.ts
|
|
2420
|
+
function cloneNumber(key) {
|
|
2421
|
+
const match = /#(\d+)$/.exec(key);
|
|
2422
|
+
return match === null ? void 0 : Number(match[1]);
|
|
2423
|
+
}
|
|
2424
|
+
function cleanClones(definitions, clones, options = {}) {
|
|
2425
|
+
if (clones === void 0) return void 0;
|
|
2426
|
+
const byKey = new Map(definitions.map((d) => [d.key, d]));
|
|
2427
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2428
|
+
const kept = [];
|
|
2429
|
+
for (const clone of clones) {
|
|
2430
|
+
if (seen.has(clone.key)) continue;
|
|
2431
|
+
const original = byKey.get(clone.from);
|
|
2432
|
+
if (original === void 0 || original.sourceKey !== void 0) continue;
|
|
2433
|
+
const existing = byKey.get(clone.key);
|
|
2434
|
+
if (existing !== void 0 && existing.sourceKey !== clone.from) continue;
|
|
2435
|
+
if (options.overrideLocks !== true && resolveWidgetLock(original).clone) {
|
|
2436
|
+
continue;
|
|
2437
|
+
}
|
|
2438
|
+
seen.add(clone.key);
|
|
2439
|
+
kept.push(clone);
|
|
2440
|
+
}
|
|
2441
|
+
if (kept.length === 0) return void 0;
|
|
2442
|
+
return kept.length === clones.length && kept.every((c, i) => c === clones[i]) ? clones : kept;
|
|
2443
|
+
}
|
|
2444
|
+
function defaultCloneTitle(original, clone, index) {
|
|
2445
|
+
const base = original.title ?? original.key;
|
|
2446
|
+
const suffix = ` (#${cloneNumber(clone.key) ?? index + 1})`;
|
|
2447
|
+
return `${base.slice(0, MAX_WIDGET_TITLE_LENGTH - suffix.length)}${suffix}`;
|
|
2448
|
+
}
|
|
2449
|
+
function withClones(definitions, layout, options = {}) {
|
|
2450
|
+
const valid = cleanClones(
|
|
2451
|
+
definitions,
|
|
2452
|
+
layout.clones,
|
|
2453
|
+
options
|
|
2454
|
+
);
|
|
2455
|
+
if (valid === void 0) return definitions;
|
|
2456
|
+
const present = new Set(definitions.map((d) => d.key));
|
|
2457
|
+
const toAdd = valid.filter((c) => !present.has(c.key));
|
|
2458
|
+
if (toAdd.length === 0) return definitions;
|
|
2459
|
+
const out = [];
|
|
2460
|
+
for (const definition of definitions) {
|
|
2461
|
+
out.push(definition);
|
|
2462
|
+
toAdd.forEach((clone, index) => {
|
|
2463
|
+
if (clone.from !== definition.key) return;
|
|
2464
|
+
const copy = { ...definition };
|
|
2465
|
+
copy.key = clone.key;
|
|
2466
|
+
copy.sourceKey = definition.key;
|
|
2467
|
+
copy.title = defaultCloneTitle(definition, clone, index);
|
|
2468
|
+
delete copy.locked;
|
|
2469
|
+
out.push(copy);
|
|
2470
|
+
});
|
|
2471
|
+
}
|
|
2472
|
+
return out;
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2122
2475
|
// src/core/layout.ts
|
|
2123
2476
|
var MAX_PAGE_TITLE_LENGTH = 30;
|
|
2124
2477
|
var MAX_PARSED_PAGES = 50;
|
|
@@ -2596,6 +2949,114 @@ function moveWidgetToPage(definitions, layout, widgetKey, targetKey, options = {
|
|
|
2596
2949
|
}
|
|
2597
2950
|
return ok(moved);
|
|
2598
2951
|
}
|
|
2952
|
+
|
|
2953
|
+
// src/core/duplicate.ts
|
|
2954
|
+
function canDuplicate(definitions, key, options = {}) {
|
|
2955
|
+
const definition = definitions.find(
|
|
2956
|
+
(d) => d.key === key
|
|
2957
|
+
);
|
|
2958
|
+
if (definition === void 0) return false;
|
|
2959
|
+
if (options.overrideLocks === true) return true;
|
|
2960
|
+
const sourceKey = definition.sourceKey ?? definition.key;
|
|
2961
|
+
const original = definitions.find(
|
|
2962
|
+
(d) => d.key === sourceKey
|
|
2963
|
+
);
|
|
2964
|
+
return original !== void 0 && !resolveWidgetLock(original).clone;
|
|
2965
|
+
}
|
|
2966
|
+
function nextCloneKey(definitions, layout, fromKey) {
|
|
2967
|
+
const taken = /* @__PURE__ */ new Set([
|
|
2968
|
+
...definitions.map((d) => d.key),
|
|
2969
|
+
...(layout.clones ?? []).map((c) => c.key)
|
|
2970
|
+
]);
|
|
2971
|
+
let highest = 0;
|
|
2972
|
+
for (const clone of layout.clones ?? []) {
|
|
2973
|
+
if (clone.from === fromKey) {
|
|
2974
|
+
highest = Math.max(highest, cloneNumber(clone.key) ?? 0);
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
let n = highest + 1;
|
|
2978
|
+
while (taken.has(`${fromKey}#${n}`)) n += 1;
|
|
2979
|
+
return `${fromKey}#${n}`;
|
|
2980
|
+
}
|
|
2981
|
+
function duplicateWidget(definitions, layout, key, options = {}) {
|
|
2982
|
+
const widget = definitions.find((d) => d.key === key);
|
|
2983
|
+
if (widget === void 0) {
|
|
2984
|
+
return err(`Widget ${JSON.stringify(key)} isn't on this dashboard.`);
|
|
2985
|
+
}
|
|
2986
|
+
const originalKey = widget.sourceKey ?? widget.key;
|
|
2987
|
+
if (!canDuplicate(definitions, key, options)) {
|
|
2988
|
+
return err(
|
|
2989
|
+
`Widget ${JSON.stringify(originalKey)} is locked against duplicating.`
|
|
2990
|
+
);
|
|
2991
|
+
}
|
|
2992
|
+
const cloneKey = nextCloneKey(definitions, layout, originalKey);
|
|
2993
|
+
const copiedSettings = (() => {
|
|
2994
|
+
const mine = layout.settings?.[key];
|
|
2995
|
+
if (mine === void 0) return void 0;
|
|
2996
|
+
const { title: _title, ...rest } = mine;
|
|
2997
|
+
return Object.keys(rest).length === 0 ? void 0 : rest;
|
|
2998
|
+
})();
|
|
2999
|
+
const settings = copiedSettings === void 0 ? layout.settings : { ...layout.settings, [cloneKey]: copiedSettings };
|
|
3000
|
+
let next = {
|
|
3001
|
+
...layout,
|
|
3002
|
+
clones: [...layout.clones ?? [], { key: cloneKey, from: originalKey }],
|
|
3003
|
+
...settings === void 0 ? {} : { settings }
|
|
3004
|
+
};
|
|
3005
|
+
const all = withClones(definitions, next, options);
|
|
3006
|
+
const assigned = assignPages(all, next);
|
|
3007
|
+
const pageKey = [...assigned].find(
|
|
3008
|
+
([, widgets]) => widgets.some((w) => w.key === key)
|
|
3009
|
+
)?.[0];
|
|
3010
|
+
if (pageKey !== void 0) {
|
|
3011
|
+
const view = pageLayout(next, pageKey);
|
|
3012
|
+
const onPage = assigned.get(pageKey) ?? [];
|
|
3013
|
+
if (view !== void 0) {
|
|
3014
|
+
const shown = visibleWidgets(onPage, view).filter((d) => !resolveWidgetLock(d).move && d.key !== cloneKey).map((d) => d.key);
|
|
3015
|
+
const at = shown.indexOf(key);
|
|
3016
|
+
const order = at === -1 ? [...shown, cloneKey] : [...shown.slice(0, at + 1), cloneKey, ...shown.slice(at + 1)];
|
|
3017
|
+
next = withPageLayout(next, pageKey, { ...view, order });
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
if (hasPages(next) && pageKey !== void 0) {
|
|
3021
|
+
const effective = effectiveDefinitions(all, next, options);
|
|
3022
|
+
const room = pageRoom(effective, next, pageKey, options);
|
|
3023
|
+
if (room !== void 0 && room.rowsUsed > room.maxRows) {
|
|
3024
|
+
const page = next.pages?.find(
|
|
3025
|
+
(p) => p.key === pageKey
|
|
3026
|
+
);
|
|
3027
|
+
const width = effective.find(
|
|
3028
|
+
(d) => d.key === cloneKey
|
|
3029
|
+
)?.width;
|
|
3030
|
+
return err(
|
|
3031
|
+
`Page "${page?.title ?? pageKey}" has no room for a widget${width === void 0 ? "" : ` ${width} wide`} (it holds ${room.maxRows} rows).`
|
|
3032
|
+
);
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
return ok({ layout: next, key: cloneKey });
|
|
3036
|
+
}
|
|
3037
|
+
function deleteClone(layout, key) {
|
|
3038
|
+
if (layout.clones?.some((clone) => clone.key === key) !== true) return layout;
|
|
3039
|
+
const without2 = (keys) => keys.includes(key) ? keys.filter((k) => k !== key) : keys;
|
|
3040
|
+
const { clones, settings, ...rest } = layout;
|
|
3041
|
+
const remaining = (clones ?? []).filter((c) => c.key !== key);
|
|
3042
|
+
const { [key]: _gone, ...otherSettings } = settings ?? {};
|
|
3043
|
+
return {
|
|
3044
|
+
...rest,
|
|
3045
|
+
order: without2(layout.order),
|
|
3046
|
+
hidden: without2(layout.hidden),
|
|
3047
|
+
minimized: without2(layout.minimized),
|
|
3048
|
+
...layout.pages === void 0 ? {} : {
|
|
3049
|
+
pages: layout.pages.map((page) => ({
|
|
3050
|
+
...page,
|
|
3051
|
+
order: without2(page.order),
|
|
3052
|
+
hidden: without2(page.hidden),
|
|
3053
|
+
minimized: without2(page.minimized)
|
|
3054
|
+
}))
|
|
3055
|
+
},
|
|
3056
|
+
...Object.keys(otherSettings).length === 0 ? {} : { settings: otherSettings },
|
|
3057
|
+
...remaining.length === 0 ? {} : { clones: remaining }
|
|
3058
|
+
};
|
|
3059
|
+
}
|
|
2599
3060
|
function InlineConfirm({
|
|
2600
3061
|
message,
|
|
2601
3062
|
onAnswer
|
|
@@ -2790,7 +3251,8 @@ function draftFrom(settings) {
|
|
|
2790
3251
|
title: settings?.title ?? "",
|
|
2791
3252
|
width: settings?.width,
|
|
2792
3253
|
flexible: settings?.flexible,
|
|
2793
|
-
options: settings?.options ?? {}
|
|
3254
|
+
options: settings?.options ?? {},
|
|
3255
|
+
view: settings?.view
|
|
2794
3256
|
};
|
|
2795
3257
|
}
|
|
2796
3258
|
function storedDefault(option) {
|
|
@@ -2815,7 +3277,9 @@ function patchFrom(definition, draft) {
|
|
|
2815
3277
|
title: title === "" ? void 0 : title,
|
|
2816
3278
|
width: draft.width,
|
|
2817
3279
|
flexible: draft.flexible === void 0 || draft.flexible === isFlexible(definition) ? void 0 : draft.flexible,
|
|
2818
|
-
options: Object.keys(options).length === 0 ? void 0 : options
|
|
3280
|
+
options: Object.keys(options).length === 0 ? void 0 : options,
|
|
3281
|
+
// Choosing the author's own default view again clears the setting.
|
|
3282
|
+
view: draft.view === void 0 || draft.view === effectiveViewKey(definition, void 0) ? void 0 : draft.view
|
|
2819
3283
|
};
|
|
2820
3284
|
}
|
|
2821
3285
|
function patchKey(patch) {
|
|
@@ -2823,19 +3287,28 @@ function patchKey(patch) {
|
|
|
2823
3287
|
for (const key of Object.keys(patch.options ?? {}).sort()) {
|
|
2824
3288
|
options[key] = patch.options[key];
|
|
2825
3289
|
}
|
|
2826
|
-
return JSON.stringify([
|
|
3290
|
+
return JSON.stringify([
|
|
3291
|
+
patch.title,
|
|
3292
|
+
patch.width,
|
|
3293
|
+
patch.flexible,
|
|
3294
|
+
options,
|
|
3295
|
+
patch.view
|
|
3296
|
+
]);
|
|
2827
3297
|
}
|
|
2828
3298
|
var EMPTY_PATCH = {
|
|
2829
3299
|
title: void 0,
|
|
2830
3300
|
width: void 0,
|
|
2831
3301
|
flexible: void 0,
|
|
2832
|
-
options: void 0
|
|
3302
|
+
options: void 0,
|
|
3303
|
+
view: void 0
|
|
2833
3304
|
};
|
|
2834
3305
|
function WidgetOptionsDialog({
|
|
2835
3306
|
definition,
|
|
2836
3307
|
settings,
|
|
2837
3308
|
baseline,
|
|
2838
3309
|
isTitleInUse: isTitleInUse2,
|
|
3310
|
+
sourceTitle,
|
|
3311
|
+
onDelete,
|
|
2839
3312
|
onApply,
|
|
2840
3313
|
onClose
|
|
2841
3314
|
}) {
|
|
@@ -2848,6 +3321,7 @@ function WidgetOptionsDialog({
|
|
|
2848
3321
|
const [confirming, setConfirming] = react.useState(
|
|
2849
3322
|
void 0
|
|
2850
3323
|
);
|
|
3324
|
+
const autoTitle = react.useRef(void 0);
|
|
2851
3325
|
react.useEffect(() => {
|
|
2852
3326
|
const dialog = dialogRef.current;
|
|
2853
3327
|
if (dialog === null) return void 0;
|
|
@@ -2867,6 +3341,7 @@ function WidgetOptionsDialog({
|
|
|
2867
3341
|
const canRevert = baselineKey !== void 0 && baselineKey !== savedKey;
|
|
2868
3342
|
const canReset = savedKey !== patchKey(EMPTY_PATCH);
|
|
2869
3343
|
const range = widthRange(definition);
|
|
3344
|
+
const views = viewChoices(definition);
|
|
2870
3345
|
const effectiveFlexible = draft.flexible ?? isFlexible(definition);
|
|
2871
3346
|
function requestClose() {
|
|
2872
3347
|
if (confirming !== void 0) {
|
|
@@ -2889,6 +3364,9 @@ function WidgetOptionsDialog({
|
|
|
2889
3364
|
} else if (which === "reset") {
|
|
2890
3365
|
onApply(EMPTY_PATCH);
|
|
2891
3366
|
onClose();
|
|
3367
|
+
} else if (which === "delete") {
|
|
3368
|
+
onDelete?.();
|
|
3369
|
+
onClose();
|
|
2892
3370
|
} else if (baseline !== void 0) {
|
|
2893
3371
|
onApply(patchFrom(definition, draftFrom(baseline.settings)));
|
|
2894
3372
|
onClose();
|
|
@@ -2904,7 +3382,8 @@ function WidgetOptionsDialog({
|
|
|
2904
3382
|
apply: labels.confirmApply,
|
|
2905
3383
|
discard: labels.confirmDiscard,
|
|
2906
3384
|
reset: labels.confirmResetOptions,
|
|
2907
|
-
revert: labels.confirmRevertOptions
|
|
3385
|
+
revert: labels.confirmRevertOptions,
|
|
3386
|
+
delete: labels.confirmDelete
|
|
2908
3387
|
};
|
|
2909
3388
|
const widths = [];
|
|
2910
3389
|
for (let width = range.min; width <= range.max; width += 1)
|
|
@@ -2992,6 +3471,46 @@ function WidgetOptionsDialog({
|
|
|
2992
3471
|
),
|
|
2993
3472
|
labels.optionsFlexible
|
|
2994
3473
|
] }),
|
|
3474
|
+
views.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dwt-options-field", children: [
|
|
3475
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: `${baseId}-view`, children: labels.optionsViewField }),
|
|
3476
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3477
|
+
"select",
|
|
3478
|
+
{
|
|
3479
|
+
id: `${baseId}-view`,
|
|
3480
|
+
value: effectiveViewKey(definition, draft.view),
|
|
3481
|
+
onChange: (event) => {
|
|
3482
|
+
const value = event.target.value;
|
|
3483
|
+
setDraft((current) => {
|
|
3484
|
+
const next = { ...current, view: value };
|
|
3485
|
+
if (sourceTitle === void 0 || current.title.trim() !== "" && current.title !== autoTitle.current) {
|
|
3486
|
+
return next;
|
|
3487
|
+
}
|
|
3488
|
+
const label = views.find(
|
|
3489
|
+
(view) => view.key === value
|
|
3490
|
+
)?.label;
|
|
3491
|
+
const startView = effectiveViewKey(
|
|
3492
|
+
definition,
|
|
3493
|
+
settings?.view
|
|
3494
|
+
);
|
|
3495
|
+
if (label === void 0 || value === startView) {
|
|
3496
|
+
autoTitle.current = void 0;
|
|
3497
|
+
return { ...next, title: "" };
|
|
3498
|
+
}
|
|
3499
|
+
let suggestion = labels.suggestedViewTitle(
|
|
3500
|
+
sourceTitle,
|
|
3501
|
+
label
|
|
3502
|
+
);
|
|
3503
|
+
for (let n = 2; isTitleInUse2?.(suggestion) === true && n < 50; n += 1) {
|
|
3504
|
+
suggestion = `${labels.suggestedViewTitle(sourceTitle, label)} (#${n})`;
|
|
3505
|
+
}
|
|
3506
|
+
autoTitle.current = suggestion;
|
|
3507
|
+
return { ...next, title: suggestion };
|
|
3508
|
+
});
|
|
3509
|
+
},
|
|
3510
|
+
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))
|
|
3511
|
+
}
|
|
3512
|
+
)
|
|
3513
|
+
] }),
|
|
2995
3514
|
(definition.options?.length ?? 0) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { className: "dwt-options-group", children: [
|
|
2996
3515
|
/* @__PURE__ */ jsxRuntime.jsx("legend", { children: labels.optionsDeclared }),
|
|
2997
3516
|
(definition.options ?? []).map((option) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -3043,6 +3562,15 @@ function WidgetOptionsDialog({
|
|
|
3043
3562
|
onClick: () => setConfirming("revert"),
|
|
3044
3563
|
children: labels.revertToBeforeEditing
|
|
3045
3564
|
}
|
|
3565
|
+
),
|
|
3566
|
+
onDelete !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3567
|
+
"button",
|
|
3568
|
+
{
|
|
3569
|
+
type: "button",
|
|
3570
|
+
className: slot("button", "dwt-button"),
|
|
3571
|
+
onClick: () => setConfirming("delete"),
|
|
3572
|
+
children: labels.optionsDelete
|
|
3573
|
+
}
|
|
3046
3574
|
)
|
|
3047
3575
|
] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
3048
3576
|
InlineConfirm,
|
|
@@ -3971,10 +4499,13 @@ function DashboardInner({
|
|
|
3971
4499
|
className
|
|
3972
4500
|
}) {
|
|
3973
4501
|
const slot = useSlotClassName();
|
|
3974
|
-
const { labels } = useWidgetSettings();
|
|
4502
|
+
const { labels, locale } = useWidgetSettings();
|
|
3975
4503
|
const effectiveWidgets = widgets.map((w) => {
|
|
3976
|
-
const settings = savedLayout.settings?.[w.definition.key];
|
|
3977
|
-
|
|
4504
|
+
const settings = !overrideLocks && isOptionsLocked(w.definition) ? void 0 : savedLayout.settings?.[w.definition.key];
|
|
4505
|
+
const styled = settings === void 0 ? w : { ...w, definition: applySettings(w.definition, settings) };
|
|
4506
|
+
return applyView(styled, settings?.view, {
|
|
4507
|
+
...locale === void 0 ? {} : { locale }
|
|
4508
|
+
});
|
|
3978
4509
|
});
|
|
3979
4510
|
const definitions = effectiveWidgets.map(
|
|
3980
4511
|
(w) => overrideLocks ? withoutLock(w.definition) : w.definition
|
|
@@ -4044,6 +4575,29 @@ function DashboardInner({
|
|
|
4044
4575
|
setPageError(void 0);
|
|
4045
4576
|
change(withPageLayout(layout, activeKey, next));
|
|
4046
4577
|
}
|
|
4578
|
+
function duplicateItems(definition, key, title) {
|
|
4579
|
+
const sources = [...authorDefinitions.values()];
|
|
4580
|
+
if (!canDuplicate(sources, key, { overrideLocks })) return [];
|
|
4581
|
+
const trial = duplicateWidget(sources, layout, key, {
|
|
4582
|
+
overrideLocks,
|
|
4583
|
+
...roomOptions
|
|
4584
|
+
});
|
|
4585
|
+
return [
|
|
4586
|
+
{
|
|
4587
|
+
kind: "action",
|
|
4588
|
+
key: "duplicate",
|
|
4589
|
+
label: labels.menuDuplicate,
|
|
4590
|
+
ariaLabel: trial.ok ? labels.duplicate(title) : labels.duplicateBlocked(title, trial.error),
|
|
4591
|
+
disabled: !trial.ok,
|
|
4592
|
+
onSelect: () => {
|
|
4593
|
+
if (!trial.ok) return;
|
|
4594
|
+
change(trial.value.layout);
|
|
4595
|
+
setOptionsKey(trial.value.key);
|
|
4596
|
+
setAnnouncement(labels.duplicated(title));
|
|
4597
|
+
}
|
|
4598
|
+
}
|
|
4599
|
+
];
|
|
4600
|
+
}
|
|
4047
4601
|
function selectPage(pageKey) {
|
|
4048
4602
|
const index = pages.findIndex((page) => page.key === pageKey);
|
|
4049
4603
|
if (index === -1) return;
|
|
@@ -4173,10 +4727,16 @@ function DashboardInner({
|
|
|
4173
4727
|
definitions,
|
|
4174
4728
|
defaultLayout ?? EMPTY_LAYOUT
|
|
4175
4729
|
);
|
|
4176
|
-
const {
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4730
|
+
const {
|
|
4731
|
+
settings: _defaultSettings,
|
|
4732
|
+
clones: _defaultClones,
|
|
4733
|
+
...arrangement
|
|
4734
|
+
} = arranged;
|
|
4735
|
+
change({
|
|
4736
|
+
...arrangement,
|
|
4737
|
+
...layout.settings === void 0 ? {} : { settings: layout.settings },
|
|
4738
|
+
...layout.clones === void 0 ? {} : { clones: layout.clones }
|
|
4739
|
+
});
|
|
4180
4740
|
} else if (yes && which === "revert" && snapshot !== void 0) {
|
|
4181
4741
|
change(snapshot);
|
|
4182
4742
|
}
|
|
@@ -4482,6 +5042,7 @@ function DashboardInner({
|
|
|
4482
5042
|
onSelect: () => setOptionsKey(key)
|
|
4483
5043
|
}
|
|
4484
5044
|
],
|
|
5045
|
+
...duplicateItems(definition, key, title),
|
|
4485
5046
|
...pageTargets,
|
|
4486
5047
|
...lock.hide ? [] : [
|
|
4487
5048
|
{
|
|
@@ -4576,6 +5137,21 @@ function DashboardInner({
|
|
|
4576
5137
|
definition: authorDefinitions.get(optionsKey),
|
|
4577
5138
|
settings: layout.settings?.[optionsKey],
|
|
4578
5139
|
...snapshot === void 0 ? {} : { baseline: { settings: snapshot.settings?.[optionsKey] } },
|
|
5140
|
+
...authorDefinitions.get(optionsKey)?.sourceKey === void 0 ? {} : {
|
|
5141
|
+
sourceTitle: effectiveWidgets.find(
|
|
5142
|
+
(w) => w.definition.key === authorDefinitions.get(optionsKey)?.sourceKey
|
|
5143
|
+
)?.definition.title ?? authorDefinitions.get(optionsKey)?.sourceKey ?? "",
|
|
5144
|
+
onDelete: () => {
|
|
5145
|
+
change(deleteClone(layout, optionsKey));
|
|
5146
|
+
setAnnouncement(
|
|
5147
|
+
labels.deleted(
|
|
5148
|
+
effectiveWidgets.find(
|
|
5149
|
+
(w) => w.definition.key === optionsKey
|
|
5150
|
+
)?.definition.title ?? optionsKey
|
|
5151
|
+
)
|
|
5152
|
+
);
|
|
5153
|
+
}
|
|
5154
|
+
},
|
|
4579
5155
|
isTitleInUse: (title) => isTitleInUse(
|
|
4580
5156
|
widgets.map((w) => w.definition),
|
|
4581
5157
|
layout,
|
|
@@ -4819,45 +5395,6 @@ function useStoredLayout(options) {
|
|
|
4819
5395
|
};
|
|
4820
5396
|
}
|
|
4821
5397
|
|
|
4822
|
-
// src/core/payload.ts
|
|
4823
|
-
var WIDGET_KINDS = [
|
|
4824
|
-
"TEXT",
|
|
4825
|
-
"KPI",
|
|
4826
|
-
"GAUGE",
|
|
4827
|
-
"TABLE",
|
|
4828
|
-
"BAR_LIST",
|
|
4829
|
-
"ALERT_LIST",
|
|
4830
|
-
"GRAPH"
|
|
4831
|
-
];
|
|
4832
|
-
function isWidgetKind(value) {
|
|
4833
|
-
return typeof value === "string" && WIDGET_KINDS.includes(value);
|
|
4834
|
-
}
|
|
4835
|
-
var VALUE_FORMATS = ["number", "currency", "percent"];
|
|
4836
|
-
var MAX_GRAPH_SERIES = 8;
|
|
4837
|
-
var DEFAULT_EMPTY_TEXT = "Nothing to show yet.";
|
|
4838
|
-
function isWidgetEmptyState(payload) {
|
|
4839
|
-
return "empty" in payload && payload.empty === true;
|
|
4840
|
-
}
|
|
4841
|
-
function emptyTextFor(payload) {
|
|
4842
|
-
if (isWidgetEmptyState(payload)) {
|
|
4843
|
-
return payload.text;
|
|
4844
|
-
}
|
|
4845
|
-
switch (payload.kind) {
|
|
4846
|
-
case "TABLE":
|
|
4847
|
-
return payload.rows.length === 0 ? DEFAULT_EMPTY_TEXT : null;
|
|
4848
|
-
case "BAR_LIST":
|
|
4849
|
-
return payload.items.length === 0 ? DEFAULT_EMPTY_TEXT : null;
|
|
4850
|
-
case "ALERT_LIST":
|
|
4851
|
-
return payload.items.length === 0 ? payload.emptyText : null;
|
|
4852
|
-
case "GRAPH":
|
|
4853
|
-
return payload.series.every((series) => series.points.length === 0) ? DEFAULT_EMPTY_TEXT : null;
|
|
4854
|
-
case "TEXT":
|
|
4855
|
-
case "KPI":
|
|
4856
|
-
case "GAUGE":
|
|
4857
|
-
return null;
|
|
4858
|
-
}
|
|
4859
|
-
}
|
|
4860
|
-
|
|
4861
5398
|
// src/core/validate.ts
|
|
4862
5399
|
var MAX_REPORTED_PROBLEMS = 10;
|
|
4863
5400
|
function describeType2(value) {
|
|
@@ -5162,7 +5699,7 @@ function loadingWidgets(definitions) {
|
|
|
5162
5699
|
return definitions.map((definition) => ({ definition, status: "loading" }));
|
|
5163
5700
|
}
|
|
5164
5701
|
function cacheKeyFor(definition, options, values) {
|
|
5165
|
-
const base = options.cacheKey === void 0 ? definition.key : options.cacheKey(definition);
|
|
5702
|
+
const base = options.cacheKey === void 0 ? definition.sourceKey ?? definition.key : options.cacheKey(definition);
|
|
5166
5703
|
return values === void 0 ? base : `${base}${optionsCacheSuffix(definition, values, options)}`;
|
|
5167
5704
|
}
|
|
5168
5705
|
function optionValuesFor(definition, options) {
|
|
@@ -5336,7 +5873,11 @@ function createWidgetLoader(options) {
|
|
|
5336
5873
|
}
|
|
5337
5874
|
}
|
|
5338
5875
|
if (!isCurrent()) return;
|
|
5339
|
-
const
|
|
5876
|
+
const providerKey = definition.sourceKey ?? key;
|
|
5877
|
+
const provider = Object.prototype.hasOwnProperty.call(
|
|
5878
|
+
providers,
|
|
5879
|
+
providerKey
|
|
5880
|
+
) ? providers[providerKey] : void 0;
|
|
5340
5881
|
const result = await resolveWidgetUnapplied(definition, provider, context, {
|
|
5341
5882
|
...runOptions,
|
|
5342
5883
|
signal: controller.signal
|