@richardmcquiston01/dashboard-widgets-toolkit 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core.cjs +2225 -1904
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +57 -3
- package/dist/core.d.ts +57 -3
- package/dist/core.js +2211 -1905
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +2225 -1904
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2211 -1905
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +381 -23
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +30 -2
- package/dist/react.d.ts +30 -2
- package/dist/react.js +381 -23
- package/dist/react.js.map +1 -1
- package/dist/{storage-DwJ-vGJg.d.cts → storage-CRBfjiVZ.d.cts} +184 -62
- package/dist/{storage-DwJ-vGJg.d.ts → storage-CRBfjiVZ.d.ts} +184 -62
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,86 +1,3 @@
|
|
|
1
|
-
// src/core/payload.ts
|
|
2
|
-
var WIDGET_KINDS = [
|
|
3
|
-
"TEXT",
|
|
4
|
-
"KPI",
|
|
5
|
-
"GAUGE",
|
|
6
|
-
"TABLE",
|
|
7
|
-
"BAR_LIST",
|
|
8
|
-
"ALERT_LIST",
|
|
9
|
-
"GRAPH"
|
|
10
|
-
];
|
|
11
|
-
function isWidgetKind(value) {
|
|
12
|
-
return typeof value === "string" && WIDGET_KINDS.includes(value);
|
|
13
|
-
}
|
|
14
|
-
var VALUE_FORMATS = ["number", "currency", "percent"];
|
|
15
|
-
var MAX_GRAPH_SERIES = 8;
|
|
16
|
-
var DEFAULT_EMPTY_TEXT = "Nothing to show yet.";
|
|
17
|
-
function emptyWidget(text = DEFAULT_EMPTY_TEXT) {
|
|
18
|
-
return { empty: true, text };
|
|
19
|
-
}
|
|
20
|
-
function isWidgetEmptyState(payload) {
|
|
21
|
-
return "empty" in payload && payload.empty === true;
|
|
22
|
-
}
|
|
23
|
-
function emptyTextFor(payload) {
|
|
24
|
-
if (isWidgetEmptyState(payload)) {
|
|
25
|
-
return payload.text;
|
|
26
|
-
}
|
|
27
|
-
switch (payload.kind) {
|
|
28
|
-
case "TABLE":
|
|
29
|
-
return payload.rows.length === 0 ? DEFAULT_EMPTY_TEXT : null;
|
|
30
|
-
case "BAR_LIST":
|
|
31
|
-
return payload.items.length === 0 ? DEFAULT_EMPTY_TEXT : null;
|
|
32
|
-
case "ALERT_LIST":
|
|
33
|
-
return payload.items.length === 0 ? payload.emptyText : null;
|
|
34
|
-
case "GRAPH":
|
|
35
|
-
return payload.series.every((series) => series.points.length === 0) ? DEFAULT_EMPTY_TEXT : null;
|
|
36
|
-
case "TEXT":
|
|
37
|
-
case "KPI":
|
|
38
|
-
case "GAUGE":
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// src/core/compat.ts
|
|
44
|
-
function isStringArray(value) {
|
|
45
|
-
return Array.isArray(value) && value.every((v) => typeof v === "string");
|
|
46
|
-
}
|
|
47
|
-
function upgradeLegacyWidgetData(value) {
|
|
48
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
49
|
-
return value;
|
|
50
|
-
}
|
|
51
|
-
const record = value;
|
|
52
|
-
if (record["kind"] === "TABLE" && isStringArray(record["columns"]) && Array.isArray(record["rows"]) && record["rows"].every(isStringArray)) {
|
|
53
|
-
const rows = record["rows"];
|
|
54
|
-
return {
|
|
55
|
-
...record,
|
|
56
|
-
columns: record["columns"].map((label) => ({ label })),
|
|
57
|
-
rows: rows.map((row) => row.map((text) => ({ text })))
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
if (record["kind"] === "GRAPH" && Array.isArray(record["points"]) && record["series"] === void 0) {
|
|
61
|
-
const { points, ...rest } = record;
|
|
62
|
-
return {
|
|
63
|
-
...rest,
|
|
64
|
-
series: [{ name: "Value", points }],
|
|
65
|
-
chartType: "bar",
|
|
66
|
-
valueFormat: "number"
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
return value;
|
|
70
|
-
}
|
|
71
|
-
function fromLegacyDefinition(legacy) {
|
|
72
|
-
if (!isWidgetKind(legacy.viewType)) {
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
return {
|
|
76
|
-
key: legacy.widgetKey,
|
|
77
|
-
title: legacy.title,
|
|
78
|
-
...legacy.description === null ? {} : { description: legacy.description },
|
|
79
|
-
kind: legacy.viewType,
|
|
80
|
-
sortOrder: legacy.sortOrder
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
1
|
// src/core/definition.ts
|
|
85
2
|
var WIDGET_SIZES = ["small", "medium", "large", "full"];
|
|
86
3
|
var WIDGET_FILLS = ["height", "width", "both"];
|
|
@@ -88,13 +5,15 @@ var NOT_LOCKED = Object.freeze({
|
|
|
88
5
|
move: false,
|
|
89
6
|
hide: false,
|
|
90
7
|
minimize: false,
|
|
91
|
-
options: false
|
|
8
|
+
options: false,
|
|
9
|
+
clone: false
|
|
92
10
|
});
|
|
93
11
|
var FULLY_LOCKED = Object.freeze({
|
|
94
12
|
move: true,
|
|
95
13
|
hide: true,
|
|
96
14
|
minimize: true,
|
|
97
|
-
options: true
|
|
15
|
+
options: true,
|
|
16
|
+
clone: true
|
|
98
17
|
});
|
|
99
18
|
function resolveWidgetLock(definition) {
|
|
100
19
|
const setting = definition.locked;
|
|
@@ -104,7 +23,8 @@ function resolveWidgetLock(definition) {
|
|
|
104
23
|
move: setting.move === true,
|
|
105
24
|
hide: setting.hide === true,
|
|
106
25
|
minimize: setting.minimize === true,
|
|
107
|
-
options: setting.options === true
|
|
26
|
+
options: setting.options === true,
|
|
27
|
+
clone: setting.clone === true
|
|
108
28
|
};
|
|
109
29
|
}
|
|
110
30
|
function defineWidget(definition) {
|
|
@@ -128,6 +48,103 @@ function isWidgetVisibleTo(definition, roles) {
|
|
|
128
48
|
return definition.roles.some((role) => roles.includes(role));
|
|
129
49
|
}
|
|
130
50
|
|
|
51
|
+
// src/core/grid.ts
|
|
52
|
+
var WIDTH_COLUMNS = 12;
|
|
53
|
+
var MIN_WIDGET_WIDTH = 2;
|
|
54
|
+
var MAX_WIDGET_WIDTH = 12;
|
|
55
|
+
function isWidgetWidth(value) {
|
|
56
|
+
return typeof value === "number" && Number.isInteger(value) && value >= MIN_WIDGET_WIDTH && value <= MAX_WIDGET_WIDTH;
|
|
57
|
+
}
|
|
58
|
+
function widthForSize(size) {
|
|
59
|
+
switch (size) {
|
|
60
|
+
case "small":
|
|
61
|
+
return 3;
|
|
62
|
+
case "large":
|
|
63
|
+
return 6;
|
|
64
|
+
case "full":
|
|
65
|
+
return 12;
|
|
66
|
+
default:
|
|
67
|
+
return 4;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function itemWidth(item) {
|
|
71
|
+
return isWidgetWidth(item.width) ? item.width : widthForSize(item.defaultSize);
|
|
72
|
+
}
|
|
73
|
+
function usesWidthColumns(items) {
|
|
74
|
+
return items.some((item) => item.width !== void 0);
|
|
75
|
+
}
|
|
76
|
+
function fillsWidth(fill) {
|
|
77
|
+
return fill === "width" || fill === "both";
|
|
78
|
+
}
|
|
79
|
+
function fillsHeight(fill) {
|
|
80
|
+
return fill === "height" || fill === "both";
|
|
81
|
+
}
|
|
82
|
+
function baseColumnSpan(size, columns, largeSpan = 2) {
|
|
83
|
+
if (size === "full") return columns;
|
|
84
|
+
if (size === "large") return Math.min(largeSpan, columns);
|
|
85
|
+
return 1;
|
|
86
|
+
}
|
|
87
|
+
function fillColumnSpans(items, columns, largeSpan = 2) {
|
|
88
|
+
return placeAndFill(
|
|
89
|
+
items,
|
|
90
|
+
columns,
|
|
91
|
+
(item, trackCount) => baseColumnSpan(item.defaultSize, trackCount, largeSpan)
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
function fillWidthSpans(items) {
|
|
95
|
+
return placeAndFill(items, WIDTH_COLUMNS, itemWidth);
|
|
96
|
+
}
|
|
97
|
+
function placeRows(items) {
|
|
98
|
+
return placeAndFillRows(items, WIDTH_COLUMNS, itemWidth).rows;
|
|
99
|
+
}
|
|
100
|
+
function fitCount(items, maxRows) {
|
|
101
|
+
const rows = placeRows(items);
|
|
102
|
+
let count = 0;
|
|
103
|
+
for (const row of rows.slice(0, Math.max(0, Math.floor(maxRows)))) {
|
|
104
|
+
count += row.keys.length;
|
|
105
|
+
}
|
|
106
|
+
return count;
|
|
107
|
+
}
|
|
108
|
+
function placeAndFill(items, columns, spanOf) {
|
|
109
|
+
return placeAndFillRows(items, columns, spanOf).spans;
|
|
110
|
+
}
|
|
111
|
+
function placeAndFillRows(items, columns, spanOf) {
|
|
112
|
+
const trackCount = Math.max(1, Math.floor(columns));
|
|
113
|
+
const spans = /* @__PURE__ */ new Map();
|
|
114
|
+
const rows = [];
|
|
115
|
+
let row = [];
|
|
116
|
+
let used = 0;
|
|
117
|
+
function closeRow() {
|
|
118
|
+
if (row.length === 0) return;
|
|
119
|
+
const fillers = row.filter(({ item }) => fillsWidth(item.fill));
|
|
120
|
+
const leftover = trackCount - used;
|
|
121
|
+
const finalSpans = /* @__PURE__ */ new Map();
|
|
122
|
+
if (fillers.length > 0) {
|
|
123
|
+
const share = Math.floor(leftover / fillers.length);
|
|
124
|
+
const remainder = leftover % fillers.length;
|
|
125
|
+
fillers.forEach(({ item, span }, index) => {
|
|
126
|
+
const grown = span + share + (index < remainder ? 1 : 0);
|
|
127
|
+
spans.set(item.key, grown);
|
|
128
|
+
finalSpans.set(item.key, grown);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
rows.push({
|
|
132
|
+
keys: row.map(({ item }) => item.key),
|
|
133
|
+
spans: row.map(({ item, span }) => finalSpans.get(item.key) ?? span)
|
|
134
|
+
});
|
|
135
|
+
row = [];
|
|
136
|
+
used = 0;
|
|
137
|
+
}
|
|
138
|
+
for (const item of items) {
|
|
139
|
+
const span = Math.min(spanOf(item, trackCount), trackCount);
|
|
140
|
+
if (used + span > trackCount && row.length > 0) closeRow();
|
|
141
|
+
row.push({ item, span });
|
|
142
|
+
used += span;
|
|
143
|
+
}
|
|
144
|
+
closeRow();
|
|
145
|
+
return { spans, rows };
|
|
146
|
+
}
|
|
147
|
+
|
|
131
148
|
// src/core/format.ts
|
|
132
149
|
var DEFAULT_CURRENCY = "USD";
|
|
133
150
|
function localeArgument(locale) {
|
|
@@ -213,6 +230,48 @@ function kpiDelta(current, previous, options = {}) {
|
|
|
213
230
|
};
|
|
214
231
|
}
|
|
215
232
|
|
|
233
|
+
// src/core/payload.ts
|
|
234
|
+
var WIDGET_KINDS = [
|
|
235
|
+
"TEXT",
|
|
236
|
+
"KPI",
|
|
237
|
+
"GAUGE",
|
|
238
|
+
"TABLE",
|
|
239
|
+
"BAR_LIST",
|
|
240
|
+
"ALERT_LIST",
|
|
241
|
+
"GRAPH"
|
|
242
|
+
];
|
|
243
|
+
function isWidgetKind(value) {
|
|
244
|
+
return typeof value === "string" && WIDGET_KINDS.includes(value);
|
|
245
|
+
}
|
|
246
|
+
var VALUE_FORMATS = ["number", "currency", "percent"];
|
|
247
|
+
var MAX_GRAPH_SERIES = 8;
|
|
248
|
+
var DEFAULT_EMPTY_TEXT = "Nothing to show yet.";
|
|
249
|
+
function emptyWidget(text = DEFAULT_EMPTY_TEXT) {
|
|
250
|
+
return { empty: true, text };
|
|
251
|
+
}
|
|
252
|
+
function isWidgetEmptyState(payload) {
|
|
253
|
+
return "empty" in payload && payload.empty === true;
|
|
254
|
+
}
|
|
255
|
+
function emptyTextFor(payload) {
|
|
256
|
+
if (isWidgetEmptyState(payload)) {
|
|
257
|
+
return payload.text;
|
|
258
|
+
}
|
|
259
|
+
switch (payload.kind) {
|
|
260
|
+
case "TABLE":
|
|
261
|
+
return payload.rows.length === 0 ? DEFAULT_EMPTY_TEXT : null;
|
|
262
|
+
case "BAR_LIST":
|
|
263
|
+
return payload.items.length === 0 ? DEFAULT_EMPTY_TEXT : null;
|
|
264
|
+
case "ALERT_LIST":
|
|
265
|
+
return payload.items.length === 0 ? payload.emptyText : null;
|
|
266
|
+
case "GRAPH":
|
|
267
|
+
return payload.series.every((series) => series.points.length === 0) ? DEFAULT_EMPTY_TEXT : null;
|
|
268
|
+
case "TEXT":
|
|
269
|
+
case "KPI":
|
|
270
|
+
case "GAUGE":
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
216
275
|
// src/core/result.ts
|
|
217
276
|
function ok(value) {
|
|
218
277
|
return { ok: true, value };
|
|
@@ -235,20 +294,308 @@ function describeUnknownError(cause) {
|
|
|
235
294
|
}
|
|
236
295
|
}
|
|
237
296
|
|
|
238
|
-
// src/core/
|
|
239
|
-
var
|
|
240
|
-
var
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
297
|
+
// src/core/views.ts
|
|
298
|
+
var BASE_VIEW_KEY = "base";
|
|
299
|
+
var MAX_WIDGET_VIEWS = 8;
|
|
300
|
+
var VIEW_CONVERSIONS = {
|
|
301
|
+
tableToBarList: { from: "TABLE", to: "BAR_LIST" },
|
|
302
|
+
tableToGraph: { from: "TABLE", to: "GRAPH" },
|
|
303
|
+
barListToTable: { from: "BAR_LIST", to: "TABLE" },
|
|
304
|
+
graphToTable: { from: "GRAPH", to: "TABLE" },
|
|
305
|
+
barListToGraph: { from: "BAR_LIST", to: "GRAPH" }
|
|
306
|
+
};
|
|
307
|
+
var KIND_LABELS = {
|
|
308
|
+
TEXT: "Text",
|
|
309
|
+
KPI: "Number",
|
|
310
|
+
GAUGE: "Gauge",
|
|
311
|
+
TABLE: "Table",
|
|
312
|
+
BAR_LIST: "Bars",
|
|
313
|
+
ALERT_LIST: "List",
|
|
314
|
+
GRAPH: "Chart"
|
|
315
|
+
};
|
|
316
|
+
function kindLabel(kind) {
|
|
317
|
+
return KIND_LABELS[kind];
|
|
246
318
|
}
|
|
247
|
-
function
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
319
|
+
function hasViews(item) {
|
|
320
|
+
return item.views !== void 0 && item.views.length > 0;
|
|
321
|
+
}
|
|
322
|
+
function findView(item, key) {
|
|
323
|
+
return item.views?.find((view) => view.key === key);
|
|
324
|
+
}
|
|
325
|
+
function viewChoices(item) {
|
|
326
|
+
if (!hasViews(item)) return [];
|
|
327
|
+
return [
|
|
328
|
+
{
|
|
329
|
+
key: BASE_VIEW_KEY,
|
|
330
|
+
label: item.baseViewLabel ?? kindLabel(item.kind),
|
|
331
|
+
kind: item.kind
|
|
332
|
+
},
|
|
333
|
+
...(item.views ?? []).map(({ key, label, kind }) => ({
|
|
334
|
+
key,
|
|
335
|
+
label,
|
|
336
|
+
kind
|
|
337
|
+
}))
|
|
338
|
+
];
|
|
339
|
+
}
|
|
340
|
+
function isProviderView(view) {
|
|
341
|
+
return view.convert === void 0;
|
|
342
|
+
}
|
|
343
|
+
function isKnownView(item, key) {
|
|
344
|
+
return key !== void 0 && (key === BASE_VIEW_KEY || findView(item, key) !== void 0);
|
|
345
|
+
}
|
|
346
|
+
function effectiveViewKey(item, chosen) {
|
|
347
|
+
if (!hasViews(item)) return BASE_VIEW_KEY;
|
|
348
|
+
if (isKnownView(item, chosen)) return chosen;
|
|
349
|
+
if (isKnownView(item, item.defaultView)) return item.defaultView;
|
|
350
|
+
return BASE_VIEW_KEY;
|
|
351
|
+
}
|
|
352
|
+
function viewKind(item, chosen) {
|
|
353
|
+
return findView(item, effectiveViewKey(item, chosen))?.kind ?? item.kind;
|
|
354
|
+
}
|
|
355
|
+
function loadViewKey(item, chosen) {
|
|
356
|
+
const key = effectiveViewKey(item, chosen);
|
|
357
|
+
const view = findView(item, key);
|
|
358
|
+
return view !== void 0 && isProviderView(view) ? key : BASE_VIEW_KEY;
|
|
359
|
+
}
|
|
360
|
+
function loadViewKind(item, chosen) {
|
|
361
|
+
return findView(item, loadViewKey(item, chosen))?.kind ?? item.kind;
|
|
362
|
+
}
|
|
363
|
+
function cellValue(cell) {
|
|
364
|
+
const value = cell?.value;
|
|
365
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
366
|
+
}
|
|
367
|
+
function columnName(table, index) {
|
|
368
|
+
return table.columns[index]?.label ?? `#${index + 1}`;
|
|
369
|
+
}
|
|
370
|
+
function checkColumn(table, index, field, context) {
|
|
371
|
+
if (!Number.isInteger(index) || index < 0 || index >= table.columns.length) {
|
|
372
|
+
return `${context.where}: ${field} column ${index} does not exist (the table has ${table.columns.length} columns, numbered from 0).`;
|
|
373
|
+
}
|
|
374
|
+
return void 0;
|
|
375
|
+
}
|
|
376
|
+
function numericColumn(table, index, context) {
|
|
377
|
+
const numbers = [];
|
|
378
|
+
for (const [rowIndex, row] of table.rows.entries()) {
|
|
379
|
+
const value = cellValue(row[index]);
|
|
380
|
+
if (value === void 0) {
|
|
381
|
+
return err(
|
|
382
|
+
`${context.where}: row ${rowIndex + 1}, column ${index + 1} ("${columnName(table, index)}") has no numeric value. Add value to the cells.`
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
numbers.push(value);
|
|
386
|
+
}
|
|
387
|
+
return ok(numbers);
|
|
388
|
+
}
|
|
389
|
+
function tableToBarList(table, conversion, context) {
|
|
390
|
+
const bad = checkColumn(table, conversion.label, "label", context) ?? checkColumn(table, conversion.value, "value", context);
|
|
391
|
+
if (bad !== void 0) return err(bad);
|
|
392
|
+
const numbers = numericColumn(table, conversion.value, context);
|
|
393
|
+
if (!numbers.ok) return numbers;
|
|
394
|
+
const items = table.rows.map((row, index) => ({
|
|
395
|
+
label: row[conversion.label]?.text ?? "",
|
|
396
|
+
value: numbers.value[index],
|
|
397
|
+
display: row[conversion.value]?.text ?? String(numbers.value[index])
|
|
398
|
+
}));
|
|
399
|
+
return ok({ kind: "BAR_LIST", items });
|
|
400
|
+
}
|
|
401
|
+
function tableToGraph(table, conversion, context) {
|
|
402
|
+
if (conversion.values.length === 0 || conversion.values.length > MAX_GRAPH_SERIES) {
|
|
403
|
+
return err(
|
|
404
|
+
`${context.where}: a chart needs 1 to ${MAX_GRAPH_SERIES} series columns, got ${conversion.values.length}.`
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
let bad = checkColumn(
|
|
408
|
+
table,
|
|
409
|
+
conversion.label,
|
|
410
|
+
"label",
|
|
411
|
+
context
|
|
412
|
+
);
|
|
413
|
+
for (const index of conversion.values) {
|
|
414
|
+
bad ??= checkColumn(table, index, "series", context);
|
|
415
|
+
}
|
|
416
|
+
if (bad !== void 0) return err(bad);
|
|
417
|
+
const series = [];
|
|
418
|
+
for (const index of conversion.values) {
|
|
419
|
+
const numbers = numericColumn(table, index, context);
|
|
420
|
+
if (!numbers.ok) return numbers;
|
|
421
|
+
series.push({
|
|
422
|
+
name: columnName(table, index),
|
|
423
|
+
points: table.rows.map((row, rowIndex) => ({
|
|
424
|
+
label: row[conversion.label]?.text ?? "",
|
|
425
|
+
value: numbers.value[rowIndex]
|
|
426
|
+
}))
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
return ok({
|
|
430
|
+
kind: "GRAPH",
|
|
431
|
+
series,
|
|
432
|
+
chartType: conversion.chartType,
|
|
433
|
+
valueFormat: conversion.valueFormat ?? "number",
|
|
434
|
+
...conversion.currency === void 0 ? {} : { currency: conversion.currency },
|
|
435
|
+
xLabel: columnName(table, conversion.label)
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
function barListToTable(list, conversion) {
|
|
439
|
+
return ok({
|
|
440
|
+
kind: "TABLE",
|
|
441
|
+
columns: [
|
|
442
|
+
{ label: conversion.labelHeading ?? "Name" },
|
|
443
|
+
{ label: conversion.valueHeading ?? "Value", numeric: true }
|
|
444
|
+
],
|
|
445
|
+
rows: list.items.map((item) => [
|
|
446
|
+
{ text: item.label },
|
|
447
|
+
{ text: item.display ?? String(item.value), value: item.value }
|
|
448
|
+
])
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
function graphToTable(graph, context) {
|
|
452
|
+
const first = graph.series[0];
|
|
453
|
+
if (first === void 0)
|
|
454
|
+
return err(`${context.where}: the chart has no series.`);
|
|
455
|
+
const format = {
|
|
456
|
+
...graph.currency === void 0 ? {} : { currency: graph.currency },
|
|
457
|
+
...context.locale === void 0 ? {} : { locale: context.locale }
|
|
458
|
+
};
|
|
459
|
+
return ok({
|
|
460
|
+
kind: "TABLE",
|
|
461
|
+
columns: [
|
|
462
|
+
{ label: graph.xLabel ?? "Label" },
|
|
463
|
+
...graph.series.map((series) => ({ label: series.name, numeric: true }))
|
|
464
|
+
],
|
|
465
|
+
rows: first.points.map((point, index) => [
|
|
466
|
+
{ text: point.label },
|
|
467
|
+
...graph.series.map((series) => {
|
|
468
|
+
const value = series.points[index]?.value;
|
|
469
|
+
return value === void 0 ? { text: "\u2014" } : { text: formatValue(value, graph.valueFormat, format), value };
|
|
470
|
+
})
|
|
471
|
+
])
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
function barListToGraph(list, conversion) {
|
|
475
|
+
return ok({
|
|
476
|
+
kind: "GRAPH",
|
|
477
|
+
series: [
|
|
478
|
+
{
|
|
479
|
+
name: conversion.seriesName ?? "Value",
|
|
480
|
+
points: list.items.map((item) => ({
|
|
481
|
+
label: item.label,
|
|
482
|
+
value: item.value
|
|
483
|
+
}))
|
|
484
|
+
}
|
|
485
|
+
],
|
|
486
|
+
chartType: conversion.chartType,
|
|
487
|
+
valueFormat: conversion.valueFormat ?? "number",
|
|
488
|
+
...conversion.currency === void 0 ? {} : { currency: conversion.currency }
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
function convertView(widgetKey, view, data, options = {}) {
|
|
492
|
+
const conversion = view.convert;
|
|
493
|
+
const context = {
|
|
494
|
+
where: `Widget ${JSON.stringify(widgetKey)} view ${JSON.stringify(view.key)}`,
|
|
495
|
+
...options.locale === void 0 ? {} : { locale: options.locale }
|
|
496
|
+
};
|
|
497
|
+
if (conversion === void 0) {
|
|
498
|
+
return err(
|
|
499
|
+
`${context.where}: the provider supplies this view, so there is nothing to convert.`
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
const expected = VIEW_CONVERSIONS[conversion.type]?.from;
|
|
503
|
+
if (expected === void 0) {
|
|
504
|
+
return err(
|
|
505
|
+
`${context.where}: unknown conversion type ${JSON.stringify(conversion.type)}.`
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
if (data.kind !== expected) {
|
|
509
|
+
return err(
|
|
510
|
+
`${context.where}: ${conversion.type} reads a ${expected} payload, but the widget returned ${data.kind}.`
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
switch (conversion.type) {
|
|
514
|
+
case "tableToBarList":
|
|
515
|
+
return tableToBarList(data, conversion, context);
|
|
516
|
+
case "tableToGraph":
|
|
517
|
+
return tableToGraph(data, conversion, context);
|
|
518
|
+
case "barListToTable":
|
|
519
|
+
return barListToTable(data, conversion);
|
|
520
|
+
case "graphToTable":
|
|
521
|
+
return graphToTable(data, context);
|
|
522
|
+
case "barListToGraph":
|
|
523
|
+
return barListToGraph(data, conversion);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
function viewNote(view, data) {
|
|
527
|
+
const conversion = view.convert;
|
|
528
|
+
if (data?.kind !== "TABLE" || conversion === void 0) return "";
|
|
529
|
+
let indexes;
|
|
530
|
+
if (conversion.type === "tableToBarList") {
|
|
531
|
+
indexes = [conversion.label, conversion.value];
|
|
532
|
+
} else if (conversion.type === "tableToGraph") {
|
|
533
|
+
indexes = [conversion.label, ...conversion.values];
|
|
534
|
+
} else {
|
|
535
|
+
return "";
|
|
536
|
+
}
|
|
537
|
+
const names = indexes.map(
|
|
538
|
+
(index) => data.columns[index]?.label ?? `#${index + 1}`
|
|
539
|
+
);
|
|
540
|
+
return `Shows ${names.join(", ")} only.`;
|
|
541
|
+
}
|
|
542
|
+
function applyView(widget, chosen, options = {}) {
|
|
543
|
+
const key = effectiveViewKey(widget.definition, chosen);
|
|
544
|
+
const view = findView(widget.definition, key);
|
|
545
|
+
const wanted = loadViewKey(widget.definition, chosen);
|
|
546
|
+
const loaded = widget.status === "ok" ? widget.view ?? BASE_VIEW_KEY : void 0;
|
|
547
|
+
const mismatched = loaded !== void 0 && loaded !== wanted;
|
|
548
|
+
if (view === void 0) {
|
|
549
|
+
return mismatched ? {
|
|
550
|
+
definition: widget.definition,
|
|
551
|
+
status: "loading",
|
|
552
|
+
...widget.options === void 0 ? {} : { options: widget.options }
|
|
553
|
+
} : widget;
|
|
554
|
+
}
|
|
555
|
+
const definition = {
|
|
556
|
+
...widget.definition,
|
|
557
|
+
kind: view.kind
|
|
558
|
+
};
|
|
559
|
+
if (widget.status !== "ok") return { ...widget, definition };
|
|
560
|
+
if (mismatched) {
|
|
561
|
+
return {
|
|
562
|
+
definition,
|
|
563
|
+
status: "loading",
|
|
564
|
+
...widget.options === void 0 ? {} : { options: widget.options }
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
if (isProviderView(view)) return { ...widget, definition };
|
|
568
|
+
const converted = convertView(
|
|
569
|
+
widget.definition.key,
|
|
570
|
+
view,
|
|
571
|
+
widget.data,
|
|
572
|
+
options
|
|
573
|
+
);
|
|
574
|
+
if (!converted.ok) {
|
|
575
|
+
return {
|
|
576
|
+
definition,
|
|
577
|
+
status: "error",
|
|
578
|
+
error: converted.error,
|
|
579
|
+
...widget.options === void 0 ? {} : { options: widget.options }
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
return { ...widget, definition, data: converted.value };
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// src/core/url.ts
|
|
586
|
+
var SCHEME_PATTERN = /^([a-z][a-z0-9+.-]*):/i;
|
|
587
|
+
var IGNORED_IN_SCHEME = /[\u0000- \u007f]/g;
|
|
588
|
+
function schemeOf(url) {
|
|
589
|
+
const match = SCHEME_PATTERN.exec(
|
|
590
|
+
url.replace(IGNORED_IN_SCHEME, "")
|
|
591
|
+
);
|
|
592
|
+
return match === null ? null : (match[1] ?? "").toLowerCase();
|
|
593
|
+
}
|
|
594
|
+
function isSafeHref(url) {
|
|
595
|
+
if (url.trim() === "") {
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
const scheme = schemeOf(url);
|
|
252
599
|
return scheme === null || scheme === "http" || scheme === "https" || scheme === "mailto";
|
|
253
600
|
}
|
|
254
601
|
function isSafeImageUrl(url) {
|
|
@@ -289,15 +636,15 @@ function defaultDetailQuery(pageSize = DEFAULT_DETAIL_PAGE_SIZE) {
|
|
|
289
636
|
function fold(text) {
|
|
290
637
|
return text.normalize("NFD").replace(/\p{M}/gu, "").toLowerCase();
|
|
291
638
|
}
|
|
292
|
-
function
|
|
639
|
+
function cellValue2(cell) {
|
|
293
640
|
return cell.value ?? cell.text;
|
|
294
641
|
}
|
|
295
642
|
function cellMatches(cell, needle) {
|
|
296
|
-
return fold(String(
|
|
643
|
+
return fold(String(cellValue2(cell))).includes(needle);
|
|
297
644
|
}
|
|
298
645
|
function compareCells(a, b, collator) {
|
|
299
|
-
const left =
|
|
300
|
-
const right =
|
|
646
|
+
const left = cellValue2(a);
|
|
647
|
+
const right = cellValue2(b);
|
|
301
648
|
if (typeof left === "number" && typeof right === "number") {
|
|
302
649
|
return left - right;
|
|
303
650
|
}
|
|
@@ -589,1492 +936,1878 @@ function deriveDetailData(data, locale) {
|
|
|
589
936
|
return void 0;
|
|
590
937
|
}
|
|
591
938
|
|
|
592
|
-
// src/core/
|
|
593
|
-
var
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
939
|
+
// src/core/options.ts
|
|
940
|
+
var DATE_RANGE_PRESETS = [
|
|
941
|
+
"today",
|
|
942
|
+
"yesterday",
|
|
943
|
+
"last7",
|
|
944
|
+
"last30",
|
|
945
|
+
"last90",
|
|
946
|
+
"thisMonth",
|
|
947
|
+
"lastMonth",
|
|
948
|
+
"thisYear",
|
|
949
|
+
"lastYear"
|
|
950
|
+
];
|
|
951
|
+
var WIDGET_OPTION_TYPES = [
|
|
952
|
+
"choice",
|
|
953
|
+
"number",
|
|
954
|
+
"boolean",
|
|
955
|
+
"text",
|
|
956
|
+
"dateRange",
|
|
957
|
+
"color",
|
|
958
|
+
"sort",
|
|
959
|
+
"columns"
|
|
960
|
+
];
|
|
961
|
+
var MAX_WIDGET_OPTIONS = 20;
|
|
962
|
+
var MAX_TEXT_OPTION_LENGTH = 500;
|
|
963
|
+
var OPTION_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_-]{0,39}$/;
|
|
964
|
+
var COLOR_PATTERN = /^#[0-9a-fA-F]{6}$/;
|
|
965
|
+
var INTERVAL_PATTERN = /^(\d{4}-\d{2}-\d{2})\/(\d{4}-\d{2}-\d{2})$/;
|
|
966
|
+
var CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/;
|
|
967
|
+
function isColumnKeyFor(kind, key) {
|
|
968
|
+
if (kind === "TABLE") return /^c\d+$/.test(key);
|
|
969
|
+
if (kind === "BAR_LIST") return key === "label" || key === "value";
|
|
970
|
+
return false;
|
|
616
971
|
}
|
|
617
|
-
function
|
|
618
|
-
return
|
|
972
|
+
function isClientApplied(option) {
|
|
973
|
+
return (option.type === "sort" || option.type === "columns") && option.apply === "client";
|
|
619
974
|
}
|
|
620
|
-
function
|
|
621
|
-
|
|
975
|
+
function civilToday(context) {
|
|
976
|
+
const now = context.now ?? Date.now();
|
|
977
|
+
let timeZone = context.timeZone ?? "UTC";
|
|
978
|
+
try {
|
|
979
|
+
new Intl.DateTimeFormat("en-US", { timeZone });
|
|
980
|
+
} catch {
|
|
981
|
+
timeZone = "UTC";
|
|
982
|
+
}
|
|
983
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
984
|
+
timeZone,
|
|
985
|
+
year: "numeric",
|
|
986
|
+
month: "2-digit",
|
|
987
|
+
day: "2-digit"
|
|
988
|
+
}).formatToParts(new Date(now));
|
|
989
|
+
const pick = (type) => Number(parts.find((part) => part.type === type)?.value ?? "0");
|
|
990
|
+
return { year: pick("year"), month: pick("month"), day: pick("day") };
|
|
622
991
|
}
|
|
623
|
-
function
|
|
624
|
-
|
|
625
|
-
if (size === "large") return Math.min(largeSpan, columns);
|
|
626
|
-
return 1;
|
|
992
|
+
function toIso(epochMs) {
|
|
993
|
+
return new Date(epochMs).toISOString().slice(0, 10);
|
|
627
994
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
995
|
+
var DAY_MS = 864e5;
|
|
996
|
+
function parseIsoDay(text) {
|
|
997
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(text);
|
|
998
|
+
if (match === null) return void 0;
|
|
999
|
+
const epoch = Date.UTC(
|
|
1000
|
+
Number(match[1]),
|
|
1001
|
+
Number(match[2]) - 1,
|
|
1002
|
+
Number(match[3])
|
|
633
1003
|
);
|
|
1004
|
+
return toIso(epoch) === text ? epoch : void 0;
|
|
634
1005
|
}
|
|
635
|
-
function
|
|
636
|
-
|
|
1006
|
+
function isIsoInterval(value) {
|
|
1007
|
+
const match = INTERVAL_PATTERN.exec(value);
|
|
1008
|
+
if (match === null) return false;
|
|
1009
|
+
const start = parseIsoDay(match[1]);
|
|
1010
|
+
const end = parseIsoDay(match[2]);
|
|
1011
|
+
return start !== void 0 && end !== void 0 && start <= end;
|
|
637
1012
|
}
|
|
638
|
-
function
|
|
639
|
-
return
|
|
1013
|
+
function isPreset(value) {
|
|
1014
|
+
return DATE_RANGE_PRESETS.includes(value);
|
|
640
1015
|
}
|
|
641
|
-
function
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
1016
|
+
function resolveDateRange(value, context = {}) {
|
|
1017
|
+
if (isIsoInterval(value)) return value;
|
|
1018
|
+
if (!isPreset(value)) return void 0;
|
|
1019
|
+
const today = civilToday(context);
|
|
1020
|
+
const todayMs = Date.UTC(today.year, today.month - 1, today.day);
|
|
1021
|
+
const range = (startMs, endMs) => `${toIso(startMs)}/${toIso(endMs)}`;
|
|
1022
|
+
switch (value) {
|
|
1023
|
+
case "today":
|
|
1024
|
+
return range(todayMs, todayMs);
|
|
1025
|
+
case "yesterday":
|
|
1026
|
+
return range(todayMs - DAY_MS, todayMs - DAY_MS);
|
|
1027
|
+
case "last7":
|
|
1028
|
+
return range(todayMs - 6 * DAY_MS, todayMs);
|
|
1029
|
+
case "last30":
|
|
1030
|
+
return range(todayMs - 29 * DAY_MS, todayMs);
|
|
1031
|
+
case "last90":
|
|
1032
|
+
return range(todayMs - 89 * DAY_MS, todayMs);
|
|
1033
|
+
case "thisMonth":
|
|
1034
|
+
return range(Date.UTC(today.year, today.month - 1, 1), todayMs);
|
|
1035
|
+
case "lastMonth":
|
|
1036
|
+
return range(
|
|
1037
|
+
Date.UTC(today.year, today.month - 2, 1),
|
|
1038
|
+
Date.UTC(today.year, today.month - 1, 1) - DAY_MS
|
|
1039
|
+
);
|
|
1040
|
+
case "thisYear":
|
|
1041
|
+
return range(Date.UTC(today.year, 0, 1), todayMs);
|
|
1042
|
+
case "lastYear":
|
|
1043
|
+
return range(
|
|
1044
|
+
Date.UTC(today.year - 1, 0, 1),
|
|
1045
|
+
Date.UTC(today.year, 0, 1) - DAY_MS
|
|
1046
|
+
);
|
|
646
1047
|
}
|
|
647
|
-
return count;
|
|
648
1048
|
}
|
|
649
|
-
function
|
|
650
|
-
|
|
1049
|
+
function onStepGrid(option, value) {
|
|
1050
|
+
if (option.step === void 0) return true;
|
|
1051
|
+
const steps = (value - option.min) / option.step;
|
|
1052
|
+
return Math.abs(steps - Math.round(steps)) < 1e-9;
|
|
651
1053
|
}
|
|
652
|
-
function
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
const
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
1054
|
+
function parseSortValue(value) {
|
|
1055
|
+
const separator = value.lastIndexOf(":");
|
|
1056
|
+
if (separator <= 0) return void 0;
|
|
1057
|
+
const column = value.slice(0, separator);
|
|
1058
|
+
const direction = value.slice(separator + 1);
|
|
1059
|
+
if (direction !== "asc" && direction !== "desc") return void 0;
|
|
1060
|
+
return { column, direction };
|
|
1061
|
+
}
|
|
1062
|
+
function sortFromOptions(definition, values) {
|
|
1063
|
+
if (values === void 0) return void 0;
|
|
1064
|
+
const sortOption = definition.options?.find(
|
|
1065
|
+
(option) => option.type === "sort"
|
|
1066
|
+
);
|
|
1067
|
+
if (sortOption === void 0) return void 0;
|
|
1068
|
+
const chosen = values[sortOption.key];
|
|
1069
|
+
return typeof chosen === "string" ? parseSortValue(chosen) : void 0;
|
|
1070
|
+
}
|
|
1071
|
+
function coerceOptionValue(option, value, context = {}) {
|
|
1072
|
+
switch (option.type) {
|
|
1073
|
+
case "choice":
|
|
1074
|
+
return typeof value === "string" && option.choices.some((choice) => choice.value === value) ? value : void 0;
|
|
1075
|
+
case "number":
|
|
1076
|
+
return typeof value === "number" && Number.isFinite(value) && value >= option.min && value <= option.max && onStepGrid(option, value) ? value : void 0;
|
|
1077
|
+
case "boolean":
|
|
1078
|
+
return typeof value === "boolean" ? value : void 0;
|
|
1079
|
+
case "text": {
|
|
1080
|
+
if (typeof value !== "string") return void 0;
|
|
1081
|
+
const trimmed = value.trim();
|
|
1082
|
+
return trimmed.length <= option.maxLength && !CONTROL_CHARACTERS.test(trimmed) ? trimmed : void 0;
|
|
1083
|
+
}
|
|
1084
|
+
case "dateRange": {
|
|
1085
|
+
if (typeof value !== "string") return void 0;
|
|
1086
|
+
if (isPreset(value) && option.presets !== void 0 && !option.presets.some((preset) => preset.value === value)) {
|
|
1087
|
+
return void 0;
|
|
1088
|
+
}
|
|
1089
|
+
return resolveDateRange(value, context);
|
|
1090
|
+
}
|
|
1091
|
+
case "color":
|
|
1092
|
+
return typeof value === "string" && COLOR_PATTERN.test(value) ? value.toLowerCase() : void 0;
|
|
1093
|
+
case "sort": {
|
|
1094
|
+
if (typeof value !== "string") return void 0;
|
|
1095
|
+
const sort = parseSortValue(value);
|
|
1096
|
+
return sort !== void 0 && option.columns.some((column) => column.key === sort.column) ? value : void 0;
|
|
1097
|
+
}
|
|
1098
|
+
case "columns": {
|
|
1099
|
+
if (!Array.isArray(value) || value.length === 0) return void 0;
|
|
1100
|
+
const keys = value;
|
|
1101
|
+
const allowed = new Set(option.columns.map((c) => c.key));
|
|
1102
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1103
|
+
for (const key of keys) {
|
|
1104
|
+
if (typeof key !== "string" || !allowed.has(key) || seen.has(key)) {
|
|
1105
|
+
return void 0;
|
|
1106
|
+
}
|
|
1107
|
+
seen.add(key);
|
|
1108
|
+
}
|
|
1109
|
+
return [...seen];
|
|
671
1110
|
}
|
|
672
|
-
rows.push({
|
|
673
|
-
keys: row.map(({ item }) => item.key),
|
|
674
|
-
spans: row.map(({ item, span }) => finalSpans.get(item.key) ?? span)
|
|
675
|
-
});
|
|
676
|
-
row = [];
|
|
677
|
-
used = 0;
|
|
678
1111
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
1112
|
+
}
|
|
1113
|
+
function defaultOptionValue(option, context = {}) {
|
|
1114
|
+
switch (option.type) {
|
|
1115
|
+
case "dateRange":
|
|
1116
|
+
return resolveDateRange(option.default, context);
|
|
1117
|
+
case "sort":
|
|
1118
|
+
return option.default;
|
|
1119
|
+
case "columns":
|
|
1120
|
+
return [...option.default];
|
|
1121
|
+
default:
|
|
1122
|
+
return option.default;
|
|
684
1123
|
}
|
|
685
|
-
closeRow();
|
|
686
|
-
return { spans, rows };
|
|
687
1124
|
}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
var KIND_LABELS = {
|
|
700
|
-
TEXT: "Text",
|
|
701
|
-
KPI: "Number",
|
|
702
|
-
GAUGE: "Gauge",
|
|
703
|
-
TABLE: "Table",
|
|
704
|
-
BAR_LIST: "Bars",
|
|
705
|
-
ALERT_LIST: "List",
|
|
706
|
-
GRAPH: "Chart"
|
|
707
|
-
};
|
|
708
|
-
function kindLabel(kind) {
|
|
709
|
-
return KIND_LABELS[kind];
|
|
710
|
-
}
|
|
711
|
-
function hasViews(item) {
|
|
712
|
-
return item.views !== void 0 && item.views.length > 0;
|
|
1125
|
+
var NO_VALUES = Object.freeze({});
|
|
1126
|
+
function resolveOptionValues(definition, chosen, context = {}) {
|
|
1127
|
+
const options = definition.options;
|
|
1128
|
+
if (options === void 0 || options.length === 0) return NO_VALUES;
|
|
1129
|
+
const values = {};
|
|
1130
|
+
for (const option of options) {
|
|
1131
|
+
const picked = chosen?.[option.key];
|
|
1132
|
+
const value = (picked === void 0 ? void 0 : coerceOptionValue(option, picked, context)) ?? defaultOptionValue(option, context);
|
|
1133
|
+
if (value !== void 0) values[option.key] = value;
|
|
1134
|
+
}
|
|
1135
|
+
return values;
|
|
713
1136
|
}
|
|
714
|
-
function
|
|
715
|
-
|
|
1137
|
+
function sameValue(a, b) {
|
|
1138
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
1139
|
+
return a.length === b.length && a.every((item, i) => item === b[i]);
|
|
1140
|
+
}
|
|
1141
|
+
return a === b;
|
|
716
1142
|
}
|
|
717
|
-
function
|
|
718
|
-
|
|
719
|
-
return [
|
|
720
|
-
{
|
|
721
|
-
key: BASE_VIEW_KEY,
|
|
722
|
-
label: item.baseViewLabel ?? kindLabel(item.kind),
|
|
723
|
-
kind: item.kind
|
|
724
|
-
},
|
|
725
|
-
...(item.views ?? []).map(({ key, label, kind }) => ({
|
|
726
|
-
key,
|
|
727
|
-
label,
|
|
728
|
-
kind
|
|
729
|
-
}))
|
|
730
|
-
];
|
|
1143
|
+
function sameOptionValues(a, b) {
|
|
1144
|
+
const keys = Object.keys({ ...a, ...b });
|
|
1145
|
+
return keys.every((key) => sameValue(a[key], b[key]));
|
|
731
1146
|
}
|
|
732
|
-
function
|
|
733
|
-
|
|
1147
|
+
function optionsCacheSuffix(definition, values, context = {}) {
|
|
1148
|
+
const parts = [];
|
|
1149
|
+
for (const option of definition.options ?? []) {
|
|
1150
|
+
if (isClientApplied(option)) continue;
|
|
1151
|
+
const value = values[option.key];
|
|
1152
|
+
if (sameValue(value, defaultOptionValue(option, context))) continue;
|
|
1153
|
+
parts.push(
|
|
1154
|
+
`${option.key}=${encodeURIComponent(JSON.stringify(value ?? null))}`
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
parts.sort();
|
|
1158
|
+
return parts.length === 0 ? "" : `?${parts.join("&")}`;
|
|
734
1159
|
}
|
|
735
|
-
function
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
if (isKnownView(item, item.defaultView)) return item.defaultView;
|
|
739
|
-
return BASE_VIEW_KEY;
|
|
1160
|
+
function tableColumnIndex(key) {
|
|
1161
|
+
const match = /^c(\d+)$/.exec(key);
|
|
1162
|
+
return match === null ? void 0 : Number(match[1]);
|
|
740
1163
|
}
|
|
741
|
-
function
|
|
742
|
-
|
|
1164
|
+
function sortBarItems(items, sort) {
|
|
1165
|
+
const sign = sort.direction === "desc" ? -1 : 1;
|
|
1166
|
+
return items.map((item, index) => ({ item, index })).sort((a, b) => {
|
|
1167
|
+
const order = sort.column === "value" ? a.item.value - b.item.value : a.item.label.localeCompare(b.item.label);
|
|
1168
|
+
return order * sign || a.index - b.index;
|
|
1169
|
+
}).map(({ item }) => item);
|
|
743
1170
|
}
|
|
744
|
-
function
|
|
745
|
-
const
|
|
746
|
-
|
|
1171
|
+
function applyClientOptions(definition, data, values) {
|
|
1172
|
+
const options = definition.options ?? [];
|
|
1173
|
+
const sortOption = options.find(
|
|
1174
|
+
(o) => o.type === "sort" && o.apply === "client"
|
|
1175
|
+
);
|
|
1176
|
+
const columnsOption = options.find(
|
|
1177
|
+
(o) => o.type === "columns" && o.apply === "client"
|
|
1178
|
+
);
|
|
1179
|
+
const sortValue = sortOption === void 0 ? void 0 : values[sortOption.key];
|
|
1180
|
+
const columnsValue = columnsOption === void 0 ? void 0 : values[columnsOption.key];
|
|
1181
|
+
let next = data;
|
|
1182
|
+
if (typeof sortValue === "string") {
|
|
1183
|
+
const sort = parseSortValue(sortValue);
|
|
1184
|
+
if (sort !== void 0 && next.kind === "BAR_LIST") {
|
|
1185
|
+
if (sort.column === "label" || sort.column === "value") {
|
|
1186
|
+
next = { ...next, items: sortBarItems(next.items, sort) };
|
|
1187
|
+
}
|
|
1188
|
+
} else if (sort !== void 0 && next.kind === "TABLE") {
|
|
1189
|
+
const index = tableColumnIndex(sort.column);
|
|
1190
|
+
if (index !== void 0 && index < next.columns.length) {
|
|
1191
|
+
const sorted = queryRows(tableDetailData(next), {
|
|
1192
|
+
page: 1,
|
|
1193
|
+
pageSize: Math.max(1, next.rows.length),
|
|
1194
|
+
sort
|
|
1195
|
+
});
|
|
1196
|
+
if (sorted.ok) next = { ...next, rows: sorted.value.rows };
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
if (Array.isArray(columnsValue) && next.kind === "TABLE") {
|
|
1201
|
+
const table = next;
|
|
1202
|
+
const indexes = columnsValue.map(tableColumnIndex).filter(
|
|
1203
|
+
(index) => index !== void 0 && index < table.columns.length
|
|
1204
|
+
);
|
|
1205
|
+
if (indexes.length > 0) {
|
|
1206
|
+
next = {
|
|
1207
|
+
...table,
|
|
1208
|
+
columns: indexes.map((i) => table.columns[i]),
|
|
1209
|
+
rows: table.rows.map((row) => indexes.map((i) => row[i]))
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
return next;
|
|
747
1214
|
}
|
|
748
|
-
|
|
749
|
-
|
|
1215
|
+
|
|
1216
|
+
// src/core/settings.ts
|
|
1217
|
+
var MAX_WIDGET_TITLE_LENGTH = 80;
|
|
1218
|
+
var MAX_PARSED_SETTINGS = 500;
|
|
1219
|
+
var MAX_COLUMN_LIST = 100;
|
|
1220
|
+
var MAX_VALUE_LENGTH = 500;
|
|
1221
|
+
function isRecord(value) {
|
|
1222
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
750
1223
|
}
|
|
751
|
-
function
|
|
752
|
-
if (
|
|
753
|
-
return
|
|
1224
|
+
function parseOptionValue(value) {
|
|
1225
|
+
if (typeof value === "string") {
|
|
1226
|
+
return value.length <= MAX_VALUE_LENGTH ? value : void 0;
|
|
1227
|
+
}
|
|
1228
|
+
if (typeof value === "number")
|
|
1229
|
+
return Number.isFinite(value) ? value : void 0;
|
|
1230
|
+
if (typeof value === "boolean") return value;
|
|
1231
|
+
if (Array.isArray(value) && value.length <= MAX_COLUMN_LIST) {
|
|
1232
|
+
return value.every(
|
|
1233
|
+
(entry) => typeof entry === "string" && entry.length <= MAX_VALUE_LENGTH
|
|
1234
|
+
) ? value : void 0;
|
|
754
1235
|
}
|
|
755
1236
|
return void 0;
|
|
756
1237
|
}
|
|
757
|
-
function
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
1238
|
+
function parseOne(raw) {
|
|
1239
|
+
if (!isRecord(raw)) return void 0;
|
|
1240
|
+
const title = raw["title"];
|
|
1241
|
+
const width = raw["width"];
|
|
1242
|
+
const flexible = raw["flexible"];
|
|
1243
|
+
const options = raw["options"];
|
|
1244
|
+
const view = raw["view"];
|
|
1245
|
+
const cleanTitle = typeof title === "string" ? title.trim().slice(0, MAX_WIDGET_TITLE_LENGTH) : "";
|
|
1246
|
+
const chosen = {};
|
|
1247
|
+
if (isRecord(options)) {
|
|
1248
|
+
for (const [key, value] of Object.entries(options)) {
|
|
1249
|
+
const parsed = parseOptionValue(value);
|
|
1250
|
+
if (parsed !== void 0) chosen[key] = parsed;
|
|
765
1251
|
}
|
|
766
|
-
numbers.push(value);
|
|
767
1252
|
}
|
|
768
|
-
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
label: row[conversion.label]?.text ?? "",
|
|
777
|
-
value: numbers.value[index],
|
|
778
|
-
display: row[conversion.value]?.text ?? String(numbers.value[index])
|
|
779
|
-
}));
|
|
780
|
-
return ok({ kind: "BAR_LIST", items });
|
|
1253
|
+
const settings = {
|
|
1254
|
+
...cleanTitle === "" ? {} : { title: cleanTitle },
|
|
1255
|
+
...isWidgetWidth(width) ? { width } : {},
|
|
1256
|
+
...typeof flexible === "boolean" ? { flexible } : {},
|
|
1257
|
+
...Object.keys(chosen).length === 0 ? {} : { options: chosen },
|
|
1258
|
+
...typeof view === "string" && view !== "" && view.length <= 80 ? { view } : {}
|
|
1259
|
+
};
|
|
1260
|
+
return Object.keys(settings).length === 0 ? void 0 : settings;
|
|
781
1261
|
}
|
|
782
|
-
function
|
|
783
|
-
if (
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
context
|
|
793
|
-
);
|
|
794
|
-
for (const index of conversion.values) {
|
|
795
|
-
bad ??= checkColumn(table, index, "series", context);
|
|
796
|
-
}
|
|
797
|
-
if (bad !== void 0) return err(bad);
|
|
798
|
-
const series = [];
|
|
799
|
-
for (const index of conversion.values) {
|
|
800
|
-
const numbers = numericColumn(table, index, context);
|
|
801
|
-
if (!numbers.ok) return numbers;
|
|
802
|
-
series.push({
|
|
803
|
-
name: columnName(table, index),
|
|
804
|
-
points: table.rows.map((row, rowIndex) => ({
|
|
805
|
-
label: row[conversion.label]?.text ?? "",
|
|
806
|
-
value: numbers.value[rowIndex]
|
|
807
|
-
}))
|
|
808
|
-
});
|
|
1262
|
+
function parseSettings(raw) {
|
|
1263
|
+
if (!isRecord(raw)) return void 0;
|
|
1264
|
+
const parsed = {};
|
|
1265
|
+
for (const [key, value] of Object.entries(raw).slice(
|
|
1266
|
+
0,
|
|
1267
|
+
MAX_PARSED_SETTINGS
|
|
1268
|
+
)) {
|
|
1269
|
+
if (key === "") continue;
|
|
1270
|
+
const one = parseOne(value);
|
|
1271
|
+
if (one !== void 0) parsed[key] = one;
|
|
809
1272
|
}
|
|
810
|
-
return
|
|
811
|
-
kind: "GRAPH",
|
|
812
|
-
series,
|
|
813
|
-
chartType: conversion.chartType,
|
|
814
|
-
valueFormat: conversion.valueFormat ?? "number",
|
|
815
|
-
...conversion.currency === void 0 ? {} : { currency: conversion.currency },
|
|
816
|
-
xLabel: columnName(table, conversion.label)
|
|
817
|
-
});
|
|
1273
|
+
return Object.keys(parsed).length === 0 ? void 0 : parsed;
|
|
818
1274
|
}
|
|
819
|
-
function
|
|
820
|
-
return
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
{
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
1275
|
+
function serializeSettings(settings) {
|
|
1276
|
+
if (settings === void 0) return void 0;
|
|
1277
|
+
const out = {};
|
|
1278
|
+
for (const key of Object.keys(settings)) {
|
|
1279
|
+
const one = settings[key];
|
|
1280
|
+
if (one === void 0) continue;
|
|
1281
|
+
const piece = {
|
|
1282
|
+
...one.title === void 0 ? {} : { title: one.title },
|
|
1283
|
+
...one.width === void 0 ? {} : { width: one.width },
|
|
1284
|
+
...one.flexible === void 0 ? {} : { flexible: one.flexible },
|
|
1285
|
+
...one.options === void 0 || Object.keys(one.options).length === 0 ? {} : { options: { ...one.options } },
|
|
1286
|
+
...one.view === void 0 ? {} : { view: one.view }
|
|
1287
|
+
};
|
|
1288
|
+
if (Object.keys(piece).length > 0) out[key] = piece;
|
|
1289
|
+
}
|
|
1290
|
+
return Object.keys(out).length === 0 ? void 0 : out;
|
|
831
1291
|
}
|
|
832
|
-
function
|
|
833
|
-
const
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
const format = {
|
|
837
|
-
...graph.currency === void 0 ? {} : { currency: graph.currency },
|
|
838
|
-
...context.locale === void 0 ? {} : { locale: context.locale }
|
|
839
|
-
};
|
|
840
|
-
return ok({
|
|
841
|
-
kind: "TABLE",
|
|
842
|
-
columns: [
|
|
843
|
-
{ label: graph.xLabel ?? "Label" },
|
|
844
|
-
...graph.series.map((series) => ({ label: series.name, numeric: true }))
|
|
845
|
-
],
|
|
846
|
-
rows: first.points.map((point, index) => [
|
|
847
|
-
{ text: point.label },
|
|
848
|
-
...graph.series.map((series) => {
|
|
849
|
-
const value = series.points[index]?.value;
|
|
850
|
-
return value === void 0 ? { text: "\u2014" } : { text: formatValue(value, graph.valueFormat, format), value };
|
|
851
|
-
})
|
|
852
|
-
])
|
|
853
|
-
});
|
|
1292
|
+
function widthRange(item) {
|
|
1293
|
+
const min = isWidgetWidth(item.minWidth) ? item.minWidth : MIN_WIDGET_WIDTH;
|
|
1294
|
+
const max = isWidgetWidth(item.maxWidth) ? item.maxWidth : MAX_WIDGET_WIDTH;
|
|
1295
|
+
return { min, max: Math.max(min, max) };
|
|
854
1296
|
}
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
});
|
|
1297
|
+
var WIDTH_SHARES = {
|
|
1298
|
+
2: "1/6",
|
|
1299
|
+
3: "1/4",
|
|
1300
|
+
4: "1/3",
|
|
1301
|
+
5: "5/12",
|
|
1302
|
+
6: "1/2",
|
|
1303
|
+
7: "7/12",
|
|
1304
|
+
8: "2/3",
|
|
1305
|
+
9: "3/4",
|
|
1306
|
+
10: "5/6",
|
|
1307
|
+
11: "11/12",
|
|
1308
|
+
12: "Full"
|
|
1309
|
+
};
|
|
1310
|
+
function widthShareLabel(width) {
|
|
1311
|
+
return WIDTH_SHARES[width] ?? `${width}/12`;
|
|
871
1312
|
}
|
|
872
|
-
function
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
1313
|
+
function isOptionsLocked(item) {
|
|
1314
|
+
return resolveWidgetLock(item).options;
|
|
1315
|
+
}
|
|
1316
|
+
function fillWith(fill, flexible) {
|
|
1317
|
+
const heightToo = fill === "height" || fill === "both";
|
|
1318
|
+
if (flexible) return heightToo ? "both" : "width";
|
|
1319
|
+
return heightToo ? "height" : void 0;
|
|
1320
|
+
}
|
|
1321
|
+
function isFlexible(item, settings) {
|
|
1322
|
+
if (settings?.flexible !== void 0) return settings.flexible;
|
|
1323
|
+
return item.fill === "width" || item.fill === "both";
|
|
1324
|
+
}
|
|
1325
|
+
function applySettings(definition, settings) {
|
|
1326
|
+
if (settings === void 0) return definition;
|
|
1327
|
+
const range = widthRange(definition);
|
|
1328
|
+
const width = settings.width === void 0 ? void 0 : Math.min(range.max, Math.max(range.min, settings.width));
|
|
1329
|
+
const title = settings.title === void 0 || settings.title.trim() === "" ? void 0 : settings.title;
|
|
1330
|
+
const flexibleChanged = settings.flexible !== void 0 && settings.flexible !== isFlexible(definition, void 0);
|
|
1331
|
+
if (title === void 0 && width === void 0 && !flexibleChanged) {
|
|
1332
|
+
return definition;
|
|
883
1333
|
}
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
1334
|
+
const next = { ...definition };
|
|
1335
|
+
if (title !== void 0) next.title = title;
|
|
1336
|
+
if (width !== void 0) next.width = width;
|
|
1337
|
+
if (flexibleChanged) {
|
|
1338
|
+
const fill = fillWith(
|
|
1339
|
+
definition.fill,
|
|
1340
|
+
settings.flexible === true
|
|
887
1341
|
);
|
|
1342
|
+
if (fill === void 0) {
|
|
1343
|
+
delete next.fill;
|
|
1344
|
+
} else {
|
|
1345
|
+
next.fill = fill;
|
|
1346
|
+
}
|
|
888
1347
|
}
|
|
889
|
-
|
|
890
|
-
case "tableToBarList":
|
|
891
|
-
return tableToBarList(data, conversion, context);
|
|
892
|
-
case "tableToGraph":
|
|
893
|
-
return tableToGraph(data, conversion, context);
|
|
894
|
-
case "barListToTable":
|
|
895
|
-
return barListToTable(data, conversion);
|
|
896
|
-
case "graphToTable":
|
|
897
|
-
return graphToTable(data, context);
|
|
898
|
-
case "barListToGraph":
|
|
899
|
-
return barListToGraph(data, conversion);
|
|
900
|
-
}
|
|
1348
|
+
return next;
|
|
901
1349
|
}
|
|
902
|
-
function
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
let indexes;
|
|
906
|
-
if (conversion.type === "tableToBarList") {
|
|
907
|
-
indexes = [conversion.label, conversion.value];
|
|
908
|
-
} else if (conversion.type === "tableToGraph") {
|
|
909
|
-
indexes = [conversion.label, ...conversion.values];
|
|
910
|
-
} else {
|
|
911
|
-
return "";
|
|
912
|
-
}
|
|
913
|
-
const names = indexes.map(
|
|
914
|
-
(index) => data.columns[index]?.label ?? `#${index + 1}`
|
|
1350
|
+
function effectiveDefinitions(definitions, layout, options = {}) {
|
|
1351
|
+
return definitions.map(
|
|
1352
|
+
(definition) => options.overrideLocks !== true && isOptionsLocked(definition) ? definition : applySettings(definition, layout.settings?.[definition.key])
|
|
915
1353
|
);
|
|
916
|
-
return `Shows ${names.join(", ")} only.`;
|
|
917
1354
|
}
|
|
918
|
-
function
|
|
919
|
-
const
|
|
920
|
-
const
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
};
|
|
926
|
-
if (widget.status === "loading") return { ...widget, definition };
|
|
927
|
-
if (widget.status === "empty") return { ...widget, definition };
|
|
928
|
-
if (widget.status === "error") return { ...widget, definition };
|
|
929
|
-
const converted = convertView(
|
|
930
|
-
widget.definition.key,
|
|
931
|
-
view,
|
|
932
|
-
widget.data,
|
|
933
|
-
options
|
|
934
|
-
);
|
|
935
|
-
if (!converted.ok) {
|
|
936
|
-
return {
|
|
937
|
-
definition,
|
|
938
|
-
status: "error",
|
|
939
|
-
error: converted.error,
|
|
940
|
-
...widget.options === void 0 ? {} : { options: widget.options }
|
|
941
|
-
};
|
|
1355
|
+
function widgetOptionValues(definition, settings) {
|
|
1356
|
+
const declared = definition.options ?? [];
|
|
1357
|
+
const values = {};
|
|
1358
|
+
for (const option of declared) {
|
|
1359
|
+
const picked = settings?.options?.[option.key];
|
|
1360
|
+
const value = (picked === void 0 ? void 0 : coerceOptionValue(option, picked)) ?? defaultOptionValue(option);
|
|
1361
|
+
if (value !== void 0) values[option.key] = value;
|
|
942
1362
|
}
|
|
943
|
-
return
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
// src/core/options.ts
|
|
947
|
-
var DATE_RANGE_PRESETS = [
|
|
948
|
-
"today",
|
|
949
|
-
"yesterday",
|
|
950
|
-
"last7",
|
|
951
|
-
"last30",
|
|
952
|
-
"last90",
|
|
953
|
-
"thisMonth",
|
|
954
|
-
"lastMonth",
|
|
955
|
-
"thisYear",
|
|
956
|
-
"lastYear"
|
|
957
|
-
];
|
|
958
|
-
var WIDGET_OPTION_TYPES = [
|
|
959
|
-
"choice",
|
|
960
|
-
"number",
|
|
961
|
-
"boolean",
|
|
962
|
-
"text",
|
|
963
|
-
"dateRange",
|
|
964
|
-
"color",
|
|
965
|
-
"sort",
|
|
966
|
-
"columns"
|
|
967
|
-
];
|
|
968
|
-
var MAX_WIDGET_OPTIONS = 20;
|
|
969
|
-
var MAX_TEXT_OPTION_LENGTH = 500;
|
|
970
|
-
var OPTION_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_-]{0,39}$/;
|
|
971
|
-
var COLOR_PATTERN = /^#[0-9a-fA-F]{6}$/;
|
|
972
|
-
var INTERVAL_PATTERN = /^(\d{4}-\d{2}-\d{2})\/(\d{4}-\d{2}-\d{2})$/;
|
|
973
|
-
var CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/;
|
|
974
|
-
function isColumnKeyFor(kind, key) {
|
|
975
|
-
if (kind === "TABLE") return /^c\d+$/.test(key);
|
|
976
|
-
if (kind === "BAR_LIST") return key === "label" || key === "value";
|
|
977
|
-
return false;
|
|
1363
|
+
return values;
|
|
978
1364
|
}
|
|
979
|
-
function
|
|
980
|
-
|
|
1365
|
+
function optionValuesFromLayout(definitions, layout, options = {}) {
|
|
1366
|
+
const out = {};
|
|
1367
|
+
for (const definition of definitions) {
|
|
1368
|
+
if (definition.options === void 0 || definition.options.length === 0) {
|
|
1369
|
+
continue;
|
|
1370
|
+
}
|
|
1371
|
+
const locked = options.overrideLocks !== true && isOptionsLocked(definition);
|
|
1372
|
+
out[definition.key] = locked ? {} : layout.settings?.[definition.key]?.options ?? {};
|
|
1373
|
+
}
|
|
1374
|
+
return out;
|
|
981
1375
|
}
|
|
982
|
-
function
|
|
983
|
-
const
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
timeZone = "UTC";
|
|
1376
|
+
function viewValuesFromLayout(definitions, layout, options = {}) {
|
|
1377
|
+
const out = {};
|
|
1378
|
+
for (const definition of definitions) {
|
|
1379
|
+
if (!(definition.views ?? []).some(isProviderView)) continue;
|
|
1380
|
+
const locked = options.overrideLocks !== true && isOptionsLocked(definition);
|
|
1381
|
+
out[definition.key] = locked ? "" : layout.settings?.[definition.key]?.view ?? "";
|
|
989
1382
|
}
|
|
990
|
-
|
|
991
|
-
timeZone,
|
|
992
|
-
year: "numeric",
|
|
993
|
-
month: "2-digit",
|
|
994
|
-
day: "2-digit"
|
|
995
|
-
}).formatToParts(new Date(now));
|
|
996
|
-
const pick = (type) => Number(parts.find((part) => part.type === type)?.value ?? "0");
|
|
997
|
-
return { year: pick("year"), month: pick("month"), day: pick("day") };
|
|
1383
|
+
return out;
|
|
998
1384
|
}
|
|
999
|
-
function
|
|
1000
|
-
|
|
1385
|
+
function sameSettings(a, b) {
|
|
1386
|
+
if (a === b) return true;
|
|
1387
|
+
return a?.title === b?.title && a?.width === b?.width && a?.flexible === b?.flexible && a?.view === b?.view && sameOptionValues(a?.options ?? {}, b?.options ?? {});
|
|
1001
1388
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
const
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
const todayMs = Date.UTC(today.year, today.month - 1, today.day);
|
|
1028
|
-
const range = (startMs, endMs) => `${toIso(startMs)}/${toIso(endMs)}`;
|
|
1029
|
-
switch (value) {
|
|
1030
|
-
case "today":
|
|
1031
|
-
return range(todayMs, todayMs);
|
|
1032
|
-
case "yesterday":
|
|
1033
|
-
return range(todayMs - DAY_MS, todayMs - DAY_MS);
|
|
1034
|
-
case "last7":
|
|
1035
|
-
return range(todayMs - 6 * DAY_MS, todayMs);
|
|
1036
|
-
case "last30":
|
|
1037
|
-
return range(todayMs - 29 * DAY_MS, todayMs);
|
|
1038
|
-
case "last90":
|
|
1039
|
-
return range(todayMs - 89 * DAY_MS, todayMs);
|
|
1040
|
-
case "thisMonth":
|
|
1041
|
-
return range(Date.UTC(today.year, today.month - 1, 1), todayMs);
|
|
1042
|
-
case "lastMonth":
|
|
1043
|
-
return range(
|
|
1044
|
-
Date.UTC(today.year, today.month - 2, 1),
|
|
1045
|
-
Date.UTC(today.year, today.month - 1, 1) - DAY_MS
|
|
1046
|
-
);
|
|
1047
|
-
case "thisYear":
|
|
1048
|
-
return range(Date.UTC(today.year, 0, 1), todayMs);
|
|
1049
|
-
case "lastYear":
|
|
1050
|
-
return range(
|
|
1051
|
-
Date.UTC(today.year - 1, 0, 1),
|
|
1052
|
-
Date.UTC(today.year, 0, 1) - DAY_MS
|
|
1053
|
-
);
|
|
1389
|
+
function setWidgetSettings(layout, key, patch) {
|
|
1390
|
+
const before = layout.settings?.[key];
|
|
1391
|
+
const draft = { ...before };
|
|
1392
|
+
for (const field of [
|
|
1393
|
+
"title",
|
|
1394
|
+
"width",
|
|
1395
|
+
"flexible",
|
|
1396
|
+
"options",
|
|
1397
|
+
"view"
|
|
1398
|
+
]) {
|
|
1399
|
+
if (!(field in patch)) continue;
|
|
1400
|
+
const value = patch[field];
|
|
1401
|
+
if (value === void 0) {
|
|
1402
|
+
delete draft[field];
|
|
1403
|
+
} else {
|
|
1404
|
+
draft[field] = value;
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
const merged = parseOne(draft);
|
|
1408
|
+
if (sameSettings(before, merged)) return layout;
|
|
1409
|
+
const rest = { ...layout.settings };
|
|
1410
|
+
if (merged === void 0) {
|
|
1411
|
+
delete rest[key];
|
|
1412
|
+
} else {
|
|
1413
|
+
rest[key] = merged;
|
|
1054
1414
|
}
|
|
1415
|
+
const { settings: _previous, ...others } = layout;
|
|
1416
|
+
return Object.keys(rest).length === 0 ? others : { ...others, settings: rest };
|
|
1055
1417
|
}
|
|
1056
|
-
function
|
|
1057
|
-
if (
|
|
1058
|
-
|
|
1059
|
-
|
|
1418
|
+
function clearWidgetSettings(layout, key) {
|
|
1419
|
+
if (layout.settings?.[key] === void 0) return layout;
|
|
1420
|
+
return setWidgetSettings(layout, key, {
|
|
1421
|
+
title: void 0,
|
|
1422
|
+
width: void 0,
|
|
1423
|
+
flexible: void 0,
|
|
1424
|
+
options: void 0,
|
|
1425
|
+
view: void 0
|
|
1426
|
+
});
|
|
1060
1427
|
}
|
|
1061
|
-
function
|
|
1062
|
-
|
|
1063
|
-
if (separator <= 0) return void 0;
|
|
1064
|
-
const column = value.slice(0, separator);
|
|
1065
|
-
const direction = value.slice(separator + 1);
|
|
1066
|
-
if (direction !== "asc" && direction !== "desc") return void 0;
|
|
1067
|
-
return { column, direction };
|
|
1428
|
+
function normalizeTitle(title) {
|
|
1429
|
+
return title.normalize("NFKC").trim().toLowerCase();
|
|
1068
1430
|
}
|
|
1069
|
-
function
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1431
|
+
function isTitleInUse(definitions, layout, key, title) {
|
|
1432
|
+
const wanted = normalizeTitle(title);
|
|
1433
|
+
if (wanted === "") return false;
|
|
1434
|
+
return effectiveDefinitions(definitions, layout, {
|
|
1435
|
+
overrideLocks: true
|
|
1436
|
+
}).some(
|
|
1437
|
+
(other) => other.key !== key && normalizeTitle(other.title ?? other.key) === wanted
|
|
1073
1438
|
);
|
|
1074
|
-
if (sortOption === void 0) return void 0;
|
|
1075
|
-
const chosen = values[sortOption.key];
|
|
1076
|
-
return typeof chosen === "string" ? parseSortValue(chosen) : void 0;
|
|
1077
1439
|
}
|
|
1078
|
-
function
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1440
|
+
function uniqueTitle(taken, title) {
|
|
1441
|
+
if (!taken.has(normalizeTitle(title))) return title;
|
|
1442
|
+
for (let n = 2; ; n += 1) {
|
|
1443
|
+
const suffix = ` (#${n})`;
|
|
1444
|
+
const candidate = `${title.slice(0, MAX_WIDGET_TITLE_LENGTH - suffix.length)}${suffix}`;
|
|
1445
|
+
if (!taken.has(normalizeTitle(candidate))) return candidate;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
function normalizeSettings(definitions, layout, options = {}) {
|
|
1449
|
+
const settings = layout.settings;
|
|
1450
|
+
if (settings === void 0) return layout;
|
|
1451
|
+
const cleaned = {};
|
|
1452
|
+
const taken = /* @__PURE__ */ new Set();
|
|
1453
|
+
for (const definition of definitions) {
|
|
1454
|
+
const renamed = settings[definition.key]?.title !== void 0 && (options.overrideLocks === true || !isOptionsLocked(definition));
|
|
1455
|
+
if (!renamed) taken.add(normalizeTitle(definition.title ?? definition.key));
|
|
1456
|
+
}
|
|
1457
|
+
for (const definition of definitions) {
|
|
1458
|
+
const one = settings[definition.key];
|
|
1459
|
+
if (one === void 0) continue;
|
|
1460
|
+
if (options.overrideLocks !== true && isOptionsLocked(definition)) continue;
|
|
1461
|
+
const range = widthRange(definition);
|
|
1462
|
+
const chosen = {};
|
|
1463
|
+
for (const option of definition.options ?? []) {
|
|
1464
|
+
const picked = one.options?.[option.key];
|
|
1465
|
+
if (picked === void 0) continue;
|
|
1466
|
+
const value = coerceOptionValue(option, picked);
|
|
1467
|
+
if (value !== void 0) {
|
|
1468
|
+
chosen[option.key] = option.type === "dateRange" ? picked : value;
|
|
1095
1469
|
}
|
|
1096
|
-
return resolveDateRange(value, context);
|
|
1097
|
-
}
|
|
1098
|
-
case "color":
|
|
1099
|
-
return typeof value === "string" && COLOR_PATTERN.test(value) ? value.toLowerCase() : void 0;
|
|
1100
|
-
case "sort": {
|
|
1101
|
-
if (typeof value !== "string") return void 0;
|
|
1102
|
-
const sort = parseSortValue(value);
|
|
1103
|
-
return sort !== void 0 && option.columns.some((column) => column.key === sort.column) ? value : void 0;
|
|
1104
1470
|
}
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
}
|
|
1114
|
-
seen.add(key);
|
|
1471
|
+
let title = one.title;
|
|
1472
|
+
if (title !== void 0) {
|
|
1473
|
+
title = title.trim().slice(0, MAX_WIDGET_TITLE_LENGTH);
|
|
1474
|
+
if (title === "") {
|
|
1475
|
+
title = void 0;
|
|
1476
|
+
} else {
|
|
1477
|
+
title = uniqueTitle(taken, title);
|
|
1478
|
+
taken.add(normalizeTitle(title));
|
|
1115
1479
|
}
|
|
1116
|
-
|
|
1480
|
+
}
|
|
1481
|
+
const next = {
|
|
1482
|
+
...title === void 0 ? {} : { title },
|
|
1483
|
+
...one.width === void 0 ? {} : { width: Math.min(range.max, Math.max(range.min, one.width)) },
|
|
1484
|
+
...one.flexible === void 0 ? {} : { flexible: one.flexible },
|
|
1485
|
+
...Object.keys(chosen).length === 0 ? {} : { options: chosen },
|
|
1486
|
+
...one.view !== void 0 && (one.view === BASE_VIEW_KEY || findView(definition, one.view) !== void 0) ? { view: one.view } : {}
|
|
1487
|
+
};
|
|
1488
|
+
if (Object.keys(next).length > 0) {
|
|
1489
|
+
cleaned[definition.key] = sameSettings(one, next) ? one : next;
|
|
1117
1490
|
}
|
|
1118
1491
|
}
|
|
1492
|
+
const keys = Object.keys(cleaned);
|
|
1493
|
+
const unchanged = keys.length === Object.keys(settings).length && keys.every((key) => cleaned[key] === settings[key]);
|
|
1494
|
+
if (unchanged) return layout;
|
|
1495
|
+
const { settings: _previous, ...others } = layout;
|
|
1496
|
+
return keys.length === 0 ? others : { ...others, settings: cleaned };
|
|
1119
1497
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
return option.default;
|
|
1126
|
-
case "columns":
|
|
1127
|
-
return [...option.default];
|
|
1128
|
-
default:
|
|
1129
|
-
return option.default;
|
|
1130
|
-
}
|
|
1498
|
+
|
|
1499
|
+
// src/core/clones.ts
|
|
1500
|
+
var MAX_PARSED_CLONES = 500;
|
|
1501
|
+
function isRecord2(value) {
|
|
1502
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1131
1503
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
const
|
|
1135
|
-
|
|
1136
|
-
const
|
|
1137
|
-
|
|
1138
|
-
const
|
|
1139
|
-
const
|
|
1140
|
-
if (
|
|
1504
|
+
function parseClones(raw) {
|
|
1505
|
+
if (!Array.isArray(raw)) return void 0;
|
|
1506
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1507
|
+
const clones = [];
|
|
1508
|
+
for (const entry of raw.slice(0, MAX_PARSED_CLONES)) {
|
|
1509
|
+
if (!isRecord2(entry)) continue;
|
|
1510
|
+
const key = entry["key"];
|
|
1511
|
+
const from = entry["from"];
|
|
1512
|
+
if (typeof key !== "string" || typeof from !== "string" || key === "" || from === "" || key === from || seen.has(key)) {
|
|
1513
|
+
continue;
|
|
1514
|
+
}
|
|
1515
|
+
seen.add(key);
|
|
1516
|
+
clones.push({ key, from });
|
|
1141
1517
|
}
|
|
1142
|
-
return
|
|
1518
|
+
return clones.length === 0 ? void 0 : clones;
|
|
1143
1519
|
}
|
|
1144
|
-
function
|
|
1145
|
-
if (
|
|
1146
|
-
|
|
1147
|
-
}
|
|
1148
|
-
return a === b;
|
|
1520
|
+
function serializeClones(clones) {
|
|
1521
|
+
if (clones === void 0 || clones.length === 0) return void 0;
|
|
1522
|
+
return clones.map(({ key, from }) => ({ key, from }));
|
|
1149
1523
|
}
|
|
1150
|
-
function
|
|
1151
|
-
const
|
|
1152
|
-
return
|
|
1524
|
+
function cloneNumber(key) {
|
|
1525
|
+
const match = /#(\d+)$/.exec(key);
|
|
1526
|
+
return match === null ? void 0 : Number(match[1]);
|
|
1153
1527
|
}
|
|
1154
|
-
function
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
);
|
|
1528
|
+
function cleanClones(definitions, clones, options = {}) {
|
|
1529
|
+
if (clones === void 0) return void 0;
|
|
1530
|
+
const byKey = new Map(definitions.map((d) => [d.key, d]));
|
|
1531
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1532
|
+
const kept = [];
|
|
1533
|
+
for (const clone of clones) {
|
|
1534
|
+
if (seen.has(clone.key)) continue;
|
|
1535
|
+
const original = byKey.get(clone.from);
|
|
1536
|
+
if (original === void 0 || original.sourceKey !== void 0) continue;
|
|
1537
|
+
const existing = byKey.get(clone.key);
|
|
1538
|
+
if (existing !== void 0 && existing.sourceKey !== clone.from) continue;
|
|
1539
|
+
if (options.overrideLocks !== true && resolveWidgetLock(original).clone) {
|
|
1540
|
+
continue;
|
|
1541
|
+
}
|
|
1542
|
+
seen.add(clone.key);
|
|
1543
|
+
kept.push(clone);
|
|
1163
1544
|
}
|
|
1164
|
-
|
|
1165
|
-
return
|
|
1545
|
+
if (kept.length === 0) return void 0;
|
|
1546
|
+
return kept.length === clones.length && kept.every((c, i) => c === clones[i]) ? clones : kept;
|
|
1166
1547
|
}
|
|
1167
|
-
function
|
|
1168
|
-
const
|
|
1169
|
-
|
|
1548
|
+
function defaultCloneTitle(original, clone, index) {
|
|
1549
|
+
const base = original.title ?? original.key;
|
|
1550
|
+
const suffix = ` (#${cloneNumber(clone.key) ?? index + 1})`;
|
|
1551
|
+
return `${base.slice(0, MAX_WIDGET_TITLE_LENGTH - suffix.length)}${suffix}`;
|
|
1170
1552
|
}
|
|
1171
|
-
function
|
|
1172
|
-
const
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
}).map(({ item }) => item);
|
|
1177
|
-
}
|
|
1178
|
-
function applyClientOptions(definition, data, values) {
|
|
1179
|
-
const options = definition.options ?? [];
|
|
1180
|
-
const sortOption = options.find(
|
|
1181
|
-
(o) => o.type === "sort" && o.apply === "client"
|
|
1182
|
-
);
|
|
1183
|
-
const columnsOption = options.find(
|
|
1184
|
-
(o) => o.type === "columns" && o.apply === "client"
|
|
1553
|
+
function withClones(definitions, layout, options = {}) {
|
|
1554
|
+
const valid = cleanClones(
|
|
1555
|
+
definitions,
|
|
1556
|
+
layout.clones,
|
|
1557
|
+
options
|
|
1185
1558
|
);
|
|
1186
|
-
|
|
1187
|
-
const
|
|
1188
|
-
|
|
1189
|
-
if (
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
});
|
|
1203
|
-
if (sorted.ok) next = { ...next, rows: sorted.value.rows };
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
if (Array.isArray(columnsValue) && next.kind === "TABLE") {
|
|
1208
|
-
const table = next;
|
|
1209
|
-
const indexes = columnsValue.map(tableColumnIndex).filter(
|
|
1210
|
-
(index) => index !== void 0 && index < table.columns.length
|
|
1211
|
-
);
|
|
1212
|
-
if (indexes.length > 0) {
|
|
1213
|
-
next = {
|
|
1214
|
-
...table,
|
|
1215
|
-
columns: indexes.map((i) => table.columns[i]),
|
|
1216
|
-
rows: table.rows.map((row) => indexes.map((i) => row[i]))
|
|
1217
|
-
};
|
|
1218
|
-
}
|
|
1559
|
+
if (valid === void 0) return definitions;
|
|
1560
|
+
const present = new Set(definitions.map((d) => d.key));
|
|
1561
|
+
const toAdd = valid.filter((c) => !present.has(c.key));
|
|
1562
|
+
if (toAdd.length === 0) return definitions;
|
|
1563
|
+
const out = [];
|
|
1564
|
+
for (const definition of definitions) {
|
|
1565
|
+
out.push(definition);
|
|
1566
|
+
toAdd.forEach((clone, index) => {
|
|
1567
|
+
if (clone.from !== definition.key) return;
|
|
1568
|
+
const copy = { ...definition };
|
|
1569
|
+
copy.key = clone.key;
|
|
1570
|
+
copy.sourceKey = definition.key;
|
|
1571
|
+
copy.title = defaultCloneTitle(definition, clone, index);
|
|
1572
|
+
delete copy.locked;
|
|
1573
|
+
out.push(copy);
|
|
1574
|
+
});
|
|
1219
1575
|
}
|
|
1220
|
-
return
|
|
1576
|
+
return out;
|
|
1221
1577
|
}
|
|
1222
1578
|
|
|
1223
|
-
// src/core/
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
var MAX_COLUMN_LIST = 100;
|
|
1227
|
-
var MAX_VALUE_LENGTH = 500;
|
|
1228
|
-
function isRecord(value) {
|
|
1229
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1579
|
+
// src/core/compat.ts
|
|
1580
|
+
function isStringArray(value) {
|
|
1581
|
+
return Array.isArray(value) && value.every((v) => typeof v === "string");
|
|
1230
1582
|
}
|
|
1231
|
-
function
|
|
1232
|
-
if (typeof value
|
|
1233
|
-
return value
|
|
1583
|
+
function upgradeLegacyWidgetData(value) {
|
|
1584
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1585
|
+
return value;
|
|
1234
1586
|
}
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
(
|
|
1241
|
-
|
|
1587
|
+
const record = value;
|
|
1588
|
+
if (record["kind"] === "TABLE" && isStringArray(record["columns"]) && Array.isArray(record["rows"]) && record["rows"].every(isStringArray)) {
|
|
1589
|
+
const rows = record["rows"];
|
|
1590
|
+
return {
|
|
1591
|
+
...record,
|
|
1592
|
+
columns: record["columns"].map((label) => ({ label })),
|
|
1593
|
+
rows: rows.map((row) => row.map((text) => ({ text })))
|
|
1594
|
+
};
|
|
1242
1595
|
}
|
|
1243
|
-
|
|
1596
|
+
if (record["kind"] === "GRAPH" && Array.isArray(record["points"]) && record["series"] === void 0) {
|
|
1597
|
+
const { points, ...rest } = record;
|
|
1598
|
+
return {
|
|
1599
|
+
...rest,
|
|
1600
|
+
series: [{ name: "Value", points }],
|
|
1601
|
+
chartType: "bar",
|
|
1602
|
+
valueFormat: "number"
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
return value;
|
|
1244
1606
|
}
|
|
1245
|
-
function
|
|
1246
|
-
if (!
|
|
1247
|
-
|
|
1248
|
-
const width = raw["width"];
|
|
1249
|
-
const flexible = raw["flexible"];
|
|
1250
|
-
const options = raw["options"];
|
|
1251
|
-
const view = raw["view"];
|
|
1252
|
-
const cleanTitle = typeof title === "string" ? title.trim().slice(0, MAX_WIDGET_TITLE_LENGTH) : "";
|
|
1253
|
-
const chosen = {};
|
|
1254
|
-
if (isRecord(options)) {
|
|
1255
|
-
for (const [key, value] of Object.entries(options)) {
|
|
1256
|
-
const parsed = parseOptionValue(value);
|
|
1257
|
-
if (parsed !== void 0) chosen[key] = parsed;
|
|
1258
|
-
}
|
|
1607
|
+
function fromLegacyDefinition(legacy) {
|
|
1608
|
+
if (!isWidgetKind(legacy.viewType)) {
|
|
1609
|
+
return null;
|
|
1259
1610
|
}
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
...
|
|
1264
|
-
|
|
1265
|
-
|
|
1611
|
+
return {
|
|
1612
|
+
key: legacy.widgetKey,
|
|
1613
|
+
title: legacy.title,
|
|
1614
|
+
...legacy.description === null ? {} : { description: legacy.description },
|
|
1615
|
+
kind: legacy.viewType,
|
|
1616
|
+
sortOrder: legacy.sortOrder
|
|
1266
1617
|
};
|
|
1267
|
-
return Object.keys(settings).length === 0 ? void 0 : settings;
|
|
1268
1618
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
)
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1619
|
+
|
|
1620
|
+
// src/core/layout.ts
|
|
1621
|
+
var MAX_PAGE_TITLE_LENGTH = 30;
|
|
1622
|
+
var MAX_PARSED_PAGES = 50;
|
|
1623
|
+
var MAX_PAGE_ROWS = 20;
|
|
1624
|
+
var EMPTY_LAYOUT = Object.freeze({
|
|
1625
|
+
order: Object.freeze([]),
|
|
1626
|
+
hidden: Object.freeze([]),
|
|
1627
|
+
minimized: Object.freeze([])
|
|
1628
|
+
});
|
|
1629
|
+
function uniqueStrings(value) {
|
|
1630
|
+
if (!Array.isArray(value)) return [];
|
|
1631
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1632
|
+
for (const entry of value) {
|
|
1633
|
+
if (typeof entry === "string" && entry !== "") seen.add(entry);
|
|
1279
1634
|
}
|
|
1280
|
-
return
|
|
1635
|
+
return [...seen];
|
|
1281
1636
|
}
|
|
1282
|
-
function
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1637
|
+
function parseLayout(raw) {
|
|
1638
|
+
let value = raw;
|
|
1639
|
+
if (typeof raw === "string") {
|
|
1640
|
+
try {
|
|
1641
|
+
value = JSON.parse(raw);
|
|
1642
|
+
} catch {
|
|
1643
|
+
return EMPTY_LAYOUT;
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1647
|
+
return EMPTY_LAYOUT;
|
|
1648
|
+
}
|
|
1649
|
+
const record = value;
|
|
1650
|
+
const order = uniqueStrings(record["order"]);
|
|
1651
|
+
const hidden = uniqueStrings(record["hidden"]);
|
|
1652
|
+
const minimized = uniqueStrings(record["minimized"]);
|
|
1653
|
+
const pages = parsePages(record["pages"]);
|
|
1654
|
+
const settings = parseSettings(
|
|
1655
|
+
record["settings"]
|
|
1656
|
+
);
|
|
1657
|
+
const clones = parseClones(
|
|
1658
|
+
record["clones"]
|
|
1659
|
+
);
|
|
1660
|
+
const extras = {
|
|
1661
|
+
...settings === void 0 ? {} : { settings },
|
|
1662
|
+
...clones === void 0 ? {} : { clones }
|
|
1663
|
+
};
|
|
1664
|
+
if (pages.length > 0) {
|
|
1665
|
+
return {
|
|
1666
|
+
order: EMPTY_LAYOUT.order,
|
|
1667
|
+
hidden: EMPTY_LAYOUT.hidden,
|
|
1668
|
+
minimized: EMPTY_LAYOUT.minimized,
|
|
1669
|
+
pages,
|
|
1670
|
+
...extras
|
|
1294
1671
|
};
|
|
1295
|
-
if (Object.keys(piece).length > 0) out[key] = piece;
|
|
1296
1672
|
}
|
|
1297
|
-
|
|
1673
|
+
if (order.length === 0 && hidden.length === 0 && minimized.length === 0) {
|
|
1674
|
+
return Object.keys(extras).length === 0 ? EMPTY_LAYOUT : { ...EMPTY_LAYOUT, ...extras };
|
|
1675
|
+
}
|
|
1676
|
+
return { order, hidden, minimized, ...extras };
|
|
1298
1677
|
}
|
|
1299
|
-
function
|
|
1300
|
-
|
|
1301
|
-
const
|
|
1302
|
-
|
|
1678
|
+
function parsePages(value) {
|
|
1679
|
+
if (!Array.isArray(value)) return [];
|
|
1680
|
+
const pages = [];
|
|
1681
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1682
|
+
for (const entry of value.slice(0, MAX_PARSED_PAGES)) {
|
|
1683
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
1684
|
+
continue;
|
|
1685
|
+
}
|
|
1686
|
+
const record = entry;
|
|
1687
|
+
const key = record["key"];
|
|
1688
|
+
if (typeof key !== "string" || key === "" || keys.has(key)) continue;
|
|
1689
|
+
keys.add(key);
|
|
1690
|
+
const rawTitle = record["title"];
|
|
1691
|
+
const title = typeof rawTitle === "string" && rawTitle.trim() !== "" ? rawTitle.trim().slice(0, MAX_PAGE_TITLE_LENGTH) : `Page ${pages.length + 1}`;
|
|
1692
|
+
const maxRows = record["maxRows"];
|
|
1693
|
+
const held = /* @__PURE__ */ new Set();
|
|
1694
|
+
for (const earlier of pages) {
|
|
1695
|
+
for (const k of [...earlier.order, ...earlier.hidden]) held.add(k);
|
|
1696
|
+
}
|
|
1697
|
+
const keep = (list) => uniqueStrings(list).filter((k) => !held.has(k));
|
|
1698
|
+
pages.push({
|
|
1699
|
+
key,
|
|
1700
|
+
title,
|
|
1701
|
+
order: keep(record["order"]),
|
|
1702
|
+
hidden: keep(record["hidden"]),
|
|
1703
|
+
minimized: keep(record["minimized"]),
|
|
1704
|
+
...typeof maxRows === "number" && Number.isInteger(maxRows) && maxRows >= 1 && maxRows <= MAX_PAGE_ROWS ? { maxRows } : {}
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1707
|
+
return pages;
|
|
1303
1708
|
}
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
|
|
1709
|
+
function serializeLayout(layout) {
|
|
1710
|
+
return JSON.stringify({
|
|
1711
|
+
order: [...layout.order],
|
|
1712
|
+
hidden: [...layout.hidden],
|
|
1713
|
+
minimized: [...layout.minimized],
|
|
1714
|
+
...layout.pages === void 0 || layout.pages.length === 0 ? {} : {
|
|
1715
|
+
pages: layout.pages.map((page) => ({
|
|
1716
|
+
key: page.key,
|
|
1717
|
+
title: page.title,
|
|
1718
|
+
order: [...page.order],
|
|
1719
|
+
hidden: [...page.hidden],
|
|
1720
|
+
minimized: [...page.minimized],
|
|
1721
|
+
...page.maxRows === void 0 ? {} : { maxRows: page.maxRows }
|
|
1722
|
+
}))
|
|
1723
|
+
},
|
|
1724
|
+
...serializeSettings(layout.settings) === void 0 ? {} : { settings: serializeSettings(layout.settings) },
|
|
1725
|
+
...serializeClones(layout.clones) === void 0 ? {} : { clones: serializeClones(layout.clones) }
|
|
1726
|
+
});
|
|
1319
1727
|
}
|
|
1320
|
-
function
|
|
1321
|
-
return
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
const heightToo = fill === "height" || fill === "both";
|
|
1325
|
-
if (flexible) return heightToo ? "both" : "width";
|
|
1326
|
-
return heightToo ? "height" : void 0;
|
|
1728
|
+
function bySortOrder(items) {
|
|
1729
|
+
return items.map((item, index) => ({ item, index })).sort(
|
|
1730
|
+
(a, b) => (a.item.sortOrder ?? 0) - (b.item.sortOrder ?? 0) || a.index - b.index
|
|
1731
|
+
).map(({ item }) => item);
|
|
1327
1732
|
}
|
|
1328
|
-
function
|
|
1329
|
-
|
|
1330
|
-
return item.fill === "width" || item.fill === "both";
|
|
1733
|
+
function isPinned(item) {
|
|
1734
|
+
return resolveWidgetLock(item).move;
|
|
1331
1735
|
}
|
|
1332
|
-
function
|
|
1333
|
-
|
|
1334
|
-
const
|
|
1335
|
-
const
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
}
|
|
1341
|
-
const next = { ...definition };
|
|
1342
|
-
if (title !== void 0) next.title = title;
|
|
1343
|
-
if (width !== void 0) next.width = width;
|
|
1344
|
-
if (flexibleChanged) {
|
|
1345
|
-
const fill = fillWith(
|
|
1346
|
-
definition.fill,
|
|
1347
|
-
settings.flexible === true
|
|
1348
|
-
);
|
|
1349
|
-
if (fill === void 0) {
|
|
1350
|
-
delete next.fill;
|
|
1351
|
-
} else {
|
|
1352
|
-
next.fill = fill;
|
|
1736
|
+
function viewerOrder(visible, layout) {
|
|
1737
|
+
const byKey = new Map(visible.map((d) => [d.key, d]));
|
|
1738
|
+
const ordered = [];
|
|
1739
|
+
for (const key of layout.order) {
|
|
1740
|
+
const definition = byKey.get(key);
|
|
1741
|
+
if (definition !== void 0) {
|
|
1742
|
+
ordered.push(definition);
|
|
1743
|
+
byKey.delete(key);
|
|
1353
1744
|
}
|
|
1354
1745
|
}
|
|
1355
|
-
|
|
1746
|
+
for (const definition of bySortOrder(visible)) {
|
|
1747
|
+
if (byKey.has(definition.key)) ordered.push(definition);
|
|
1748
|
+
}
|
|
1749
|
+
return ordered;
|
|
1356
1750
|
}
|
|
1357
|
-
function
|
|
1358
|
-
|
|
1359
|
-
(
|
|
1751
|
+
function placeAroundPinned(visible, free) {
|
|
1752
|
+
const slots = bySortOrder(visible).map(
|
|
1753
|
+
(item) => isPinned(item) ? item : void 0
|
|
1360
1754
|
);
|
|
1755
|
+
let next = 0;
|
|
1756
|
+
return slots.map((pinned) => pinned ?? free[next++]);
|
|
1361
1757
|
}
|
|
1362
|
-
function
|
|
1363
|
-
const
|
|
1364
|
-
const
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
}
|
|
1372
|
-
function optionValuesFromLayout(definitions, layout, options = {}) {
|
|
1373
|
-
const out = {};
|
|
1374
|
-
for (const definition of definitions) {
|
|
1375
|
-
if (definition.options === void 0 || definition.options.length === 0) {
|
|
1376
|
-
continue;
|
|
1377
|
-
}
|
|
1378
|
-
const locked = options.overrideLocks !== true && isOptionsLocked(definition);
|
|
1379
|
-
out[definition.key] = locked ? {} : layout.settings?.[definition.key]?.options ?? {};
|
|
1380
|
-
}
|
|
1381
|
-
return out;
|
|
1758
|
+
function visibleWidgets(definitions, layout) {
|
|
1759
|
+
const hidden = new Set(layout.hidden);
|
|
1760
|
+
const visible = definitions.filter((d) => !hidden.has(d.key));
|
|
1761
|
+
const ordered = viewerOrder(visible, layout);
|
|
1762
|
+
if (!visible.some(isPinned)) return ordered;
|
|
1763
|
+
return placeAroundPinned(
|
|
1764
|
+
visible,
|
|
1765
|
+
ordered.filter((item) => !isPinned(item))
|
|
1766
|
+
);
|
|
1382
1767
|
}
|
|
1383
|
-
function
|
|
1384
|
-
|
|
1385
|
-
return
|
|
1768
|
+
function hiddenWidgets(definitions, layout) {
|
|
1769
|
+
const hidden = new Set(layout.hidden);
|
|
1770
|
+
return bySortOrder(definitions.filter((d) => hidden.has(d.key)));
|
|
1386
1771
|
}
|
|
1387
|
-
function
|
|
1388
|
-
const
|
|
1389
|
-
const
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
"flexible",
|
|
1394
|
-
"options",
|
|
1395
|
-
"view"
|
|
1396
|
-
]) {
|
|
1397
|
-
if (!(field in patch)) continue;
|
|
1398
|
-
const value = patch[field];
|
|
1399
|
-
if (value === void 0) {
|
|
1400
|
-
delete draft[field];
|
|
1401
|
-
} else {
|
|
1402
|
-
draft[field] = value;
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
const merged = parseOne(draft);
|
|
1406
|
-
if (sameSettings(before, merged)) return layout;
|
|
1407
|
-
const rest = { ...layout.settings };
|
|
1408
|
-
if (merged === void 0) {
|
|
1409
|
-
delete rest[key];
|
|
1410
|
-
} else {
|
|
1411
|
-
rest[key] = merged;
|
|
1772
|
+
function moveWidget(definitions, layout, key, toIndex) {
|
|
1773
|
+
const current = visibleWidgets(definitions, layout);
|
|
1774
|
+
const fromIndex = current.findIndex((d) => d.key === key);
|
|
1775
|
+
const mover = current[fromIndex];
|
|
1776
|
+
if (mover === void 0 || !Number.isInteger(toIndex) || toIndex < 0 || toIndex >= current.length || toIndex === fromIndex || isPinned(mover) || isPinned(current[toIndex])) {
|
|
1777
|
+
return layout;
|
|
1412
1778
|
}
|
|
1413
|
-
const
|
|
1414
|
-
|
|
1779
|
+
const freeSlots = freeSlotIndexes(current);
|
|
1780
|
+
const free = current.filter((item) => !isPinned(item));
|
|
1781
|
+
const fromFree = freeSlots.indexOf(fromIndex);
|
|
1782
|
+
const toFree = freeSlots.indexOf(toIndex);
|
|
1783
|
+
free.splice(fromFree, 1);
|
|
1784
|
+
free.splice(toFree, 0, mover);
|
|
1785
|
+
const next = placeAroundPinned(current, free);
|
|
1786
|
+
return { ...layout, order: next.map((d) => d.key) };
|
|
1415
1787
|
}
|
|
1416
|
-
function
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
width: void 0,
|
|
1421
|
-
flexible: void 0,
|
|
1422
|
-
options: void 0,
|
|
1423
|
-
view: void 0
|
|
1788
|
+
function freeSlotIndexes(ordered) {
|
|
1789
|
+
const slots = [];
|
|
1790
|
+
ordered.forEach((item, index) => {
|
|
1791
|
+
if (!isPinned(item)) slots.push(index);
|
|
1424
1792
|
});
|
|
1793
|
+
return slots;
|
|
1425
1794
|
}
|
|
1426
|
-
function
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
const
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
(
|
|
1795
|
+
function moveWidgetBy(definitions, layout, key, offset) {
|
|
1796
|
+
const current = visibleWidgets(definitions, layout);
|
|
1797
|
+
const fromIndex = current.findIndex((d) => d.key === key);
|
|
1798
|
+
if (fromIndex === -1) return layout;
|
|
1799
|
+
const freeSlots = freeSlotIndexes(current);
|
|
1800
|
+
const fromFree = freeSlots.indexOf(fromIndex);
|
|
1801
|
+
if (fromFree === -1) return layout;
|
|
1802
|
+
const toFree = Math.max(
|
|
1803
|
+
0,
|
|
1804
|
+
Math.min(freeSlots.length - 1, fromFree + Math.trunc(offset))
|
|
1436
1805
|
);
|
|
1806
|
+
return moveWidget(definitions, layout, key, freeSlots[toFree]);
|
|
1437
1807
|
}
|
|
1438
|
-
function
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1808
|
+
function moveWidgetUp(definitions, layout, key) {
|
|
1809
|
+
return moveWidgetBy(definitions, layout, key, -1);
|
|
1810
|
+
}
|
|
1811
|
+
function moveWidgetDown(definitions, layout, key) {
|
|
1812
|
+
return moveWidgetBy(definitions, layout, key, 1);
|
|
1813
|
+
}
|
|
1814
|
+
function isHidden(layout, key) {
|
|
1815
|
+
return layout.hidden.includes(key);
|
|
1816
|
+
}
|
|
1817
|
+
function isMinimized(layout, key) {
|
|
1818
|
+
return layout.minimized.includes(key);
|
|
1819
|
+
}
|
|
1820
|
+
function hideWidget(layout, key) {
|
|
1821
|
+
if (isHidden(layout, key)) return layout;
|
|
1822
|
+
return { ...layout, hidden: [...layout.hidden, key] };
|
|
1823
|
+
}
|
|
1824
|
+
function showWidget(layout, key) {
|
|
1825
|
+
if (!isHidden(layout, key)) return layout;
|
|
1826
|
+
return {
|
|
1827
|
+
...layout,
|
|
1828
|
+
hidden: layout.hidden.filter((k) => k !== key),
|
|
1829
|
+
minimized: layout.minimized.filter((k) => k !== key)
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1832
|
+
function toggleHidden(layout, key) {
|
|
1833
|
+
return isHidden(layout, key) ? showWidget(layout, key) : hideWidget(layout, key);
|
|
1834
|
+
}
|
|
1835
|
+
function minimizeWidget(layout, key) {
|
|
1836
|
+
if (isMinimized(layout, key)) return layout;
|
|
1837
|
+
return { ...layout, minimized: [...layout.minimized, key] };
|
|
1838
|
+
}
|
|
1839
|
+
function restoreWidget(layout, key) {
|
|
1840
|
+
if (!isMinimized(layout, key)) return layout;
|
|
1841
|
+
return { ...layout, minimized: layout.minimized.filter((k) => k !== key) };
|
|
1842
|
+
}
|
|
1843
|
+
function toggleMinimized(layout, key) {
|
|
1844
|
+
return isMinimized(layout, key) ? restoreWidget(layout, key) : minimizeWidget(layout, key);
|
|
1845
|
+
}
|
|
1846
|
+
function pruneLayout(definitions, layout) {
|
|
1847
|
+
const known = new Set(definitions.map((d) => d.key));
|
|
1848
|
+
const keep = (keys) => keys.every((k) => known.has(k)) ? keys : keys.filter((k) => known.has(k));
|
|
1849
|
+
return mapLayoutLists(layout, (lists) => ({
|
|
1850
|
+
order: keep(lists.order),
|
|
1851
|
+
hidden: keep(lists.hidden),
|
|
1852
|
+
minimized: keep(lists.minimized)
|
|
1853
|
+
}));
|
|
1854
|
+
}
|
|
1855
|
+
function mapLayoutLists(layout, change) {
|
|
1856
|
+
const top = change(layout);
|
|
1857
|
+
const pages = layout.pages?.map((page) => {
|
|
1858
|
+
const next = change(page);
|
|
1859
|
+
return next.order === page.order && next.hidden === page.hidden && next.minimized === page.minimized ? page : { ...page, ...next };
|
|
1860
|
+
});
|
|
1861
|
+
const pagesChanged = pages !== void 0 && pages.some((page, i) => page !== layout.pages?.[i]);
|
|
1862
|
+
if (top.order === layout.order && top.hidden === layout.hidden && top.minimized === layout.minimized && !pagesChanged) {
|
|
1863
|
+
return layout;
|
|
1444
1864
|
}
|
|
1865
|
+
return {
|
|
1866
|
+
...layout,
|
|
1867
|
+
...top,
|
|
1868
|
+
...pages === void 0 ? {} : { pages }
|
|
1869
|
+
};
|
|
1445
1870
|
}
|
|
1446
|
-
function
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
const
|
|
1450
|
-
const
|
|
1871
|
+
function enforceLocks(definitions, layout, options = {}) {
|
|
1872
|
+
if (options.overrideLocks === true) return layout;
|
|
1873
|
+
const noMove = /* @__PURE__ */ new Set();
|
|
1874
|
+
const noHide = /* @__PURE__ */ new Set();
|
|
1875
|
+
const noMinimize = /* @__PURE__ */ new Set();
|
|
1451
1876
|
for (const definition of definitions) {
|
|
1452
|
-
const
|
|
1453
|
-
if (
|
|
1877
|
+
const lock = resolveWidgetLock(definition);
|
|
1878
|
+
if (lock.move) noMove.add(definition.key);
|
|
1879
|
+
if (lock.hide) noHide.add(definition.key);
|
|
1880
|
+
if (lock.minimize) noMinimize.add(definition.key);
|
|
1454
1881
|
}
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
for (const option of definition.options ?? []) {
|
|
1462
|
-
const picked = one.options?.[option.key];
|
|
1463
|
-
if (picked === void 0) continue;
|
|
1464
|
-
const value = coerceOptionValue(option, picked);
|
|
1465
|
-
if (value !== void 0) {
|
|
1466
|
-
chosen[option.key] = option.type === "dateRange" ? picked : value;
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
let title = one.title;
|
|
1470
|
-
if (title !== void 0) {
|
|
1471
|
-
title = title.trim().slice(0, MAX_WIDGET_TITLE_LENGTH);
|
|
1472
|
-
if (title === "") {
|
|
1473
|
-
title = void 0;
|
|
1474
|
-
} else {
|
|
1475
|
-
title = uniqueTitle(taken, title);
|
|
1476
|
-
taken.add(normalizeTitle(title));
|
|
1477
|
-
}
|
|
1478
|
-
}
|
|
1479
|
-
const next = {
|
|
1480
|
-
...title === void 0 ? {} : { title },
|
|
1481
|
-
...one.width === void 0 ? {} : { width: Math.min(range.max, Math.max(range.min, one.width)) },
|
|
1482
|
-
...one.flexible === void 0 ? {} : { flexible: one.flexible },
|
|
1483
|
-
...Object.keys(chosen).length === 0 ? {} : { options: chosen },
|
|
1484
|
-
...one.view !== void 0 && (one.view === BASE_VIEW_KEY || findView(definition, one.view) !== void 0) ? { view: one.view } : {}
|
|
1485
|
-
};
|
|
1486
|
-
if (Object.keys(next).length > 0) {
|
|
1487
|
-
cleaned[definition.key] = sameSettings(one, next) ? one : next;
|
|
1488
|
-
}
|
|
1489
|
-
}
|
|
1490
|
-
const keys = Object.keys(cleaned);
|
|
1491
|
-
const unchanged = keys.length === Object.keys(settings).length && keys.every((key) => cleaned[key] === settings[key]);
|
|
1492
|
-
if (unchanged) return layout;
|
|
1493
|
-
const { settings: _previous, ...others } = layout;
|
|
1494
|
-
return keys.length === 0 ? others : { ...others, settings: cleaned };
|
|
1882
|
+
const without2 = (keys, drop) => keys.some((key) => drop.has(key)) ? keys.filter((key) => !drop.has(key)) : keys;
|
|
1883
|
+
return mapLayoutLists(layout, (lists) => ({
|
|
1884
|
+
order: without2(lists.order, noMove),
|
|
1885
|
+
hidden: without2(lists.hidden, noHide),
|
|
1886
|
+
minimized: without2(lists.minimized, noMinimize)
|
|
1887
|
+
}));
|
|
1495
1888
|
}
|
|
1496
1889
|
|
|
1497
|
-
// src/core/
|
|
1498
|
-
var
|
|
1499
|
-
var
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
hidden: Object.freeze([]),
|
|
1504
|
-
minimized: Object.freeze([])
|
|
1505
|
-
});
|
|
1506
|
-
function uniqueStrings(value) {
|
|
1507
|
-
if (!Array.isArray(value)) return [];
|
|
1508
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1509
|
-
for (const entry of value) {
|
|
1510
|
-
if (typeof entry === "string" && entry !== "") seen.add(entry);
|
|
1511
|
-
}
|
|
1512
|
-
return [...seen];
|
|
1890
|
+
// src/core/pages.ts
|
|
1891
|
+
var DEFAULT_PAGE_KEY = "default";
|
|
1892
|
+
var DEFAULT_PAGE_ROWS = 4;
|
|
1893
|
+
function rowsAllowed(page, options) {
|
|
1894
|
+
const rows = page.maxRows ?? options.maxRows ?? DEFAULT_PAGE_ROWS;
|
|
1895
|
+
return Math.max(1, Math.min(MAX_PAGE_ROWS, Math.floor(rows)));
|
|
1513
1896
|
}
|
|
1514
|
-
function
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
return EMPTY_LAYOUT;
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1524
|
-
return EMPTY_LAYOUT;
|
|
1897
|
+
function hasPages(layout) {
|
|
1898
|
+
return layout.pages !== void 0 && layout.pages.length > 0;
|
|
1899
|
+
}
|
|
1900
|
+
function pageList(layout) {
|
|
1901
|
+
if (layout.pages !== void 0 && layout.pages.length > 0) {
|
|
1902
|
+
return layout.pages;
|
|
1525
1903
|
}
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1904
|
+
return [
|
|
1905
|
+
{
|
|
1906
|
+
key: DEFAULT_PAGE_KEY,
|
|
1907
|
+
title: "Page 1",
|
|
1908
|
+
order: layout.order,
|
|
1909
|
+
hidden: layout.hidden,
|
|
1910
|
+
minimized: layout.minimized
|
|
1911
|
+
}
|
|
1912
|
+
];
|
|
1913
|
+
}
|
|
1914
|
+
function withExplicitPages(layout) {
|
|
1915
|
+
if (hasPages(layout)) return layout;
|
|
1916
|
+
return {
|
|
1917
|
+
...layout,
|
|
1918
|
+
order: [],
|
|
1919
|
+
hidden: [],
|
|
1920
|
+
minimized: [],
|
|
1921
|
+
pages: pageList(layout)
|
|
1922
|
+
};
|
|
1923
|
+
}
|
|
1924
|
+
function sameKeys(a, b) {
|
|
1925
|
+
return a === b || a.length === b.length && a.every((k, i) => k === b[i]);
|
|
1926
|
+
}
|
|
1927
|
+
function pageLayout(layout, pageKey) {
|
|
1928
|
+
if (!hasPages(layout))
|
|
1929
|
+
return pageKey === DEFAULT_PAGE_KEY ? layout : void 0;
|
|
1930
|
+
const page = layout.pages?.find(
|
|
1931
|
+
(p) => p.key === pageKey
|
|
1533
1932
|
);
|
|
1534
|
-
|
|
1535
|
-
|
|
1933
|
+
if (page === void 0) return void 0;
|
|
1934
|
+
const { pages, ...rest } = layout;
|
|
1935
|
+
return {
|
|
1936
|
+
...rest,
|
|
1937
|
+
order: page.order,
|
|
1938
|
+
hidden: page.hidden,
|
|
1939
|
+
minimized: page.minimized
|
|
1940
|
+
};
|
|
1941
|
+
}
|
|
1942
|
+
function withPageLayout(layout, pageKey, next) {
|
|
1943
|
+
const current = pageLayout(layout, pageKey);
|
|
1944
|
+
if (current === void 0 || sameKeys(current.order, next.order) && sameKeys(current.hidden, next.hidden) && sameKeys(current.minimized, next.minimized)) {
|
|
1945
|
+
return layout;
|
|
1946
|
+
}
|
|
1947
|
+
if (!hasPages(layout)) {
|
|
1536
1948
|
return {
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
...withSettings
|
|
1949
|
+
...layout,
|
|
1950
|
+
order: next.order,
|
|
1951
|
+
hidden: next.hidden,
|
|
1952
|
+
minimized: next.minimized
|
|
1542
1953
|
};
|
|
1543
1954
|
}
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
continue;
|
|
1556
|
-
}
|
|
1557
|
-
const record = entry;
|
|
1558
|
-
const key = record["key"];
|
|
1559
|
-
if (typeof key !== "string" || key === "" || keys.has(key)) continue;
|
|
1560
|
-
keys.add(key);
|
|
1561
|
-
const rawTitle = record["title"];
|
|
1562
|
-
const title = typeof rawTitle === "string" && rawTitle.trim() !== "" ? rawTitle.trim().slice(0, MAX_PAGE_TITLE_LENGTH) : `Page ${pages.length + 1}`;
|
|
1563
|
-
const maxRows = record["maxRows"];
|
|
1564
|
-
const held = /* @__PURE__ */ new Set();
|
|
1565
|
-
for (const earlier of pages) {
|
|
1566
|
-
for (const k of [...earlier.order, ...earlier.hidden]) held.add(k);
|
|
1567
|
-
}
|
|
1568
|
-
const keep = (list) => uniqueStrings(list).filter((k) => !held.has(k));
|
|
1569
|
-
pages.push({
|
|
1570
|
-
key,
|
|
1571
|
-
title,
|
|
1572
|
-
order: keep(record["order"]),
|
|
1573
|
-
hidden: keep(record["hidden"]),
|
|
1574
|
-
minimized: keep(record["minimized"]),
|
|
1575
|
-
...typeof maxRows === "number" && Number.isInteger(maxRows) && maxRows >= 1 && maxRows <= MAX_PAGE_ROWS ? { maxRows } : {}
|
|
1576
|
-
});
|
|
1577
|
-
}
|
|
1578
|
-
return pages;
|
|
1955
|
+
return {
|
|
1956
|
+
...layout,
|
|
1957
|
+
pages: (layout.pages ?? []).map(
|
|
1958
|
+
(page) => page.key === pageKey ? {
|
|
1959
|
+
...page,
|
|
1960
|
+
order: next.order,
|
|
1961
|
+
hidden: next.hidden,
|
|
1962
|
+
minimized: next.minimized
|
|
1963
|
+
} : page
|
|
1964
|
+
)
|
|
1965
|
+
};
|
|
1579
1966
|
}
|
|
1580
|
-
function
|
|
1581
|
-
return
|
|
1582
|
-
order: [...layout.order],
|
|
1583
|
-
hidden: [...layout.hidden],
|
|
1584
|
-
minimized: [...layout.minimized],
|
|
1585
|
-
...layout.pages === void 0 || layout.pages.length === 0 ? {} : {
|
|
1586
|
-
pages: layout.pages.map((page) => ({
|
|
1587
|
-
key: page.key,
|
|
1588
|
-
title: page.title,
|
|
1589
|
-
order: [...page.order],
|
|
1590
|
-
hidden: [...page.hidden],
|
|
1591
|
-
minimized: [...page.minimized],
|
|
1592
|
-
...page.maxRows === void 0 ? {} : { maxRows: page.maxRows }
|
|
1593
|
-
}))
|
|
1594
|
-
},
|
|
1595
|
-
...serializeSettings(layout.settings) === void 0 ? {} : { settings: serializeSettings(layout.settings) }
|
|
1596
|
-
});
|
|
1967
|
+
function normalizedTitle(title) {
|
|
1968
|
+
return title.trim().normalize("NFKC").toLowerCase();
|
|
1597
1969
|
}
|
|
1598
|
-
function
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1970
|
+
function resolvePageKey(layout, reference) {
|
|
1971
|
+
if (reference === void 0 || reference === "") return void 0;
|
|
1972
|
+
const pages = pageList(layout);
|
|
1973
|
+
const byKey = pages.find((p) => p.key === reference);
|
|
1974
|
+
if (byKey !== void 0) return byKey.key;
|
|
1975
|
+
const wanted = normalizedTitle(reference);
|
|
1976
|
+
return pages.find((p) => normalizedTitle(p.title) === wanted)?.key;
|
|
1602
1977
|
}
|
|
1603
|
-
function
|
|
1604
|
-
return
|
|
1978
|
+
function homePageKey(layout, item) {
|
|
1979
|
+
return resolvePageKey(layout, item.page) ?? pageList(layout)[0].key;
|
|
1605
1980
|
}
|
|
1606
|
-
function
|
|
1607
|
-
const
|
|
1608
|
-
const
|
|
1609
|
-
for (const
|
|
1610
|
-
|
|
1611
|
-
if (definition
|
|
1612
|
-
|
|
1613
|
-
|
|
1981
|
+
function assignPages(definitions, layout) {
|
|
1982
|
+
const pages = pageList(layout);
|
|
1983
|
+
const assigned = new Map(pages.map((p) => [p.key, []]));
|
|
1984
|
+
for (const definition of definitions) {
|
|
1985
|
+
let pageKey;
|
|
1986
|
+
if (!resolveWidgetLock(definition).move) {
|
|
1987
|
+
pageKey = pages.find(
|
|
1988
|
+
(p) => p.order.includes(definition.key) || p.hidden.includes(definition.key)
|
|
1989
|
+
)?.key;
|
|
1614
1990
|
}
|
|
1991
|
+
assigned.get(pageKey ?? homePageKey(layout, definition))?.push(definition);
|
|
1615
1992
|
}
|
|
1616
|
-
|
|
1617
|
-
|
|
1993
|
+
return assigned;
|
|
1994
|
+
}
|
|
1995
|
+
function pageOf(definitions, layout, key) {
|
|
1996
|
+
for (const [pageKey, widgets] of assignPages(definitions, layout)) {
|
|
1997
|
+
if (widgets.some((w) => w.key === key)) return pageKey;
|
|
1618
1998
|
}
|
|
1619
|
-
return
|
|
1999
|
+
return void 0;
|
|
1620
2000
|
}
|
|
1621
|
-
function
|
|
1622
|
-
const
|
|
1623
|
-
|
|
1624
|
-
);
|
|
1625
|
-
let next = 0;
|
|
1626
|
-
return slots.map((pinned) => pinned ?? free[next++]);
|
|
2001
|
+
function pageWidgets(definitions, layout, pageKey) {
|
|
2002
|
+
const onPage = assignPages(definitions, layout).get(pageKey) ?? [];
|
|
2003
|
+
const view = pageLayout(layout, pageKey);
|
|
2004
|
+
return view === void 0 ? [] : visibleWidgets(onPage, view);
|
|
1627
2005
|
}
|
|
1628
|
-
function
|
|
1629
|
-
const
|
|
1630
|
-
|
|
1631
|
-
const ordered = viewerOrder(visible, layout);
|
|
1632
|
-
if (!visible.some(isPinned)) return ordered;
|
|
1633
|
-
return placeAroundPinned(
|
|
1634
|
-
visible,
|
|
1635
|
-
ordered.filter((item) => !isPinned(item))
|
|
2006
|
+
function pageRoom(definitions, layout, pageKey, options = {}) {
|
|
2007
|
+
const page = pageList(layout).find(
|
|
2008
|
+
(p) => p.key === pageKey
|
|
1636
2009
|
);
|
|
2010
|
+
if (page === void 0) return void 0;
|
|
2011
|
+
const maxRows = rowsAllowed(page, options);
|
|
2012
|
+
const rowsUsed = placeRows(
|
|
2013
|
+
pageWidgets(definitions, layout, pageKey)
|
|
2014
|
+
).length;
|
|
2015
|
+
return { maxRows, rowsUsed, rowsFree: Math.max(0, maxRows - rowsUsed) };
|
|
1637
2016
|
}
|
|
1638
|
-
function
|
|
1639
|
-
|
|
1640
|
-
return bySortOrder(definitions.filter((d) => hidden.has(d.key)));
|
|
1641
|
-
}
|
|
1642
|
-
function moveWidget(definitions, layout, key, toIndex) {
|
|
1643
|
-
const current = visibleWidgets(definitions, layout);
|
|
1644
|
-
const fromIndex = current.findIndex((d) => d.key === key);
|
|
1645
|
-
const mover = current[fromIndex];
|
|
1646
|
-
if (mover === void 0 || !Number.isInteger(toIndex) || toIndex < 0 || toIndex >= current.length || toIndex === fromIndex || isPinned(mover) || isPinned(current[toIndex])) {
|
|
1647
|
-
return layout;
|
|
1648
|
-
}
|
|
1649
|
-
const freeSlots = freeSlotIndexes(current);
|
|
1650
|
-
const free = current.filter((item) => !isPinned(item));
|
|
1651
|
-
const fromFree = freeSlots.indexOf(fromIndex);
|
|
1652
|
-
const toFree = freeSlots.indexOf(toIndex);
|
|
1653
|
-
free.splice(fromFree, 1);
|
|
1654
|
-
free.splice(toFree, 0, mover);
|
|
1655
|
-
const next = placeAroundPinned(current, free);
|
|
1656
|
-
return { ...layout, order: next.map((d) => d.key) };
|
|
2017
|
+
function describePage(page) {
|
|
2018
|
+
return `Page "${page.title}"`;
|
|
1657
2019
|
}
|
|
1658
|
-
function
|
|
1659
|
-
|
|
1660
|
-
ordered.forEach((item, index) => {
|
|
1661
|
-
if (!isPinned(item)) slots.push(index);
|
|
1662
|
-
});
|
|
1663
|
-
return slots;
|
|
2020
|
+
function titleOf(item) {
|
|
2021
|
+
return item.title ?? item.key;
|
|
1664
2022
|
}
|
|
1665
|
-
function
|
|
1666
|
-
const
|
|
1667
|
-
|
|
1668
|
-
if (
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
2023
|
+
function checkTitle(layout, title, exceptKey) {
|
|
2024
|
+
const trimmed = title.trim();
|
|
2025
|
+
if (trimmed === "") return err("A page title must not be empty.");
|
|
2026
|
+
if (trimmed.length > MAX_PAGE_TITLE_LENGTH) {
|
|
2027
|
+
return err(
|
|
2028
|
+
`Page title "${trimmed}" is ${trimmed.length} characters; the most is ${MAX_PAGE_TITLE_LENGTH}.`
|
|
2029
|
+
);
|
|
2030
|
+
}
|
|
2031
|
+
const wanted = normalizedTitle(trimmed);
|
|
2032
|
+
const clash = pageList(layout).find(
|
|
2033
|
+
(p) => p.key !== exceptKey && normalizedTitle(p.title) === wanted
|
|
1675
2034
|
);
|
|
1676
|
-
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
|
-
return
|
|
1680
|
-
}
|
|
1681
|
-
function moveWidgetDown(definitions, layout, key) {
|
|
1682
|
-
return moveWidgetBy(definitions, layout, key, 1);
|
|
1683
|
-
}
|
|
1684
|
-
function isHidden(layout, key) {
|
|
1685
|
-
return layout.hidden.includes(key);
|
|
2035
|
+
if (clash !== void 0) {
|
|
2036
|
+
return err(`Another page is already called "${clash.title}".`);
|
|
2037
|
+
}
|
|
2038
|
+
return ok(trimmed);
|
|
1686
2039
|
}
|
|
1687
|
-
function
|
|
1688
|
-
|
|
2040
|
+
function nextPageKey(layout) {
|
|
2041
|
+
const keys = new Set(pageList(layout).map((p) => p.key));
|
|
2042
|
+
let n = 1;
|
|
2043
|
+
while (keys.has(`page-${n}`)) n += 1;
|
|
2044
|
+
return `page-${n}`;
|
|
1689
2045
|
}
|
|
1690
|
-
function
|
|
1691
|
-
|
|
1692
|
-
|
|
2046
|
+
function nextPageTitle(layout) {
|
|
2047
|
+
const used = new Set(
|
|
2048
|
+
pageList(layout).map((p) => normalizedTitle(p.title))
|
|
2049
|
+
);
|
|
2050
|
+
let n = pageList(layout).length + 1;
|
|
2051
|
+
while (used.has(normalizedTitle(`Page ${n}`))) n += 1;
|
|
2052
|
+
return `Page ${n}`;
|
|
1693
2053
|
}
|
|
1694
|
-
function
|
|
1695
|
-
if (
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
2054
|
+
function addPage(layout, options = {}) {
|
|
2055
|
+
if (pageList(layout).length >= MAX_PARSED_PAGES) {
|
|
2056
|
+
return err(
|
|
2057
|
+
`The layout already has ${MAX_PARSED_PAGES} pages, the most it can keep.`
|
|
2058
|
+
);
|
|
2059
|
+
}
|
|
2060
|
+
const key = options.key ?? nextPageKey(layout);
|
|
2061
|
+
if (pageList(layout).some((p) => p.key === key)) {
|
|
2062
|
+
return err(`A page with the key "${key}" already exists.`);
|
|
2063
|
+
}
|
|
2064
|
+
const title = checkTitle(
|
|
2065
|
+
layout,
|
|
2066
|
+
options.title ?? nextPageTitle(layout)
|
|
2067
|
+
);
|
|
2068
|
+
if (!title.ok) return title;
|
|
2069
|
+
const base = withExplicitPages(layout);
|
|
2070
|
+
const page = {
|
|
2071
|
+
key,
|
|
2072
|
+
title: title.value,
|
|
2073
|
+
order: [],
|
|
2074
|
+
hidden: [],
|
|
2075
|
+
minimized: []
|
|
1700
2076
|
};
|
|
2077
|
+
return ok({ ...base, pages: [...base.pages ?? [], page] });
|
|
1701
2078
|
}
|
|
1702
|
-
function
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
if (
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
return {
|
|
2079
|
+
function renamePage(layout, pageKey, title) {
|
|
2080
|
+
const page = pageList(layout).find(
|
|
2081
|
+
(p) => p.key === pageKey
|
|
2082
|
+
);
|
|
2083
|
+
if (page === void 0) return err(`Page "${pageKey}" doesn't exist.`);
|
|
2084
|
+
const checked = checkTitle(layout, title, pageKey);
|
|
2085
|
+
if (!checked.ok) return checked;
|
|
2086
|
+
if (checked.value === page.title) return ok(layout);
|
|
2087
|
+
const base = withExplicitPages(layout);
|
|
2088
|
+
return ok({
|
|
2089
|
+
...base,
|
|
2090
|
+
pages: (base.pages ?? []).map(
|
|
2091
|
+
(p) => p.key === pageKey ? { ...p, title: checked.value } : p
|
|
2092
|
+
)
|
|
2093
|
+
});
|
|
1712
2094
|
}
|
|
1713
|
-
function
|
|
1714
|
-
|
|
2095
|
+
function movePage(layout, pageKey, offset) {
|
|
2096
|
+
const pages = layout.pages ?? [];
|
|
2097
|
+
const from = pages.findIndex((p) => p.key === pageKey);
|
|
2098
|
+
if (from === -1) return layout;
|
|
2099
|
+
const to = Math.max(
|
|
2100
|
+
0,
|
|
2101
|
+
Math.min(pages.length - 1, from + Math.trunc(offset))
|
|
2102
|
+
);
|
|
2103
|
+
if (to === from) return layout;
|
|
2104
|
+
const next = [...pages];
|
|
2105
|
+
const [moved] = next.splice(from, 1);
|
|
2106
|
+
next.splice(to, 0, moved);
|
|
2107
|
+
return { ...layout, pages: next };
|
|
1715
2108
|
}
|
|
1716
|
-
function
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
order: keep(lists.order),
|
|
1721
|
-
hidden: keep(lists.hidden),
|
|
1722
|
-
minimized: keep(lists.minimized)
|
|
1723
|
-
}));
|
|
2109
|
+
function overflowing(definitions, layout, options) {
|
|
2110
|
+
return pageList(layout).find(
|
|
2111
|
+
(page) => placeRows(pageWidgets(definitions, layout, page.key)).length > rowsAllowed(page, options)
|
|
2112
|
+
);
|
|
1724
2113
|
}
|
|
1725
|
-
function
|
|
1726
|
-
const
|
|
1727
|
-
const
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
2114
|
+
function removePage(definitions, layout, pageKey, options = {}) {
|
|
2115
|
+
const pages = pageList(layout);
|
|
2116
|
+
const index = pages.findIndex((p) => p.key === pageKey);
|
|
2117
|
+
const page = pages[index];
|
|
2118
|
+
if (page === void 0) return err(`Page "${pageKey}" doesn't exist.`);
|
|
2119
|
+
if (pages.length === 1) {
|
|
2120
|
+
return err(
|
|
2121
|
+
`${describePage(page)} is the only page, so it can't be removed.`
|
|
2122
|
+
);
|
|
1734
2123
|
}
|
|
1735
|
-
|
|
2124
|
+
const target = pages[index > 0 ? index - 1 : 1];
|
|
2125
|
+
const merged = {
|
|
1736
2126
|
...layout,
|
|
1737
|
-
|
|
1738
|
-
|
|
2127
|
+
pages: pages.filter((p) => p.key !== pageKey).map(
|
|
2128
|
+
(p) => p.key === target.key ? {
|
|
2129
|
+
...p,
|
|
2130
|
+
order: index > 0 ? [...p.order, ...page.order] : [...page.order, ...p.order],
|
|
2131
|
+
hidden: [...p.hidden, ...page.hidden],
|
|
2132
|
+
minimized: [...p.minimized, ...page.minimized]
|
|
2133
|
+
} : p
|
|
2134
|
+
)
|
|
1739
2135
|
};
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
if (lock.move) noMove.add(definition.key);
|
|
1749
|
-
if (lock.hide) noHide.add(definition.key);
|
|
1750
|
-
if (lock.minimize) noMinimize.add(definition.key);
|
|
1751
|
-
}
|
|
1752
|
-
const without2 = (keys, drop) => keys.some((key) => drop.has(key)) ? keys.filter((key) => !drop.has(key)) : keys;
|
|
1753
|
-
return mapLayoutLists(layout, (lists) => ({
|
|
1754
|
-
order: without2(lists.order, noMove),
|
|
1755
|
-
hidden: without2(lists.hidden, noHide),
|
|
1756
|
-
minimized: without2(lists.minimized, noMinimize)
|
|
1757
|
-
}));
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
// src/core/validate.ts
|
|
1761
|
-
var MAX_REPORTED_PROBLEMS = 10;
|
|
1762
|
-
function describeType2(value) {
|
|
1763
|
-
if (value === null) return "null";
|
|
1764
|
-
if (Array.isArray(value)) return "array";
|
|
1765
|
-
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
1766
|
-
return String(value);
|
|
1767
|
-
}
|
|
1768
|
-
return typeof value;
|
|
1769
|
-
}
|
|
1770
|
-
function isRecord2(value) {
|
|
1771
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1772
|
-
}
|
|
1773
|
-
function join(path, field) {
|
|
1774
|
-
return path === "" ? field : `${path}.${field}`;
|
|
1775
|
-
}
|
|
1776
|
-
function requireString(record, field, path, problems) {
|
|
1777
|
-
const value = record[field];
|
|
1778
|
-
if (typeof value !== "string") {
|
|
1779
|
-
problems.push(
|
|
1780
|
-
`${join(path, field)} must be a string, got ${describeType2(value)}.`
|
|
2136
|
+
const full = overflowing(
|
|
2137
|
+
definitions,
|
|
2138
|
+
merged,
|
|
2139
|
+
options
|
|
2140
|
+
);
|
|
2141
|
+
if (full !== void 0) {
|
|
2142
|
+
return err(
|
|
2143
|
+
`${describePage(page)} has widgets that don't fit on the other pages (${describePage(full)} would need more than ${rowsAllowed(full, options)} rows). Move or hide some first.`
|
|
1781
2144
|
);
|
|
1782
2145
|
}
|
|
2146
|
+
return ok(merged);
|
|
1783
2147
|
}
|
|
1784
|
-
function
|
|
1785
|
-
|
|
1786
|
-
requireString(record, field, path, problems);
|
|
1787
|
-
}
|
|
1788
|
-
}
|
|
1789
|
-
function requireNumber(record, field, path, problems) {
|
|
1790
|
-
const value = record[field];
|
|
1791
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
1792
|
-
problems.push(
|
|
1793
|
-
`${join(path, field)} must be a finite number, got ${describeType2(value)}.`
|
|
1794
|
-
);
|
|
1795
|
-
}
|
|
1796
|
-
}
|
|
1797
|
-
function optionalNumber(record, field, path, problems) {
|
|
1798
|
-
if (record[field] !== void 0) {
|
|
1799
|
-
requireNumber(record, field, path, problems);
|
|
1800
|
-
}
|
|
2148
|
+
function without(keys, key) {
|
|
2149
|
+
return keys.includes(key) ? keys.filter((k) => k !== key) : keys;
|
|
1801
2150
|
}
|
|
1802
|
-
function
|
|
1803
|
-
const
|
|
1804
|
-
if (
|
|
1805
|
-
|
|
1806
|
-
|
|
2151
|
+
function moveWidgetToPage(definitions, layout, widgetKey, targetKey, options = {}) {
|
|
2152
|
+
const widget = definitions.find((d) => d.key === widgetKey);
|
|
2153
|
+
if (widget === void 0) {
|
|
2154
|
+
return err(
|
|
2155
|
+
`Widget "${widgetKey}" isn't one of the widgets, so it can't be moved to a page.`
|
|
1807
2156
|
);
|
|
1808
2157
|
}
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
2158
|
+
const pages = pageList(layout);
|
|
2159
|
+
const target = pages.find((p) => p.key === targetKey);
|
|
2160
|
+
if (target === void 0) return err(`Page "${targetKey}" doesn't exist.`);
|
|
2161
|
+
const sourceKey = pageOf(definitions, layout, widgetKey);
|
|
2162
|
+
if (sourceKey === targetKey) return ok(layout);
|
|
2163
|
+
if (resolveWidgetLock(widget).move) {
|
|
2164
|
+
return err(
|
|
2165
|
+
`Widget "${titleOf(widget)}" is locked against moving, so it can't be moved to ${describePage(target)}.`
|
|
1815
2166
|
);
|
|
1816
2167
|
}
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
2168
|
+
const base = withExplicitPages(layout);
|
|
2169
|
+
const source = (base.pages ?? []).find(
|
|
2170
|
+
(p) => p.key === sourceKey
|
|
2171
|
+
);
|
|
2172
|
+
const hidden = source.hidden.includes(widgetKey);
|
|
2173
|
+
const minimized = source.minimized.includes(widgetKey);
|
|
2174
|
+
const targetOrder = pageWidgets(
|
|
2175
|
+
definitions,
|
|
2176
|
+
base,
|
|
2177
|
+
targetKey
|
|
2178
|
+
).map((d) => d.key);
|
|
2179
|
+
const moved = {
|
|
2180
|
+
...base,
|
|
2181
|
+
pages: (base.pages ?? []).map((p) => {
|
|
2182
|
+
if (p.key === sourceKey) {
|
|
2183
|
+
return {
|
|
2184
|
+
...p,
|
|
2185
|
+
order: without(p.order, widgetKey),
|
|
2186
|
+
hidden: without(p.hidden, widgetKey),
|
|
2187
|
+
minimized: without(p.minimized, widgetKey)
|
|
2188
|
+
};
|
|
2189
|
+
}
|
|
2190
|
+
if (p.key === targetKey) {
|
|
2191
|
+
return {
|
|
2192
|
+
...p,
|
|
2193
|
+
order: hidden ? p.order : [...targetOrder, widgetKey],
|
|
2194
|
+
hidden: hidden ? [...p.hidden, widgetKey] : p.hidden,
|
|
2195
|
+
minimized: minimized ? [...p.minimized, widgetKey] : p.minimized
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
return p;
|
|
2199
|
+
})
|
|
2200
|
+
};
|
|
2201
|
+
if (!hidden) {
|
|
2202
|
+
const allowed = rowsAllowed(target, options);
|
|
2203
|
+
const needed = placeRows(
|
|
2204
|
+
pageWidgets(definitions, moved, targetKey)
|
|
2205
|
+
).length;
|
|
2206
|
+
if (needed > allowed) {
|
|
2207
|
+
return err(
|
|
2208
|
+
`${describePage(target)} has no room for "${titleOf(widget)}": it would need row ${needed}, and the page allows ${allowed}.`
|
|
2209
|
+
);
|
|
2210
|
+
}
|
|
1829
2211
|
}
|
|
2212
|
+
return ok(moved);
|
|
1830
2213
|
}
|
|
1831
|
-
function
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
);
|
|
1837
|
-
return null;
|
|
1838
|
-
}
|
|
1839
|
-
return value;
|
|
2214
|
+
function withoutLockField(definition) {
|
|
2215
|
+
if (definition.locked === void 0) return definition;
|
|
2216
|
+
const copy = { ...definition };
|
|
2217
|
+
delete copy.locked;
|
|
2218
|
+
return copy;
|
|
1840
2219
|
}
|
|
1841
|
-
function
|
|
1842
|
-
|
|
1843
|
-
if (
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
2220
|
+
function uniqueTitle2(taken, title, index) {
|
|
2221
|
+
let base = title.trim().slice(0, MAX_PAGE_TITLE_LENGTH);
|
|
2222
|
+
if (base === "") base = `Page ${index + 1}`;
|
|
2223
|
+
let candidate = base;
|
|
2224
|
+
let n = 1;
|
|
2225
|
+
while (taken.has(normalizedTitle(candidate))) {
|
|
2226
|
+
const suffix = ` (#${n})`;
|
|
2227
|
+
candidate = `${base.slice(0, MAX_PAGE_TITLE_LENGTH - suffix.length)}${suffix}`;
|
|
2228
|
+
n += 1;
|
|
1847
2229
|
}
|
|
2230
|
+
taken.add(normalizedTitle(candidate));
|
|
2231
|
+
return candidate;
|
|
1848
2232
|
}
|
|
1849
|
-
function
|
|
1850
|
-
|
|
1851
|
-
const
|
|
1852
|
-
|
|
1853
|
-
check(item, itemPath);
|
|
1854
|
-
} else {
|
|
1855
|
-
problems.push(
|
|
1856
|
-
`${itemPath} must be an object, got ${describeType2(item)}.`
|
|
1857
|
-
);
|
|
1858
|
-
}
|
|
2233
|
+
function samePages(a, b) {
|
|
2234
|
+
return a.length === b.length && a.every((p, i) => {
|
|
2235
|
+
const q = b[i];
|
|
2236
|
+
return p.key === q.key && p.title === q.title && p.maxRows === q.maxRows && sameKeys(p.order, q.order) && sameKeys(p.hidden, q.hidden) && sameKeys(p.minimized, q.minimized);
|
|
1859
2237
|
});
|
|
1860
2238
|
}
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
2239
|
+
function normalizeLayout(definitions, layout, options = {}) {
|
|
2240
|
+
const clones = cleanClones(definitions, layout.clones, options);
|
|
2241
|
+
let base = layout;
|
|
2242
|
+
if (clones !== layout.clones) {
|
|
2243
|
+
const { clones: _dropped, ...others } = layout;
|
|
2244
|
+
base = clones === void 0 ? others : { ...others, clones };
|
|
2245
|
+
}
|
|
2246
|
+
return normalizeWidgets(
|
|
2247
|
+
withClones(definitions, base, options),
|
|
2248
|
+
base,
|
|
2249
|
+
options
|
|
2250
|
+
);
|
|
2251
|
+
}
|
|
2252
|
+
function normalizeWidgets(definitions, layout, options) {
|
|
2253
|
+
const known = pruneLayout(definitions, layout);
|
|
2254
|
+
const enforced = enforceLocks(definitions, known, options);
|
|
2255
|
+
const locked = normalizeSettings(
|
|
2256
|
+
definitions,
|
|
2257
|
+
enforced,
|
|
2258
|
+
options
|
|
2259
|
+
);
|
|
2260
|
+
if (!hasPages(locked)) return locked === layout ? layout : locked;
|
|
2261
|
+
const defs = effectiveDefinitions(
|
|
2262
|
+
options.overrideLocks === true ? definitions.map(withoutLockField) : definitions,
|
|
2263
|
+
locked,
|
|
2264
|
+
options
|
|
2265
|
+
);
|
|
2266
|
+
const taken = /* @__PURE__ */ new Set();
|
|
2267
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
2268
|
+
const held = /* @__PURE__ */ new Set();
|
|
2269
|
+
let pages = [];
|
|
2270
|
+
(locked.pages ?? []).forEach((page, index) => {
|
|
2271
|
+
if (seenKeys.has(page.key)) return;
|
|
2272
|
+
seenKeys.add(page.key);
|
|
2273
|
+
const keep = (list) => [
|
|
2274
|
+
...new Set(list.filter((k) => !held.has(k)))
|
|
2275
|
+
];
|
|
2276
|
+
const next = {
|
|
2277
|
+
...page,
|
|
2278
|
+
title: uniqueTitle2(taken, page.title, index),
|
|
2279
|
+
order: keep(page.order),
|
|
2280
|
+
hidden: keep(page.hidden),
|
|
2281
|
+
minimized: keep(page.minimized)
|
|
2282
|
+
};
|
|
2283
|
+
for (const k of [...next.order, ...next.hidden]) held.add(k);
|
|
2284
|
+
pages.push(next);
|
|
2285
|
+
});
|
|
2286
|
+
const work = () => ({ ...locked, pages });
|
|
2287
|
+
const pinned = defs.filter((d) => resolveWidgetLock(d).move);
|
|
2288
|
+
for (const widget of pinned) {
|
|
2289
|
+
const home = homePageKey(work(), widget);
|
|
2290
|
+
const keysHere = (p, list) => p.key !== home && p[list].includes(widget.key);
|
|
2291
|
+
const wasHidden = pages.some((p) => keysHere(p, "hidden"));
|
|
2292
|
+
const wasMinimized = pages.some((p) => keysHere(p, "minimized"));
|
|
2293
|
+
if (!wasHidden && !wasMinimized) continue;
|
|
2294
|
+
pages = pages.map((p) => {
|
|
2295
|
+
let next = p;
|
|
2296
|
+
if (keysHere(p, "hidden")) {
|
|
2297
|
+
next = { ...next, hidden: without(next.hidden, widget.key) };
|
|
1873
2298
|
}
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
requireString(data, "label", "", problems);
|
|
1877
|
-
optionalString(data, "hint", "", problems);
|
|
1878
|
-
optionalBoolean(data, "higherIsBetter", "", problems);
|
|
1879
|
-
return;
|
|
1880
|
-
case "GAUGE":
|
|
1881
|
-
requireNumber(data, "value", "", problems);
|
|
1882
|
-
requireNumber(data, "max", "", problems);
|
|
1883
|
-
requireString(data, "label", "", problems);
|
|
1884
|
-
if (typeof data["max"] === "number" && data["max"] <= 0) {
|
|
1885
|
-
problems.push(`max must be greater than 0, got ${data["max"]}.`);
|
|
2299
|
+
if (keysHere(p, "minimized")) {
|
|
2300
|
+
next = { ...next, minimized: without(next.minimized, widget.key) };
|
|
1886
2301
|
}
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
optionalBoolean(column, "numeric", path, problems);
|
|
1894
|
-
});
|
|
2302
|
+
if (p.key === home) {
|
|
2303
|
+
next = {
|
|
2304
|
+
...next,
|
|
2305
|
+
hidden: wasHidden && !next.hidden.includes(widget.key) ? [...next.hidden, widget.key] : next.hidden,
|
|
2306
|
+
minimized: wasMinimized && !next.minimized.includes(widget.key) ? [...next.minimized, widget.key] : next.minimized
|
|
2307
|
+
};
|
|
1895
2308
|
}
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
2309
|
+
return next;
|
|
2310
|
+
});
|
|
2311
|
+
}
|
|
2312
|
+
const placedKeys = /* @__PURE__ */ new Set();
|
|
2313
|
+
for (const p of pages) {
|
|
2314
|
+
for (const k of [...p.order, ...p.hidden]) placedKeys.add(k);
|
|
2315
|
+
}
|
|
2316
|
+
const pinnedKeys = new Set(pinned.map((d) => d.key));
|
|
2317
|
+
const unplaced = defs.filter((d) => !placedKeys.has(d.key) && !pinnedKeys.has(d.key)).map((definition, index) => ({ definition, index })).sort(
|
|
2318
|
+
(a, b) => (a.definition.sortOrder ?? 0) - (b.definition.sortOrder ?? 0) || a.index - b.index
|
|
2319
|
+
).map(({ definition }) => definition);
|
|
2320
|
+
for (const widget of unplaced) {
|
|
2321
|
+
const home = homePageKey(work(), widget);
|
|
2322
|
+
pages = pages.map(
|
|
2323
|
+
(p) => p.key === home ? { ...p, order: [...p.order, widget.key] } : p
|
|
2324
|
+
);
|
|
2325
|
+
}
|
|
2326
|
+
for (let i = 0; i < pages.length; i += 1) {
|
|
2327
|
+
const page = pages[i];
|
|
2328
|
+
const shown = pageWidgets(defs, work(), page.key);
|
|
2329
|
+
const fits = fitCount(shown, rowsAllowed(page, options));
|
|
2330
|
+
if (fits >= shown.length) continue;
|
|
2331
|
+
const moving = shown.slice(fits).filter((d) => !pinnedKeys.has(d.key)).map((d) => d.key);
|
|
2332
|
+
if (moving.length === 0) continue;
|
|
2333
|
+
const moveSet = new Set(moving);
|
|
2334
|
+
const movedMinimized = page.minimized.filter(
|
|
2335
|
+
(k) => moveSet.has(k)
|
|
2336
|
+
);
|
|
2337
|
+
pages = pages.map(
|
|
2338
|
+
(p) => p.key === page.key ? {
|
|
2339
|
+
...p,
|
|
2340
|
+
order: p.order.filter((k) => !moveSet.has(k)),
|
|
2341
|
+
minimized: p.minimized.filter((k) => !moveSet.has(k))
|
|
2342
|
+
} : p
|
|
2343
|
+
);
|
|
2344
|
+
let placed = false;
|
|
2345
|
+
for (let j = i + 1; j < pages.length && !placed; j += 1) {
|
|
2346
|
+
const candidate = pages[j];
|
|
2347
|
+
const trial = pages.map(
|
|
2348
|
+
(p) => p.key === candidate.key ? {
|
|
2349
|
+
...p,
|
|
2350
|
+
order: [...moving, ...p.order],
|
|
2351
|
+
minimized: [...p.minimized, ...movedMinimized]
|
|
2352
|
+
} : p
|
|
2353
|
+
);
|
|
2354
|
+
const items = pageWidgets(
|
|
2355
|
+
defs,
|
|
2356
|
+
{ ...locked, pages: trial },
|
|
2357
|
+
candidate.key
|
|
2358
|
+
);
|
|
2359
|
+
if (placeRows(items).length <= rowsAllowed(candidate, options)) {
|
|
2360
|
+
pages = trial;
|
|
2361
|
+
placed = true;
|
|
1936
2362
|
}
|
|
1937
|
-
optionalNumber(data, "total", "", problems);
|
|
1938
|
-
return;
|
|
1939
2363
|
}
|
|
1940
|
-
|
|
1941
|
-
const
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
isSafeImageUrl,
|
|
1952
|
-
IMAGE_RULE
|
|
1953
|
-
);
|
|
1954
|
-
requireString(item, "valueLabel", path, problems);
|
|
1955
|
-
optionalString(item, "detail", path, problems);
|
|
1956
|
-
});
|
|
1957
|
-
}
|
|
1958
|
-
requireNumber(data, "total", "", problems);
|
|
1959
|
-
requireString(data, "emptyText", "", problems);
|
|
1960
|
-
if (items !== null && typeof data["total"] === "number" && data["total"] < items.length) {
|
|
1961
|
-
problems.push(
|
|
1962
|
-
`total (${data["total"]}) must be at least the number of items (${items.length}).`
|
|
1963
|
-
);
|
|
1964
|
-
}
|
|
1965
|
-
return;
|
|
2364
|
+
if (!placed) {
|
|
2365
|
+
const key = nextPageKey(work());
|
|
2366
|
+
const title = uniqueTitle2(
|
|
2367
|
+
new Set(pages.map((p) => normalizedTitle(p.title))),
|
|
2368
|
+
`Page ${pages.length + 1}`,
|
|
2369
|
+
pages.length
|
|
2370
|
+
);
|
|
2371
|
+
pages = [
|
|
2372
|
+
...pages,
|
|
2373
|
+
{ key, title, order: moving, hidden: [], minimized: movedMinimized }
|
|
2374
|
+
];
|
|
1966
2375
|
}
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
optionalString(data, "xLabel", "", problems);
|
|
1995
|
-
return;
|
|
2376
|
+
}
|
|
2377
|
+
const result = work();
|
|
2378
|
+
return samePages(layout.pages ?? [], pages) && sameKeys(layout.order, result.order) && locked.settings === layout.settings ? layout : result;
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
// src/core/duplicate.ts
|
|
2382
|
+
function canDuplicate(definitions, key, options = {}) {
|
|
2383
|
+
const definition = definitions.find(
|
|
2384
|
+
(d) => d.key === key
|
|
2385
|
+
);
|
|
2386
|
+
if (definition === void 0) return false;
|
|
2387
|
+
if (options.overrideLocks === true) return true;
|
|
2388
|
+
const sourceKey = definition.sourceKey ?? definition.key;
|
|
2389
|
+
const original = definitions.find(
|
|
2390
|
+
(d) => d.key === sourceKey
|
|
2391
|
+
);
|
|
2392
|
+
return original !== void 0 && !resolveWidgetLock(original).clone;
|
|
2393
|
+
}
|
|
2394
|
+
function nextCloneKey(definitions, layout, fromKey) {
|
|
2395
|
+
const taken = /* @__PURE__ */ new Set([
|
|
2396
|
+
...definitions.map((d) => d.key),
|
|
2397
|
+
...(layout.clones ?? []).map((c) => c.key)
|
|
2398
|
+
]);
|
|
2399
|
+
let highest = 0;
|
|
2400
|
+
for (const clone of layout.clones ?? []) {
|
|
2401
|
+
if (clone.from === fromKey) {
|
|
2402
|
+
highest = Math.max(highest, cloneNumber(clone.key) ?? 0);
|
|
1996
2403
|
}
|
|
1997
2404
|
}
|
|
2405
|
+
let n = highest + 1;
|
|
2406
|
+
while (taken.has(`${fromKey}#${n}`)) n += 1;
|
|
2407
|
+
return `${fromKey}#${n}`;
|
|
1998
2408
|
}
|
|
1999
|
-
function
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
return;
|
|
2409
|
+
function suggestedViewTitle(title, viewLabel) {
|
|
2410
|
+
return `${title} ${viewLabel}`.trim().slice(0, MAX_WIDGET_TITLE_LENGTH);
|
|
2411
|
+
}
|
|
2412
|
+
function duplicateWidget(definitions, layout, key, options = {}) {
|
|
2413
|
+
const widget = definitions.find((d) => d.key === key);
|
|
2414
|
+
if (widget === void 0) {
|
|
2415
|
+
return err(`Widget ${JSON.stringify(key)} isn't on this dashboard.`);
|
|
2006
2416
|
}
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
`detail.pageSize must be a whole number from 1 to ${MAX_DETAIL_PAGE_SIZE}, got ${typeof pageSize === "number" ? String(pageSize) : describeType2(pageSize)}.`
|
|
2417
|
+
const originalKey = widget.sourceKey ?? widget.key;
|
|
2418
|
+
if (!canDuplicate(definitions, key, options)) {
|
|
2419
|
+
return err(
|
|
2420
|
+
`Widget ${JSON.stringify(originalKey)} is locked against duplicating.`
|
|
2012
2421
|
);
|
|
2013
2422
|
}
|
|
2014
|
-
|
|
2015
|
-
|
|
2423
|
+
const cloneKey = nextCloneKey(definitions, layout, originalKey);
|
|
2424
|
+
const copiedSettings = (() => {
|
|
2425
|
+
const mine = layout.settings?.[key];
|
|
2426
|
+
if (mine === void 0) return void 0;
|
|
2427
|
+
const { title: _title, ...rest } = mine;
|
|
2428
|
+
return Object.keys(rest).length === 0 ? void 0 : rest;
|
|
2429
|
+
})();
|
|
2430
|
+
const settings = copiedSettings === void 0 ? layout.settings : { ...layout.settings, [cloneKey]: copiedSettings };
|
|
2431
|
+
let next = {
|
|
2432
|
+
...layout,
|
|
2433
|
+
clones: [...layout.clones ?? [], { key: cloneKey, from: originalKey }],
|
|
2434
|
+
...settings === void 0 ? {} : { settings }
|
|
2435
|
+
};
|
|
2436
|
+
const all = withClones(definitions, next, options);
|
|
2437
|
+
const assigned = assignPages(all, next);
|
|
2438
|
+
const pageKey = [...assigned].find(
|
|
2439
|
+
([, widgets]) => widgets.some((w) => w.key === key)
|
|
2440
|
+
)?.[0];
|
|
2441
|
+
if (pageKey !== void 0) {
|
|
2442
|
+
const view = pageLayout(next, pageKey);
|
|
2443
|
+
const onPage = assigned.get(pageKey) ?? [];
|
|
2444
|
+
if (view !== void 0) {
|
|
2445
|
+
const shown = visibleWidgets(onPage, view).filter((d) => !resolveWidgetLock(d).move && d.key !== cloneKey).map((d) => d.key);
|
|
2446
|
+
const at = shown.indexOf(key);
|
|
2447
|
+
const order = at === -1 ? [...shown, cloneKey] : [...shown.slice(0, at + 1), cloneKey, ...shown.slice(at + 1)];
|
|
2448
|
+
next = withPageLayout(next, pageKey, { ...view, order });
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
if (hasPages(next) && pageKey !== void 0) {
|
|
2452
|
+
const effective = effectiveDefinitions(all, next, options);
|
|
2453
|
+
const room = pageRoom(effective, next, pageKey, options);
|
|
2454
|
+
if (room !== void 0 && room.rowsUsed > room.maxRows) {
|
|
2455
|
+
const page = next.pages?.find(
|
|
2456
|
+
(p) => p.key === pageKey
|
|
2457
|
+
);
|
|
2458
|
+
const width = effective.find(
|
|
2459
|
+
(d) => d.key === cloneKey
|
|
2460
|
+
)?.width;
|
|
2461
|
+
return err(
|
|
2462
|
+
`Page "${page?.title ?? pageKey}" has no room for a widget${width === void 0 ? "" : ` ${width} wide`} (it holds ${room.maxRows} rows).`
|
|
2463
|
+
);
|
|
2464
|
+
}
|
|
2016
2465
|
}
|
|
2466
|
+
return ok({ layout: next, key: cloneKey });
|
|
2017
2467
|
}
|
|
2018
|
-
function
|
|
2019
|
-
if (
|
|
2020
|
-
|
|
2468
|
+
function deleteClone(layout, key) {
|
|
2469
|
+
if (layout.clones?.some((clone) => clone.key === key) !== true) return layout;
|
|
2470
|
+
const without2 = (keys) => keys.includes(key) ? keys.filter((k) => k !== key) : keys;
|
|
2471
|
+
const { clones, settings, ...rest } = layout;
|
|
2472
|
+
const remaining = (clones ?? []).filter((c) => c.key !== key);
|
|
2473
|
+
const { [key]: _gone, ...otherSettings } = settings ?? {};
|
|
2474
|
+
return {
|
|
2475
|
+
...rest,
|
|
2476
|
+
order: without2(layout.order),
|
|
2477
|
+
hidden: without2(layout.hidden),
|
|
2478
|
+
minimized: without2(layout.minimized),
|
|
2479
|
+
...layout.pages === void 0 ? {} : {
|
|
2480
|
+
pages: layout.pages.map((page) => ({
|
|
2481
|
+
...page,
|
|
2482
|
+
order: without2(page.order),
|
|
2483
|
+
hidden: without2(page.hidden),
|
|
2484
|
+
minimized: without2(page.minimized)
|
|
2485
|
+
}))
|
|
2486
|
+
},
|
|
2487
|
+
...Object.keys(otherSettings).length === 0 ? {} : { settings: otherSettings },
|
|
2488
|
+
...remaining.length === 0 ? {} : { clones: remaining }
|
|
2489
|
+
};
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
// src/core/validate.ts
|
|
2493
|
+
var MAX_REPORTED_PROBLEMS = 10;
|
|
2494
|
+
function describeType2(value) {
|
|
2495
|
+
if (value === null) return "null";
|
|
2496
|
+
if (Array.isArray(value)) return "array";
|
|
2497
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
2498
|
+
return String(value);
|
|
2499
|
+
}
|
|
2500
|
+
return typeof value;
|
|
2501
|
+
}
|
|
2502
|
+
function isRecord3(value) {
|
|
2503
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2504
|
+
}
|
|
2505
|
+
function join(path, field) {
|
|
2506
|
+
return path === "" ? field : `${path}.${field}`;
|
|
2507
|
+
}
|
|
2508
|
+
function requireString(record, field, path, problems) {
|
|
2509
|
+
const value = record[field];
|
|
2510
|
+
if (typeof value !== "string") {
|
|
2021
2511
|
problems.push(
|
|
2022
|
-
|
|
2512
|
+
`${join(path, field)} must be a string, got ${describeType2(value)}.`
|
|
2023
2513
|
);
|
|
2024
|
-
return;
|
|
2025
2514
|
}
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2515
|
+
}
|
|
2516
|
+
function optionalString(record, field, path, problems) {
|
|
2517
|
+
if (record[field] !== void 0) {
|
|
2518
|
+
requireString(record, field, path, problems);
|
|
2029
2519
|
}
|
|
2030
|
-
|
|
2520
|
+
}
|
|
2521
|
+
function requireNumber(record, field, path, problems) {
|
|
2522
|
+
const value = record[field];
|
|
2523
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
2031
2524
|
problems.push(
|
|
2032
|
-
|
|
2525
|
+
`${join(path, field)} must be a finite number, got ${describeType2(value)}.`
|
|
2033
2526
|
);
|
|
2034
2527
|
}
|
|
2035
2528
|
}
|
|
2036
|
-
function
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
if (limit !== void 0 && !isWidgetWidth(limit)) {
|
|
2040
|
-
problems.push(
|
|
2041
|
-
`${field} must be an integer from ${MIN_WIDGET_WIDTH} to ${MAX_WIDGET_WIDTH} (twelfths of the row), got ${typeof limit === "number" ? String(limit) : describeType2(limit)}.`
|
|
2042
|
-
);
|
|
2043
|
-
return;
|
|
2044
|
-
}
|
|
2529
|
+
function optionalNumber(record, field, path, problems) {
|
|
2530
|
+
if (record[field] !== void 0) {
|
|
2531
|
+
requireNumber(record, field, path, problems);
|
|
2045
2532
|
}
|
|
2046
|
-
|
|
2047
|
-
|
|
2533
|
+
}
|
|
2534
|
+
function optionalSortValue(record, path, problems) {
|
|
2535
|
+
const value = record["value"];
|
|
2536
|
+
if (value !== void 0 && typeof value !== "string" && !(typeof value === "number" && Number.isFinite(value))) {
|
|
2048
2537
|
problems.push(
|
|
2049
|
-
|
|
2538
|
+
`${path}.value must be a string or a finite number, got ${describeType2(value)}.`
|
|
2050
2539
|
);
|
|
2051
|
-
return;
|
|
2052
|
-
}
|
|
2053
|
-
if (isWidgetWidth(width)) {
|
|
2054
|
-
if (isWidgetWidth(minWidth) && width < minWidth) {
|
|
2055
|
-
problems.push(
|
|
2056
|
-
`width (${width}) must not be less than minWidth (${minWidth}).`
|
|
2057
|
-
);
|
|
2058
|
-
}
|
|
2059
|
-
if (isWidgetWidth(maxWidth) && width > maxWidth) {
|
|
2060
|
-
problems.push(
|
|
2061
|
-
`width (${width}) must not be more than maxWidth (${maxWidth}).`
|
|
2062
|
-
);
|
|
2063
|
-
}
|
|
2064
2540
|
}
|
|
2065
2541
|
}
|
|
2066
|
-
function
|
|
2067
|
-
|
|
2068
|
-
if (
|
|
2542
|
+
function optionalBoolean(record, field, path, problems) {
|
|
2543
|
+
const value = record[field];
|
|
2544
|
+
if (value !== void 0 && typeof value !== "boolean") {
|
|
2069
2545
|
problems.push(
|
|
2070
|
-
|
|
2546
|
+
`${join(path, field)} must be a boolean, got ${describeType2(value)}.`
|
|
2071
2547
|
);
|
|
2072
|
-
return;
|
|
2073
2548
|
}
|
|
2074
|
-
|
|
2549
|
+
}
|
|
2550
|
+
function optionalUrl(record, field, path, problems, isSafe, allowed) {
|
|
2551
|
+
const value = record[field];
|
|
2552
|
+
if (value === void 0) return;
|
|
2553
|
+
if (typeof value !== "string") {
|
|
2554
|
+
problems.push(
|
|
2555
|
+
`${join(path, field)} must be a string, got ${describeType2(value)}.`
|
|
2556
|
+
);
|
|
2557
|
+
} else if (!isSafe(value)) {
|
|
2558
|
+
problems.push(
|
|
2559
|
+
`${join(path, field)} must be ${allowed}, got ${JSON.stringify(value.slice(0, 80))}.`
|
|
2560
|
+
);
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
function requireArray(record, field, path, problems) {
|
|
2564
|
+
const value = record[field];
|
|
2565
|
+
if (!Array.isArray(value)) {
|
|
2566
|
+
problems.push(
|
|
2567
|
+
`${join(path, field)} must be an array, got ${describeType2(value)}.`
|
|
2568
|
+
);
|
|
2569
|
+
return null;
|
|
2570
|
+
}
|
|
2571
|
+
return value;
|
|
2572
|
+
}
|
|
2573
|
+
function requireOneOf(record, field, options, path, problems) {
|
|
2574
|
+
const value = record[field];
|
|
2575
|
+
if (typeof value !== "string" || !options.includes(value)) {
|
|
2576
|
+
problems.push(
|
|
2577
|
+
`${join(path, field)} must be one of ${options.map((o) => `"${o}"`).join(", ")}, got ${typeof value === "string" ? JSON.stringify(value) : describeType2(value)}.`
|
|
2578
|
+
);
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
function eachRecord(items, path, problems, check) {
|
|
2582
|
+
items.forEach((item, index) => {
|
|
2583
|
+
const itemPath = `${path}[${index}]`;
|
|
2584
|
+
if (isRecord3(item)) {
|
|
2585
|
+
check(item, itemPath);
|
|
2586
|
+
} else {
|
|
2587
|
+
problems.push(
|
|
2588
|
+
`${itemPath} must be an object, got ${describeType2(item)}.`
|
|
2589
|
+
);
|
|
2590
|
+
}
|
|
2591
|
+
});
|
|
2592
|
+
}
|
|
2593
|
+
var HREF_RULE = "an http(s), mailto or relative URL";
|
|
2594
|
+
var IMAGE_RULE = "an http(s) or relative URL";
|
|
2595
|
+
function checkPayloadFields(kind, data, problems) {
|
|
2596
|
+
switch (kind) {
|
|
2597
|
+
case "TEXT":
|
|
2598
|
+
requireString(data, "value", "", problems);
|
|
2599
|
+
requireString(data, "label", "", problems);
|
|
2600
|
+
return;
|
|
2601
|
+
case "KPI":
|
|
2602
|
+
requireNumber(data, "value", "", problems);
|
|
2603
|
+
if (data["previous"] !== null) {
|
|
2604
|
+
optionalNumber(data, "previous", "", problems);
|
|
2605
|
+
}
|
|
2606
|
+
requireOneOf(data, "format", VALUE_FORMATS, "", problems);
|
|
2607
|
+
optionalString(data, "currency", "", problems);
|
|
2608
|
+
requireString(data, "label", "", problems);
|
|
2609
|
+
optionalString(data, "hint", "", problems);
|
|
2610
|
+
optionalBoolean(data, "higherIsBetter", "", problems);
|
|
2611
|
+
return;
|
|
2612
|
+
case "GAUGE":
|
|
2613
|
+
requireNumber(data, "value", "", problems);
|
|
2614
|
+
requireNumber(data, "max", "", problems);
|
|
2615
|
+
requireString(data, "label", "", problems);
|
|
2616
|
+
if (typeof data["max"] === "number" && data["max"] <= 0) {
|
|
2617
|
+
problems.push(`max must be greater than 0, got ${data["max"]}.`);
|
|
2618
|
+
}
|
|
2619
|
+
return;
|
|
2620
|
+
case "TABLE": {
|
|
2621
|
+
const columns = requireArray(data, "columns", "", problems);
|
|
2622
|
+
if (columns !== null) {
|
|
2623
|
+
eachRecord(columns, "columns", problems, (column, path) => {
|
|
2624
|
+
requireString(column, "label", path, problems);
|
|
2625
|
+
optionalBoolean(column, "numeric", path, problems);
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
const rows = requireArray(data, "rows", "", problems);
|
|
2629
|
+
if (rows !== null) {
|
|
2630
|
+
rows.forEach((row, rowIndex) => {
|
|
2631
|
+
const rowPath = `rows[${rowIndex}]`;
|
|
2632
|
+
if (!Array.isArray(row)) {
|
|
2633
|
+
problems.push(
|
|
2634
|
+
`${rowPath} must be an array of cells, got ${describeType2(row)}.`
|
|
2635
|
+
);
|
|
2636
|
+
return;
|
|
2637
|
+
}
|
|
2638
|
+
if (columns !== null && row.length !== columns.length) {
|
|
2639
|
+
problems.push(
|
|
2640
|
+
`${rowPath} has ${row.length} cell(s) but there are ${columns.length} column(s).`
|
|
2641
|
+
);
|
|
2642
|
+
}
|
|
2643
|
+
eachRecord(row, rowPath, problems, (cell, cellPath) => {
|
|
2644
|
+
requireString(cell, "text", cellPath, problems);
|
|
2645
|
+
optionalSortValue(cell, cellPath, problems);
|
|
2646
|
+
optionalUrl(
|
|
2647
|
+
cell,
|
|
2648
|
+
"href",
|
|
2649
|
+
cellPath,
|
|
2650
|
+
problems,
|
|
2651
|
+
isSafeHref,
|
|
2652
|
+
HREF_RULE
|
|
2653
|
+
);
|
|
2654
|
+
});
|
|
2655
|
+
});
|
|
2656
|
+
}
|
|
2657
|
+
optionalString(data, "footer", "", problems);
|
|
2658
|
+
return;
|
|
2659
|
+
}
|
|
2660
|
+
case "BAR_LIST": {
|
|
2661
|
+
const items = requireArray(data, "items", "", problems);
|
|
2662
|
+
if (items !== null) {
|
|
2663
|
+
eachRecord(items, "items", problems, (item, path) => {
|
|
2664
|
+
requireString(item, "label", path, problems);
|
|
2665
|
+
requireNumber(item, "value", path, problems);
|
|
2666
|
+
optionalString(item, "display", path, problems);
|
|
2667
|
+
});
|
|
2668
|
+
}
|
|
2669
|
+
optionalNumber(data, "total", "", problems);
|
|
2670
|
+
return;
|
|
2671
|
+
}
|
|
2672
|
+
case "ALERT_LIST": {
|
|
2673
|
+
const items = requireArray(data, "items", "", problems);
|
|
2674
|
+
if (items !== null) {
|
|
2675
|
+
eachRecord(items, "items", problems, (item, path) => {
|
|
2676
|
+
requireString(item, "title", path, problems);
|
|
2677
|
+
optionalUrl(item, "href", path, problems, isSafeHref, HREF_RULE);
|
|
2678
|
+
optionalUrl(
|
|
2679
|
+
item,
|
|
2680
|
+
"thumbnailUrl",
|
|
2681
|
+
path,
|
|
2682
|
+
problems,
|
|
2683
|
+
isSafeImageUrl,
|
|
2684
|
+
IMAGE_RULE
|
|
2685
|
+
);
|
|
2686
|
+
requireString(item, "valueLabel", path, problems);
|
|
2687
|
+
optionalString(item, "detail", path, problems);
|
|
2688
|
+
});
|
|
2689
|
+
}
|
|
2690
|
+
requireNumber(data, "total", "", problems);
|
|
2691
|
+
requireString(data, "emptyText", "", problems);
|
|
2692
|
+
if (items !== null && typeof data["total"] === "number" && data["total"] < items.length) {
|
|
2693
|
+
problems.push(
|
|
2694
|
+
`total (${data["total"]}) must be at least the number of items (${items.length}).`
|
|
2695
|
+
);
|
|
2696
|
+
}
|
|
2697
|
+
return;
|
|
2698
|
+
}
|
|
2699
|
+
case "GRAPH": {
|
|
2700
|
+
const series = requireArray(data, "series", "", problems);
|
|
2701
|
+
if (series !== null) {
|
|
2702
|
+
if (series.length > MAX_GRAPH_SERIES) {
|
|
2703
|
+
problems.push(
|
|
2704
|
+
`series has ${series.length} entries; at most ${MAX_GRAPH_SERIES} are allowed (fold the rest into "Other").`
|
|
2705
|
+
);
|
|
2706
|
+
}
|
|
2707
|
+
eachRecord(series, "series", problems, (entry, path) => {
|
|
2708
|
+
requireString(entry, "name", path, problems);
|
|
2709
|
+
const points = requireArray(entry, "points", path, problems);
|
|
2710
|
+
if (points !== null) {
|
|
2711
|
+
eachRecord(
|
|
2712
|
+
points,
|
|
2713
|
+
`${path}.points`,
|
|
2714
|
+
problems,
|
|
2715
|
+
(point, pointPath) => {
|
|
2716
|
+
requireString(point, "label", pointPath, problems);
|
|
2717
|
+
requireNumber(point, "value", pointPath, problems);
|
|
2718
|
+
}
|
|
2719
|
+
);
|
|
2720
|
+
}
|
|
2721
|
+
});
|
|
2722
|
+
}
|
|
2723
|
+
requireOneOf(data, "chartType", ["bar", "line"], "", problems);
|
|
2724
|
+
requireOneOf(data, "valueFormat", VALUE_FORMATS, "", problems);
|
|
2725
|
+
optionalString(data, "currency", "", problems);
|
|
2726
|
+
optionalString(data, "xLabel", "", problems);
|
|
2727
|
+
return;
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
function checkDetailOption(detail, problems) {
|
|
2732
|
+
if (detail === void 0 || typeof detail === "boolean") return;
|
|
2733
|
+
if (!isRecord3(detail)) {
|
|
2734
|
+
problems.push(
|
|
2735
|
+
`detail must be true, false or an options object, got ${describeType2(detail)}.`
|
|
2736
|
+
);
|
|
2737
|
+
return;
|
|
2738
|
+
}
|
|
2739
|
+
optionalString(detail, "title", "detail", problems);
|
|
2740
|
+
const pageSize = detail["pageSize"];
|
|
2741
|
+
if (pageSize !== void 0 && !(typeof pageSize === "number" && Number.isInteger(pageSize) && pageSize >= 1 && pageSize <= MAX_DETAIL_PAGE_SIZE)) {
|
|
2742
|
+
problems.push(
|
|
2743
|
+
`detail.pageSize must be a whole number from 1 to ${MAX_DETAIL_PAGE_SIZE}, got ${typeof pageSize === "number" ? String(pageSize) : describeType2(pageSize)}.`
|
|
2744
|
+
);
|
|
2745
|
+
}
|
|
2746
|
+
if (detail["mode"] !== void 0) {
|
|
2747
|
+
requireOneOf(detail, "mode", DETAIL_MODES, "detail", problems);
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
function checkTableControlsOption(controls, kind, problems) {
|
|
2751
|
+
if (controls === void 0 || controls === false) return;
|
|
2752
|
+
if (typeof controls !== "boolean" && !isRecord3(controls)) {
|
|
2753
|
+
problems.push(
|
|
2754
|
+
`tableControls must be true, false or an options object, got ${describeType2(controls)}.`
|
|
2755
|
+
);
|
|
2756
|
+
return;
|
|
2757
|
+
}
|
|
2758
|
+
if (isRecord3(controls)) {
|
|
2759
|
+
optionalBoolean(controls, "search", "tableControls", problems);
|
|
2760
|
+
optionalBoolean(controls, "sort", "tableControls", problems);
|
|
2761
|
+
}
|
|
2762
|
+
if (typeof kind === "string" && kind !== "TABLE") {
|
|
2763
|
+
problems.push(
|
|
2764
|
+
`tableControls only applies to TABLE widgets, but kind is "${kind}".`
|
|
2765
|
+
);
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
function checkWidthLimits(value, problems) {
|
|
2769
|
+
for (const field of ["minWidth", "maxWidth"]) {
|
|
2770
|
+
const limit = value[field];
|
|
2771
|
+
if (limit !== void 0 && !isWidgetWidth(limit)) {
|
|
2772
|
+
problems.push(
|
|
2773
|
+
`${field} must be an integer from ${MIN_WIDGET_WIDTH} to ${MAX_WIDGET_WIDTH} (twelfths of the row), got ${typeof limit === "number" ? String(limit) : describeType2(limit)}.`
|
|
2774
|
+
);
|
|
2775
|
+
return;
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
const { minWidth, maxWidth, width } = value;
|
|
2779
|
+
if (isWidgetWidth(minWidth) && isWidgetWidth(maxWidth) && minWidth > maxWidth) {
|
|
2780
|
+
problems.push(
|
|
2781
|
+
`minWidth (${minWidth}) must not be more than maxWidth (${maxWidth}).`
|
|
2782
|
+
);
|
|
2783
|
+
return;
|
|
2784
|
+
}
|
|
2785
|
+
if (isWidgetWidth(width)) {
|
|
2786
|
+
if (isWidgetWidth(minWidth) && width < minWidth) {
|
|
2787
|
+
problems.push(
|
|
2788
|
+
`width (${width}) must not be less than minWidth (${minWidth}).`
|
|
2789
|
+
);
|
|
2790
|
+
}
|
|
2791
|
+
if (isWidgetWidth(maxWidth) && width > maxWidth) {
|
|
2792
|
+
problems.push(
|
|
2793
|
+
`width (${width}) must not be more than maxWidth (${maxWidth}).`
|
|
2794
|
+
);
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
function checkLockedOption(locked, problems) {
|
|
2799
|
+
if (locked === void 0 || typeof locked === "boolean") return;
|
|
2800
|
+
if (!isRecord3(locked)) {
|
|
2801
|
+
problems.push(
|
|
2802
|
+
`locked must be true, false or an object, got ${describeType2(locked)}.`
|
|
2803
|
+
);
|
|
2804
|
+
return;
|
|
2805
|
+
}
|
|
2806
|
+
optionalBoolean(locked, "move", "locked", problems);
|
|
2075
2807
|
optionalBoolean(locked, "hide", "locked", problems);
|
|
2076
2808
|
optionalBoolean(locked, "minimize", "locked", problems);
|
|
2077
2809
|
optionalBoolean(locked, "options", "locked", problems);
|
|
2810
|
+
optionalBoolean(locked, "clone", "locked", problems);
|
|
2078
2811
|
}
|
|
2079
2812
|
function checkOptionColumns(option, path, kind, problems) {
|
|
2080
2813
|
const columns = option["columns"];
|
|
@@ -2087,7 +2820,7 @@ function checkOptionColumns(option, path, kind, problems) {
|
|
|
2087
2820
|
const keys = [];
|
|
2088
2821
|
columns.forEach((entry, index) => {
|
|
2089
2822
|
const entryPath = `${path}.columns[${index}]`;
|
|
2090
|
-
if (!
|
|
2823
|
+
if (!isRecord3(entry)) {
|
|
2091
2824
|
problems.push(
|
|
2092
2825
|
`${entryPath} must be an object, got ${describeType2(entry)}.`
|
|
2093
2826
|
);
|
|
@@ -2112,7 +2845,7 @@ function checkOptionColumns(option, path, kind, problems) {
|
|
|
2112
2845
|
return keys;
|
|
2113
2846
|
}
|
|
2114
2847
|
function checkOption(option, path, kind, seenKeys, seenTypes, problems) {
|
|
2115
|
-
if (!
|
|
2848
|
+
if (!isRecord3(option)) {
|
|
2116
2849
|
problems.push(`${path} must be an object, got ${describeType2(option)}.`);
|
|
2117
2850
|
return;
|
|
2118
2851
|
}
|
|
@@ -2144,7 +2877,7 @@ function checkOption(option, path, kind, seenKeys, seenTypes, problems) {
|
|
|
2144
2877
|
} else {
|
|
2145
2878
|
choices.forEach((choice, index) => {
|
|
2146
2879
|
const choicePath = `${path}.choices[${index}]`;
|
|
2147
|
-
if (!
|
|
2880
|
+
if (!isRecord3(choice)) {
|
|
2148
2881
|
problems.push(
|
|
2149
2882
|
`${choicePath} must be an object, got ${describeType2(choice)}.`
|
|
2150
2883
|
);
|
|
@@ -2234,7 +2967,7 @@ function checkOption(option, path, kind, seenKeys, seenTypes, problems) {
|
|
|
2234
2967
|
} else {
|
|
2235
2968
|
presets.forEach((preset, index) => {
|
|
2236
2969
|
const presetPath = `${path}.presets[${index}]`;
|
|
2237
|
-
if (!
|
|
2970
|
+
if (!isRecord3(preset)) {
|
|
2238
2971
|
problems.push(
|
|
2239
2972
|
`${presetPath} must be an object, got ${describeType2(preset)}.`
|
|
2240
2973
|
);
|
|
@@ -2363,7 +3096,7 @@ function checkColumnIndex(value, path, problems) {
|
|
|
2363
3096
|
}
|
|
2364
3097
|
}
|
|
2365
3098
|
function checkConversion(conversion, path, baseKind, viewKind2, problems) {
|
|
2366
|
-
if (!
|
|
3099
|
+
if (!isRecord3(conversion)) {
|
|
2367
3100
|
problems.push(
|
|
2368
3101
|
`${path} must be an object with a type, got ${describeType2(conversion)}.`
|
|
2369
3102
|
);
|
|
@@ -2441,7 +3174,7 @@ function checkViews(value, problems) {
|
|
|
2441
3174
|
} else {
|
|
2442
3175
|
views.forEach((view, index) => {
|
|
2443
3176
|
const path = `views[${index}]`;
|
|
2444
|
-
if (!
|
|
3177
|
+
if (!isRecord3(view)) {
|
|
2445
3178
|
problems.push(
|
|
2446
3179
|
`${path} must be an object, got ${describeType2(view)}.`
|
|
2447
3180
|
);
|
|
@@ -2461,11 +3194,7 @@ function checkViews(value, problems) {
|
|
|
2461
3194
|
problems.push(`${path}.label must be a non-empty string.`);
|
|
2462
3195
|
}
|
|
2463
3196
|
requireOneOf(view, "kind", WIDGET_KINDS, path, problems);
|
|
2464
|
-
if (view["convert"]
|
|
2465
|
-
problems.push(
|
|
2466
|
-
`${path}.convert is required: views supplied by a provider are not supported yet.`
|
|
2467
|
-
);
|
|
2468
|
-
} else {
|
|
3197
|
+
if (view["convert"] !== void 0) {
|
|
2469
3198
|
checkConversion(
|
|
2470
3199
|
view["convert"],
|
|
2471
3200
|
`${path}.convert`,
|
|
@@ -2492,7 +3221,7 @@ function formatProblems(prefix, problems) {
|
|
|
2492
3221
|
}
|
|
2493
3222
|
function validateWidgetData(value, options = {}) {
|
|
2494
3223
|
const subject = options.widgetKey === void 0 ? "Widget data" : `Widget ${JSON.stringify(options.widgetKey)}`;
|
|
2495
|
-
if (!
|
|
3224
|
+
if (!isRecord3(value)) {
|
|
2496
3225
|
return err(`${subject}: must be an object, got ${describeType2(value)}.`);
|
|
2497
3226
|
}
|
|
2498
3227
|
if (value["empty"] === true) {
|
|
@@ -2522,7 +3251,7 @@ function validateWidgetData(value, options = {}) {
|
|
|
2522
3251
|
return ok(value);
|
|
2523
3252
|
}
|
|
2524
3253
|
function validateWidgetDefinition(value) {
|
|
2525
|
-
if (!
|
|
3254
|
+
if (!isRecord3(value)) {
|
|
2526
3255
|
return err(
|
|
2527
3256
|
`Widget definition: must be an object, got ${describeType2(value)}.`
|
|
2528
3257
|
);
|
|
@@ -2586,7 +3315,10 @@ function resolvePayload(definition, payload, options = {}) {
|
|
|
2586
3315
|
} else {
|
|
2587
3316
|
const result = validateWidgetData(payload, {
|
|
2588
3317
|
widgetKey: definition.key,
|
|
2589
|
-
expectedKind:
|
|
3318
|
+
expectedKind: loadViewKind(
|
|
3319
|
+
definition,
|
|
3320
|
+
options.viewValues?.[definition.key]
|
|
3321
|
+
)
|
|
2590
3322
|
});
|
|
2591
3323
|
if (!result.ok) {
|
|
2592
3324
|
return { definition, status: "error", error: result.error };
|
|
@@ -2597,7 +3329,14 @@ function resolvePayload(definition, payload, options = {}) {
|
|
|
2597
3329
|
if (emptyText !== null) {
|
|
2598
3330
|
return { definition, status: "empty", emptyText };
|
|
2599
3331
|
}
|
|
2600
|
-
return {
|
|
3332
|
+
return {
|
|
3333
|
+
definition,
|
|
3334
|
+
status: "ok",
|
|
3335
|
+
data: checked,
|
|
3336
|
+
...hasViews(definition) ? {
|
|
3337
|
+
view: loadViewKey(definition, options.viewValues?.[definition.key])
|
|
3338
|
+
} : {}
|
|
3339
|
+
};
|
|
2601
3340
|
}
|
|
2602
3341
|
function failedWidget(definition, error) {
|
|
2603
3342
|
return { definition, status: "error", error };
|
|
@@ -2606,8 +3345,15 @@ function loadingWidgets(definitions) {
|
|
|
2606
3345
|
return definitions.map((definition) => ({ definition, status: "loading" }));
|
|
2607
3346
|
}
|
|
2608
3347
|
function cacheKeyFor(definition, options, values) {
|
|
2609
|
-
const base = options.cacheKey === void 0 ? definition.key : options.cacheKey(definition);
|
|
2610
|
-
|
|
3348
|
+
const base = options.cacheKey === void 0 ? definition.sourceKey ?? definition.key : options.cacheKey(definition);
|
|
3349
|
+
const view = loadViewKey(
|
|
3350
|
+
definition,
|
|
3351
|
+
options.viewValues?.[definition.key]
|
|
3352
|
+
);
|
|
3353
|
+
const viewPart = view === BASE_VIEW_KEY ? "" : `view=${encodeURIComponent(view)}`;
|
|
3354
|
+
const optionPart = values === void 0 ? "" : optionsCacheSuffix(definition, values, options);
|
|
3355
|
+
if (viewPart === "") return `${base}${optionPart}`;
|
|
3356
|
+
return optionPart === "" ? `${base}?${viewPart}` : `${base}?${viewPart}&${optionPart.slice(1)}`;
|
|
2611
3357
|
}
|
|
2612
3358
|
function optionValuesFor(definition, options) {
|
|
2613
3359
|
return resolveOptionValues(
|
|
@@ -2618,6 +3364,9 @@ function optionValuesFor(definition, options) {
|
|
|
2618
3364
|
}
|
|
2619
3365
|
function finalizeWidget(widget, values) {
|
|
2620
3366
|
if (Object.keys(values).length === 0) return widget;
|
|
3367
|
+
if (widget.status === "ok" && widget.view !== void 0 && widget.view !== BASE_VIEW_KEY) {
|
|
3368
|
+
return { ...widget, options: values };
|
|
3369
|
+
}
|
|
2621
3370
|
if (widget.status === "ok") {
|
|
2622
3371
|
const data = applyClientOptions(
|
|
2623
3372
|
widget.definition,
|
|
@@ -2675,7 +3424,8 @@ async function resolveWidgetUnapplied(definition, provider, context, options = {
|
|
|
2675
3424
|
if (controller.signal.aborted) throw new LoadInterruption();
|
|
2676
3425
|
return provider(context, definition, {
|
|
2677
3426
|
signal: controller.signal,
|
|
2678
|
-
options: values
|
|
3427
|
+
options: values,
|
|
3428
|
+
view: loadViewKey(definition, options.viewValues?.[definition.key])
|
|
2679
3429
|
});
|
|
2680
3430
|
}),
|
|
2681
3431
|
interrupted
|
|
@@ -2720,7 +3470,10 @@ async function resolveWidgets(definitions, providers, context, options = {}) {
|
|
|
2720
3470
|
visible.map(
|
|
2721
3471
|
(definition) => resolveWidget(
|
|
2722
3472
|
definition,
|
|
2723
|
-
Object.prototype.hasOwnProperty.call(
|
|
3473
|
+
Object.prototype.hasOwnProperty.call(
|
|
3474
|
+
providers,
|
|
3475
|
+
definition.sourceKey ?? definition.key
|
|
3476
|
+
) ? providers[definition.sourceKey ?? definition.key] : void 0,
|
|
2724
3477
|
context,
|
|
2725
3478
|
options
|
|
2726
3479
|
)
|
|
@@ -2736,9 +3489,18 @@ function createWidgetLoader(options) {
|
|
|
2736
3489
|
([key, values]) => values === void 0 ? [] : [[key, values]]
|
|
2737
3490
|
)
|
|
2738
3491
|
);
|
|
3492
|
+
const chosenViews = new Map(
|
|
3493
|
+
Object.entries(baseOptions.viewValues ?? {}).flatMap(
|
|
3494
|
+
([key, view]) => view === void 0 ? [] : [[key, view]]
|
|
3495
|
+
)
|
|
3496
|
+
);
|
|
2739
3497
|
const resolveOptions = baseOptions;
|
|
2740
3498
|
function currentOptions() {
|
|
2741
|
-
return {
|
|
3499
|
+
return {
|
|
3500
|
+
...resolveOptions,
|
|
3501
|
+
optionValues: Object.fromEntries(chosen),
|
|
3502
|
+
viewValues: Object.fromEntries(chosenViews)
|
|
3503
|
+
};
|
|
2742
3504
|
}
|
|
2743
3505
|
const unapplied = /* @__PURE__ */ new Map();
|
|
2744
3506
|
const visible = widgetsFor(definitions, context.roles);
|
|
@@ -2800,7 +3562,11 @@ function createWidgetLoader(options) {
|
|
|
2800
3562
|
}
|
|
2801
3563
|
}
|
|
2802
3564
|
if (!isCurrent()) return;
|
|
2803
|
-
const
|
|
3565
|
+
const providerKey = definition.sourceKey ?? key;
|
|
3566
|
+
const provider = Object.prototype.hasOwnProperty.call(
|
|
3567
|
+
providers,
|
|
3568
|
+
providerKey
|
|
3569
|
+
) ? providers[providerKey] : void 0;
|
|
2804
3570
|
const result = await resolveWidgetUnapplied(definition, provider, context, {
|
|
2805
3571
|
...runOptions,
|
|
2806
3572
|
signal: controller.signal
|
|
@@ -2867,6 +3633,23 @@ function createWidgetLoader(options) {
|
|
|
2867
3633
|
}
|
|
2868
3634
|
start(definition, true);
|
|
2869
3635
|
}
|
|
3636
|
+
function setView(key, view) {
|
|
3637
|
+
if (disposed) return;
|
|
3638
|
+
const definition = definitionFor(key);
|
|
3639
|
+
if (definition === void 0) return;
|
|
3640
|
+
const before = loadViewKey(definition, chosenViews.get(key));
|
|
3641
|
+
if (view === void 0 || view === "") chosenViews.delete(key);
|
|
3642
|
+
else chosenViews.set(key, view);
|
|
3643
|
+
const after = loadViewKey(definition, chosenViews.get(key));
|
|
3644
|
+
if (before === after || !started.has(key)) return;
|
|
3645
|
+
const current = snapshot.find(
|
|
3646
|
+
(widget) => widget.definition.key === key
|
|
3647
|
+
);
|
|
3648
|
+
if (current !== void 0 && current.status !== "loading") {
|
|
3649
|
+
replace(key, { definition, status: "loading" });
|
|
3650
|
+
}
|
|
3651
|
+
start(definition, true);
|
|
3652
|
+
}
|
|
2870
3653
|
function dispose() {
|
|
2871
3654
|
disposed = true;
|
|
2872
3655
|
abortAll(new Error("The widget loader was disposed."));
|
|
@@ -2885,488 +3668,11 @@ function createWidgetLoader(options) {
|
|
|
2885
3668
|
loadAll,
|
|
2886
3669
|
refresh,
|
|
2887
3670
|
setOptions,
|
|
3671
|
+
setView,
|
|
2888
3672
|
dispose
|
|
2889
3673
|
};
|
|
2890
3674
|
}
|
|
2891
3675
|
|
|
2892
|
-
// src/core/pages.ts
|
|
2893
|
-
var DEFAULT_PAGE_KEY = "default";
|
|
2894
|
-
var DEFAULT_PAGE_ROWS = 4;
|
|
2895
|
-
function rowsAllowed(page, options) {
|
|
2896
|
-
const rows = page.maxRows ?? options.maxRows ?? DEFAULT_PAGE_ROWS;
|
|
2897
|
-
return Math.max(1, Math.min(MAX_PAGE_ROWS, Math.floor(rows)));
|
|
2898
|
-
}
|
|
2899
|
-
function hasPages(layout) {
|
|
2900
|
-
return layout.pages !== void 0 && layout.pages.length > 0;
|
|
2901
|
-
}
|
|
2902
|
-
function pageList(layout) {
|
|
2903
|
-
if (layout.pages !== void 0 && layout.pages.length > 0) {
|
|
2904
|
-
return layout.pages;
|
|
2905
|
-
}
|
|
2906
|
-
return [
|
|
2907
|
-
{
|
|
2908
|
-
key: DEFAULT_PAGE_KEY,
|
|
2909
|
-
title: "Page 1",
|
|
2910
|
-
order: layout.order,
|
|
2911
|
-
hidden: layout.hidden,
|
|
2912
|
-
minimized: layout.minimized
|
|
2913
|
-
}
|
|
2914
|
-
];
|
|
2915
|
-
}
|
|
2916
|
-
function withExplicitPages(layout) {
|
|
2917
|
-
if (hasPages(layout)) return layout;
|
|
2918
|
-
return {
|
|
2919
|
-
...layout,
|
|
2920
|
-
order: [],
|
|
2921
|
-
hidden: [],
|
|
2922
|
-
minimized: [],
|
|
2923
|
-
pages: pageList(layout)
|
|
2924
|
-
};
|
|
2925
|
-
}
|
|
2926
|
-
function sameKeys(a, b) {
|
|
2927
|
-
return a === b || a.length === b.length && a.every((k, i) => k === b[i]);
|
|
2928
|
-
}
|
|
2929
|
-
function pageLayout(layout, pageKey) {
|
|
2930
|
-
if (!hasPages(layout))
|
|
2931
|
-
return pageKey === DEFAULT_PAGE_KEY ? layout : void 0;
|
|
2932
|
-
const page = layout.pages?.find(
|
|
2933
|
-
(p) => p.key === pageKey
|
|
2934
|
-
);
|
|
2935
|
-
if (page === void 0) return void 0;
|
|
2936
|
-
const { pages, ...rest } = layout;
|
|
2937
|
-
return {
|
|
2938
|
-
...rest,
|
|
2939
|
-
order: page.order,
|
|
2940
|
-
hidden: page.hidden,
|
|
2941
|
-
minimized: page.minimized
|
|
2942
|
-
};
|
|
2943
|
-
}
|
|
2944
|
-
function withPageLayout(layout, pageKey, next) {
|
|
2945
|
-
const current = pageLayout(layout, pageKey);
|
|
2946
|
-
if (current === void 0 || sameKeys(current.order, next.order) && sameKeys(current.hidden, next.hidden) && sameKeys(current.minimized, next.minimized)) {
|
|
2947
|
-
return layout;
|
|
2948
|
-
}
|
|
2949
|
-
if (!hasPages(layout)) {
|
|
2950
|
-
return {
|
|
2951
|
-
...layout,
|
|
2952
|
-
order: next.order,
|
|
2953
|
-
hidden: next.hidden,
|
|
2954
|
-
minimized: next.minimized
|
|
2955
|
-
};
|
|
2956
|
-
}
|
|
2957
|
-
return {
|
|
2958
|
-
...layout,
|
|
2959
|
-
pages: (layout.pages ?? []).map(
|
|
2960
|
-
(page) => page.key === pageKey ? {
|
|
2961
|
-
...page,
|
|
2962
|
-
order: next.order,
|
|
2963
|
-
hidden: next.hidden,
|
|
2964
|
-
minimized: next.minimized
|
|
2965
|
-
} : page
|
|
2966
|
-
)
|
|
2967
|
-
};
|
|
2968
|
-
}
|
|
2969
|
-
function normalizedTitle(title) {
|
|
2970
|
-
return title.trim().normalize("NFKC").toLowerCase();
|
|
2971
|
-
}
|
|
2972
|
-
function resolvePageKey(layout, reference) {
|
|
2973
|
-
if (reference === void 0 || reference === "") return void 0;
|
|
2974
|
-
const pages = pageList(layout);
|
|
2975
|
-
const byKey = pages.find((p) => p.key === reference);
|
|
2976
|
-
if (byKey !== void 0) return byKey.key;
|
|
2977
|
-
const wanted = normalizedTitle(reference);
|
|
2978
|
-
return pages.find((p) => normalizedTitle(p.title) === wanted)?.key;
|
|
2979
|
-
}
|
|
2980
|
-
function homePageKey(layout, item) {
|
|
2981
|
-
return resolvePageKey(layout, item.page) ?? pageList(layout)[0].key;
|
|
2982
|
-
}
|
|
2983
|
-
function assignPages(definitions, layout) {
|
|
2984
|
-
const pages = pageList(layout);
|
|
2985
|
-
const assigned = new Map(pages.map((p) => [p.key, []]));
|
|
2986
|
-
for (const definition of definitions) {
|
|
2987
|
-
let pageKey;
|
|
2988
|
-
if (!resolveWidgetLock(definition).move) {
|
|
2989
|
-
pageKey = pages.find(
|
|
2990
|
-
(p) => p.order.includes(definition.key) || p.hidden.includes(definition.key)
|
|
2991
|
-
)?.key;
|
|
2992
|
-
}
|
|
2993
|
-
assigned.get(pageKey ?? homePageKey(layout, definition))?.push(definition);
|
|
2994
|
-
}
|
|
2995
|
-
return assigned;
|
|
2996
|
-
}
|
|
2997
|
-
function pageOf(definitions, layout, key) {
|
|
2998
|
-
for (const [pageKey, widgets] of assignPages(definitions, layout)) {
|
|
2999
|
-
if (widgets.some((w) => w.key === key)) return pageKey;
|
|
3000
|
-
}
|
|
3001
|
-
return void 0;
|
|
3002
|
-
}
|
|
3003
|
-
function pageWidgets(definitions, layout, pageKey) {
|
|
3004
|
-
const onPage = assignPages(definitions, layout).get(pageKey) ?? [];
|
|
3005
|
-
const view = pageLayout(layout, pageKey);
|
|
3006
|
-
return view === void 0 ? [] : visibleWidgets(onPage, view);
|
|
3007
|
-
}
|
|
3008
|
-
function pageRoom(definitions, layout, pageKey, options = {}) {
|
|
3009
|
-
const page = pageList(layout).find(
|
|
3010
|
-
(p) => p.key === pageKey
|
|
3011
|
-
);
|
|
3012
|
-
if (page === void 0) return void 0;
|
|
3013
|
-
const maxRows = rowsAllowed(page, options);
|
|
3014
|
-
const rowsUsed = placeRows(
|
|
3015
|
-
pageWidgets(definitions, layout, pageKey)
|
|
3016
|
-
).length;
|
|
3017
|
-
return { maxRows, rowsUsed, rowsFree: Math.max(0, maxRows - rowsUsed) };
|
|
3018
|
-
}
|
|
3019
|
-
function describePage(page) {
|
|
3020
|
-
return `Page "${page.title}"`;
|
|
3021
|
-
}
|
|
3022
|
-
function titleOf(item) {
|
|
3023
|
-
return item.title ?? item.key;
|
|
3024
|
-
}
|
|
3025
|
-
function checkTitle(layout, title, exceptKey) {
|
|
3026
|
-
const trimmed = title.trim();
|
|
3027
|
-
if (trimmed === "") return err("A page title must not be empty.");
|
|
3028
|
-
if (trimmed.length > MAX_PAGE_TITLE_LENGTH) {
|
|
3029
|
-
return err(
|
|
3030
|
-
`Page title "${trimmed}" is ${trimmed.length} characters; the most is ${MAX_PAGE_TITLE_LENGTH}.`
|
|
3031
|
-
);
|
|
3032
|
-
}
|
|
3033
|
-
const wanted = normalizedTitle(trimmed);
|
|
3034
|
-
const clash = pageList(layout).find(
|
|
3035
|
-
(p) => p.key !== exceptKey && normalizedTitle(p.title) === wanted
|
|
3036
|
-
);
|
|
3037
|
-
if (clash !== void 0) {
|
|
3038
|
-
return err(`Another page is already called "${clash.title}".`);
|
|
3039
|
-
}
|
|
3040
|
-
return ok(trimmed);
|
|
3041
|
-
}
|
|
3042
|
-
function nextPageKey(layout) {
|
|
3043
|
-
const keys = new Set(pageList(layout).map((p) => p.key));
|
|
3044
|
-
let n = 1;
|
|
3045
|
-
while (keys.has(`page-${n}`)) n += 1;
|
|
3046
|
-
return `page-${n}`;
|
|
3047
|
-
}
|
|
3048
|
-
function nextPageTitle(layout) {
|
|
3049
|
-
const used = new Set(
|
|
3050
|
-
pageList(layout).map((p) => normalizedTitle(p.title))
|
|
3051
|
-
);
|
|
3052
|
-
let n = pageList(layout).length + 1;
|
|
3053
|
-
while (used.has(normalizedTitle(`Page ${n}`))) n += 1;
|
|
3054
|
-
return `Page ${n}`;
|
|
3055
|
-
}
|
|
3056
|
-
function addPage(layout, options = {}) {
|
|
3057
|
-
if (pageList(layout).length >= MAX_PARSED_PAGES) {
|
|
3058
|
-
return err(
|
|
3059
|
-
`The layout already has ${MAX_PARSED_PAGES} pages, the most it can keep.`
|
|
3060
|
-
);
|
|
3061
|
-
}
|
|
3062
|
-
const key = options.key ?? nextPageKey(layout);
|
|
3063
|
-
if (pageList(layout).some((p) => p.key === key)) {
|
|
3064
|
-
return err(`A page with the key "${key}" already exists.`);
|
|
3065
|
-
}
|
|
3066
|
-
const title = checkTitle(
|
|
3067
|
-
layout,
|
|
3068
|
-
options.title ?? nextPageTitle(layout)
|
|
3069
|
-
);
|
|
3070
|
-
if (!title.ok) return title;
|
|
3071
|
-
const base = withExplicitPages(layout);
|
|
3072
|
-
const page = {
|
|
3073
|
-
key,
|
|
3074
|
-
title: title.value,
|
|
3075
|
-
order: [],
|
|
3076
|
-
hidden: [],
|
|
3077
|
-
minimized: []
|
|
3078
|
-
};
|
|
3079
|
-
return ok({ ...base, pages: [...base.pages ?? [], page] });
|
|
3080
|
-
}
|
|
3081
|
-
function renamePage(layout, pageKey, title) {
|
|
3082
|
-
const page = pageList(layout).find(
|
|
3083
|
-
(p) => p.key === pageKey
|
|
3084
|
-
);
|
|
3085
|
-
if (page === void 0) return err(`Page "${pageKey}" doesn't exist.`);
|
|
3086
|
-
const checked = checkTitle(layout, title, pageKey);
|
|
3087
|
-
if (!checked.ok) return checked;
|
|
3088
|
-
if (checked.value === page.title) return ok(layout);
|
|
3089
|
-
const base = withExplicitPages(layout);
|
|
3090
|
-
return ok({
|
|
3091
|
-
...base,
|
|
3092
|
-
pages: (base.pages ?? []).map(
|
|
3093
|
-
(p) => p.key === pageKey ? { ...p, title: checked.value } : p
|
|
3094
|
-
)
|
|
3095
|
-
});
|
|
3096
|
-
}
|
|
3097
|
-
function movePage(layout, pageKey, offset) {
|
|
3098
|
-
const pages = layout.pages ?? [];
|
|
3099
|
-
const from = pages.findIndex((p) => p.key === pageKey);
|
|
3100
|
-
if (from === -1) return layout;
|
|
3101
|
-
const to = Math.max(
|
|
3102
|
-
0,
|
|
3103
|
-
Math.min(pages.length - 1, from + Math.trunc(offset))
|
|
3104
|
-
);
|
|
3105
|
-
if (to === from) return layout;
|
|
3106
|
-
const next = [...pages];
|
|
3107
|
-
const [moved] = next.splice(from, 1);
|
|
3108
|
-
next.splice(to, 0, moved);
|
|
3109
|
-
return { ...layout, pages: next };
|
|
3110
|
-
}
|
|
3111
|
-
function overflowing(definitions, layout, options) {
|
|
3112
|
-
return pageList(layout).find(
|
|
3113
|
-
(page) => placeRows(pageWidgets(definitions, layout, page.key)).length > rowsAllowed(page, options)
|
|
3114
|
-
);
|
|
3115
|
-
}
|
|
3116
|
-
function removePage(definitions, layout, pageKey, options = {}) {
|
|
3117
|
-
const pages = pageList(layout);
|
|
3118
|
-
const index = pages.findIndex((p) => p.key === pageKey);
|
|
3119
|
-
const page = pages[index];
|
|
3120
|
-
if (page === void 0) return err(`Page "${pageKey}" doesn't exist.`);
|
|
3121
|
-
if (pages.length === 1) {
|
|
3122
|
-
return err(
|
|
3123
|
-
`${describePage(page)} is the only page, so it can't be removed.`
|
|
3124
|
-
);
|
|
3125
|
-
}
|
|
3126
|
-
const target = pages[index > 0 ? index - 1 : 1];
|
|
3127
|
-
const merged = {
|
|
3128
|
-
...layout,
|
|
3129
|
-
pages: pages.filter((p) => p.key !== pageKey).map(
|
|
3130
|
-
(p) => p.key === target.key ? {
|
|
3131
|
-
...p,
|
|
3132
|
-
order: index > 0 ? [...p.order, ...page.order] : [...page.order, ...p.order],
|
|
3133
|
-
hidden: [...p.hidden, ...page.hidden],
|
|
3134
|
-
minimized: [...p.minimized, ...page.minimized]
|
|
3135
|
-
} : p
|
|
3136
|
-
)
|
|
3137
|
-
};
|
|
3138
|
-
const full = overflowing(
|
|
3139
|
-
definitions,
|
|
3140
|
-
merged,
|
|
3141
|
-
options
|
|
3142
|
-
);
|
|
3143
|
-
if (full !== void 0) {
|
|
3144
|
-
return err(
|
|
3145
|
-
`${describePage(page)} has widgets that don't fit on the other pages (${describePage(full)} would need more than ${rowsAllowed(full, options)} rows). Move or hide some first.`
|
|
3146
|
-
);
|
|
3147
|
-
}
|
|
3148
|
-
return ok(merged);
|
|
3149
|
-
}
|
|
3150
|
-
function without(keys, key) {
|
|
3151
|
-
return keys.includes(key) ? keys.filter((k) => k !== key) : keys;
|
|
3152
|
-
}
|
|
3153
|
-
function moveWidgetToPage(definitions, layout, widgetKey, targetKey, options = {}) {
|
|
3154
|
-
const widget = definitions.find((d) => d.key === widgetKey);
|
|
3155
|
-
if (widget === void 0) {
|
|
3156
|
-
return err(
|
|
3157
|
-
`Widget "${widgetKey}" isn't one of the widgets, so it can't be moved to a page.`
|
|
3158
|
-
);
|
|
3159
|
-
}
|
|
3160
|
-
const pages = pageList(layout);
|
|
3161
|
-
const target = pages.find((p) => p.key === targetKey);
|
|
3162
|
-
if (target === void 0) return err(`Page "${targetKey}" doesn't exist.`);
|
|
3163
|
-
const sourceKey = pageOf(definitions, layout, widgetKey);
|
|
3164
|
-
if (sourceKey === targetKey) return ok(layout);
|
|
3165
|
-
if (resolveWidgetLock(widget).move) {
|
|
3166
|
-
return err(
|
|
3167
|
-
`Widget "${titleOf(widget)}" is locked against moving, so it can't be moved to ${describePage(target)}.`
|
|
3168
|
-
);
|
|
3169
|
-
}
|
|
3170
|
-
const base = withExplicitPages(layout);
|
|
3171
|
-
const source = (base.pages ?? []).find(
|
|
3172
|
-
(p) => p.key === sourceKey
|
|
3173
|
-
);
|
|
3174
|
-
const hidden = source.hidden.includes(widgetKey);
|
|
3175
|
-
const minimized = source.minimized.includes(widgetKey);
|
|
3176
|
-
const targetOrder = pageWidgets(
|
|
3177
|
-
definitions,
|
|
3178
|
-
base,
|
|
3179
|
-
targetKey
|
|
3180
|
-
).map((d) => d.key);
|
|
3181
|
-
const moved = {
|
|
3182
|
-
...base,
|
|
3183
|
-
pages: (base.pages ?? []).map((p) => {
|
|
3184
|
-
if (p.key === sourceKey) {
|
|
3185
|
-
return {
|
|
3186
|
-
...p,
|
|
3187
|
-
order: without(p.order, widgetKey),
|
|
3188
|
-
hidden: without(p.hidden, widgetKey),
|
|
3189
|
-
minimized: without(p.minimized, widgetKey)
|
|
3190
|
-
};
|
|
3191
|
-
}
|
|
3192
|
-
if (p.key === targetKey) {
|
|
3193
|
-
return {
|
|
3194
|
-
...p,
|
|
3195
|
-
order: hidden ? p.order : [...targetOrder, widgetKey],
|
|
3196
|
-
hidden: hidden ? [...p.hidden, widgetKey] : p.hidden,
|
|
3197
|
-
minimized: minimized ? [...p.minimized, widgetKey] : p.minimized
|
|
3198
|
-
};
|
|
3199
|
-
}
|
|
3200
|
-
return p;
|
|
3201
|
-
})
|
|
3202
|
-
};
|
|
3203
|
-
if (!hidden) {
|
|
3204
|
-
const allowed = rowsAllowed(target, options);
|
|
3205
|
-
const needed = placeRows(
|
|
3206
|
-
pageWidgets(definitions, moved, targetKey)
|
|
3207
|
-
).length;
|
|
3208
|
-
if (needed > allowed) {
|
|
3209
|
-
return err(
|
|
3210
|
-
`${describePage(target)} has no room for "${titleOf(widget)}": it would need row ${needed}, and the page allows ${allowed}.`
|
|
3211
|
-
);
|
|
3212
|
-
}
|
|
3213
|
-
}
|
|
3214
|
-
return ok(moved);
|
|
3215
|
-
}
|
|
3216
|
-
function withoutLockField(definition) {
|
|
3217
|
-
if (definition.locked === void 0) return definition;
|
|
3218
|
-
const copy = { ...definition };
|
|
3219
|
-
delete copy.locked;
|
|
3220
|
-
return copy;
|
|
3221
|
-
}
|
|
3222
|
-
function uniqueTitle2(taken, title, index) {
|
|
3223
|
-
let base = title.trim().slice(0, MAX_PAGE_TITLE_LENGTH);
|
|
3224
|
-
if (base === "") base = `Page ${index + 1}`;
|
|
3225
|
-
let candidate = base;
|
|
3226
|
-
let n = 1;
|
|
3227
|
-
while (taken.has(normalizedTitle(candidate))) {
|
|
3228
|
-
const suffix = ` (#${n})`;
|
|
3229
|
-
candidate = `${base.slice(0, MAX_PAGE_TITLE_LENGTH - suffix.length)}${suffix}`;
|
|
3230
|
-
n += 1;
|
|
3231
|
-
}
|
|
3232
|
-
taken.add(normalizedTitle(candidate));
|
|
3233
|
-
return candidate;
|
|
3234
|
-
}
|
|
3235
|
-
function samePages(a, b) {
|
|
3236
|
-
return a.length === b.length && a.every((p, i) => {
|
|
3237
|
-
const q = b[i];
|
|
3238
|
-
return p.key === q.key && p.title === q.title && p.maxRows === q.maxRows && sameKeys(p.order, q.order) && sameKeys(p.hidden, q.hidden) && sameKeys(p.minimized, q.minimized);
|
|
3239
|
-
});
|
|
3240
|
-
}
|
|
3241
|
-
function normalizeLayout(definitions, layout, options = {}) {
|
|
3242
|
-
const known = pruneLayout(definitions, layout);
|
|
3243
|
-
const enforced = enforceLocks(definitions, known, options);
|
|
3244
|
-
const locked = normalizeSettings(
|
|
3245
|
-
definitions,
|
|
3246
|
-
enforced,
|
|
3247
|
-
options
|
|
3248
|
-
);
|
|
3249
|
-
if (!hasPages(locked)) return locked === layout ? layout : locked;
|
|
3250
|
-
const defs = effectiveDefinitions(
|
|
3251
|
-
options.overrideLocks === true ? definitions.map(withoutLockField) : definitions,
|
|
3252
|
-
locked,
|
|
3253
|
-
options
|
|
3254
|
-
);
|
|
3255
|
-
const taken = /* @__PURE__ */ new Set();
|
|
3256
|
-
const seenKeys = /* @__PURE__ */ new Set();
|
|
3257
|
-
const held = /* @__PURE__ */ new Set();
|
|
3258
|
-
let pages = [];
|
|
3259
|
-
(locked.pages ?? []).forEach((page, index) => {
|
|
3260
|
-
if (seenKeys.has(page.key)) return;
|
|
3261
|
-
seenKeys.add(page.key);
|
|
3262
|
-
const keep = (list) => [
|
|
3263
|
-
...new Set(list.filter((k) => !held.has(k)))
|
|
3264
|
-
];
|
|
3265
|
-
const next = {
|
|
3266
|
-
...page,
|
|
3267
|
-
title: uniqueTitle2(taken, page.title, index),
|
|
3268
|
-
order: keep(page.order),
|
|
3269
|
-
hidden: keep(page.hidden),
|
|
3270
|
-
minimized: keep(page.minimized)
|
|
3271
|
-
};
|
|
3272
|
-
for (const k of [...next.order, ...next.hidden]) held.add(k);
|
|
3273
|
-
pages.push(next);
|
|
3274
|
-
});
|
|
3275
|
-
const work = () => ({ ...locked, pages });
|
|
3276
|
-
const pinned = defs.filter((d) => resolveWidgetLock(d).move);
|
|
3277
|
-
for (const widget of pinned) {
|
|
3278
|
-
const home = homePageKey(work(), widget);
|
|
3279
|
-
const keysHere = (p, list) => p.key !== home && p[list].includes(widget.key);
|
|
3280
|
-
const wasHidden = pages.some((p) => keysHere(p, "hidden"));
|
|
3281
|
-
const wasMinimized = pages.some((p) => keysHere(p, "minimized"));
|
|
3282
|
-
if (!wasHidden && !wasMinimized) continue;
|
|
3283
|
-
pages = pages.map((p) => {
|
|
3284
|
-
let next = p;
|
|
3285
|
-
if (keysHere(p, "hidden")) {
|
|
3286
|
-
next = { ...next, hidden: without(next.hidden, widget.key) };
|
|
3287
|
-
}
|
|
3288
|
-
if (keysHere(p, "minimized")) {
|
|
3289
|
-
next = { ...next, minimized: without(next.minimized, widget.key) };
|
|
3290
|
-
}
|
|
3291
|
-
if (p.key === home) {
|
|
3292
|
-
next = {
|
|
3293
|
-
...next,
|
|
3294
|
-
hidden: wasHidden && !next.hidden.includes(widget.key) ? [...next.hidden, widget.key] : next.hidden,
|
|
3295
|
-
minimized: wasMinimized && !next.minimized.includes(widget.key) ? [...next.minimized, widget.key] : next.minimized
|
|
3296
|
-
};
|
|
3297
|
-
}
|
|
3298
|
-
return next;
|
|
3299
|
-
});
|
|
3300
|
-
}
|
|
3301
|
-
const placedKeys = /* @__PURE__ */ new Set();
|
|
3302
|
-
for (const p of pages) {
|
|
3303
|
-
for (const k of [...p.order, ...p.hidden]) placedKeys.add(k);
|
|
3304
|
-
}
|
|
3305
|
-
const pinnedKeys = new Set(pinned.map((d) => d.key));
|
|
3306
|
-
const unplaced = defs.filter((d) => !placedKeys.has(d.key) && !pinnedKeys.has(d.key)).map((definition, index) => ({ definition, index })).sort(
|
|
3307
|
-
(a, b) => (a.definition.sortOrder ?? 0) - (b.definition.sortOrder ?? 0) || a.index - b.index
|
|
3308
|
-
).map(({ definition }) => definition);
|
|
3309
|
-
for (const widget of unplaced) {
|
|
3310
|
-
const home = homePageKey(work(), widget);
|
|
3311
|
-
pages = pages.map(
|
|
3312
|
-
(p) => p.key === home ? { ...p, order: [...p.order, widget.key] } : p
|
|
3313
|
-
);
|
|
3314
|
-
}
|
|
3315
|
-
for (let i = 0; i < pages.length; i += 1) {
|
|
3316
|
-
const page = pages[i];
|
|
3317
|
-
const shown = pageWidgets(defs, work(), page.key);
|
|
3318
|
-
const fits = fitCount(shown, rowsAllowed(page, options));
|
|
3319
|
-
if (fits >= shown.length) continue;
|
|
3320
|
-
const moving = shown.slice(fits).filter((d) => !pinnedKeys.has(d.key)).map((d) => d.key);
|
|
3321
|
-
if (moving.length === 0) continue;
|
|
3322
|
-
const moveSet = new Set(moving);
|
|
3323
|
-
const movedMinimized = page.minimized.filter(
|
|
3324
|
-
(k) => moveSet.has(k)
|
|
3325
|
-
);
|
|
3326
|
-
pages = pages.map(
|
|
3327
|
-
(p) => p.key === page.key ? {
|
|
3328
|
-
...p,
|
|
3329
|
-
order: p.order.filter((k) => !moveSet.has(k)),
|
|
3330
|
-
minimized: p.minimized.filter((k) => !moveSet.has(k))
|
|
3331
|
-
} : p
|
|
3332
|
-
);
|
|
3333
|
-
let placed = false;
|
|
3334
|
-
for (let j = i + 1; j < pages.length && !placed; j += 1) {
|
|
3335
|
-
const candidate = pages[j];
|
|
3336
|
-
const trial = pages.map(
|
|
3337
|
-
(p) => p.key === candidate.key ? {
|
|
3338
|
-
...p,
|
|
3339
|
-
order: [...moving, ...p.order],
|
|
3340
|
-
minimized: [...p.minimized, ...movedMinimized]
|
|
3341
|
-
} : p
|
|
3342
|
-
);
|
|
3343
|
-
const items = pageWidgets(
|
|
3344
|
-
defs,
|
|
3345
|
-
{ ...locked, pages: trial },
|
|
3346
|
-
candidate.key
|
|
3347
|
-
);
|
|
3348
|
-
if (placeRows(items).length <= rowsAllowed(candidate, options)) {
|
|
3349
|
-
pages = trial;
|
|
3350
|
-
placed = true;
|
|
3351
|
-
}
|
|
3352
|
-
}
|
|
3353
|
-
if (!placed) {
|
|
3354
|
-
const key = nextPageKey(work());
|
|
3355
|
-
const title = uniqueTitle2(
|
|
3356
|
-
new Set(pages.map((p) => normalizedTitle(p.title))),
|
|
3357
|
-
`Page ${pages.length + 1}`,
|
|
3358
|
-
pages.length
|
|
3359
|
-
);
|
|
3360
|
-
pages = [
|
|
3361
|
-
...pages,
|
|
3362
|
-
{ key, title, order: moving, hidden: [], minimized: movedMinimized }
|
|
3363
|
-
];
|
|
3364
|
-
}
|
|
3365
|
-
}
|
|
3366
|
-
const result = work();
|
|
3367
|
-
return samePages(layout.pages ?? [], pages) && sameKeys(layout.order, result.order) && locked.settings === layout.settings ? layout : result;
|
|
3368
|
-
}
|
|
3369
|
-
|
|
3370
3676
|
// src/core/scale.ts
|
|
3371
3677
|
function niceStep(value) {
|
|
3372
3678
|
if (!(value > 0) || !Number.isFinite(value)) return 1;
|
|
@@ -4196,6 +4502,6 @@ function indent(block) {
|
|
|
4196
4502
|
return block.split("\n").map((line) => ` ${line}`).join("\n");
|
|
4197
4503
|
}
|
|
4198
4504
|
|
|
4199
|
-
export { BASE_VIEW_KEY, DATE_RANGE_PRESETS, DEFAULT_COMPARISON_LABEL, DEFAULT_CURRENCY, DEFAULT_DETAIL_PAGE_SIZE, DEFAULT_EMPTY_TEXT, DEFAULT_MAX_LAYOUT_BYTES, DEFAULT_PAGE_KEY, DEFAULT_PAGE_ROWS, DETAIL_MODES, EMPTY_LAYOUT, LAYOUT_ENVELOPE_VERSION, MAX_DETAIL_PAGE_SIZE, MAX_GRAPH_SERIES, MAX_PAGE_ROWS, MAX_PAGE_TITLE_LENGTH, MAX_PARSED_PAGES, MAX_PARSED_SETTINGS, MAX_REPORTED_PROBLEMS, MAX_TEXT_OPTION_LENGTH, MAX_WIDGET_OPTIONS, MAX_WIDGET_TITLE_LENGTH, MAX_WIDGET_VIEWS, MAX_WIDGET_WIDTH, MIN_WIDGET_WIDTH, OPTION_KEY_PATTERN, THEME_TOKEN_VARIABLES, VALUE_FORMATS, VIEW_CONVERSIONS, WIDGET_FILLS, WIDGET_KINDS, WIDGET_OPTION_TYPES, WIDGET_SIZES, WIDTH_COLUMNS, addPage, applyClientOptions, applySettings, applyView, assignPages, baseColumnSpan, cacheKeyFor, clearWidgetSettings, coerceOptionValue, convertView, createLayoutPersistence, createTheme, createWidgetLoader, defaultDetailQuery, defaultOptionValue, defineWidget, deriveDetailData, describeUnknownError, effectiveDefinitions, effectiveViewKey, emptyTextFor, emptyWidget, enforceLocks, err, failedWidget, fillColumnSpans, fillWidthSpans, fillsHeight, fillsWidth, finalizeWidget, findView, fitCount, formatCount, formatPercentChange, formatValue, fromLegacyDefinition, hasPages, hasViews, hiddenWidgets, hideWidget, isClientApplied, isColumnKeyFor, isFlexible, isHidden, isIsoInterval, isMinimized, isOptionsLocked, isSafeHref, isSafeImageUrl, isTitleInUse, isWidgetEmptyState, isWidgetKind, isWidgetVisibleTo, isWidgetWidth, itemWidth, kindLabel, kpiDelta, labelStride, loadingWidgets, memoryAdapter, minimizeWidget, movePage, moveWidget, moveWidgetBy, moveWidgetDown, moveWidgetToPage, moveWidgetUp, nextPageKey, niceDomain, niceStep, normalizeLayout, normalizeSettings, normalizeTitle, ok, optionValuesFor, optionValuesFromLayout, optionsCacheSuffix, pageLayout, pageList, pageOf, pageRoom, pageWidgets, parseDetailQuery, parseLayout, parseSettings, parseSortValue, percentChange, placeRows, pruneLayout, queryRows, readOnly, removePage, renamePage, resolveDateRange, resolveDetailOptions, resolveOptionValues, resolvePageKey, resolvePayload, resolveTableControls, resolveWidget, resolveWidgetLock, resolveWidgetUnapplied, resolveWidgets, restoreWidget, sameOptionValues, serializeDetailQuery, serializeLayout, serializeSettings, setWidgetSettings, shareOf, showWidget, sortDefinitions, sortFromOptions, tableDetailData, toggleHidden, toggleMinimized, upgradeLegacyWidgetData, usesWidthColumns, utf8Length, validateDetailData, validateWidgetData, validateWidgetDefinition, viewChoices, viewKind, viewNote, visibleWidgets, widgetOptionValues, widgetsFor, widthForSize, widthRange, widthShareLabel, withEncoding, withFallback, withLogging, withPageLayout, withPrefix, withReadCache, withRetry };
|
|
4505
|
+
export { BASE_VIEW_KEY, DATE_RANGE_PRESETS, DEFAULT_COMPARISON_LABEL, DEFAULT_CURRENCY, DEFAULT_DETAIL_PAGE_SIZE, DEFAULT_EMPTY_TEXT, DEFAULT_MAX_LAYOUT_BYTES, DEFAULT_PAGE_KEY, DEFAULT_PAGE_ROWS, DETAIL_MODES, EMPTY_LAYOUT, LAYOUT_ENVELOPE_VERSION, MAX_DETAIL_PAGE_SIZE, MAX_GRAPH_SERIES, MAX_PAGE_ROWS, MAX_PAGE_TITLE_LENGTH, MAX_PARSED_CLONES, MAX_PARSED_PAGES, MAX_PARSED_SETTINGS, MAX_REPORTED_PROBLEMS, MAX_TEXT_OPTION_LENGTH, MAX_WIDGET_OPTIONS, MAX_WIDGET_TITLE_LENGTH, MAX_WIDGET_VIEWS, MAX_WIDGET_WIDTH, MIN_WIDGET_WIDTH, OPTION_KEY_PATTERN, THEME_TOKEN_VARIABLES, VALUE_FORMATS, VIEW_CONVERSIONS, WIDGET_FILLS, WIDGET_KINDS, WIDGET_OPTION_TYPES, WIDGET_SIZES, WIDTH_COLUMNS, addPage, applyClientOptions, applySettings, applyView, assignPages, baseColumnSpan, cacheKeyFor, canDuplicate, cleanClones, clearWidgetSettings, cloneNumber, coerceOptionValue, convertView, createLayoutPersistence, createTheme, createWidgetLoader, defaultDetailQuery, defaultOptionValue, defineWidget, deleteClone, deriveDetailData, describeUnknownError, duplicateWidget, effectiveDefinitions, effectiveViewKey, emptyTextFor, emptyWidget, enforceLocks, err, failedWidget, fillColumnSpans, fillWidthSpans, fillsHeight, fillsWidth, finalizeWidget, findView, fitCount, formatCount, formatPercentChange, formatValue, fromLegacyDefinition, hasPages, hasViews, hiddenWidgets, hideWidget, isClientApplied, isColumnKeyFor, isFlexible, isHidden, isIsoInterval, isMinimized, isOptionsLocked, isProviderView, isSafeHref, isSafeImageUrl, isTitleInUse, isWidgetEmptyState, isWidgetKind, isWidgetVisibleTo, isWidgetWidth, itemWidth, kindLabel, kpiDelta, labelStride, loadViewKey, loadViewKind, loadingWidgets, memoryAdapter, minimizeWidget, movePage, moveWidget, moveWidgetBy, moveWidgetDown, moveWidgetToPage, moveWidgetUp, nextCloneKey, nextPageKey, niceDomain, niceStep, normalizeLayout, normalizeSettings, normalizeTitle, ok, optionValuesFor, optionValuesFromLayout, optionsCacheSuffix, pageLayout, pageList, pageOf, pageRoom, pageWidgets, parseClones, parseDetailQuery, parseLayout, parseSettings, parseSortValue, percentChange, placeRows, pruneLayout, queryRows, readOnly, removePage, renamePage, resolveDateRange, resolveDetailOptions, resolveOptionValues, resolvePageKey, resolvePayload, resolveTableControls, resolveWidget, resolveWidgetLock, resolveWidgetUnapplied, resolveWidgets, restoreWidget, sameOptionValues, serializeClones, serializeDetailQuery, serializeLayout, serializeSettings, setWidgetSettings, shareOf, showWidget, sortDefinitions, sortFromOptions, suggestedViewTitle, tableDetailData, toggleHidden, toggleMinimized, upgradeLegacyWidgetData, usesWidthColumns, utf8Length, validateDetailData, validateWidgetData, validateWidgetDefinition, viewChoices, viewKind, viewNote, viewValuesFromLayout, visibleWidgets, widgetOptionValues, widgetsFor, widthForSize, widthRange, widthShareLabel, withClones, withEncoding, withFallback, withLogging, withPageLayout, withPrefix, withReadCache, withRetry };
|
|
4200
4506
|
//# sourceMappingURL=index.js.map
|
|
4201
4507
|
//# sourceMappingURL=index.js.map
|