@richardmcquiston01/dashboard-widgets-toolkit 0.6.0 → 0.8.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 +6 -1
- package/dist/core.cjs +109 -19
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +108 -20
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +109 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +108 -20
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +164 -66
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +8 -1
- package/dist/react.d.ts +8 -1
- package/dist/react.js +164 -66
- package/dist/react.js.map +1 -1
- package/dist/{scale-D51ZGIF4.d.cts → scale-CrXxawQn.d.cts} +64 -5
- package/dist/{scale-D51ZGIF4.d.ts → scale-CrXxawQn.d.ts} +64 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ The widget kinds:
|
|
|
31
31
|
| `TEXT` | `value: string`, `label` | A count or status as text |
|
|
32
32
|
| `KPI` | `value: number`, `previous?: number \| null`, `format`, `currency?`, `label`, `hint?`, `higherIsBetter?` | Stat tile with a change vs previous period |
|
|
33
33
|
| `GAUGE` | `value`, `max`, `label` | A meter (unread of total, quota used) |
|
|
34
|
-
| `TABLE` | `columns: {label, numeric?}[]`, `rows: {text, href?}[][]`, `footer?`
|
|
34
|
+
| `TABLE` | `columns: {label, numeric?}[]`, `rows: {text, href?, value?}[][]`, `footer?` | Small data tables |
|
|
35
35
|
| `BAR_LIST` | `items: {label, value, display?}[]`, `total?` | Ratings breakdown, purchases by country |
|
|
36
36
|
| `ALERT_LIST` | `items: {title, href?, thumbnailUrl?, valueLabel, detail?}[]`, `total`, `emptyText` | Low stock, stale listings, expiring soon |
|
|
37
37
|
| `GRAPH` | `series: {name, points: {label, value}[]}[]`, `chartType: 'bar' \| 'line'`, `valueFormat`, `currency?`, `xLabel?` | Revenue by month, views per day |
|
|
@@ -41,6 +41,11 @@ in major units (12.5 is $12.50) and percents are fractions (0.25 is 25%). Any
|
|
|
41
41
|
widget may instead return an empty state, `{ empty: true, text: 'No orders
|
|
42
42
|
synced yet.' }`.
|
|
43
43
|
|
|
44
|
+
A `TABLE` cell's optional `value` (a string or number) is what the card's
|
|
45
|
+
table controls (`tableControls` on the definition) and the detail view sort
|
|
46
|
+
and search on, when `text` is formatted for reading: a timestamp behind
|
|
47
|
+
"3 days ago", a rating behind stars, an amount behind "$1,234.00".
|
|
48
|
+
|
|
44
49
|
## Getting Started
|
|
45
50
|
|
|
46
51
|
See [GETTING_STARTED.md](./GETTING_STARTED.md)
|
package/dist/core.cjs
CHANGED
|
@@ -86,6 +86,26 @@ function fromLegacyDefinition(legacy) {
|
|
|
86
86
|
// src/core/definition.ts
|
|
87
87
|
var WIDGET_SIZES = ["small", "medium", "large", "full"];
|
|
88
88
|
var WIDGET_FILLS = ["height", "width", "both"];
|
|
89
|
+
var NOT_LOCKED = Object.freeze({
|
|
90
|
+
move: false,
|
|
91
|
+
hide: false,
|
|
92
|
+
minimize: false
|
|
93
|
+
});
|
|
94
|
+
var FULLY_LOCKED = Object.freeze({
|
|
95
|
+
move: true,
|
|
96
|
+
hide: true,
|
|
97
|
+
minimize: true
|
|
98
|
+
});
|
|
99
|
+
function resolveWidgetLock(definition) {
|
|
100
|
+
const setting = definition.locked;
|
|
101
|
+
if (setting === true) return FULLY_LOCKED;
|
|
102
|
+
if (setting === void 0 || setting === false) return NOT_LOCKED;
|
|
103
|
+
return {
|
|
104
|
+
move: setting.move === true,
|
|
105
|
+
hide: setting.hide === true,
|
|
106
|
+
minimize: setting.minimize === true
|
|
107
|
+
};
|
|
108
|
+
}
|
|
89
109
|
function defineWidget(definition) {
|
|
90
110
|
return definition;
|
|
91
111
|
}
|
|
@@ -689,9 +709,10 @@ function bySortOrder(items) {
|
|
|
689
709
|
(a, b) => (a.item.sortOrder ?? 0) - (b.item.sortOrder ?? 0) || a.index - b.index
|
|
690
710
|
).map(({ item }) => item);
|
|
691
711
|
}
|
|
692
|
-
function
|
|
693
|
-
|
|
694
|
-
|
|
712
|
+
function isPinned(item) {
|
|
713
|
+
return resolveWidgetLock(item).move;
|
|
714
|
+
}
|
|
715
|
+
function viewerOrder(visible, layout) {
|
|
695
716
|
const byKey = new Map(visible.map((d) => [d.key, d]));
|
|
696
717
|
const ordered = [];
|
|
697
718
|
for (const key of layout.order) {
|
|
@@ -706,34 +727,62 @@ function visibleWidgets(definitions, layout) {
|
|
|
706
727
|
}
|
|
707
728
|
return ordered;
|
|
708
729
|
}
|
|
730
|
+
function placeAroundPinned(visible, free) {
|
|
731
|
+
const slots = bySortOrder(visible).map(
|
|
732
|
+
(item) => isPinned(item) ? item : void 0
|
|
733
|
+
);
|
|
734
|
+
let next = 0;
|
|
735
|
+
return slots.map((pinned) => pinned ?? free[next++]);
|
|
736
|
+
}
|
|
737
|
+
function visibleWidgets(definitions, layout) {
|
|
738
|
+
const hidden = new Set(layout.hidden);
|
|
739
|
+
const visible = definitions.filter((d) => !hidden.has(d.key));
|
|
740
|
+
const ordered = viewerOrder(visible, layout);
|
|
741
|
+
if (!visible.some(isPinned)) return ordered;
|
|
742
|
+
return placeAroundPinned(
|
|
743
|
+
visible,
|
|
744
|
+
ordered.filter((item) => !isPinned(item))
|
|
745
|
+
);
|
|
746
|
+
}
|
|
709
747
|
function hiddenWidgets(definitions, layout) {
|
|
710
748
|
const hidden = new Set(layout.hidden);
|
|
711
749
|
return bySortOrder(definitions.filter((d) => hidden.has(d.key)));
|
|
712
750
|
}
|
|
713
751
|
function moveWidget(definitions, layout, key, toIndex) {
|
|
714
|
-
const current = visibleWidgets(definitions, layout)
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
if (fromIndex === -1 || !Number.isInteger(toIndex) || toIndex < 0 || toIndex >= current.length || toIndex === fromIndex) {
|
|
752
|
+
const current = visibleWidgets(definitions, layout);
|
|
753
|
+
const fromIndex = current.findIndex((d) => d.key === key);
|
|
754
|
+
const mover = current[fromIndex];
|
|
755
|
+
if (mover === void 0 || !Number.isInteger(toIndex) || toIndex < 0 || toIndex >= current.length || toIndex === fromIndex || isPinned(mover) || isPinned(current[toIndex])) {
|
|
719
756
|
return layout;
|
|
720
757
|
}
|
|
721
|
-
const
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
758
|
+
const freeSlots = freeSlotIndexes(current);
|
|
759
|
+
const free = current.filter((item) => !isPinned(item));
|
|
760
|
+
const fromFree = freeSlots.indexOf(fromIndex);
|
|
761
|
+
const toFree = freeSlots.indexOf(toIndex);
|
|
762
|
+
free.splice(fromFree, 1);
|
|
763
|
+
free.splice(toFree, 0, mover);
|
|
764
|
+
const next = placeAroundPinned(current, free);
|
|
765
|
+
return { ...layout, order: next.map((d) => d.key) };
|
|
766
|
+
}
|
|
767
|
+
function freeSlotIndexes(ordered) {
|
|
768
|
+
const slots = [];
|
|
769
|
+
ordered.forEach((item, index) => {
|
|
770
|
+
if (!isPinned(item)) slots.push(index);
|
|
771
|
+
});
|
|
772
|
+
return slots;
|
|
725
773
|
}
|
|
726
774
|
function moveWidgetBy(definitions, layout, key, offset) {
|
|
727
|
-
const current = visibleWidgets(definitions, layout)
|
|
728
|
-
|
|
729
|
-
);
|
|
730
|
-
const fromIndex = current.indexOf(key);
|
|
775
|
+
const current = visibleWidgets(definitions, layout);
|
|
776
|
+
const fromIndex = current.findIndex((d) => d.key === key);
|
|
731
777
|
if (fromIndex === -1) return layout;
|
|
732
|
-
const
|
|
778
|
+
const freeSlots = freeSlotIndexes(current);
|
|
779
|
+
const fromFree = freeSlots.indexOf(fromIndex);
|
|
780
|
+
if (fromFree === -1) return layout;
|
|
781
|
+
const toFree = Math.max(
|
|
733
782
|
0,
|
|
734
|
-
Math.min(
|
|
783
|
+
Math.min(freeSlots.length - 1, fromFree + Math.trunc(offset))
|
|
735
784
|
);
|
|
736
|
-
return moveWidget(definitions, layout, key,
|
|
785
|
+
return moveWidget(definitions, layout, key, freeSlots[toFree]);
|
|
737
786
|
}
|
|
738
787
|
function moveWidgetUp(definitions, layout, key) {
|
|
739
788
|
return moveWidgetBy(definitions, layout, key, -1);
|
|
@@ -783,6 +832,23 @@ function pruneLayout(definitions, layout) {
|
|
|
783
832
|
};
|
|
784
833
|
return next.order.length === layout.order.length && next.hidden.length === layout.hidden.length && next.minimized.length === layout.minimized.length ? layout : next;
|
|
785
834
|
}
|
|
835
|
+
function enforceLocks(definitions, layout, options = {}) {
|
|
836
|
+
if (options.overrideLocks === true) return layout;
|
|
837
|
+
const noMove = /* @__PURE__ */ new Set();
|
|
838
|
+
const noHide = /* @__PURE__ */ new Set();
|
|
839
|
+
const noMinimize = /* @__PURE__ */ new Set();
|
|
840
|
+
for (const definition of definitions) {
|
|
841
|
+
const lock = resolveWidgetLock(definition);
|
|
842
|
+
if (lock.move) noMove.add(definition.key);
|
|
843
|
+
if (lock.hide) noHide.add(definition.key);
|
|
844
|
+
if (lock.minimize) noMinimize.add(definition.key);
|
|
845
|
+
}
|
|
846
|
+
const without = (keys, drop) => keys.some((key) => drop.has(key)) ? keys.filter((key) => !drop.has(key)) : keys;
|
|
847
|
+
const order = without(layout.order, noMove);
|
|
848
|
+
const hidden = without(layout.hidden, noHide);
|
|
849
|
+
const minimized = without(layout.minimized, noMinimize);
|
|
850
|
+
return order === layout.order && hidden === layout.hidden && minimized === layout.minimized ? layout : { ...layout, order, hidden, minimized };
|
|
851
|
+
}
|
|
786
852
|
|
|
787
853
|
// src/core/validate.ts
|
|
788
854
|
var MAX_REPORTED_PROBLEMS = 10;
|
|
@@ -826,6 +892,14 @@ function optionalNumber(record, field, path, problems) {
|
|
|
826
892
|
requireNumber(record, field, path, problems);
|
|
827
893
|
}
|
|
828
894
|
}
|
|
895
|
+
function optionalSortValue(record, path, problems) {
|
|
896
|
+
const value = record["value"];
|
|
897
|
+
if (value !== void 0 && typeof value !== "string" && !(typeof value === "number" && Number.isFinite(value))) {
|
|
898
|
+
problems.push(
|
|
899
|
+
`${path}.value must be a string or a finite number, got ${describeType2(value)}.`
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
829
903
|
function optionalBoolean(record, field, path, problems) {
|
|
830
904
|
const value = record[field];
|
|
831
905
|
if (value !== void 0 && typeof value !== "boolean") {
|
|
@@ -929,6 +1003,7 @@ function checkPayloadFields(kind, data, problems) {
|
|
|
929
1003
|
}
|
|
930
1004
|
eachRecord(row, rowPath, problems, (cell, cellPath) => {
|
|
931
1005
|
requireString(cell, "text", cellPath, problems);
|
|
1006
|
+
optionalSortValue(cell, cellPath, problems);
|
|
932
1007
|
optionalUrl(
|
|
933
1008
|
cell,
|
|
934
1009
|
"href",
|
|
@@ -1051,6 +1126,18 @@ function checkTableControlsOption(controls, kind, problems) {
|
|
|
1051
1126
|
);
|
|
1052
1127
|
}
|
|
1053
1128
|
}
|
|
1129
|
+
function checkLockedOption(locked, problems) {
|
|
1130
|
+
if (locked === void 0 || typeof locked === "boolean") return;
|
|
1131
|
+
if (!isRecord(locked)) {
|
|
1132
|
+
problems.push(
|
|
1133
|
+
`locked must be true, false or an object, got ${describeType2(locked)}.`
|
|
1134
|
+
);
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
optionalBoolean(locked, "move", "locked", problems);
|
|
1138
|
+
optionalBoolean(locked, "hide", "locked", problems);
|
|
1139
|
+
optionalBoolean(locked, "minimize", "locked", problems);
|
|
1140
|
+
}
|
|
1054
1141
|
function formatProblems(prefix, problems) {
|
|
1055
1142
|
const shown = problems.slice(0, MAX_REPORTED_PROBLEMS);
|
|
1056
1143
|
const more = problems.length - shown.length;
|
|
@@ -1124,6 +1211,7 @@ function validateWidgetDefinition(value) {
|
|
|
1124
1211
|
}
|
|
1125
1212
|
checkDetailOption(value["detail"], problems);
|
|
1126
1213
|
checkTableControlsOption(value["tableControls"], value["kind"], problems);
|
|
1214
|
+
checkLockedOption(value["locked"], problems);
|
|
1127
1215
|
if (value["fill"] !== void 0) {
|
|
1128
1216
|
requireOneOf(value, "fill", WIDGET_FILLS, "", problems);
|
|
1129
1217
|
}
|
|
@@ -1432,6 +1520,7 @@ exports.deriveDetailData = deriveDetailData;
|
|
|
1432
1520
|
exports.describeUnknownError = describeUnknownError;
|
|
1433
1521
|
exports.emptyTextFor = emptyTextFor;
|
|
1434
1522
|
exports.emptyWidget = emptyWidget;
|
|
1523
|
+
exports.enforceLocks = enforceLocks;
|
|
1435
1524
|
exports.err = err;
|
|
1436
1525
|
exports.failedWidget = failedWidget;
|
|
1437
1526
|
exports.fillColumnSpans = fillColumnSpans;
|
|
@@ -1473,6 +1562,7 @@ exports.resolveDetailOptions = resolveDetailOptions;
|
|
|
1473
1562
|
exports.resolvePayload = resolvePayload;
|
|
1474
1563
|
exports.resolveTableControls = resolveTableControls;
|
|
1475
1564
|
exports.resolveWidget = resolveWidget;
|
|
1565
|
+
exports.resolveWidgetLock = resolveWidgetLock;
|
|
1476
1566
|
exports.resolveWidgets = resolveWidgets;
|
|
1477
1567
|
exports.restoreWidget = restoreWidget;
|
|
1478
1568
|
exports.serializeDetailQuery = serializeDetailQuery;
|