@richardmcquiston01/dashboard-widgets-toolkit 0.2.0 → 0.4.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/README.md +90 -8
- package/dist/core.cjs +642 -69
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +61 -18
- package/dist/core.d.ts +61 -18
- package/dist/core.js +623 -70
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +642 -69
- 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 +623 -70
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +1516 -86
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +155 -14
- package/dist/react.d.ts +155 -14
- package/dist/react.js +1514 -88
- package/dist/react.js.map +1 -1
- package/dist/{scale-Bhjc5Eqt.d.cts → scale-dWDGE1zW.d.cts} +327 -118
- package/dist/{scale-Bhjc5Eqt.d.ts → scale-dWDGE1zW.d.ts} +327 -118
- package/dist/styles.css +152 -6
- package/package.json +1 -1
package/dist/core.js
CHANGED
|
@@ -190,7 +190,395 @@ function kpiDelta(current, previous, options = {}) {
|
|
|
190
190
|
};
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
// src/core/result.ts
|
|
194
|
+
function ok(value) {
|
|
195
|
+
return { ok: true, value };
|
|
196
|
+
}
|
|
197
|
+
function err(error) {
|
|
198
|
+
return { ok: false, error };
|
|
199
|
+
}
|
|
200
|
+
function describeUnknownError(cause) {
|
|
201
|
+
if (cause instanceof Error) {
|
|
202
|
+
return cause.message === "" ? cause.name : cause.message;
|
|
203
|
+
}
|
|
204
|
+
if (typeof cause === "string") {
|
|
205
|
+
return cause;
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
const json = JSON.stringify(cause);
|
|
209
|
+
return json ?? String(cause);
|
|
210
|
+
} catch {
|
|
211
|
+
return String(cause);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/core/url.ts
|
|
216
|
+
var SCHEME_PATTERN = /^([a-z][a-z0-9+.-]*):/i;
|
|
217
|
+
var IGNORED_IN_SCHEME = /[\u0000- \u007f]/g;
|
|
218
|
+
function schemeOf(url) {
|
|
219
|
+
const match = SCHEME_PATTERN.exec(
|
|
220
|
+
url.replace(IGNORED_IN_SCHEME, "")
|
|
221
|
+
);
|
|
222
|
+
return match === null ? null : (match[1] ?? "").toLowerCase();
|
|
223
|
+
}
|
|
224
|
+
function isSafeHref(url) {
|
|
225
|
+
if (url.trim() === "") {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
const scheme = schemeOf(url);
|
|
229
|
+
return scheme === null || scheme === "http" || scheme === "https" || scheme === "mailto";
|
|
230
|
+
}
|
|
231
|
+
function isSafeImageUrl(url) {
|
|
232
|
+
if (url.trim() === "") {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
const scheme = schemeOf(url);
|
|
236
|
+
return scheme === null || scheme === "http" || scheme === "https";
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// src/core/detail.ts
|
|
240
|
+
var DETAIL_MODES = ["client", "server"];
|
|
241
|
+
var DEFAULT_DETAIL_PAGE_SIZE = 25;
|
|
242
|
+
var MAX_DETAIL_PAGE_SIZE = 200;
|
|
243
|
+
function resolveDetailOptions(definition) {
|
|
244
|
+
const detail = definition.detail;
|
|
245
|
+
if (detail === void 0 || detail === false) return void 0;
|
|
246
|
+
const options = detail === true ? {} : detail;
|
|
247
|
+
return {
|
|
248
|
+
title: options.title ?? definition.title,
|
|
249
|
+
pageSize: options.pageSize ?? DEFAULT_DETAIL_PAGE_SIZE,
|
|
250
|
+
mode: options.mode ?? "client"
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function defaultDetailQuery(pageSize = DEFAULT_DETAIL_PAGE_SIZE) {
|
|
254
|
+
return { page: 1, pageSize };
|
|
255
|
+
}
|
|
256
|
+
function fold(text) {
|
|
257
|
+
return text.normalize("NFD").replace(/\p{M}/gu, "").toLowerCase();
|
|
258
|
+
}
|
|
259
|
+
function cellValue(cell) {
|
|
260
|
+
return cell.value ?? cell.text;
|
|
261
|
+
}
|
|
262
|
+
function cellMatches(cell, needle) {
|
|
263
|
+
return fold(String(cellValue(cell))).includes(needle);
|
|
264
|
+
}
|
|
265
|
+
function compareCells(a, b, collator) {
|
|
266
|
+
const left = cellValue(a);
|
|
267
|
+
const right = cellValue(b);
|
|
268
|
+
if (typeof left === "number" && typeof right === "number") {
|
|
269
|
+
return left - right;
|
|
270
|
+
}
|
|
271
|
+
if (typeof left === "number") return -1;
|
|
272
|
+
if (typeof right === "number") return 1;
|
|
273
|
+
return collator.compare(left, right);
|
|
274
|
+
}
|
|
275
|
+
function queryRows(data, query, locale) {
|
|
276
|
+
const indexOf = new Map(
|
|
277
|
+
data.columns.map((column, index) => [column.key, index])
|
|
278
|
+
);
|
|
279
|
+
const filterEntries = [];
|
|
280
|
+
for (const [key, text] of Object.entries(query.filters ?? {})) {
|
|
281
|
+
const index = indexOf.get(key);
|
|
282
|
+
if (index === void 0) {
|
|
283
|
+
return err(
|
|
284
|
+
`Detail query: filter column "${key}" does not exist. Columns: ${listKeys(data)}.`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
const needle = fold(text.trim());
|
|
288
|
+
if (needle !== "") filterEntries.push({ index, needle });
|
|
289
|
+
}
|
|
290
|
+
let sortIndex = -1;
|
|
291
|
+
if (query.sort !== void 0) {
|
|
292
|
+
const found = indexOf.get(query.sort.column);
|
|
293
|
+
const column = found === void 0 ? void 0 : data.columns[found];
|
|
294
|
+
if (found === void 0 || column === void 0) {
|
|
295
|
+
return err(
|
|
296
|
+
`Detail query: sort column "${query.sort.column}" does not exist. Columns: ${listKeys(data)}.`
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
if (column.sortable === false) {
|
|
300
|
+
return err(
|
|
301
|
+
`Detail query: column "${column.key}" is not sortable (sortable: false).`
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
sortIndex = found;
|
|
305
|
+
}
|
|
306
|
+
const search = fold((query.search ?? "").trim());
|
|
307
|
+
let rows = data.rows.filter(
|
|
308
|
+
(row) => filterEntries.every(({ index, needle }) => {
|
|
309
|
+
const cell = row[index];
|
|
310
|
+
return cell !== void 0 && cellMatches(cell, needle);
|
|
311
|
+
}) && (search === "" || row.some((cell) => cellMatches(cell, search)))
|
|
312
|
+
);
|
|
313
|
+
if (query.sort !== void 0 && sortIndex >= 0) {
|
|
314
|
+
const collator = new Intl.Collator(locale, {
|
|
315
|
+
numeric: true,
|
|
316
|
+
sensitivity: "base"
|
|
317
|
+
});
|
|
318
|
+
const sign = query.sort.direction === "desc" ? -1 : 1;
|
|
319
|
+
rows = rows.map((row, position) => ({ row, position })).sort((a, b) => {
|
|
320
|
+
const left = a.row[sortIndex];
|
|
321
|
+
const right = b.row[sortIndex];
|
|
322
|
+
const order = left === void 0 || right === void 0 ? 0 : sign * compareCells(left, right, collator);
|
|
323
|
+
return order || a.position - b.position;
|
|
324
|
+
}).map(({ row }) => row);
|
|
325
|
+
}
|
|
326
|
+
const pageSize = Math.max(1, Math.floor(query.pageSize));
|
|
327
|
+
const pageCount = Math.max(1, Math.ceil(rows.length / pageSize));
|
|
328
|
+
const page = Math.min(Math.max(1, Math.floor(query.page)), pageCount);
|
|
329
|
+
return ok({
|
|
330
|
+
rows: rows.slice((page - 1) * pageSize, page * pageSize),
|
|
331
|
+
totalRows: rows.length,
|
|
332
|
+
page,
|
|
333
|
+
pageCount
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
function listKeys(data) {
|
|
337
|
+
return data.columns.map((column) => `"${column.key}"`).join(", ");
|
|
338
|
+
}
|
|
339
|
+
var FILTER_PREFIX = "f.";
|
|
340
|
+
function serializeDetailQuery(query) {
|
|
341
|
+
const parts = [];
|
|
342
|
+
const add = (key, value) => {
|
|
343
|
+
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
|
344
|
+
};
|
|
345
|
+
if (query.search !== void 0 && query.search !== "") {
|
|
346
|
+
add("q", query.search);
|
|
347
|
+
}
|
|
348
|
+
if (query.sort !== void 0) {
|
|
349
|
+
add("sort", `${query.sort.column}:${query.sort.direction}`);
|
|
350
|
+
}
|
|
351
|
+
for (const [column, text] of Object.entries(query.filters ?? {})) {
|
|
352
|
+
if (text !== "") add(`${FILTER_PREFIX}${column}`, text);
|
|
353
|
+
}
|
|
354
|
+
if (query.page > 1) add("page", String(query.page));
|
|
355
|
+
return parts.join("&");
|
|
356
|
+
}
|
|
357
|
+
function parseDetailQuery(queryString, pageSize = DEFAULT_DETAIL_PAGE_SIZE, data) {
|
|
358
|
+
let search;
|
|
359
|
+
let sort;
|
|
360
|
+
let page = 1;
|
|
361
|
+
const filters = {};
|
|
362
|
+
const columnKeys = data === void 0 ? void 0 : new Set(data.columns.map((column) => column.key));
|
|
363
|
+
const text = queryString.startsWith("?") ? queryString.slice(1) : queryString;
|
|
364
|
+
for (const pair of text.split("&")) {
|
|
365
|
+
if (pair === "") continue;
|
|
366
|
+
const equals = pair.indexOf("=");
|
|
367
|
+
const rawKey = equals < 0 ? pair : pair.slice(0, equals);
|
|
368
|
+
const rawValue = equals < 0 ? "" : pair.slice(equals + 1);
|
|
369
|
+
let key;
|
|
370
|
+
let value;
|
|
371
|
+
try {
|
|
372
|
+
key = decodeURIComponent(rawKey.replace(/\+/g, " "));
|
|
373
|
+
value = decodeURIComponent(rawValue.replace(/\+/g, " "));
|
|
374
|
+
} catch {
|
|
375
|
+
return err(
|
|
376
|
+
`Detail query: "${pair}" is not valid percent-encoding in the URL.`
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
if (key === "q") {
|
|
380
|
+
search = value;
|
|
381
|
+
} else if (key === "page") {
|
|
382
|
+
if (!/^[1-9]\d*$/.test(value)) {
|
|
383
|
+
return err(
|
|
384
|
+
`Detail query: page must be a whole number from 1, got "${value}".`
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
page = Number(value);
|
|
388
|
+
} else if (key === "sort") {
|
|
389
|
+
const colon = value.lastIndexOf(":");
|
|
390
|
+
const column = colon < 0 ? value : value.slice(0, colon);
|
|
391
|
+
const direction = colon < 0 ? "asc" : value.slice(colon + 1);
|
|
392
|
+
if (column === "" || direction !== "asc" && direction !== "desc") {
|
|
393
|
+
return err(
|
|
394
|
+
`Detail query: sort must look like "column:asc" or "column:desc", got "${value}".`
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
if (columnKeys !== void 0 && !columnKeys.has(column)) {
|
|
398
|
+
return err(`Detail query: sort column "${column}" does not exist.`);
|
|
399
|
+
}
|
|
400
|
+
sort = { column, direction };
|
|
401
|
+
} else if (key.startsWith(FILTER_PREFIX)) {
|
|
402
|
+
const column = key.slice(FILTER_PREFIX.length);
|
|
403
|
+
if (columnKeys !== void 0 && !columnKeys.has(column)) {
|
|
404
|
+
return err(`Detail query: filter column "${column}" does not exist.`);
|
|
405
|
+
}
|
|
406
|
+
if (value !== "") filters[column] = value;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return ok({
|
|
410
|
+
...search === void 0 || search === "" ? {} : { search },
|
|
411
|
+
...Object.keys(filters).length === 0 ? {} : { filters },
|
|
412
|
+
...sort === void 0 ? {} : { sort },
|
|
413
|
+
page,
|
|
414
|
+
pageSize
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
function describeType(value) {
|
|
418
|
+
if (value === null) return "null";
|
|
419
|
+
if (Array.isArray(value)) return "array";
|
|
420
|
+
return typeof value;
|
|
421
|
+
}
|
|
422
|
+
function validateDetailData(data, subject = "Detail data") {
|
|
423
|
+
const problems = [];
|
|
424
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
425
|
+
return err(`${subject}: must be an object, got ${describeType(data)}.`);
|
|
426
|
+
}
|
|
427
|
+
const record = data;
|
|
428
|
+
const columns = record["columns"];
|
|
429
|
+
const rows = record["rows"];
|
|
430
|
+
const keys = /* @__PURE__ */ new Set();
|
|
431
|
+
let columnCount = 0;
|
|
432
|
+
if (!Array.isArray(columns) || columns.length === 0) {
|
|
433
|
+
problems.push(
|
|
434
|
+
`columns must be a non-empty array, got ${describeType(columns)}.`
|
|
435
|
+
);
|
|
436
|
+
} else {
|
|
437
|
+
columnCount = columns.length;
|
|
438
|
+
columns.forEach((column, index) => {
|
|
439
|
+
const at = `columns[${index}]`;
|
|
440
|
+
if (typeof column !== "object" || column === null) {
|
|
441
|
+
problems.push(`${at} must be an object, got ${describeType(column)}.`);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
const fields = column;
|
|
445
|
+
const key = fields["key"];
|
|
446
|
+
if (typeof key !== "string" || key === "") {
|
|
447
|
+
problems.push(`${at}.key must be a non-empty string.`);
|
|
448
|
+
} else if (keys.has(key)) {
|
|
449
|
+
problems.push(`${at}.key "${key}" is used by an earlier column.`);
|
|
450
|
+
} else {
|
|
451
|
+
keys.add(key);
|
|
452
|
+
}
|
|
453
|
+
if (typeof fields["label"] !== "string") {
|
|
454
|
+
problems.push(
|
|
455
|
+
`${at}.label must be a string, got ${describeType(fields["label"])}.`
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
for (const flag of ["numeric", "sortable", "filterable"]) {
|
|
459
|
+
const flagValue = fields[flag];
|
|
460
|
+
if (flagValue !== void 0 && typeof flagValue !== "boolean") {
|
|
461
|
+
problems.push(
|
|
462
|
+
`${at}.${flag} must be a boolean, got ${describeType(flagValue)}.`
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
if (!Array.isArray(rows)) {
|
|
469
|
+
problems.push(`rows must be an array, got ${describeType(rows)}.`);
|
|
470
|
+
} else {
|
|
471
|
+
rows.forEach((row, rowIndex) => {
|
|
472
|
+
if (!Array.isArray(row)) {
|
|
473
|
+
problems.push(
|
|
474
|
+
`rows[${rowIndex}] must be an array of cells, got ${describeType(row)}.`
|
|
475
|
+
);
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
if (columnCount > 0 && row.length !== columnCount) {
|
|
479
|
+
problems.push(
|
|
480
|
+
`rows[${rowIndex}] has ${row.length} cells but there are ${columnCount} columns.`
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
row.forEach((cell, cellIndex) => {
|
|
484
|
+
const at = `rows[${rowIndex}][${cellIndex}]`;
|
|
485
|
+
if (typeof cell !== "object" || cell === null) {
|
|
486
|
+
problems.push(`${at} must be an object, got ${describeType(cell)}.`);
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
const fields = cell;
|
|
490
|
+
if (typeof fields["text"] !== "string") {
|
|
491
|
+
problems.push(
|
|
492
|
+
`${at}.text must be a string, got ${describeType(fields["text"])}.`
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
const value = fields["value"];
|
|
496
|
+
if (value !== void 0 && typeof value !== "string" && !(typeof value === "number" && Number.isFinite(value))) {
|
|
497
|
+
problems.push(
|
|
498
|
+
`${at}.value must be a string or a finite number, got ${describeType(value)}.`
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
const href = fields["href"];
|
|
502
|
+
if (href !== void 0) {
|
|
503
|
+
if (typeof href !== "string") {
|
|
504
|
+
problems.push(
|
|
505
|
+
`${at}.href must be a string, got ${describeType(href)}.`
|
|
506
|
+
);
|
|
507
|
+
} else if (!isSafeHref(href)) {
|
|
508
|
+
problems.push(
|
|
509
|
+
`${at}.href "${href}" is not allowed (only http, https, mailto and relative URLs).`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
const totalRows = record["totalRows"];
|
|
517
|
+
if (totalRows !== void 0 && !(typeof totalRows === "number" && Number.isInteger(totalRows) && totalRows >= 0)) {
|
|
518
|
+
problems.push(
|
|
519
|
+
`totalRows must be a non-negative whole number, got ${describeType(totalRows)}.`
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
if (problems.length > 0) {
|
|
523
|
+
return err(`${subject}: ${problems.join(" ")}`);
|
|
524
|
+
}
|
|
525
|
+
return ok(data);
|
|
526
|
+
}
|
|
527
|
+
function deriveDetailData(data, locale) {
|
|
528
|
+
if (data.kind === "TABLE") {
|
|
529
|
+
if (data.footer !== void 0) return void 0;
|
|
530
|
+
return {
|
|
531
|
+
columns: data.columns.map((column, index) => ({
|
|
532
|
+
key: `c${index}`,
|
|
533
|
+
label: column.label,
|
|
534
|
+
...column.numeric === void 0 ? {} : { numeric: column.numeric }
|
|
535
|
+
})),
|
|
536
|
+
rows: data.rows
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
if (data.kind === "BAR_LIST") {
|
|
540
|
+
return {
|
|
541
|
+
columns: [
|
|
542
|
+
{ key: "label", label: "Name" },
|
|
543
|
+
{ key: "value", label: "Value", numeric: true }
|
|
544
|
+
],
|
|
545
|
+
rows: data.items.map((item) => [
|
|
546
|
+
{ text: item.label },
|
|
547
|
+
{
|
|
548
|
+
text: item.display ?? formatValue(item.value, "number", { locale }),
|
|
549
|
+
value: item.value
|
|
550
|
+
}
|
|
551
|
+
])
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
return void 0;
|
|
555
|
+
}
|
|
556
|
+
|
|
193
557
|
// src/core/grid.ts
|
|
558
|
+
var WIDTH_COLUMNS = 12;
|
|
559
|
+
var MIN_WIDGET_WIDTH = 2;
|
|
560
|
+
var MAX_WIDGET_WIDTH = 12;
|
|
561
|
+
function isWidgetWidth(value) {
|
|
562
|
+
return typeof value === "number" && Number.isInteger(value) && value >= MIN_WIDGET_WIDTH && value <= MAX_WIDGET_WIDTH;
|
|
563
|
+
}
|
|
564
|
+
function widthForSize(size) {
|
|
565
|
+
switch (size) {
|
|
566
|
+
case "small":
|
|
567
|
+
return 3;
|
|
568
|
+
case "large":
|
|
569
|
+
return 6;
|
|
570
|
+
case "full":
|
|
571
|
+
return 12;
|
|
572
|
+
default:
|
|
573
|
+
return 4;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
function itemWidth(item) {
|
|
577
|
+
return isWidgetWidth(item.width) ? item.width : widthForSize(item.defaultSize);
|
|
578
|
+
}
|
|
579
|
+
function usesWidthColumns(items) {
|
|
580
|
+
return items.some((item) => item.width !== void 0);
|
|
581
|
+
}
|
|
194
582
|
function fillsWidth(fill) {
|
|
195
583
|
return fill === "width" || fill === "both";
|
|
196
584
|
}
|
|
@@ -203,6 +591,16 @@ function baseColumnSpan(size, columns, largeSpan = 2) {
|
|
|
203
591
|
return 1;
|
|
204
592
|
}
|
|
205
593
|
function fillColumnSpans(items, columns, largeSpan = 2) {
|
|
594
|
+
return placeAndFill(
|
|
595
|
+
items,
|
|
596
|
+
columns,
|
|
597
|
+
(item, trackCount) => baseColumnSpan(item.defaultSize, trackCount, largeSpan)
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
function fillWidthSpans(items) {
|
|
601
|
+
return placeAndFill(items, WIDTH_COLUMNS, itemWidth);
|
|
602
|
+
}
|
|
603
|
+
function placeAndFill(items, columns, spanOf) {
|
|
206
604
|
const trackCount = Math.max(1, Math.floor(columns));
|
|
207
605
|
const spans = /* @__PURE__ */ new Map();
|
|
208
606
|
let row = [];
|
|
@@ -221,11 +619,7 @@ function fillColumnSpans(items, columns, largeSpan = 2) {
|
|
|
221
619
|
used = 0;
|
|
222
620
|
}
|
|
223
621
|
for (const item of items) {
|
|
224
|
-
const span =
|
|
225
|
-
item.defaultSize,
|
|
226
|
-
trackCount,
|
|
227
|
-
largeSpan
|
|
228
|
-
);
|
|
622
|
+
const span = Math.min(spanOf(item, trackCount), trackCount);
|
|
229
623
|
if (used + span > trackCount && row.length > 0) closeRow();
|
|
230
624
|
row.push({ item, span });
|
|
231
625
|
used += span;
|
|
@@ -376,55 +770,9 @@ function pruneLayout(definitions, layout) {
|
|
|
376
770
|
return next.order.length === layout.order.length && next.hidden.length === layout.hidden.length && next.minimized.length === layout.minimized.length ? layout : next;
|
|
377
771
|
}
|
|
378
772
|
|
|
379
|
-
// src/core/result.ts
|
|
380
|
-
function ok(value) {
|
|
381
|
-
return { ok: true, value };
|
|
382
|
-
}
|
|
383
|
-
function err(error) {
|
|
384
|
-
return { ok: false, error };
|
|
385
|
-
}
|
|
386
|
-
function describeUnknownError(cause) {
|
|
387
|
-
if (cause instanceof Error) {
|
|
388
|
-
return cause.message === "" ? cause.name : cause.message;
|
|
389
|
-
}
|
|
390
|
-
if (typeof cause === "string") {
|
|
391
|
-
return cause;
|
|
392
|
-
}
|
|
393
|
-
try {
|
|
394
|
-
const json = JSON.stringify(cause);
|
|
395
|
-
return json ?? String(cause);
|
|
396
|
-
} catch {
|
|
397
|
-
return String(cause);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// src/core/url.ts
|
|
402
|
-
var SCHEME_PATTERN = /^([a-z][a-z0-9+.-]*):/i;
|
|
403
|
-
var IGNORED_IN_SCHEME = /[\u0000- \u007f]/g;
|
|
404
|
-
function schemeOf(url) {
|
|
405
|
-
const match = SCHEME_PATTERN.exec(
|
|
406
|
-
url.replace(IGNORED_IN_SCHEME, "")
|
|
407
|
-
);
|
|
408
|
-
return match === null ? null : (match[1] ?? "").toLowerCase();
|
|
409
|
-
}
|
|
410
|
-
function isSafeHref(url) {
|
|
411
|
-
if (url.trim() === "") {
|
|
412
|
-
return false;
|
|
413
|
-
}
|
|
414
|
-
const scheme = schemeOf(url);
|
|
415
|
-
return scheme === null || scheme === "http" || scheme === "https" || scheme === "mailto";
|
|
416
|
-
}
|
|
417
|
-
function isSafeImageUrl(url) {
|
|
418
|
-
if (url.trim() === "") {
|
|
419
|
-
return false;
|
|
420
|
-
}
|
|
421
|
-
const scheme = schemeOf(url);
|
|
422
|
-
return scheme === null || scheme === "http" || scheme === "https";
|
|
423
|
-
}
|
|
424
|
-
|
|
425
773
|
// src/core/validate.ts
|
|
426
774
|
var MAX_REPORTED_PROBLEMS = 10;
|
|
427
|
-
function
|
|
775
|
+
function describeType2(value) {
|
|
428
776
|
if (value === null) return "null";
|
|
429
777
|
if (Array.isArray(value)) return "array";
|
|
430
778
|
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
@@ -442,7 +790,7 @@ function requireString(record, field, path, problems) {
|
|
|
442
790
|
const value = record[field];
|
|
443
791
|
if (typeof value !== "string") {
|
|
444
792
|
problems.push(
|
|
445
|
-
`${join(path, field)} must be a string, got ${
|
|
793
|
+
`${join(path, field)} must be a string, got ${describeType2(value)}.`
|
|
446
794
|
);
|
|
447
795
|
}
|
|
448
796
|
}
|
|
@@ -455,7 +803,7 @@ function requireNumber(record, field, path, problems) {
|
|
|
455
803
|
const value = record[field];
|
|
456
804
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
457
805
|
problems.push(
|
|
458
|
-
`${join(path, field)} must be a finite number, got ${
|
|
806
|
+
`${join(path, field)} must be a finite number, got ${describeType2(value)}.`
|
|
459
807
|
);
|
|
460
808
|
}
|
|
461
809
|
}
|
|
@@ -468,7 +816,7 @@ function optionalBoolean(record, field, path, problems) {
|
|
|
468
816
|
const value = record[field];
|
|
469
817
|
if (value !== void 0 && typeof value !== "boolean") {
|
|
470
818
|
problems.push(
|
|
471
|
-
`${join(path, field)} must be a boolean, got ${
|
|
819
|
+
`${join(path, field)} must be a boolean, got ${describeType2(value)}.`
|
|
472
820
|
);
|
|
473
821
|
}
|
|
474
822
|
}
|
|
@@ -477,7 +825,7 @@ function optionalUrl(record, field, path, problems, isSafe, allowed) {
|
|
|
477
825
|
if (value === void 0) return;
|
|
478
826
|
if (typeof value !== "string") {
|
|
479
827
|
problems.push(
|
|
480
|
-
`${join(path, field)} must be a string, got ${
|
|
828
|
+
`${join(path, field)} must be a string, got ${describeType2(value)}.`
|
|
481
829
|
);
|
|
482
830
|
} else if (!isSafe(value)) {
|
|
483
831
|
problems.push(
|
|
@@ -489,7 +837,7 @@ function requireArray(record, field, path, problems) {
|
|
|
489
837
|
const value = record[field];
|
|
490
838
|
if (!Array.isArray(value)) {
|
|
491
839
|
problems.push(
|
|
492
|
-
`${join(path, field)} must be an array, got ${
|
|
840
|
+
`${join(path, field)} must be an array, got ${describeType2(value)}.`
|
|
493
841
|
);
|
|
494
842
|
return null;
|
|
495
843
|
}
|
|
@@ -499,7 +847,7 @@ function requireOneOf(record, field, options, path, problems) {
|
|
|
499
847
|
const value = record[field];
|
|
500
848
|
if (typeof value !== "string" || !options.includes(value)) {
|
|
501
849
|
problems.push(
|
|
502
|
-
`${join(path, field)} must be one of ${options.map((o) => `"${o}"`).join(", ")}, got ${typeof value === "string" ? JSON.stringify(value) :
|
|
850
|
+
`${join(path, field)} must be one of ${options.map((o) => `"${o}"`).join(", ")}, got ${typeof value === "string" ? JSON.stringify(value) : describeType2(value)}.`
|
|
503
851
|
);
|
|
504
852
|
}
|
|
505
853
|
}
|
|
@@ -510,7 +858,7 @@ function eachRecord(items, path, problems, check) {
|
|
|
510
858
|
check(item, itemPath);
|
|
511
859
|
} else {
|
|
512
860
|
problems.push(
|
|
513
|
-
`${itemPath} must be an object, got ${
|
|
861
|
+
`${itemPath} must be an object, got ${describeType2(item)}.`
|
|
514
862
|
);
|
|
515
863
|
}
|
|
516
864
|
});
|
|
@@ -556,7 +904,7 @@ function checkPayloadFields(kind, data, problems) {
|
|
|
556
904
|
const rowPath = `rows[${rowIndex}]`;
|
|
557
905
|
if (!Array.isArray(row)) {
|
|
558
906
|
problems.push(
|
|
559
|
-
`${rowPath} must be an array of cells, got ${
|
|
907
|
+
`${rowPath} must be an array of cells, got ${describeType2(row)}.`
|
|
560
908
|
);
|
|
561
909
|
return;
|
|
562
910
|
}
|
|
@@ -652,6 +1000,25 @@ function checkPayloadFields(kind, data, problems) {
|
|
|
652
1000
|
}
|
|
653
1001
|
}
|
|
654
1002
|
}
|
|
1003
|
+
function checkDetailOption(detail, problems) {
|
|
1004
|
+
if (detail === void 0 || typeof detail === "boolean") return;
|
|
1005
|
+
if (!isRecord(detail)) {
|
|
1006
|
+
problems.push(
|
|
1007
|
+
`detail must be true, false or an options object, got ${describeType2(detail)}.`
|
|
1008
|
+
);
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
optionalString(detail, "title", "detail", problems);
|
|
1012
|
+
const pageSize = detail["pageSize"];
|
|
1013
|
+
if (pageSize !== void 0 && !(typeof pageSize === "number" && Number.isInteger(pageSize) && pageSize >= 1 && pageSize <= MAX_DETAIL_PAGE_SIZE)) {
|
|
1014
|
+
problems.push(
|
|
1015
|
+
`detail.pageSize must be a whole number from 1 to ${MAX_DETAIL_PAGE_SIZE}, got ${typeof pageSize === "number" ? String(pageSize) : describeType2(pageSize)}.`
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
1018
|
+
if (detail["mode"] !== void 0) {
|
|
1019
|
+
requireOneOf(detail, "mode", DETAIL_MODES, "detail", problems);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
655
1022
|
function formatProblems(prefix, problems) {
|
|
656
1023
|
const shown = problems.slice(0, MAX_REPORTED_PROBLEMS);
|
|
657
1024
|
const more = problems.length - shown.length;
|
|
@@ -660,12 +1027,12 @@ function formatProblems(prefix, problems) {
|
|
|
660
1027
|
function validateWidgetData(value, options = {}) {
|
|
661
1028
|
const subject = options.widgetKey === void 0 ? "Widget data" : `Widget ${JSON.stringify(options.widgetKey)}`;
|
|
662
1029
|
if (!isRecord(value)) {
|
|
663
|
-
return err(`${subject}: must be an object, got ${
|
|
1030
|
+
return err(`${subject}: must be an object, got ${describeType2(value)}.`);
|
|
664
1031
|
}
|
|
665
1032
|
if (value["empty"] === true) {
|
|
666
1033
|
if (typeof value["text"] !== "string") {
|
|
667
1034
|
return err(
|
|
668
|
-
`${subject} (empty): text must be a string, got ${
|
|
1035
|
+
`${subject} (empty): text must be a string, got ${describeType2(value["text"])}.`
|
|
669
1036
|
);
|
|
670
1037
|
}
|
|
671
1038
|
return ok(value);
|
|
@@ -673,7 +1040,7 @@ function validateWidgetData(value, options = {}) {
|
|
|
673
1040
|
const kind = value["kind"];
|
|
674
1041
|
if (!isWidgetKind(kind)) {
|
|
675
1042
|
return err(
|
|
676
|
-
`${subject}: kind must be one of ${WIDGET_KINDS.join(", ")} (or the payload must be {"empty": true, "text": ...}), got ${typeof kind === "string" ? JSON.stringify(kind) :
|
|
1043
|
+
`${subject}: kind must be one of ${WIDGET_KINDS.join(", ")} (or the payload must be {"empty": true, "text": ...}), got ${typeof kind === "string" ? JSON.stringify(kind) : describeType2(kind)}.`
|
|
677
1044
|
);
|
|
678
1045
|
}
|
|
679
1046
|
if (options.expectedKind !== void 0 && kind !== options.expectedKind) {
|
|
@@ -691,7 +1058,7 @@ function validateWidgetData(value, options = {}) {
|
|
|
691
1058
|
function validateWidgetDefinition(value) {
|
|
692
1059
|
if (!isRecord(value)) {
|
|
693
1060
|
return err(
|
|
694
|
-
`Widget definition: must be an object, got ${
|
|
1061
|
+
`Widget definition: must be an object, got ${describeType2(value)}.`
|
|
695
1062
|
);
|
|
696
1063
|
}
|
|
697
1064
|
const subject = typeof value["key"] === "string" ? `Widget definition ${JSON.stringify(value["key"])}` : "Widget definition";
|
|
@@ -709,7 +1076,7 @@ function validateWidgetDefinition(value) {
|
|
|
709
1076
|
roles?.forEach((role, index) => {
|
|
710
1077
|
if (typeof role !== "string") {
|
|
711
1078
|
problems.push(
|
|
712
|
-
`roles[${index}] must be a string, got ${
|
|
1079
|
+
`roles[${index}] must be a string, got ${describeType2(role)}.`
|
|
713
1080
|
);
|
|
714
1081
|
}
|
|
715
1082
|
});
|
|
@@ -718,6 +1085,12 @@ function validateWidgetDefinition(value) {
|
|
|
718
1085
|
if (value["defaultSize"] !== void 0) {
|
|
719
1086
|
requireOneOf(value, "defaultSize", WIDGET_SIZES, "", problems);
|
|
720
1087
|
}
|
|
1088
|
+
if (value["width"] !== void 0 && !isWidgetWidth(value["width"])) {
|
|
1089
|
+
problems.push(
|
|
1090
|
+
`width must be an integer from ${MIN_WIDGET_WIDTH} to ${MAX_WIDGET_WIDTH} (twelfths of the row), got ${typeof value["width"] === "number" ? String(value["width"]) : describeType2(value["width"])}.`
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
checkDetailOption(value["detail"], problems);
|
|
721
1094
|
if (value["fill"] !== void 0) {
|
|
722
1095
|
requireOneOf(value, "fill", WIDGET_FILLS, "", problems);
|
|
723
1096
|
}
|
|
@@ -754,23 +1127,78 @@ function failedWidget(definition, error) {
|
|
|
754
1127
|
function loadingWidgets(definitions) {
|
|
755
1128
|
return definitions.map((definition) => ({ definition, status: "loading" }));
|
|
756
1129
|
}
|
|
1130
|
+
function cacheKeyFor(definition, options) {
|
|
1131
|
+
return options.cacheKey === void 0 ? definition.key : options.cacheKey(definition);
|
|
1132
|
+
}
|
|
1133
|
+
var LoadInterruption = class extends Error {
|
|
1134
|
+
};
|
|
757
1135
|
async function resolveWidget(definition, provider, context, options = {}) {
|
|
1136
|
+
const quotedKey = JSON.stringify(definition.key);
|
|
758
1137
|
if (provider === void 0) {
|
|
759
1138
|
return failedWidget(
|
|
760
1139
|
definition,
|
|
761
|
-
`Widget ${
|
|
1140
|
+
`Widget ${quotedKey}: no provider is registered for this key.`
|
|
1141
|
+
);
|
|
1142
|
+
}
|
|
1143
|
+
if (options.signal?.aborted === true) {
|
|
1144
|
+
return failedWidget(
|
|
1145
|
+
definition,
|
|
1146
|
+
`Widget ${quotedKey}: loading was canceled before it started.`
|
|
762
1147
|
);
|
|
763
1148
|
}
|
|
1149
|
+
const controller = new AbortController();
|
|
1150
|
+
const onExternalAbort = () => controller.abort(options.signal?.reason);
|
|
1151
|
+
options.signal?.addEventListener("abort", onExternalAbort, { once: true });
|
|
1152
|
+
let timedOut = false;
|
|
1153
|
+
const timeoutMs = options.timeoutMs !== void 0 && options.timeoutMs > 0 ? options.timeoutMs : void 0;
|
|
1154
|
+
const timer = timeoutMs === void 0 ? void 0 : setTimeout(() => {
|
|
1155
|
+
timedOut = true;
|
|
1156
|
+
controller.abort(new Error(`Timed out after ${timeoutMs} ms.`));
|
|
1157
|
+
}, timeoutMs);
|
|
1158
|
+
const interrupted = new Promise((_resolve, reject) => {
|
|
1159
|
+
controller.signal.addEventListener(
|
|
1160
|
+
"abort",
|
|
1161
|
+
() => reject(new LoadInterruption()),
|
|
1162
|
+
{ once: true }
|
|
1163
|
+
);
|
|
1164
|
+
});
|
|
764
1165
|
let payload;
|
|
765
1166
|
try {
|
|
766
|
-
payload = await
|
|
1167
|
+
payload = await Promise.race([
|
|
1168
|
+
Promise.resolve().then(() => {
|
|
1169
|
+
if (controller.signal.aborted) throw new LoadInterruption();
|
|
1170
|
+
return provider(context, definition, { signal: controller.signal });
|
|
1171
|
+
}),
|
|
1172
|
+
interrupted
|
|
1173
|
+
]);
|
|
767
1174
|
} catch (cause) {
|
|
1175
|
+
if (cause instanceof LoadInterruption) {
|
|
1176
|
+
return failedWidget(
|
|
1177
|
+
definition,
|
|
1178
|
+
timedOut ? `Widget ${quotedKey}: provider timed out after ${timeoutMs} ms.` : `Widget ${quotedKey}: loading was canceled.`
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
768
1181
|
return failedWidget(
|
|
769
1182
|
definition,
|
|
770
|
-
`Widget ${
|
|
1183
|
+
`Widget ${quotedKey}: provider failed: ${describeUnknownError(cause)}`
|
|
771
1184
|
);
|
|
1185
|
+
} finally {
|
|
1186
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1187
|
+
options.signal?.removeEventListener("abort", onExternalAbort);
|
|
1188
|
+
}
|
|
1189
|
+
const resolved = resolvePayload(definition, payload, options);
|
|
1190
|
+
if (resolved.status !== "ok") return resolved;
|
|
1191
|
+
const storedAt = Date.now();
|
|
1192
|
+
if (options.cache !== void 0) {
|
|
1193
|
+
try {
|
|
1194
|
+
await options.cache.set(cacheKeyFor(definition, options), {
|
|
1195
|
+
payload: resolved.data,
|
|
1196
|
+
storedAt
|
|
1197
|
+
});
|
|
1198
|
+
} catch {
|
|
1199
|
+
}
|
|
772
1200
|
}
|
|
773
|
-
return
|
|
1201
|
+
return { ...resolved, updatedAt: storedAt };
|
|
774
1202
|
}
|
|
775
1203
|
function widgetsFor(definitions, roles) {
|
|
776
1204
|
return sortDefinitions(
|
|
@@ -791,6 +1219,131 @@ async function resolveWidgets(definitions, providers, context, options = {}) {
|
|
|
791
1219
|
);
|
|
792
1220
|
}
|
|
793
1221
|
|
|
1222
|
+
// src/core/loader.ts
|
|
1223
|
+
function createWidgetLoader(options) {
|
|
1224
|
+
const { definitions, providers, context, ...resolveOptions } = options;
|
|
1225
|
+
const visible = widgetsFor(definitions, context.roles);
|
|
1226
|
+
let snapshot = loadingWidgets(visible);
|
|
1227
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1228
|
+
const started = /* @__PURE__ */ new Set();
|
|
1229
|
+
const generations = /* @__PURE__ */ new Map();
|
|
1230
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
1231
|
+
let disposed = false;
|
|
1232
|
+
function publish(next) {
|
|
1233
|
+
snapshot = next;
|
|
1234
|
+
for (const listener of [...listeners]) {
|
|
1235
|
+
try {
|
|
1236
|
+
listener();
|
|
1237
|
+
} catch {
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
function replace(key, widget) {
|
|
1242
|
+
publish(
|
|
1243
|
+
snapshot.map(
|
|
1244
|
+
(current) => current.definition.key === key ? widget : current
|
|
1245
|
+
)
|
|
1246
|
+
);
|
|
1247
|
+
}
|
|
1248
|
+
function abortAll(reason) {
|
|
1249
|
+
for (const controller of inFlight.values()) controller.abort(reason);
|
|
1250
|
+
inFlight.clear();
|
|
1251
|
+
}
|
|
1252
|
+
resolveOptions.signal?.addEventListener(
|
|
1253
|
+
"abort",
|
|
1254
|
+
() => abortAll(resolveOptions.signal?.reason),
|
|
1255
|
+
{ once: true }
|
|
1256
|
+
);
|
|
1257
|
+
async function run(definition, readCache) {
|
|
1258
|
+
const key = definition.key;
|
|
1259
|
+
const generation = (generations.get(key) ?? 0) + 1;
|
|
1260
|
+
generations.set(key, generation);
|
|
1261
|
+
inFlight.get(key)?.abort(new Error("Superseded by a newer load."));
|
|
1262
|
+
const controller = new AbortController();
|
|
1263
|
+
inFlight.set(key, controller);
|
|
1264
|
+
const isCurrent = () => !disposed && generations.get(key) === generation;
|
|
1265
|
+
if (readCache && resolveOptions.cache !== void 0) {
|
|
1266
|
+
try {
|
|
1267
|
+
const entry = await resolveOptions.cache.get(
|
|
1268
|
+
cacheKeyFor(definition, resolveOptions)
|
|
1269
|
+
);
|
|
1270
|
+
if (entry !== void 0 && isCurrent()) {
|
|
1271
|
+
const cached = resolvePayload(
|
|
1272
|
+
definition,
|
|
1273
|
+
entry.payload,
|
|
1274
|
+
resolveOptions
|
|
1275
|
+
);
|
|
1276
|
+
if (cached.status === "ok") {
|
|
1277
|
+
replace(key, { ...cached, updatedAt: entry.storedAt, stale: true });
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
} catch {
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
if (!isCurrent()) return;
|
|
1284
|
+
const provider = Object.prototype.hasOwnProperty.call(providers, key) ? providers[key] : void 0;
|
|
1285
|
+
const result = await resolveWidget(definition, provider, context, {
|
|
1286
|
+
...resolveOptions,
|
|
1287
|
+
signal: controller.signal
|
|
1288
|
+
});
|
|
1289
|
+
if (!isCurrent()) return;
|
|
1290
|
+
inFlight.delete(key);
|
|
1291
|
+
replace(key, result);
|
|
1292
|
+
}
|
|
1293
|
+
function start(definition, readCache) {
|
|
1294
|
+
started.add(definition.key);
|
|
1295
|
+
void run(definition, readCache);
|
|
1296
|
+
}
|
|
1297
|
+
function definitionFor(key) {
|
|
1298
|
+
return visible.find((definition) => definition.key === key);
|
|
1299
|
+
}
|
|
1300
|
+
function load(key) {
|
|
1301
|
+
if (disposed || started.has(key)) return;
|
|
1302
|
+
const definition = definitionFor(key);
|
|
1303
|
+
if (definition !== void 0) start(definition, true);
|
|
1304
|
+
}
|
|
1305
|
+
function loadAll() {
|
|
1306
|
+
for (const definition of visible) load(definition.key);
|
|
1307
|
+
}
|
|
1308
|
+
function refresh(key, refreshOptions) {
|
|
1309
|
+
if (disposed) return;
|
|
1310
|
+
const keys = key === void 0 ? [...started] : [key];
|
|
1311
|
+
for (const target of keys) {
|
|
1312
|
+
const definition = definitionFor(target);
|
|
1313
|
+
if (definition === void 0) continue;
|
|
1314
|
+
if (refreshOptions?.skipInFlight === true && inFlight.has(target)) {
|
|
1315
|
+
continue;
|
|
1316
|
+
}
|
|
1317
|
+
const current = snapshot.find(
|
|
1318
|
+
(widget) => widget.definition.key === target
|
|
1319
|
+
);
|
|
1320
|
+
if (current?.status === "error") {
|
|
1321
|
+
replace(target, { definition, status: "loading" });
|
|
1322
|
+
}
|
|
1323
|
+
start(definition, false);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
function dispose() {
|
|
1327
|
+
disposed = true;
|
|
1328
|
+
abortAll(new Error("The widget loader was disposed."));
|
|
1329
|
+
listeners.clear();
|
|
1330
|
+
}
|
|
1331
|
+
function subscribe(listener) {
|
|
1332
|
+
listeners.add(listener);
|
|
1333
|
+
return () => {
|
|
1334
|
+
listeners.delete(listener);
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
return {
|
|
1338
|
+
getSnapshot: () => snapshot,
|
|
1339
|
+
subscribe,
|
|
1340
|
+
load,
|
|
1341
|
+
loadAll,
|
|
1342
|
+
refresh,
|
|
1343
|
+
dispose
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
|
|
794
1347
|
// src/core/scale.ts
|
|
795
1348
|
function niceStep(value) {
|
|
796
1349
|
if (!(value > 0) || !Number.isFinite(value)) return 1;
|
|
@@ -821,6 +1374,6 @@ function labelStride(count, maxLabels = 6) {
|
|
|
821
1374
|
return Math.max(1, Math.ceil(count / Math.max(1, maxLabels)));
|
|
822
1375
|
}
|
|
823
1376
|
|
|
824
|
-
export { DEFAULT_COMPARISON_LABEL, DEFAULT_CURRENCY, DEFAULT_EMPTY_TEXT, EMPTY_LAYOUT, MAX_GRAPH_SERIES, MAX_REPORTED_PROBLEMS, VALUE_FORMATS, WIDGET_FILLS, WIDGET_KINDS, WIDGET_SIZES, baseColumnSpan, defineWidget, describeUnknownError, emptyTextFor, emptyWidget, err, failedWidget, fillColumnSpans, fillsHeight, fillsWidth, formatCount, formatPercentChange, formatValue, fromLegacyDefinition, hiddenWidgets, hideWidget, isHidden, isMinimized, isSafeHref, isSafeImageUrl, isWidgetEmptyState, isWidgetKind, isWidgetVisibleTo, kpiDelta, labelStride, loadingWidgets, minimizeWidget, moveWidget, moveWidgetBy, moveWidgetDown, moveWidgetUp, niceDomain, niceStep, ok, parseLayout, percentChange, pruneLayout, resolvePayload, resolveWidget, resolveWidgets, restoreWidget, serializeLayout, shareOf, showWidget, sortDefinitions, toggleHidden, toggleMinimized, upgradeLegacyWidgetData, validateWidgetData, validateWidgetDefinition, visibleWidgets, widgetsFor };
|
|
1377
|
+
export { DEFAULT_COMPARISON_LABEL, DEFAULT_CURRENCY, DEFAULT_DETAIL_PAGE_SIZE, DEFAULT_EMPTY_TEXT, DETAIL_MODES, EMPTY_LAYOUT, MAX_DETAIL_PAGE_SIZE, MAX_GRAPH_SERIES, MAX_REPORTED_PROBLEMS, MAX_WIDGET_WIDTH, MIN_WIDGET_WIDTH, VALUE_FORMATS, WIDGET_FILLS, WIDGET_KINDS, WIDGET_SIZES, WIDTH_COLUMNS, baseColumnSpan, cacheKeyFor, createWidgetLoader, defaultDetailQuery, defineWidget, deriveDetailData, describeUnknownError, emptyTextFor, emptyWidget, err, failedWidget, fillColumnSpans, fillWidthSpans, fillsHeight, fillsWidth, formatCount, formatPercentChange, formatValue, fromLegacyDefinition, hiddenWidgets, hideWidget, isHidden, isMinimized, isSafeHref, isSafeImageUrl, isWidgetEmptyState, isWidgetKind, isWidgetVisibleTo, isWidgetWidth, itemWidth, kpiDelta, labelStride, loadingWidgets, minimizeWidget, moveWidget, moveWidgetBy, moveWidgetDown, moveWidgetUp, niceDomain, niceStep, ok, parseDetailQuery, parseLayout, percentChange, pruneLayout, queryRows, resolveDetailOptions, resolvePayload, resolveWidget, resolveWidgets, restoreWidget, serializeDetailQuery, serializeLayout, shareOf, showWidget, sortDefinitions, toggleHidden, toggleMinimized, upgradeLegacyWidgetData, usesWidthColumns, validateDetailData, validateWidgetData, validateWidgetDefinition, visibleWidgets, widgetsFor, widthForSize };
|
|
825
1378
|
//# sourceMappingURL=core.js.map
|
|
826
1379
|
//# sourceMappingURL=core.js.map
|