@richardmcquiston01/dashboard-widgets-toolkit 0.13.0 → 0.15.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 +1089 -347
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +1061 -348
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +1089 -347
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1061 -348
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +1173 -121
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +57 -4
- package/dist/react.d.ts +57 -4
- package/dist/react.js +1173 -122
- package/dist/react.js.map +1 -1
- package/dist/{storage-DNB7gjvT.d.cts → storage-DwJ-vGJg.d.cts} +264 -3
- package/dist/{storage-DNB7gjvT.d.ts → storage-DwJ-vGJg.d.ts} +264 -3
- package/dist/styles.css +146 -0
- package/package.json +1 -1
package/dist/react.cjs
CHANGED
|
@@ -829,6 +829,32 @@ var DEFAULT_LABELS = {
|
|
|
829
829
|
menuMoveEarlier: "Move earlier",
|
|
830
830
|
menuMoveLater: "Move later",
|
|
831
831
|
menuHide: "Hide",
|
|
832
|
+
menuOptions: "Options\u2026",
|
|
833
|
+
optionsFor: (title) => `Options for ${title}`,
|
|
834
|
+
optionsTitleField: "Title",
|
|
835
|
+
titleInUse: "Another widget already uses this title.",
|
|
836
|
+
optionsWidthField: "Width",
|
|
837
|
+
widthDefault: "Default",
|
|
838
|
+
widthOption: (width, share) => `${width} of 12 (${share})`,
|
|
839
|
+
optionsFlexible: "Grow to fill empty space in the row",
|
|
840
|
+
optionsViewField: "View",
|
|
841
|
+
viewDefault: (viewLabel) => `${viewLabel} (default)`,
|
|
842
|
+
optionsDeclared: "Data",
|
|
843
|
+
apply: "Apply",
|
|
844
|
+
cancel: "Cancel",
|
|
845
|
+
resetToDefaults: "Reset to defaults",
|
|
846
|
+
revertToBeforeEditing: "Revert to before editing",
|
|
847
|
+
confirmApply: "Apply these changes?",
|
|
848
|
+
confirmDiscard: "Discard your changes?",
|
|
849
|
+
confirmResetOptions: "Reset these options to their defaults?",
|
|
850
|
+
confirmRevertOptions: "Revert these options to how they were before you started editing?",
|
|
851
|
+
optionsSaved: (title) => `Options saved for ${title}`,
|
|
852
|
+
sortDefault: "Default order",
|
|
853
|
+
sortAscending: "Ascending",
|
|
854
|
+
sortDescending: "Descending",
|
|
855
|
+
moveColumnUp: (column) => `Move ${column} up`,
|
|
856
|
+
moveColumnDown: (column) => `Move ${column} down`,
|
|
857
|
+
customRange: (value) => `Custom: ${value}`,
|
|
832
858
|
moveToPage: (title) => `Move ${title} to another page`,
|
|
833
859
|
moveToPagePlaceholder: "Move to page\u2026",
|
|
834
860
|
moveToPageOption: (title, rowsFree) => `${title} (${rowsFree} ${rowsFree === 1 ? "row" : "rows"} free)`,
|
|
@@ -1912,12 +1938,14 @@ function ResolvedWidgetCard({
|
|
|
1912
1938
|
var NOT_LOCKED = Object.freeze({
|
|
1913
1939
|
move: false,
|
|
1914
1940
|
hide: false,
|
|
1915
|
-
minimize: false
|
|
1941
|
+
minimize: false,
|
|
1942
|
+
options: false
|
|
1916
1943
|
});
|
|
1917
1944
|
var FULLY_LOCKED = Object.freeze({
|
|
1918
1945
|
move: true,
|
|
1919
1946
|
hide: true,
|
|
1920
|
-
minimize: true
|
|
1947
|
+
minimize: true,
|
|
1948
|
+
options: true
|
|
1921
1949
|
});
|
|
1922
1950
|
function resolveWidgetLock(definition) {
|
|
1923
1951
|
const setting = definition.locked;
|
|
@@ -1926,7 +1954,8 @@ function resolveWidgetLock(definition) {
|
|
|
1926
1954
|
return {
|
|
1927
1955
|
move: setting.move === true,
|
|
1928
1956
|
hide: setting.hide === true,
|
|
1929
|
-
minimize: setting.minimize === true
|
|
1957
|
+
minimize: setting.minimize === true,
|
|
1958
|
+
options: setting.options === true
|
|
1930
1959
|
};
|
|
1931
1960
|
}
|
|
1932
1961
|
function sortDefinitions(definitions) {
|
|
@@ -1947,6 +1976,435 @@ function isWidgetVisibleTo(definition, roles) {
|
|
|
1947
1976
|
return definition.roles.some((role) => roles.includes(role));
|
|
1948
1977
|
}
|
|
1949
1978
|
|
|
1979
|
+
// src/core/payload.ts
|
|
1980
|
+
var WIDGET_KINDS = [
|
|
1981
|
+
"TEXT",
|
|
1982
|
+
"KPI",
|
|
1983
|
+
"GAUGE",
|
|
1984
|
+
"TABLE",
|
|
1985
|
+
"BAR_LIST",
|
|
1986
|
+
"ALERT_LIST",
|
|
1987
|
+
"GRAPH"
|
|
1988
|
+
];
|
|
1989
|
+
function isWidgetKind(value) {
|
|
1990
|
+
return typeof value === "string" && WIDGET_KINDS.includes(value);
|
|
1991
|
+
}
|
|
1992
|
+
var VALUE_FORMATS = ["number", "currency", "percent"];
|
|
1993
|
+
var MAX_GRAPH_SERIES = 8;
|
|
1994
|
+
var DEFAULT_EMPTY_TEXT = "Nothing to show yet.";
|
|
1995
|
+
function isWidgetEmptyState(payload) {
|
|
1996
|
+
return "empty" in payload && payload.empty === true;
|
|
1997
|
+
}
|
|
1998
|
+
function emptyTextFor(payload) {
|
|
1999
|
+
if (isWidgetEmptyState(payload)) {
|
|
2000
|
+
return payload.text;
|
|
2001
|
+
}
|
|
2002
|
+
switch (payload.kind) {
|
|
2003
|
+
case "TABLE":
|
|
2004
|
+
return payload.rows.length === 0 ? DEFAULT_EMPTY_TEXT : null;
|
|
2005
|
+
case "BAR_LIST":
|
|
2006
|
+
return payload.items.length === 0 ? DEFAULT_EMPTY_TEXT : null;
|
|
2007
|
+
case "ALERT_LIST":
|
|
2008
|
+
return payload.items.length === 0 ? payload.emptyText : null;
|
|
2009
|
+
case "GRAPH":
|
|
2010
|
+
return payload.series.every((series) => series.points.length === 0) ? DEFAULT_EMPTY_TEXT : null;
|
|
2011
|
+
case "TEXT":
|
|
2012
|
+
case "KPI":
|
|
2013
|
+
case "GAUGE":
|
|
2014
|
+
return null;
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
// src/core/views.ts
|
|
2019
|
+
var BASE_VIEW_KEY = "base";
|
|
2020
|
+
var VIEW_CONVERSIONS = {
|
|
2021
|
+
tableToBarList: { from: "TABLE", to: "BAR_LIST" },
|
|
2022
|
+
tableToGraph: { from: "TABLE", to: "GRAPH" },
|
|
2023
|
+
barListToTable: { from: "BAR_LIST", to: "TABLE" },
|
|
2024
|
+
graphToTable: { from: "GRAPH", to: "TABLE" },
|
|
2025
|
+
barListToGraph: { from: "BAR_LIST", to: "GRAPH" }
|
|
2026
|
+
};
|
|
2027
|
+
var KIND_LABELS = {
|
|
2028
|
+
TEXT: "Text",
|
|
2029
|
+
KPI: "Number",
|
|
2030
|
+
GAUGE: "Gauge",
|
|
2031
|
+
TABLE: "Table",
|
|
2032
|
+
BAR_LIST: "Bars",
|
|
2033
|
+
ALERT_LIST: "List",
|
|
2034
|
+
GRAPH: "Chart"
|
|
2035
|
+
};
|
|
2036
|
+
function kindLabel(kind) {
|
|
2037
|
+
return KIND_LABELS[kind];
|
|
2038
|
+
}
|
|
2039
|
+
function hasViews(item) {
|
|
2040
|
+
return item.views !== void 0 && item.views.length > 0;
|
|
2041
|
+
}
|
|
2042
|
+
function findView(item, key) {
|
|
2043
|
+
return item.views?.find((view) => view.key === key);
|
|
2044
|
+
}
|
|
2045
|
+
function viewChoices(item) {
|
|
2046
|
+
if (!hasViews(item)) return [];
|
|
2047
|
+
return [
|
|
2048
|
+
{
|
|
2049
|
+
key: BASE_VIEW_KEY,
|
|
2050
|
+
label: item.baseViewLabel ?? kindLabel(item.kind),
|
|
2051
|
+
kind: item.kind
|
|
2052
|
+
},
|
|
2053
|
+
...(item.views ?? []).map(({ key, label, kind }) => ({
|
|
2054
|
+
key,
|
|
2055
|
+
label,
|
|
2056
|
+
kind
|
|
2057
|
+
}))
|
|
2058
|
+
];
|
|
2059
|
+
}
|
|
2060
|
+
function isKnownView(item, key) {
|
|
2061
|
+
return key !== void 0 && (key === BASE_VIEW_KEY || findView(item, key) !== void 0);
|
|
2062
|
+
}
|
|
2063
|
+
function effectiveViewKey(item, chosen) {
|
|
2064
|
+
if (!hasViews(item)) return BASE_VIEW_KEY;
|
|
2065
|
+
if (isKnownView(item, chosen)) return chosen;
|
|
2066
|
+
if (isKnownView(item, item.defaultView)) return item.defaultView;
|
|
2067
|
+
return BASE_VIEW_KEY;
|
|
2068
|
+
}
|
|
2069
|
+
function cellValue2(cell) {
|
|
2070
|
+
const value = cell?.value;
|
|
2071
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
2072
|
+
}
|
|
2073
|
+
function columnName(table, index) {
|
|
2074
|
+
return table.columns[index]?.label ?? `#${index + 1}`;
|
|
2075
|
+
}
|
|
2076
|
+
function checkColumn(table, index, field, context) {
|
|
2077
|
+
if (!Number.isInteger(index) || index < 0 || index >= table.columns.length) {
|
|
2078
|
+
return `${context.where}: ${field} column ${index} does not exist (the table has ${table.columns.length} columns, numbered from 0).`;
|
|
2079
|
+
}
|
|
2080
|
+
return void 0;
|
|
2081
|
+
}
|
|
2082
|
+
function numericColumn(table, index, context) {
|
|
2083
|
+
const numbers = [];
|
|
2084
|
+
for (const [rowIndex, row] of table.rows.entries()) {
|
|
2085
|
+
const value = cellValue2(row[index]);
|
|
2086
|
+
if (value === void 0) {
|
|
2087
|
+
return err(
|
|
2088
|
+
`${context.where}: row ${rowIndex + 1}, column ${index + 1} ("${columnName(table, index)}") has no numeric value. Add value to the cells.`
|
|
2089
|
+
);
|
|
2090
|
+
}
|
|
2091
|
+
numbers.push(value);
|
|
2092
|
+
}
|
|
2093
|
+
return ok(numbers);
|
|
2094
|
+
}
|
|
2095
|
+
function tableToBarList(table, conversion, context) {
|
|
2096
|
+
const bad = checkColumn(table, conversion.label, "label", context) ?? checkColumn(table, conversion.value, "value", context);
|
|
2097
|
+
if (bad !== void 0) return err(bad);
|
|
2098
|
+
const numbers = numericColumn(table, conversion.value, context);
|
|
2099
|
+
if (!numbers.ok) return numbers;
|
|
2100
|
+
const items = table.rows.map((row, index) => ({
|
|
2101
|
+
label: row[conversion.label]?.text ?? "",
|
|
2102
|
+
value: numbers.value[index],
|
|
2103
|
+
display: row[conversion.value]?.text ?? String(numbers.value[index])
|
|
2104
|
+
}));
|
|
2105
|
+
return ok({ kind: "BAR_LIST", items });
|
|
2106
|
+
}
|
|
2107
|
+
function tableToGraph(table, conversion, context) {
|
|
2108
|
+
if (conversion.values.length === 0 || conversion.values.length > MAX_GRAPH_SERIES) {
|
|
2109
|
+
return err(
|
|
2110
|
+
`${context.where}: a chart needs 1 to ${MAX_GRAPH_SERIES} series columns, got ${conversion.values.length}.`
|
|
2111
|
+
);
|
|
2112
|
+
}
|
|
2113
|
+
let bad = checkColumn(
|
|
2114
|
+
table,
|
|
2115
|
+
conversion.label,
|
|
2116
|
+
"label",
|
|
2117
|
+
context
|
|
2118
|
+
);
|
|
2119
|
+
for (const index of conversion.values) {
|
|
2120
|
+
bad ??= checkColumn(table, index, "series", context);
|
|
2121
|
+
}
|
|
2122
|
+
if (bad !== void 0) return err(bad);
|
|
2123
|
+
const series = [];
|
|
2124
|
+
for (const index of conversion.values) {
|
|
2125
|
+
const numbers = numericColumn(table, index, context);
|
|
2126
|
+
if (!numbers.ok) return numbers;
|
|
2127
|
+
series.push({
|
|
2128
|
+
name: columnName(table, index),
|
|
2129
|
+
points: table.rows.map((row, rowIndex) => ({
|
|
2130
|
+
label: row[conversion.label]?.text ?? "",
|
|
2131
|
+
value: numbers.value[rowIndex]
|
|
2132
|
+
}))
|
|
2133
|
+
});
|
|
2134
|
+
}
|
|
2135
|
+
return ok({
|
|
2136
|
+
kind: "GRAPH",
|
|
2137
|
+
series,
|
|
2138
|
+
chartType: conversion.chartType,
|
|
2139
|
+
valueFormat: conversion.valueFormat ?? "number",
|
|
2140
|
+
...conversion.currency === void 0 ? {} : { currency: conversion.currency },
|
|
2141
|
+
xLabel: columnName(table, conversion.label)
|
|
2142
|
+
});
|
|
2143
|
+
}
|
|
2144
|
+
function barListToTable(list, conversion) {
|
|
2145
|
+
return ok({
|
|
2146
|
+
kind: "TABLE",
|
|
2147
|
+
columns: [
|
|
2148
|
+
{ label: conversion.labelHeading ?? "Name" },
|
|
2149
|
+
{ label: conversion.valueHeading ?? "Value", numeric: true }
|
|
2150
|
+
],
|
|
2151
|
+
rows: list.items.map((item) => [
|
|
2152
|
+
{ text: item.label },
|
|
2153
|
+
{ text: item.display ?? String(item.value), value: item.value }
|
|
2154
|
+
])
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2157
|
+
function graphToTable(graph, context) {
|
|
2158
|
+
const first = graph.series[0];
|
|
2159
|
+
if (first === void 0)
|
|
2160
|
+
return err(`${context.where}: the chart has no series.`);
|
|
2161
|
+
const format = {
|
|
2162
|
+
...graph.currency === void 0 ? {} : { currency: graph.currency },
|
|
2163
|
+
...context.locale === void 0 ? {} : { locale: context.locale }
|
|
2164
|
+
};
|
|
2165
|
+
return ok({
|
|
2166
|
+
kind: "TABLE",
|
|
2167
|
+
columns: [
|
|
2168
|
+
{ label: graph.xLabel ?? "Label" },
|
|
2169
|
+
...graph.series.map((series) => ({ label: series.name, numeric: true }))
|
|
2170
|
+
],
|
|
2171
|
+
rows: first.points.map((point, index) => [
|
|
2172
|
+
{ text: point.label },
|
|
2173
|
+
...graph.series.map((series) => {
|
|
2174
|
+
const value = series.points[index]?.value;
|
|
2175
|
+
return value === void 0 ? { text: "\u2014" } : { text: formatValue(value, graph.valueFormat, format), value };
|
|
2176
|
+
})
|
|
2177
|
+
])
|
|
2178
|
+
});
|
|
2179
|
+
}
|
|
2180
|
+
function barListToGraph(list, conversion) {
|
|
2181
|
+
return ok({
|
|
2182
|
+
kind: "GRAPH",
|
|
2183
|
+
series: [
|
|
2184
|
+
{
|
|
2185
|
+
name: conversion.seriesName ?? "Value",
|
|
2186
|
+
points: list.items.map((item) => ({
|
|
2187
|
+
label: item.label,
|
|
2188
|
+
value: item.value
|
|
2189
|
+
}))
|
|
2190
|
+
}
|
|
2191
|
+
],
|
|
2192
|
+
chartType: conversion.chartType,
|
|
2193
|
+
valueFormat: conversion.valueFormat ?? "number",
|
|
2194
|
+
...conversion.currency === void 0 ? {} : { currency: conversion.currency }
|
|
2195
|
+
});
|
|
2196
|
+
}
|
|
2197
|
+
function convertView(widgetKey, view, data, options = {}) {
|
|
2198
|
+
const conversion = view.convert;
|
|
2199
|
+
const context = {
|
|
2200
|
+
where: `Widget ${JSON.stringify(widgetKey)} view ${JSON.stringify(view.key)}`,
|
|
2201
|
+
...options.locale === void 0 ? {} : { locale: options.locale }
|
|
2202
|
+
};
|
|
2203
|
+
const expected = VIEW_CONVERSIONS[conversion.type]?.from;
|
|
2204
|
+
if (expected === void 0) {
|
|
2205
|
+
return err(
|
|
2206
|
+
`${context.where}: unknown conversion type ${JSON.stringify(conversion.type)}.`
|
|
2207
|
+
);
|
|
2208
|
+
}
|
|
2209
|
+
if (data.kind !== expected) {
|
|
2210
|
+
return err(
|
|
2211
|
+
`${context.where}: ${conversion.type} reads a ${expected} payload, but the widget returned ${data.kind}.`
|
|
2212
|
+
);
|
|
2213
|
+
}
|
|
2214
|
+
switch (conversion.type) {
|
|
2215
|
+
case "tableToBarList":
|
|
2216
|
+
return tableToBarList(data, conversion, context);
|
|
2217
|
+
case "tableToGraph":
|
|
2218
|
+
return tableToGraph(data, conversion, context);
|
|
2219
|
+
case "barListToTable":
|
|
2220
|
+
return barListToTable(data, conversion);
|
|
2221
|
+
case "graphToTable":
|
|
2222
|
+
return graphToTable(data, context);
|
|
2223
|
+
case "barListToGraph":
|
|
2224
|
+
return barListToGraph(data, conversion);
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
function applyView(widget, chosen, options = {}) {
|
|
2228
|
+
const key = effectiveViewKey(widget.definition, chosen);
|
|
2229
|
+
const view = findView(widget.definition, key);
|
|
2230
|
+
if (view === void 0) return widget;
|
|
2231
|
+
const definition = {
|
|
2232
|
+
...widget.definition,
|
|
2233
|
+
kind: view.kind
|
|
2234
|
+
};
|
|
2235
|
+
if (widget.status === "loading") return { ...widget, definition };
|
|
2236
|
+
if (widget.status === "empty") return { ...widget, definition };
|
|
2237
|
+
if (widget.status === "error") return { ...widget, definition };
|
|
2238
|
+
const converted = convertView(
|
|
2239
|
+
widget.definition.key,
|
|
2240
|
+
view,
|
|
2241
|
+
widget.data,
|
|
2242
|
+
options
|
|
2243
|
+
);
|
|
2244
|
+
if (!converted.ok) {
|
|
2245
|
+
return {
|
|
2246
|
+
definition,
|
|
2247
|
+
status: "error",
|
|
2248
|
+
error: converted.error,
|
|
2249
|
+
...widget.options === void 0 ? {} : { options: widget.options }
|
|
2250
|
+
};
|
|
2251
|
+
}
|
|
2252
|
+
return { ...widget, definition, data: converted.value };
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
// src/core/settings.ts
|
|
2256
|
+
var MAX_WIDGET_TITLE_LENGTH = 80;
|
|
2257
|
+
var MAX_COLUMN_LIST = 100;
|
|
2258
|
+
var MAX_VALUE_LENGTH = 500;
|
|
2259
|
+
function isRecord(value) {
|
|
2260
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2261
|
+
}
|
|
2262
|
+
function parseOptionValue(value) {
|
|
2263
|
+
if (typeof value === "string") {
|
|
2264
|
+
return value.length <= MAX_VALUE_LENGTH ? value : void 0;
|
|
2265
|
+
}
|
|
2266
|
+
if (typeof value === "number")
|
|
2267
|
+
return Number.isFinite(value) ? value : void 0;
|
|
2268
|
+
if (typeof value === "boolean") return value;
|
|
2269
|
+
if (Array.isArray(value) && value.length <= MAX_COLUMN_LIST) {
|
|
2270
|
+
return value.every(
|
|
2271
|
+
(entry) => typeof entry === "string" && entry.length <= MAX_VALUE_LENGTH
|
|
2272
|
+
) ? value : void 0;
|
|
2273
|
+
}
|
|
2274
|
+
return void 0;
|
|
2275
|
+
}
|
|
2276
|
+
function parseOne(raw) {
|
|
2277
|
+
if (!isRecord(raw)) return void 0;
|
|
2278
|
+
const title = raw["title"];
|
|
2279
|
+
const width = raw["width"];
|
|
2280
|
+
const flexible = raw["flexible"];
|
|
2281
|
+
const options = raw["options"];
|
|
2282
|
+
const view = raw["view"];
|
|
2283
|
+
const cleanTitle = typeof title === "string" ? title.trim().slice(0, MAX_WIDGET_TITLE_LENGTH) : "";
|
|
2284
|
+
const chosen = {};
|
|
2285
|
+
if (isRecord(options)) {
|
|
2286
|
+
for (const [key, value] of Object.entries(options)) {
|
|
2287
|
+
const parsed = parseOptionValue(value);
|
|
2288
|
+
if (parsed !== void 0) chosen[key] = parsed;
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
const settings = {
|
|
2292
|
+
...cleanTitle === "" ? {} : { title: cleanTitle },
|
|
2293
|
+
...isWidgetWidth(width) ? { width } : {},
|
|
2294
|
+
...typeof flexible === "boolean" ? { flexible } : {},
|
|
2295
|
+
...Object.keys(chosen).length === 0 ? {} : { options: chosen },
|
|
2296
|
+
...typeof view === "string" && view !== "" && view.length <= 80 ? { view } : {}
|
|
2297
|
+
};
|
|
2298
|
+
return Object.keys(settings).length === 0 ? void 0 : settings;
|
|
2299
|
+
}
|
|
2300
|
+
function widthRange(item) {
|
|
2301
|
+
const min = isWidgetWidth(item.minWidth) ? item.minWidth : MIN_WIDGET_WIDTH;
|
|
2302
|
+
const max = isWidgetWidth(item.maxWidth) ? item.maxWidth : MAX_WIDGET_WIDTH;
|
|
2303
|
+
return { min, max: Math.max(min, max) };
|
|
2304
|
+
}
|
|
2305
|
+
var WIDTH_SHARES = {
|
|
2306
|
+
2: "1/6",
|
|
2307
|
+
3: "1/4",
|
|
2308
|
+
4: "1/3",
|
|
2309
|
+
5: "5/12",
|
|
2310
|
+
6: "1/2",
|
|
2311
|
+
7: "7/12",
|
|
2312
|
+
8: "2/3",
|
|
2313
|
+
9: "3/4",
|
|
2314
|
+
10: "5/6",
|
|
2315
|
+
11: "11/12",
|
|
2316
|
+
12: "Full"
|
|
2317
|
+
};
|
|
2318
|
+
function widthShareLabel(width) {
|
|
2319
|
+
return WIDTH_SHARES[width] ?? `${width}/12`;
|
|
2320
|
+
}
|
|
2321
|
+
function isOptionsLocked(item) {
|
|
2322
|
+
return resolveWidgetLock(item).options;
|
|
2323
|
+
}
|
|
2324
|
+
function fillWith(fill, flexible) {
|
|
2325
|
+
const heightToo = fill === "height" || fill === "both";
|
|
2326
|
+
if (flexible) return heightToo ? "both" : "width";
|
|
2327
|
+
return heightToo ? "height" : void 0;
|
|
2328
|
+
}
|
|
2329
|
+
function isFlexible(item, settings) {
|
|
2330
|
+
return item.fill === "width" || item.fill === "both";
|
|
2331
|
+
}
|
|
2332
|
+
function applySettings(definition, settings) {
|
|
2333
|
+
if (settings === void 0) return definition;
|
|
2334
|
+
const range = widthRange(definition);
|
|
2335
|
+
const width = settings.width === void 0 ? void 0 : Math.min(range.max, Math.max(range.min, settings.width));
|
|
2336
|
+
const title = settings.title === void 0 || settings.title.trim() === "" ? void 0 : settings.title;
|
|
2337
|
+
const flexibleChanged = settings.flexible !== void 0 && settings.flexible !== isFlexible(definition);
|
|
2338
|
+
if (title === void 0 && width === void 0 && !flexibleChanged) {
|
|
2339
|
+
return definition;
|
|
2340
|
+
}
|
|
2341
|
+
const next = { ...definition };
|
|
2342
|
+
if (title !== void 0) next.title = title;
|
|
2343
|
+
if (width !== void 0) next.width = width;
|
|
2344
|
+
if (flexibleChanged) {
|
|
2345
|
+
const fill = fillWith(
|
|
2346
|
+
definition.fill,
|
|
2347
|
+
settings.flexible === true
|
|
2348
|
+
);
|
|
2349
|
+
if (fill === void 0) {
|
|
2350
|
+
delete next.fill;
|
|
2351
|
+
} else {
|
|
2352
|
+
next.fill = fill;
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
return next;
|
|
2356
|
+
}
|
|
2357
|
+
function effectiveDefinitions(definitions, layout, options = {}) {
|
|
2358
|
+
return definitions.map(
|
|
2359
|
+
(definition) => options.overrideLocks !== true && isOptionsLocked(definition) ? definition : applySettings(definition, layout.settings?.[definition.key])
|
|
2360
|
+
);
|
|
2361
|
+
}
|
|
2362
|
+
function sameSettings(a, b) {
|
|
2363
|
+
if (a === b) return true;
|
|
2364
|
+
return a?.title === b?.title && a?.width === b?.width && a?.flexible === b?.flexible && a?.view === b?.view && sameOptionValues(a?.options ?? {}, b?.options ?? {});
|
|
2365
|
+
}
|
|
2366
|
+
function setWidgetSettings(layout, key, patch) {
|
|
2367
|
+
const before = layout.settings?.[key];
|
|
2368
|
+
const draft = { ...before };
|
|
2369
|
+
for (const field of [
|
|
2370
|
+
"title",
|
|
2371
|
+
"width",
|
|
2372
|
+
"flexible",
|
|
2373
|
+
"options",
|
|
2374
|
+
"view"
|
|
2375
|
+
]) {
|
|
2376
|
+
if (!(field in patch)) continue;
|
|
2377
|
+
const value = patch[field];
|
|
2378
|
+
if (value === void 0) {
|
|
2379
|
+
delete draft[field];
|
|
2380
|
+
} else {
|
|
2381
|
+
draft[field] = value;
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
const merged = parseOne(draft);
|
|
2385
|
+
if (sameSettings(before, merged)) return layout;
|
|
2386
|
+
const rest = { ...layout.settings };
|
|
2387
|
+
if (merged === void 0) {
|
|
2388
|
+
delete rest[key];
|
|
2389
|
+
} else {
|
|
2390
|
+
rest[key] = merged;
|
|
2391
|
+
}
|
|
2392
|
+
const { settings: _previous, ...others } = layout;
|
|
2393
|
+
return Object.keys(rest).length === 0 ? others : { ...others, settings: rest };
|
|
2394
|
+
}
|
|
2395
|
+
function normalizeTitle(title) {
|
|
2396
|
+
return title.normalize("NFKC").trim().toLowerCase();
|
|
2397
|
+
}
|
|
2398
|
+
function isTitleInUse(definitions, layout, key, title) {
|
|
2399
|
+
const wanted = normalizeTitle(title);
|
|
2400
|
+
if (wanted === "") return false;
|
|
2401
|
+
return effectiveDefinitions(definitions, layout, {
|
|
2402
|
+
overrideLocks: true
|
|
2403
|
+
}).some(
|
|
2404
|
+
(other) => other.key !== key && normalizeTitle(other.title ?? other.key) === wanted
|
|
2405
|
+
);
|
|
2406
|
+
}
|
|
2407
|
+
|
|
1950
2408
|
// src/core/layout.ts
|
|
1951
2409
|
var MAX_PAGE_TITLE_LENGTH = 30;
|
|
1952
2410
|
var MAX_PARSED_PAGES = 50;
|
|
@@ -2424,6 +2882,57 @@ function moveWidgetToPage(definitions, layout, widgetKey, targetKey, options = {
|
|
|
2424
2882
|
}
|
|
2425
2883
|
return ok(moved);
|
|
2426
2884
|
}
|
|
2885
|
+
function InlineConfirm({
|
|
2886
|
+
message,
|
|
2887
|
+
onAnswer
|
|
2888
|
+
}) {
|
|
2889
|
+
const slot = useSlotClassName();
|
|
2890
|
+
const { labels } = useWidgetSettings();
|
|
2891
|
+
const noRef = react.useRef(null);
|
|
2892
|
+
react.useEffect(() => {
|
|
2893
|
+
noRef.current?.focus();
|
|
2894
|
+
}, []);
|
|
2895
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2896
|
+
"span",
|
|
2897
|
+
{
|
|
2898
|
+
role: "group",
|
|
2899
|
+
"aria-label": message,
|
|
2900
|
+
className: "dwt-confirm",
|
|
2901
|
+
onKeyDown: (event) => {
|
|
2902
|
+
if (event.key === "Escape") {
|
|
2903
|
+
event.stopPropagation();
|
|
2904
|
+
onAnswer(false);
|
|
2905
|
+
}
|
|
2906
|
+
},
|
|
2907
|
+
children: [
|
|
2908
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { role: "alert", className: "dwt-confirm-message", children: message }),
|
|
2909
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2910
|
+
"button",
|
|
2911
|
+
{
|
|
2912
|
+
type: "button",
|
|
2913
|
+
className: slot("button", "dwt-button", "dwt-icon-button"),
|
|
2914
|
+
"aria-label": labels.confirmYes,
|
|
2915
|
+
title: labels.confirmYes,
|
|
2916
|
+
onClick: () => onAnswer(true),
|
|
2917
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\u2713" })
|
|
2918
|
+
}
|
|
2919
|
+
),
|
|
2920
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2921
|
+
"button",
|
|
2922
|
+
{
|
|
2923
|
+
ref: noRef,
|
|
2924
|
+
type: "button",
|
|
2925
|
+
className: slot("button", "dwt-button", "dwt-icon-button"),
|
|
2926
|
+
"aria-label": labels.confirmNo,
|
|
2927
|
+
title: labels.confirmNo,
|
|
2928
|
+
onClick: () => onAnswer(false),
|
|
2929
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\u2715" })
|
|
2930
|
+
}
|
|
2931
|
+
)
|
|
2932
|
+
]
|
|
2933
|
+
}
|
|
2934
|
+
);
|
|
2935
|
+
}
|
|
2427
2936
|
var EditMenuInitialOpenContext = react.createContext(false);
|
|
2428
2937
|
var ITEM_SELECTOR = '[role="menuitem"]:not(:disabled)';
|
|
2429
2938
|
function WidgetEditMenu({
|
|
@@ -2534,34 +3043,615 @@ function WidgetEditMenu({
|
|
|
2534
3043
|
(item) => item.kind === "heading" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2535
3044
|
"p",
|
|
2536
3045
|
{
|
|
2537
|
-
className: "dwt-edit-heading",
|
|
2538
|
-
role: "presentation",
|
|
2539
|
-
children: item.label
|
|
2540
|
-
},
|
|
2541
|
-
item.key
|
|
2542
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
3046
|
+
className: "dwt-edit-heading",
|
|
3047
|
+
role: "presentation",
|
|
3048
|
+
children: item.label
|
|
3049
|
+
},
|
|
3050
|
+
item.key
|
|
3051
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
3052
|
+
"button",
|
|
3053
|
+
{
|
|
3054
|
+
type: "button",
|
|
3055
|
+
role: "menuitem",
|
|
3056
|
+
className: "dwt-edit-option",
|
|
3057
|
+
...item.ariaLabel === void 0 ? {} : { "aria-label": item.ariaLabel },
|
|
3058
|
+
disabled: item.disabled === true,
|
|
3059
|
+
onClick: () => {
|
|
3060
|
+
close(false);
|
|
3061
|
+
item.onSelect();
|
|
3062
|
+
},
|
|
3063
|
+
children: item.label
|
|
3064
|
+
},
|
|
3065
|
+
item.key
|
|
3066
|
+
)
|
|
3067
|
+
)
|
|
3068
|
+
}
|
|
3069
|
+
)
|
|
3070
|
+
]
|
|
3071
|
+
}
|
|
3072
|
+
);
|
|
3073
|
+
}
|
|
3074
|
+
function draftFrom(settings) {
|
|
3075
|
+
return {
|
|
3076
|
+
title: settings?.title ?? "",
|
|
3077
|
+
width: settings?.width,
|
|
3078
|
+
flexible: settings?.flexible,
|
|
3079
|
+
options: settings?.options ?? {},
|
|
3080
|
+
view: settings?.view
|
|
3081
|
+
};
|
|
3082
|
+
}
|
|
3083
|
+
function storedDefault(option) {
|
|
3084
|
+
return option.type === "dateRange" ? option.default : defaultOptionValue(option);
|
|
3085
|
+
}
|
|
3086
|
+
function sameValue2(a, b) {
|
|
3087
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
3088
|
+
return a.length === b.length && a.every((item, i) => item === b[i]);
|
|
3089
|
+
}
|
|
3090
|
+
return a === b;
|
|
3091
|
+
}
|
|
3092
|
+
function patchFrom(definition, draft) {
|
|
3093
|
+
const title = draft.title.trim();
|
|
3094
|
+
const options = {};
|
|
3095
|
+
for (const option of definition.options ?? []) {
|
|
3096
|
+
const value = draft.options[option.key];
|
|
3097
|
+
if (value === void 0) continue;
|
|
3098
|
+
if (sameValue2(value, storedDefault(option))) continue;
|
|
3099
|
+
options[option.key] = value;
|
|
3100
|
+
}
|
|
3101
|
+
return {
|
|
3102
|
+
title: title === "" ? void 0 : title,
|
|
3103
|
+
width: draft.width,
|
|
3104
|
+
flexible: draft.flexible === void 0 || draft.flexible === isFlexible(definition) ? void 0 : draft.flexible,
|
|
3105
|
+
options: Object.keys(options).length === 0 ? void 0 : options,
|
|
3106
|
+
// Choosing the author's own default view again clears the setting.
|
|
3107
|
+
view: draft.view === void 0 || draft.view === effectiveViewKey(definition, void 0) ? void 0 : draft.view
|
|
3108
|
+
};
|
|
3109
|
+
}
|
|
3110
|
+
function patchKey(patch) {
|
|
3111
|
+
const options = {};
|
|
3112
|
+
for (const key of Object.keys(patch.options ?? {}).sort()) {
|
|
3113
|
+
options[key] = patch.options[key];
|
|
3114
|
+
}
|
|
3115
|
+
return JSON.stringify([
|
|
3116
|
+
patch.title,
|
|
3117
|
+
patch.width,
|
|
3118
|
+
patch.flexible,
|
|
3119
|
+
options,
|
|
3120
|
+
patch.view
|
|
3121
|
+
]);
|
|
3122
|
+
}
|
|
3123
|
+
var EMPTY_PATCH = {
|
|
3124
|
+
title: void 0,
|
|
3125
|
+
width: void 0,
|
|
3126
|
+
flexible: void 0,
|
|
3127
|
+
options: void 0,
|
|
3128
|
+
view: void 0
|
|
3129
|
+
};
|
|
3130
|
+
function WidgetOptionsDialog({
|
|
3131
|
+
definition,
|
|
3132
|
+
settings,
|
|
3133
|
+
baseline,
|
|
3134
|
+
isTitleInUse: isTitleInUse2,
|
|
3135
|
+
onApply,
|
|
3136
|
+
onClose
|
|
3137
|
+
}) {
|
|
3138
|
+
const slot = useSlotClassName();
|
|
3139
|
+
const { labels } = useWidgetSettings();
|
|
3140
|
+
const dialogRef = react.useRef(null);
|
|
3141
|
+
const headingId = react.useId();
|
|
3142
|
+
const baseId = react.useId();
|
|
3143
|
+
const [draft, setDraft] = react.useState(() => draftFrom(settings));
|
|
3144
|
+
const [confirming, setConfirming] = react.useState(
|
|
3145
|
+
void 0
|
|
3146
|
+
);
|
|
3147
|
+
react.useEffect(() => {
|
|
3148
|
+
const dialog = dialogRef.current;
|
|
3149
|
+
if (dialog === null) return void 0;
|
|
3150
|
+
const opener = document.activeElement;
|
|
3151
|
+
if (!dialog.open) dialog.showModal();
|
|
3152
|
+
return () => {
|
|
3153
|
+
if (dialog.open) dialog.close();
|
|
3154
|
+
if (opener instanceof HTMLElement) opener.focus();
|
|
3155
|
+
};
|
|
3156
|
+
}, []);
|
|
3157
|
+
const savedKey = patchKey(patchFrom(definition, draftFrom(settings)));
|
|
3158
|
+
const draftPatch = patchFrom(definition, draft);
|
|
3159
|
+
const dirty = patchKey(draftPatch) !== savedKey;
|
|
3160
|
+
const titleTaken = draft.title.trim() !== "" && isTitleInUse2?.(draft.title.trim()) === true;
|
|
3161
|
+
const canApply = dirty && !titleTaken;
|
|
3162
|
+
const baselineKey = baseline === void 0 ? void 0 : patchKey(patchFrom(definition, draftFrom(baseline.settings)));
|
|
3163
|
+
const canRevert = baselineKey !== void 0 && baselineKey !== savedKey;
|
|
3164
|
+
const canReset = savedKey !== patchKey(EMPTY_PATCH);
|
|
3165
|
+
const range = widthRange(definition);
|
|
3166
|
+
const views = viewChoices(definition);
|
|
3167
|
+
const effectiveFlexible = draft.flexible ?? isFlexible(definition);
|
|
3168
|
+
function requestClose() {
|
|
3169
|
+
if (confirming !== void 0) {
|
|
3170
|
+
setConfirming(void 0);
|
|
3171
|
+
} else if (dirty) {
|
|
3172
|
+
setConfirming("discard");
|
|
3173
|
+
} else {
|
|
3174
|
+
onClose();
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
function answer(yes) {
|
|
3178
|
+
const which = confirming;
|
|
3179
|
+
setConfirming(void 0);
|
|
3180
|
+
if (!yes || which === void 0) return;
|
|
3181
|
+
if (which === "discard") {
|
|
3182
|
+
onClose();
|
|
3183
|
+
} else if (which === "apply") {
|
|
3184
|
+
onApply(draftPatch);
|
|
3185
|
+
onClose();
|
|
3186
|
+
} else if (which === "reset") {
|
|
3187
|
+
onApply(EMPTY_PATCH);
|
|
3188
|
+
onClose();
|
|
3189
|
+
} else if (baseline !== void 0) {
|
|
3190
|
+
onApply(patchFrom(definition, draftFrom(baseline.settings)));
|
|
3191
|
+
onClose();
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
function setOption(key, value) {
|
|
3195
|
+
setDraft((current) => ({
|
|
3196
|
+
...current,
|
|
3197
|
+
options: { ...current.options, [key]: value }
|
|
3198
|
+
}));
|
|
3199
|
+
}
|
|
3200
|
+
const confirmMessage = {
|
|
3201
|
+
apply: labels.confirmApply,
|
|
3202
|
+
discard: labels.confirmDiscard,
|
|
3203
|
+
reset: labels.confirmResetOptions,
|
|
3204
|
+
revert: labels.confirmRevertOptions
|
|
3205
|
+
};
|
|
3206
|
+
const widths = [];
|
|
3207
|
+
for (let width = range.min; width <= range.max; width += 1)
|
|
3208
|
+
widths.push(width);
|
|
3209
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3210
|
+
"dialog",
|
|
3211
|
+
{
|
|
3212
|
+
ref: dialogRef,
|
|
3213
|
+
className: slot("dialog", "dwt-options-dialog"),
|
|
3214
|
+
"aria-labelledby": headingId,
|
|
3215
|
+
onCancel: (event) => {
|
|
3216
|
+
event.preventDefault();
|
|
3217
|
+
requestClose();
|
|
3218
|
+
},
|
|
3219
|
+
onClick: (event) => {
|
|
3220
|
+
if (event.target === dialogRef.current) requestClose();
|
|
3221
|
+
},
|
|
3222
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3223
|
+
"form",
|
|
3224
|
+
{
|
|
3225
|
+
className: "dwt-options",
|
|
3226
|
+
onSubmit: (event) => {
|
|
3227
|
+
event.preventDefault();
|
|
3228
|
+
if (canApply && confirming === void 0) setConfirming("apply");
|
|
3229
|
+
},
|
|
3230
|
+
children: [
|
|
3231
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { id: headingId, className: "dwt-options-heading", children: labels.optionsFor(definition.title) }),
|
|
3232
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dwt-options-field", children: [
|
|
3233
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: `${baseId}-title`, children: labels.optionsTitleField }),
|
|
3234
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3235
|
+
"input",
|
|
3236
|
+
{
|
|
3237
|
+
id: `${baseId}-title`,
|
|
3238
|
+
type: "text",
|
|
3239
|
+
value: draft.title,
|
|
3240
|
+
maxLength: MAX_WIDGET_TITLE_LENGTH,
|
|
3241
|
+
placeholder: definition.title,
|
|
3242
|
+
"aria-invalid": titleTaken,
|
|
3243
|
+
...titleTaken ? { "aria-describedby": `${baseId}-title-error` } : {},
|
|
3244
|
+
onChange: (event) => setDraft((current) => ({ ...current, title: event.target.value }))
|
|
3245
|
+
}
|
|
3246
|
+
),
|
|
3247
|
+
titleTaken && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3248
|
+
"p",
|
|
3249
|
+
{
|
|
3250
|
+
id: `${baseId}-title-error`,
|
|
3251
|
+
role: "alert",
|
|
3252
|
+
className: "dwt-options-error",
|
|
3253
|
+
children: labels.titleInUse
|
|
3254
|
+
}
|
|
3255
|
+
)
|
|
3256
|
+
] }),
|
|
3257
|
+
range.min < range.max && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dwt-options-field", children: [
|
|
3258
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: `${baseId}-width`, children: labels.optionsWidthField }),
|
|
3259
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3260
|
+
"select",
|
|
3261
|
+
{
|
|
3262
|
+
id: `${baseId}-width`,
|
|
3263
|
+
value: draft.width === void 0 ? "" : String(draft.width),
|
|
3264
|
+
onChange: (event) => {
|
|
3265
|
+
const value = event.target.value;
|
|
3266
|
+
setDraft((current) => ({
|
|
3267
|
+
...current,
|
|
3268
|
+
width: value === "" ? void 0 : Number(value)
|
|
3269
|
+
}));
|
|
3270
|
+
},
|
|
3271
|
+
children: [
|
|
3272
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: labels.widthDefault }),
|
|
3273
|
+
widths.map((width) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: String(width), children: labels.widthOption(width, widthShareLabel(width)) }, width))
|
|
3274
|
+
]
|
|
3275
|
+
}
|
|
3276
|
+
)
|
|
3277
|
+
] }),
|
|
3278
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "dwt-options-check", children: [
|
|
3279
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3280
|
+
"input",
|
|
3281
|
+
{
|
|
3282
|
+
type: "checkbox",
|
|
3283
|
+
checked: effectiveFlexible,
|
|
3284
|
+
onChange: (event) => setDraft((current) => ({
|
|
3285
|
+
...current,
|
|
3286
|
+
flexible: event.target.checked
|
|
3287
|
+
}))
|
|
3288
|
+
}
|
|
3289
|
+
),
|
|
3290
|
+
labels.optionsFlexible
|
|
3291
|
+
] }),
|
|
3292
|
+
views.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dwt-options-field", children: [
|
|
3293
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: `${baseId}-view`, children: labels.optionsViewField }),
|
|
3294
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3295
|
+
"select",
|
|
3296
|
+
{
|
|
3297
|
+
id: `${baseId}-view`,
|
|
3298
|
+
value: effectiveViewKey(definition, draft.view),
|
|
3299
|
+
onChange: (event) => {
|
|
3300
|
+
const value = event.target.value;
|
|
3301
|
+
setDraft((current) => ({ ...current, view: value }));
|
|
3302
|
+
},
|
|
3303
|
+
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
|
+
}
|
|
3305
|
+
)
|
|
3306
|
+
] }),
|
|
3307
|
+
(definition.options?.length ?? 0) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { className: "dwt-options-group", children: [
|
|
3308
|
+
/* @__PURE__ */ jsxRuntime.jsx("legend", { children: labels.optionsDeclared }),
|
|
3309
|
+
(definition.options ?? []).map((option) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3310
|
+
OptionField,
|
|
3311
|
+
{
|
|
3312
|
+
option,
|
|
3313
|
+
value: draft.options[option.key],
|
|
3314
|
+
id: `${baseId}-opt-${option.key}`,
|
|
3315
|
+
onChange: (value) => setOption(option.key, value)
|
|
3316
|
+
},
|
|
3317
|
+
option.key
|
|
3318
|
+
))
|
|
3319
|
+
] }),
|
|
3320
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "dwt-options-actions", children: confirming === void 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3321
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3322
|
+
"button",
|
|
3323
|
+
{
|
|
3324
|
+
type: "submit",
|
|
3325
|
+
className: slot("button", "dwt-button", "dwt-button-primary"),
|
|
3326
|
+
disabled: !canApply,
|
|
3327
|
+
children: labels.apply
|
|
3328
|
+
}
|
|
3329
|
+
),
|
|
3330
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3331
|
+
"button",
|
|
3332
|
+
{
|
|
3333
|
+
type: "button",
|
|
3334
|
+
className: slot("button", "dwt-button"),
|
|
3335
|
+
onClick: requestClose,
|
|
3336
|
+
children: labels.cancel
|
|
3337
|
+
}
|
|
3338
|
+
),
|
|
3339
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3340
|
+
"button",
|
|
3341
|
+
{
|
|
3342
|
+
type: "button",
|
|
3343
|
+
className: slot("button", "dwt-button"),
|
|
3344
|
+
disabled: !canReset,
|
|
3345
|
+
onClick: () => setConfirming("reset"),
|
|
3346
|
+
children: labels.resetToDefaults
|
|
3347
|
+
}
|
|
3348
|
+
),
|
|
3349
|
+
baseline !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2543
3350
|
"button",
|
|
2544
3351
|
{
|
|
2545
3352
|
type: "button",
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
close(false);
|
|
2552
|
-
item.onSelect();
|
|
2553
|
-
},
|
|
2554
|
-
children: item.label
|
|
2555
|
-
},
|
|
2556
|
-
item.key
|
|
3353
|
+
className: slot("button", "dwt-button"),
|
|
3354
|
+
disabled: !canRevert,
|
|
3355
|
+
onClick: () => setConfirming("revert"),
|
|
3356
|
+
children: labels.revertToBeforeEditing
|
|
3357
|
+
}
|
|
2557
3358
|
)
|
|
2558
|
-
)
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
3359
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
3360
|
+
InlineConfirm,
|
|
3361
|
+
{
|
|
3362
|
+
message: confirmMessage[confirming],
|
|
3363
|
+
onAnswer: answer
|
|
3364
|
+
}
|
|
3365
|
+
) })
|
|
3366
|
+
]
|
|
3367
|
+
}
|
|
3368
|
+
)
|
|
2562
3369
|
}
|
|
2563
3370
|
);
|
|
2564
3371
|
}
|
|
3372
|
+
function OptionField({
|
|
3373
|
+
option,
|
|
3374
|
+
value,
|
|
3375
|
+
id,
|
|
3376
|
+
onChange
|
|
3377
|
+
}) {
|
|
3378
|
+
const current = value ?? storedDefault(option);
|
|
3379
|
+
switch (option.type) {
|
|
3380
|
+
case "choice":
|
|
3381
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dwt-options-field", children: [
|
|
3382
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, children: option.label }),
|
|
3383
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3384
|
+
"select",
|
|
3385
|
+
{
|
|
3386
|
+
id,
|
|
3387
|
+
value: String(current ?? option.default),
|
|
3388
|
+
onChange: (event) => onChange(event.target.value),
|
|
3389
|
+
children: option.choices.map((choice) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: choice.value, children: choice.label }, choice.value))
|
|
3390
|
+
}
|
|
3391
|
+
)
|
|
3392
|
+
] });
|
|
3393
|
+
case "number":
|
|
3394
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3395
|
+
NumberField,
|
|
3396
|
+
{
|
|
3397
|
+
option,
|
|
3398
|
+
id,
|
|
3399
|
+
value: typeof current === "number" ? current : option.default,
|
|
3400
|
+
onChange
|
|
3401
|
+
}
|
|
3402
|
+
);
|
|
3403
|
+
case "boolean":
|
|
3404
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "dwt-options-check", children: [
|
|
3405
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3406
|
+
"input",
|
|
3407
|
+
{
|
|
3408
|
+
type: "checkbox",
|
|
3409
|
+
checked: current === true,
|
|
3410
|
+
onChange: (event) => onChange(event.target.checked)
|
|
3411
|
+
}
|
|
3412
|
+
),
|
|
3413
|
+
option.label
|
|
3414
|
+
] });
|
|
3415
|
+
case "text":
|
|
3416
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dwt-options-field", children: [
|
|
3417
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, children: option.label }),
|
|
3418
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3419
|
+
"input",
|
|
3420
|
+
{
|
|
3421
|
+
id,
|
|
3422
|
+
type: "text",
|
|
3423
|
+
value: typeof current === "string" ? current : "",
|
|
3424
|
+
maxLength: option.maxLength,
|
|
3425
|
+
onChange: (event) => onChange(event.target.value)
|
|
3426
|
+
}
|
|
3427
|
+
)
|
|
3428
|
+
] });
|
|
3429
|
+
case "color":
|
|
3430
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dwt-options-field", children: [
|
|
3431
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, children: option.label }),
|
|
3432
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3433
|
+
"input",
|
|
3434
|
+
{
|
|
3435
|
+
id,
|
|
3436
|
+
type: "color",
|
|
3437
|
+
value: typeof current === "string" ? current : option.default,
|
|
3438
|
+
onChange: (event) => onChange(event.target.value)
|
|
3439
|
+
}
|
|
3440
|
+
)
|
|
3441
|
+
] });
|
|
3442
|
+
case "dateRange":
|
|
3443
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3444
|
+
DateRangeField,
|
|
3445
|
+
{
|
|
3446
|
+
option,
|
|
3447
|
+
id,
|
|
3448
|
+
value: typeof current === "string" ? current : option.default,
|
|
3449
|
+
onChange
|
|
3450
|
+
}
|
|
3451
|
+
);
|
|
3452
|
+
case "sort":
|
|
3453
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3454
|
+
SortField,
|
|
3455
|
+
{
|
|
3456
|
+
option,
|
|
3457
|
+
id,
|
|
3458
|
+
value: typeof current === "string" ? current : void 0,
|
|
3459
|
+
onChange
|
|
3460
|
+
}
|
|
3461
|
+
);
|
|
3462
|
+
case "columns":
|
|
3463
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3464
|
+
ColumnsField,
|
|
3465
|
+
{
|
|
3466
|
+
option,
|
|
3467
|
+
value: Array.isArray(current) ? current : option.default,
|
|
3468
|
+
onChange
|
|
3469
|
+
}
|
|
3470
|
+
);
|
|
3471
|
+
}
|
|
3472
|
+
}
|
|
3473
|
+
function NumberField({
|
|
3474
|
+
option,
|
|
3475
|
+
id,
|
|
3476
|
+
value,
|
|
3477
|
+
onChange
|
|
3478
|
+
}) {
|
|
3479
|
+
const [text, setText] = react.useState(String(value));
|
|
3480
|
+
react.useEffect(() => {
|
|
3481
|
+
setText((typed) => Number(typed) === value ? typed : String(value));
|
|
3482
|
+
}, [value]);
|
|
3483
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dwt-options-field", children: [
|
|
3484
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, children: option.label }),
|
|
3485
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3486
|
+
"input",
|
|
3487
|
+
{
|
|
3488
|
+
id,
|
|
3489
|
+
type: "number",
|
|
3490
|
+
value: text,
|
|
3491
|
+
min: option.min,
|
|
3492
|
+
max: option.max,
|
|
3493
|
+
step: option.step ?? 1,
|
|
3494
|
+
onChange: (event) => {
|
|
3495
|
+
setText(event.target.value);
|
|
3496
|
+
const parsed = Number(event.target.value);
|
|
3497
|
+
if (event.target.value.trim() !== "" && coerceOptionValue(option, parsed) !== void 0) {
|
|
3498
|
+
onChange(parsed);
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
}
|
|
3502
|
+
)
|
|
3503
|
+
] });
|
|
3504
|
+
}
|
|
3505
|
+
var PRESET_LABELS = {
|
|
3506
|
+
today: "Today",
|
|
3507
|
+
yesterday: "Yesterday",
|
|
3508
|
+
last7: "Last 7 days",
|
|
3509
|
+
last30: "Last 30 days",
|
|
3510
|
+
last90: "Last 90 days",
|
|
3511
|
+
thisMonth: "This month",
|
|
3512
|
+
lastMonth: "Last month",
|
|
3513
|
+
thisYear: "This year",
|
|
3514
|
+
lastYear: "Last year"
|
|
3515
|
+
};
|
|
3516
|
+
function DateRangeField({
|
|
3517
|
+
option,
|
|
3518
|
+
id,
|
|
3519
|
+
value,
|
|
3520
|
+
onChange
|
|
3521
|
+
}) {
|
|
3522
|
+
const { labels } = useWidgetSettings();
|
|
3523
|
+
const presets = option.presets ?? DATE_RANGE_PRESETS.map((preset) => ({
|
|
3524
|
+
value: preset,
|
|
3525
|
+
label: PRESET_LABELS[preset]
|
|
3526
|
+
}));
|
|
3527
|
+
const known = presets.some((preset) => preset.value === value);
|
|
3528
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dwt-options-field", children: [
|
|
3529
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, children: option.label }),
|
|
3530
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3531
|
+
"select",
|
|
3532
|
+
{
|
|
3533
|
+
id,
|
|
3534
|
+
value,
|
|
3535
|
+
onChange: (event) => onChange(event.target.value),
|
|
3536
|
+
children: [
|
|
3537
|
+
!known && /* @__PURE__ */ jsxRuntime.jsx("option", { value, children: labels.customRange(value) }),
|
|
3538
|
+
presets.map((preset) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: preset.value, children: preset.label }, preset.value))
|
|
3539
|
+
]
|
|
3540
|
+
}
|
|
3541
|
+
)
|
|
3542
|
+
] });
|
|
3543
|
+
}
|
|
3544
|
+
function SortField({
|
|
3545
|
+
option,
|
|
3546
|
+
id,
|
|
3547
|
+
value,
|
|
3548
|
+
onChange
|
|
3549
|
+
}) {
|
|
3550
|
+
const { labels } = useWidgetSettings();
|
|
3551
|
+
const parsed = value === void 0 ? void 0 : parseSortValue(value);
|
|
3552
|
+
const column = parsed?.column ?? "";
|
|
3553
|
+
const direction = parsed?.direction ?? "desc";
|
|
3554
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dwt-options-field", children: [
|
|
3555
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, children: option.label }),
|
|
3556
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "dwt-options-pair", children: [
|
|
3557
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3558
|
+
"select",
|
|
3559
|
+
{
|
|
3560
|
+
id,
|
|
3561
|
+
value: column,
|
|
3562
|
+
onChange: (event) => onChange(`${event.target.value}:${direction}`),
|
|
3563
|
+
children: [
|
|
3564
|
+
option.default === void 0 && column === "" && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: labels.sortDefault }),
|
|
3565
|
+
option.columns.map((entry) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: entry.key, children: entry.label }, entry.key))
|
|
3566
|
+
]
|
|
3567
|
+
}
|
|
3568
|
+
),
|
|
3569
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3570
|
+
"select",
|
|
3571
|
+
{
|
|
3572
|
+
"aria-label": option.label,
|
|
3573
|
+
value: direction,
|
|
3574
|
+
disabled: column === "",
|
|
3575
|
+
onChange: (event) => onChange(`${column}:${event.target.value}`),
|
|
3576
|
+
children: [
|
|
3577
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "asc", children: labels.sortAscending }),
|
|
3578
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "desc", children: labels.sortDescending })
|
|
3579
|
+
]
|
|
3580
|
+
}
|
|
3581
|
+
)
|
|
3582
|
+
] })
|
|
3583
|
+
] });
|
|
3584
|
+
}
|
|
3585
|
+
function ColumnsField({
|
|
3586
|
+
option,
|
|
3587
|
+
value,
|
|
3588
|
+
onChange
|
|
3589
|
+
}) {
|
|
3590
|
+
const { labels } = useWidgetSettings();
|
|
3591
|
+
const slot = useSlotClassName();
|
|
3592
|
+
const ordered = [
|
|
3593
|
+
...value,
|
|
3594
|
+
...option.columns.map((c) => c.key).filter((key) => !value.includes(key))
|
|
3595
|
+
];
|
|
3596
|
+
const labelOf = (key) => option.columns.find((c) => c.key === key)?.label ?? key;
|
|
3597
|
+
function move(key, by) {
|
|
3598
|
+
const next = [...value];
|
|
3599
|
+
const from = next.indexOf(key);
|
|
3600
|
+
const to = from + by;
|
|
3601
|
+
if (from === -1 || to < 0 || to >= next.length) return;
|
|
3602
|
+
[next[from], next[to]] = [next[to], next[from]];
|
|
3603
|
+
onChange(next);
|
|
3604
|
+
}
|
|
3605
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { className: "dwt-options-columns", children: [
|
|
3606
|
+
/* @__PURE__ */ jsxRuntime.jsx("legend", { children: option.label }),
|
|
3607
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { children: ordered.map((key) => {
|
|
3608
|
+
const shown = value.includes(key);
|
|
3609
|
+
const name = labelOf(key);
|
|
3610
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("li", { children: [
|
|
3611
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
3612
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3613
|
+
"input",
|
|
3614
|
+
{
|
|
3615
|
+
type: "checkbox",
|
|
3616
|
+
checked: shown,
|
|
3617
|
+
disabled: shown && value.length === 1,
|
|
3618
|
+
onChange: (event) => onChange(
|
|
3619
|
+
event.target.checked ? [...value, key] : value.filter((k) => k !== key)
|
|
3620
|
+
)
|
|
3621
|
+
}
|
|
3622
|
+
),
|
|
3623
|
+
name
|
|
3624
|
+
] }),
|
|
3625
|
+
shown && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "dwt-options-move", children: [
|
|
3626
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3627
|
+
"button",
|
|
3628
|
+
{
|
|
3629
|
+
type: "button",
|
|
3630
|
+
className: slot("button", "dwt-button", "dwt-icon-button"),
|
|
3631
|
+
"aria-label": labels.moveColumnUp(name),
|
|
3632
|
+
title: labels.moveColumnUp(name),
|
|
3633
|
+
disabled: value.indexOf(key) === 0,
|
|
3634
|
+
onClick: () => move(key, -1),
|
|
3635
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\u2191" })
|
|
3636
|
+
}
|
|
3637
|
+
),
|
|
3638
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3639
|
+
"button",
|
|
3640
|
+
{
|
|
3641
|
+
type: "button",
|
|
3642
|
+
className: slot("button", "dwt-button", "dwt-icon-button"),
|
|
3643
|
+
"aria-label": labels.moveColumnDown(name),
|
|
3644
|
+
title: labels.moveColumnDown(name),
|
|
3645
|
+
disabled: value.indexOf(key) === value.length - 1,
|
|
3646
|
+
onClick: () => move(key, 1),
|
|
3647
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\u2193" })
|
|
3648
|
+
}
|
|
3649
|
+
)
|
|
3650
|
+
] })
|
|
3651
|
+
] }, key);
|
|
3652
|
+
}) })
|
|
3653
|
+
] });
|
|
3654
|
+
}
|
|
2565
3655
|
function nextSort(current, column) {
|
|
2566
3656
|
if (current?.column !== column) return { column, direction: "asc" };
|
|
2567
3657
|
return current.direction === "asc" ? { column, direction: "desc" } : void 0;
|
|
@@ -2989,57 +4079,6 @@ function DetailHost({
|
|
|
2989
4079
|
}
|
|
2990
4080
|
) });
|
|
2991
4081
|
}
|
|
2992
|
-
function InlineConfirm({
|
|
2993
|
-
message,
|
|
2994
|
-
onAnswer
|
|
2995
|
-
}) {
|
|
2996
|
-
const slot = useSlotClassName();
|
|
2997
|
-
const { labels } = useWidgetSettings();
|
|
2998
|
-
const noRef = react.useRef(null);
|
|
2999
|
-
react.useEffect(() => {
|
|
3000
|
-
noRef.current?.focus();
|
|
3001
|
-
}, []);
|
|
3002
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3003
|
-
"span",
|
|
3004
|
-
{
|
|
3005
|
-
role: "group",
|
|
3006
|
-
"aria-label": message,
|
|
3007
|
-
className: "dwt-confirm",
|
|
3008
|
-
onKeyDown: (event) => {
|
|
3009
|
-
if (event.key === "Escape") {
|
|
3010
|
-
event.stopPropagation();
|
|
3011
|
-
onAnswer(false);
|
|
3012
|
-
}
|
|
3013
|
-
},
|
|
3014
|
-
children: [
|
|
3015
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { role: "alert", className: "dwt-confirm-message", children: message }),
|
|
3016
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3017
|
-
"button",
|
|
3018
|
-
{
|
|
3019
|
-
type: "button",
|
|
3020
|
-
className: slot("button", "dwt-button", "dwt-icon-button"),
|
|
3021
|
-
"aria-label": labels.confirmYes,
|
|
3022
|
-
title: labels.confirmYes,
|
|
3023
|
-
onClick: () => onAnswer(true),
|
|
3024
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\u2713" })
|
|
3025
|
-
}
|
|
3026
|
-
),
|
|
3027
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3028
|
-
"button",
|
|
3029
|
-
{
|
|
3030
|
-
ref: noRef,
|
|
3031
|
-
type: "button",
|
|
3032
|
-
className: slot("button", "dwt-button", "dwt-icon-button"),
|
|
3033
|
-
"aria-label": labels.confirmNo,
|
|
3034
|
-
title: labels.confirmNo,
|
|
3035
|
-
onClick: () => onAnswer(false),
|
|
3036
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\u2715" })
|
|
3037
|
-
}
|
|
3038
|
-
)
|
|
3039
|
-
]
|
|
3040
|
-
}
|
|
3041
|
-
);
|
|
3042
|
-
}
|
|
3043
4082
|
function PageTabs({
|
|
3044
4083
|
pages,
|
|
3045
4084
|
activeKey,
|
|
@@ -3244,13 +4283,23 @@ function DashboardInner({
|
|
|
3244
4283
|
className
|
|
3245
4284
|
}) {
|
|
3246
4285
|
const slot = useSlotClassName();
|
|
3247
|
-
const { labels } = useWidgetSettings();
|
|
3248
|
-
const
|
|
4286
|
+
const { labels, locale } = useWidgetSettings();
|
|
4287
|
+
const effectiveWidgets = widgets.map((w) => {
|
|
4288
|
+
const settings = !overrideLocks && isOptionsLocked(w.definition) ? void 0 : savedLayout.settings?.[w.definition.key];
|
|
4289
|
+
const styled = settings === void 0 ? w : { ...w, definition: applySettings(w.definition, settings) };
|
|
4290
|
+
return applyView(styled, settings?.view, {
|
|
4291
|
+
...locale === void 0 ? {} : { locale }
|
|
4292
|
+
});
|
|
4293
|
+
});
|
|
4294
|
+
const definitions = effectiveWidgets.map(
|
|
3249
4295
|
(w) => overrideLocks ? withoutLock(w.definition) : w.definition
|
|
3250
4296
|
);
|
|
3251
4297
|
const layout = enforceLocks(definitions, savedLayout);
|
|
3252
4298
|
const byKey = new Map(
|
|
3253
|
-
|
|
4299
|
+
effectiveWidgets.map((w) => [w.definition.key, w])
|
|
4300
|
+
);
|
|
4301
|
+
const authorDefinitions = new Map(
|
|
4302
|
+
widgets.map((w) => [w.definition.key, w.definition])
|
|
3254
4303
|
);
|
|
3255
4304
|
const pages = pageList(layout);
|
|
3256
4305
|
const paged = pages.length >= 2;
|
|
@@ -3295,6 +4344,7 @@ function DashboardInner({
|
|
|
3295
4344
|
const minimized = editable ? visible.filter((definition) => isMinimized(view, definition.key)) : [];
|
|
3296
4345
|
const shown = editable ? visible.filter((definition) => !isMinimized(view, definition.key)) : visible;
|
|
3297
4346
|
const [detailKey, setDetailKey] = react.useState(void 0);
|
|
4347
|
+
const [optionsKey, setOptionsKey] = react.useState(void 0);
|
|
3298
4348
|
const gridRef = react.useRef(null);
|
|
3299
4349
|
const twelve = usesWidthColumns(shown);
|
|
3300
4350
|
const spans = useFillSpans(
|
|
@@ -3434,7 +4484,14 @@ function DashboardInner({
|
|
|
3434
4484
|
const which = pending;
|
|
3435
4485
|
setConfirming(void 0);
|
|
3436
4486
|
if (yes && which === "reset") {
|
|
3437
|
-
|
|
4487
|
+
const arranged = enforceLocks(
|
|
4488
|
+
definitions,
|
|
4489
|
+
defaultLayout ?? EMPTY_LAYOUT
|
|
4490
|
+
);
|
|
4491
|
+
const { settings: _defaultSettings, ...arrangement } = arranged;
|
|
4492
|
+
change(
|
|
4493
|
+
layout.settings === void 0 ? arrangement : { ...arrangement, settings: layout.settings }
|
|
4494
|
+
);
|
|
3438
4495
|
} else if (yes && which === "revert" && snapshot !== void 0) {
|
|
3439
4496
|
change(snapshot);
|
|
3440
4497
|
}
|
|
@@ -3731,6 +4788,15 @@ function DashboardInner({
|
|
|
3731
4788
|
onSelect: () => changePage(moveWidgetBy(pageDefs, view, key, 1))
|
|
3732
4789
|
}
|
|
3733
4790
|
],
|
|
4791
|
+
...lock.options ? [] : [
|
|
4792
|
+
{
|
|
4793
|
+
kind: "action",
|
|
4794
|
+
key: "options",
|
|
4795
|
+
label: labels.menuOptions,
|
|
4796
|
+
ariaLabel: labels.optionsFor(title),
|
|
4797
|
+
onSelect: () => setOptionsKey(key)
|
|
4798
|
+
}
|
|
4799
|
+
],
|
|
3734
4800
|
...pageTargets,
|
|
3735
4801
|
...lock.hide ? [] : [
|
|
3736
4802
|
{
|
|
@@ -3819,6 +4885,30 @@ function DashboardInner({
|
|
|
3819
4885
|
]
|
|
3820
4886
|
}
|
|
3821
4887
|
),
|
|
4888
|
+
optionsKey !== void 0 && authorDefinitions.get(optionsKey) !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4889
|
+
WidgetOptionsDialog,
|
|
4890
|
+
{
|
|
4891
|
+
definition: authorDefinitions.get(optionsKey),
|
|
4892
|
+
settings: layout.settings?.[optionsKey],
|
|
4893
|
+
...snapshot === void 0 ? {} : { baseline: { settings: snapshot.settings?.[optionsKey] } },
|
|
4894
|
+
isTitleInUse: (title) => isTitleInUse(
|
|
4895
|
+
widgets.map((w) => w.definition),
|
|
4896
|
+
layout,
|
|
4897
|
+
optionsKey,
|
|
4898
|
+
title
|
|
4899
|
+
),
|
|
4900
|
+
onApply: (patch) => {
|
|
4901
|
+
change(setWidgetSettings(layout, optionsKey, patch));
|
|
4902
|
+
setAnnouncement(
|
|
4903
|
+
labels.optionsSaved(
|
|
4904
|
+
authorDefinitions.get(optionsKey)?.title ?? optionsKey
|
|
4905
|
+
)
|
|
4906
|
+
);
|
|
4907
|
+
},
|
|
4908
|
+
onClose: () => setOptionsKey(void 0)
|
|
4909
|
+
},
|
|
4910
|
+
optionsKey
|
|
4911
|
+
),
|
|
3822
4912
|
detailKey !== void 0 && byKey.get(detailKey) !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3823
4913
|
DetailHost,
|
|
3824
4914
|
{
|
|
@@ -4044,45 +5134,6 @@ function useStoredLayout(options) {
|
|
|
4044
5134
|
};
|
|
4045
5135
|
}
|
|
4046
5136
|
|
|
4047
|
-
// src/core/payload.ts
|
|
4048
|
-
var WIDGET_KINDS = [
|
|
4049
|
-
"TEXT",
|
|
4050
|
-
"KPI",
|
|
4051
|
-
"GAUGE",
|
|
4052
|
-
"TABLE",
|
|
4053
|
-
"BAR_LIST",
|
|
4054
|
-
"ALERT_LIST",
|
|
4055
|
-
"GRAPH"
|
|
4056
|
-
];
|
|
4057
|
-
function isWidgetKind(value) {
|
|
4058
|
-
return typeof value === "string" && WIDGET_KINDS.includes(value);
|
|
4059
|
-
}
|
|
4060
|
-
var VALUE_FORMATS = ["number", "currency", "percent"];
|
|
4061
|
-
var MAX_GRAPH_SERIES = 8;
|
|
4062
|
-
var DEFAULT_EMPTY_TEXT = "Nothing to show yet.";
|
|
4063
|
-
function isWidgetEmptyState(payload) {
|
|
4064
|
-
return "empty" in payload && payload.empty === true;
|
|
4065
|
-
}
|
|
4066
|
-
function emptyTextFor(payload) {
|
|
4067
|
-
if (isWidgetEmptyState(payload)) {
|
|
4068
|
-
return payload.text;
|
|
4069
|
-
}
|
|
4070
|
-
switch (payload.kind) {
|
|
4071
|
-
case "TABLE":
|
|
4072
|
-
return payload.rows.length === 0 ? DEFAULT_EMPTY_TEXT : null;
|
|
4073
|
-
case "BAR_LIST":
|
|
4074
|
-
return payload.items.length === 0 ? DEFAULT_EMPTY_TEXT : null;
|
|
4075
|
-
case "ALERT_LIST":
|
|
4076
|
-
return payload.items.length === 0 ? payload.emptyText : null;
|
|
4077
|
-
case "GRAPH":
|
|
4078
|
-
return payload.series.every((series) => series.points.length === 0) ? DEFAULT_EMPTY_TEXT : null;
|
|
4079
|
-
case "TEXT":
|
|
4080
|
-
case "KPI":
|
|
4081
|
-
case "GAUGE":
|
|
4082
|
-
return null;
|
|
4083
|
-
}
|
|
4084
|
-
}
|
|
4085
|
-
|
|
4086
5137
|
// src/core/validate.ts
|
|
4087
5138
|
var MAX_REPORTED_PROBLEMS = 10;
|
|
4088
5139
|
function describeType2(value) {
|
|
@@ -4093,7 +5144,7 @@ function describeType2(value) {
|
|
|
4093
5144
|
}
|
|
4094
5145
|
return typeof value;
|
|
4095
5146
|
}
|
|
4096
|
-
function
|
|
5147
|
+
function isRecord2(value) {
|
|
4097
5148
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4098
5149
|
}
|
|
4099
5150
|
function join(path, field) {
|
|
@@ -4175,7 +5226,7 @@ function requireOneOf(record, field, options, path, problems) {
|
|
|
4175
5226
|
function eachRecord(items, path, problems, check) {
|
|
4176
5227
|
items.forEach((item, index) => {
|
|
4177
5228
|
const itemPath = `${path}[${index}]`;
|
|
4178
|
-
if (
|
|
5229
|
+
if (isRecord2(item)) {
|
|
4179
5230
|
check(item, itemPath);
|
|
4180
5231
|
} else {
|
|
4181
5232
|
problems.push(
|
|
@@ -4329,7 +5380,7 @@ function formatProblems(prefix, problems) {
|
|
|
4329
5380
|
}
|
|
4330
5381
|
function validateWidgetData(value, options = {}) {
|
|
4331
5382
|
const subject = options.widgetKey === void 0 ? "Widget data" : `Widget ${JSON.stringify(options.widgetKey)}`;
|
|
4332
|
-
if (!
|
|
5383
|
+
if (!isRecord2(value)) {
|
|
4333
5384
|
return err(`${subject}: must be an object, got ${describeType2(value)}.`);
|
|
4334
5385
|
}
|
|
4335
5386
|
if (value["empty"] === true) {
|
|
@@ -4792,6 +5843,7 @@ exports.WidgetDetail = WidgetDetail;
|
|
|
4792
5843
|
exports.WidgetDetailDialog = WidgetDetailDialog;
|
|
4793
5844
|
exports.WidgetGrid = WidgetGrid;
|
|
4794
5845
|
exports.WidgetLink = WidgetLink;
|
|
5846
|
+
exports.WidgetOptionsDialog = WidgetOptionsDialog;
|
|
4795
5847
|
exports.WidgetSettingsProvider = WidgetSettingsProvider;
|
|
4796
5848
|
exports.barPath = barPath;
|
|
4797
5849
|
exports.buildChartModel = buildChartModel;
|