@richardmcquiston01/dashboard-widgets-toolkit 0.12.0 → 0.14.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 +951 -370
- 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 +933 -371
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +951 -370
- 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 +933 -371
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +836 -60
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +53 -4
- package/dist/react.d.ts +53 -4
- package/dist/react.js +836 -61
- package/dist/react.js.map +1 -1
- package/dist/{storage-VaaysVnZ.d.cts → storage-_Eb8Y98-.d.cts} +144 -3
- package/dist/{storage-VaaysVnZ.d.ts → storage-_Eb8Y98-.d.ts} +144 -3
- package/dist/styles.css +146 -0
- package/package.json +1 -1
package/dist/core.cjs
CHANGED
|
@@ -89,12 +89,14 @@ var WIDGET_FILLS = ["height", "width", "both"];
|
|
|
89
89
|
var NOT_LOCKED = Object.freeze({
|
|
90
90
|
move: false,
|
|
91
91
|
hide: false,
|
|
92
|
-
minimize: false
|
|
92
|
+
minimize: false,
|
|
93
|
+
options: false
|
|
93
94
|
});
|
|
94
95
|
var FULLY_LOCKED = Object.freeze({
|
|
95
96
|
move: true,
|
|
96
97
|
hide: true,
|
|
97
|
-
minimize: true
|
|
98
|
+
minimize: true,
|
|
99
|
+
options: true
|
|
98
100
|
});
|
|
99
101
|
function resolveWidgetLock(definition) {
|
|
100
102
|
const setting = definition.locked;
|
|
@@ -103,7 +105,8 @@ function resolveWidgetLock(definition) {
|
|
|
103
105
|
return {
|
|
104
106
|
move: setting.move === true,
|
|
105
107
|
hide: setting.hide === true,
|
|
106
|
-
minimize: setting.minimize === true
|
|
108
|
+
minimize: setting.minimize === true,
|
|
109
|
+
options: setting.options === true
|
|
107
110
|
};
|
|
108
111
|
}
|
|
109
112
|
function defineWidget(definition) {
|
|
@@ -685,263 +688,6 @@ function placeAndFillRows(items, columns, spanOf) {
|
|
|
685
688
|
return { spans, rows };
|
|
686
689
|
}
|
|
687
690
|
|
|
688
|
-
// src/core/layout.ts
|
|
689
|
-
var MAX_PAGE_TITLE_LENGTH = 30;
|
|
690
|
-
var MAX_PARSED_PAGES = 50;
|
|
691
|
-
var MAX_PAGE_ROWS = 20;
|
|
692
|
-
var EMPTY_LAYOUT = Object.freeze({
|
|
693
|
-
order: Object.freeze([]),
|
|
694
|
-
hidden: Object.freeze([]),
|
|
695
|
-
minimized: Object.freeze([])
|
|
696
|
-
});
|
|
697
|
-
function uniqueStrings(value) {
|
|
698
|
-
if (!Array.isArray(value)) return [];
|
|
699
|
-
const seen = /* @__PURE__ */ new Set();
|
|
700
|
-
for (const entry of value) {
|
|
701
|
-
if (typeof entry === "string" && entry !== "") seen.add(entry);
|
|
702
|
-
}
|
|
703
|
-
return [...seen];
|
|
704
|
-
}
|
|
705
|
-
function parseLayout(raw) {
|
|
706
|
-
let value = raw;
|
|
707
|
-
if (typeof raw === "string") {
|
|
708
|
-
try {
|
|
709
|
-
value = JSON.parse(raw);
|
|
710
|
-
} catch {
|
|
711
|
-
return EMPTY_LAYOUT;
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
715
|
-
return EMPTY_LAYOUT;
|
|
716
|
-
}
|
|
717
|
-
const record = value;
|
|
718
|
-
const order = uniqueStrings(record["order"]);
|
|
719
|
-
const hidden = uniqueStrings(record["hidden"]);
|
|
720
|
-
const minimized = uniqueStrings(record["minimized"]);
|
|
721
|
-
const pages = parsePages(record["pages"]);
|
|
722
|
-
if (pages.length > 0) {
|
|
723
|
-
return {
|
|
724
|
-
order: EMPTY_LAYOUT.order,
|
|
725
|
-
hidden: EMPTY_LAYOUT.hidden,
|
|
726
|
-
minimized: EMPTY_LAYOUT.minimized,
|
|
727
|
-
pages
|
|
728
|
-
};
|
|
729
|
-
}
|
|
730
|
-
if (order.length === 0 && hidden.length === 0 && minimized.length === 0) {
|
|
731
|
-
return EMPTY_LAYOUT;
|
|
732
|
-
}
|
|
733
|
-
return { order, hidden, minimized };
|
|
734
|
-
}
|
|
735
|
-
function parsePages(value) {
|
|
736
|
-
if (!Array.isArray(value)) return [];
|
|
737
|
-
const pages = [];
|
|
738
|
-
const keys = /* @__PURE__ */ new Set();
|
|
739
|
-
for (const entry of value.slice(0, MAX_PARSED_PAGES)) {
|
|
740
|
-
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
741
|
-
continue;
|
|
742
|
-
}
|
|
743
|
-
const record = entry;
|
|
744
|
-
const key = record["key"];
|
|
745
|
-
if (typeof key !== "string" || key === "" || keys.has(key)) continue;
|
|
746
|
-
keys.add(key);
|
|
747
|
-
const rawTitle = record["title"];
|
|
748
|
-
const title = typeof rawTitle === "string" && rawTitle.trim() !== "" ? rawTitle.trim().slice(0, MAX_PAGE_TITLE_LENGTH) : `Page ${pages.length + 1}`;
|
|
749
|
-
const maxRows = record["maxRows"];
|
|
750
|
-
const held = /* @__PURE__ */ new Set();
|
|
751
|
-
for (const earlier of pages) {
|
|
752
|
-
for (const k of [...earlier.order, ...earlier.hidden]) held.add(k);
|
|
753
|
-
}
|
|
754
|
-
const keep = (list) => uniqueStrings(list).filter((k) => !held.has(k));
|
|
755
|
-
pages.push({
|
|
756
|
-
key,
|
|
757
|
-
title,
|
|
758
|
-
order: keep(record["order"]),
|
|
759
|
-
hidden: keep(record["hidden"]),
|
|
760
|
-
minimized: keep(record["minimized"]),
|
|
761
|
-
...typeof maxRows === "number" && Number.isInteger(maxRows) && maxRows >= 1 && maxRows <= MAX_PAGE_ROWS ? { maxRows } : {}
|
|
762
|
-
});
|
|
763
|
-
}
|
|
764
|
-
return pages;
|
|
765
|
-
}
|
|
766
|
-
function serializeLayout(layout) {
|
|
767
|
-
return JSON.stringify({
|
|
768
|
-
order: [...layout.order],
|
|
769
|
-
hidden: [...layout.hidden],
|
|
770
|
-
minimized: [...layout.minimized],
|
|
771
|
-
...layout.pages === void 0 || layout.pages.length === 0 ? {} : {
|
|
772
|
-
pages: layout.pages.map((page) => ({
|
|
773
|
-
key: page.key,
|
|
774
|
-
title: page.title,
|
|
775
|
-
order: [...page.order],
|
|
776
|
-
hidden: [...page.hidden],
|
|
777
|
-
minimized: [...page.minimized],
|
|
778
|
-
...page.maxRows === void 0 ? {} : { maxRows: page.maxRows }
|
|
779
|
-
}))
|
|
780
|
-
}
|
|
781
|
-
});
|
|
782
|
-
}
|
|
783
|
-
function bySortOrder(items) {
|
|
784
|
-
return items.map((item, index) => ({ item, index })).sort(
|
|
785
|
-
(a, b) => (a.item.sortOrder ?? 0) - (b.item.sortOrder ?? 0) || a.index - b.index
|
|
786
|
-
).map(({ item }) => item);
|
|
787
|
-
}
|
|
788
|
-
function isPinned(item) {
|
|
789
|
-
return resolveWidgetLock(item).move;
|
|
790
|
-
}
|
|
791
|
-
function viewerOrder(visible, layout) {
|
|
792
|
-
const byKey = new Map(visible.map((d) => [d.key, d]));
|
|
793
|
-
const ordered = [];
|
|
794
|
-
for (const key of layout.order) {
|
|
795
|
-
const definition = byKey.get(key);
|
|
796
|
-
if (definition !== void 0) {
|
|
797
|
-
ordered.push(definition);
|
|
798
|
-
byKey.delete(key);
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
for (const definition of bySortOrder(visible)) {
|
|
802
|
-
if (byKey.has(definition.key)) ordered.push(definition);
|
|
803
|
-
}
|
|
804
|
-
return ordered;
|
|
805
|
-
}
|
|
806
|
-
function placeAroundPinned(visible, free) {
|
|
807
|
-
const slots = bySortOrder(visible).map(
|
|
808
|
-
(item) => isPinned(item) ? item : void 0
|
|
809
|
-
);
|
|
810
|
-
let next = 0;
|
|
811
|
-
return slots.map((pinned) => pinned ?? free[next++]);
|
|
812
|
-
}
|
|
813
|
-
function visibleWidgets(definitions, layout) {
|
|
814
|
-
const hidden = new Set(layout.hidden);
|
|
815
|
-
const visible = definitions.filter((d) => !hidden.has(d.key));
|
|
816
|
-
const ordered = viewerOrder(visible, layout);
|
|
817
|
-
if (!visible.some(isPinned)) return ordered;
|
|
818
|
-
return placeAroundPinned(
|
|
819
|
-
visible,
|
|
820
|
-
ordered.filter((item) => !isPinned(item))
|
|
821
|
-
);
|
|
822
|
-
}
|
|
823
|
-
function hiddenWidgets(definitions, layout) {
|
|
824
|
-
const hidden = new Set(layout.hidden);
|
|
825
|
-
return bySortOrder(definitions.filter((d) => hidden.has(d.key)));
|
|
826
|
-
}
|
|
827
|
-
function moveWidget(definitions, layout, key, toIndex) {
|
|
828
|
-
const current = visibleWidgets(definitions, layout);
|
|
829
|
-
const fromIndex = current.findIndex((d) => d.key === key);
|
|
830
|
-
const mover = current[fromIndex];
|
|
831
|
-
if (mover === void 0 || !Number.isInteger(toIndex) || toIndex < 0 || toIndex >= current.length || toIndex === fromIndex || isPinned(mover) || isPinned(current[toIndex])) {
|
|
832
|
-
return layout;
|
|
833
|
-
}
|
|
834
|
-
const freeSlots = freeSlotIndexes(current);
|
|
835
|
-
const free = current.filter((item) => !isPinned(item));
|
|
836
|
-
const fromFree = freeSlots.indexOf(fromIndex);
|
|
837
|
-
const toFree = freeSlots.indexOf(toIndex);
|
|
838
|
-
free.splice(fromFree, 1);
|
|
839
|
-
free.splice(toFree, 0, mover);
|
|
840
|
-
const next = placeAroundPinned(current, free);
|
|
841
|
-
return { ...layout, order: next.map((d) => d.key) };
|
|
842
|
-
}
|
|
843
|
-
function freeSlotIndexes(ordered) {
|
|
844
|
-
const slots = [];
|
|
845
|
-
ordered.forEach((item, index) => {
|
|
846
|
-
if (!isPinned(item)) slots.push(index);
|
|
847
|
-
});
|
|
848
|
-
return slots;
|
|
849
|
-
}
|
|
850
|
-
function moveWidgetBy(definitions, layout, key, offset) {
|
|
851
|
-
const current = visibleWidgets(definitions, layout);
|
|
852
|
-
const fromIndex = current.findIndex((d) => d.key === key);
|
|
853
|
-
if (fromIndex === -1) return layout;
|
|
854
|
-
const freeSlots = freeSlotIndexes(current);
|
|
855
|
-
const fromFree = freeSlots.indexOf(fromIndex);
|
|
856
|
-
if (fromFree === -1) return layout;
|
|
857
|
-
const toFree = Math.max(
|
|
858
|
-
0,
|
|
859
|
-
Math.min(freeSlots.length - 1, fromFree + Math.trunc(offset))
|
|
860
|
-
);
|
|
861
|
-
return moveWidget(definitions, layout, key, freeSlots[toFree]);
|
|
862
|
-
}
|
|
863
|
-
function moveWidgetUp(definitions, layout, key) {
|
|
864
|
-
return moveWidgetBy(definitions, layout, key, -1);
|
|
865
|
-
}
|
|
866
|
-
function moveWidgetDown(definitions, layout, key) {
|
|
867
|
-
return moveWidgetBy(definitions, layout, key, 1);
|
|
868
|
-
}
|
|
869
|
-
function isHidden(layout, key) {
|
|
870
|
-
return layout.hidden.includes(key);
|
|
871
|
-
}
|
|
872
|
-
function isMinimized(layout, key) {
|
|
873
|
-
return layout.minimized.includes(key);
|
|
874
|
-
}
|
|
875
|
-
function hideWidget(layout, key) {
|
|
876
|
-
if (isHidden(layout, key)) return layout;
|
|
877
|
-
return { ...layout, hidden: [...layout.hidden, key] };
|
|
878
|
-
}
|
|
879
|
-
function showWidget(layout, key) {
|
|
880
|
-
if (!isHidden(layout, key)) return layout;
|
|
881
|
-
return {
|
|
882
|
-
...layout,
|
|
883
|
-
hidden: layout.hidden.filter((k) => k !== key),
|
|
884
|
-
minimized: layout.minimized.filter((k) => k !== key)
|
|
885
|
-
};
|
|
886
|
-
}
|
|
887
|
-
function toggleHidden(layout, key) {
|
|
888
|
-
return isHidden(layout, key) ? showWidget(layout, key) : hideWidget(layout, key);
|
|
889
|
-
}
|
|
890
|
-
function minimizeWidget(layout, key) {
|
|
891
|
-
if (isMinimized(layout, key)) return layout;
|
|
892
|
-
return { ...layout, minimized: [...layout.minimized, key] };
|
|
893
|
-
}
|
|
894
|
-
function restoreWidget(layout, key) {
|
|
895
|
-
if (!isMinimized(layout, key)) return layout;
|
|
896
|
-
return { ...layout, minimized: layout.minimized.filter((k) => k !== key) };
|
|
897
|
-
}
|
|
898
|
-
function toggleMinimized(layout, key) {
|
|
899
|
-
return isMinimized(layout, key) ? restoreWidget(layout, key) : minimizeWidget(layout, key);
|
|
900
|
-
}
|
|
901
|
-
function pruneLayout(definitions, layout) {
|
|
902
|
-
const known = new Set(definitions.map((d) => d.key));
|
|
903
|
-
const keep = (keys) => keys.every((k) => known.has(k)) ? keys : keys.filter((k) => known.has(k));
|
|
904
|
-
return mapLayoutLists(layout, (lists) => ({
|
|
905
|
-
order: keep(lists.order),
|
|
906
|
-
hidden: keep(lists.hidden),
|
|
907
|
-
minimized: keep(lists.minimized)
|
|
908
|
-
}));
|
|
909
|
-
}
|
|
910
|
-
function mapLayoutLists(layout, change) {
|
|
911
|
-
const top = change(layout);
|
|
912
|
-
const pages = layout.pages?.map((page) => {
|
|
913
|
-
const next = change(page);
|
|
914
|
-
return next.order === page.order && next.hidden === page.hidden && next.minimized === page.minimized ? page : { ...page, ...next };
|
|
915
|
-
});
|
|
916
|
-
const pagesChanged = pages !== void 0 && pages.some((page, i) => page !== layout.pages?.[i]);
|
|
917
|
-
if (top.order === layout.order && top.hidden === layout.hidden && top.minimized === layout.minimized && !pagesChanged) {
|
|
918
|
-
return layout;
|
|
919
|
-
}
|
|
920
|
-
return {
|
|
921
|
-
...layout,
|
|
922
|
-
...top,
|
|
923
|
-
...pages === void 0 ? {} : { pages }
|
|
924
|
-
};
|
|
925
|
-
}
|
|
926
|
-
function enforceLocks(definitions, layout, options = {}) {
|
|
927
|
-
if (options.overrideLocks === true) return layout;
|
|
928
|
-
const noMove = /* @__PURE__ */ new Set();
|
|
929
|
-
const noHide = /* @__PURE__ */ new Set();
|
|
930
|
-
const noMinimize = /* @__PURE__ */ new Set();
|
|
931
|
-
for (const definition of definitions) {
|
|
932
|
-
const lock = resolveWidgetLock(definition);
|
|
933
|
-
if (lock.move) noMove.add(definition.key);
|
|
934
|
-
if (lock.hide) noHide.add(definition.key);
|
|
935
|
-
if (lock.minimize) noMinimize.add(definition.key);
|
|
936
|
-
}
|
|
937
|
-
const without2 = (keys, drop) => keys.some((key) => drop.has(key)) ? keys.filter((key) => !drop.has(key)) : keys;
|
|
938
|
-
return mapLayoutLists(layout, (lists) => ({
|
|
939
|
-
order: without2(lists.order, noMove),
|
|
940
|
-
hidden: without2(lists.hidden, noHide),
|
|
941
|
-
minimized: without2(lists.minimized, noMinimize)
|
|
942
|
-
}));
|
|
943
|
-
}
|
|
944
|
-
|
|
945
691
|
// src/core/options.ts
|
|
946
692
|
var DATE_RANGE_PRESETS = [
|
|
947
693
|
"today",
|
|
@@ -1116,107 +862,633 @@ function coerceOptionValue(option, value, context = {}) {
|
|
|
1116
862
|
}
|
|
1117
863
|
}
|
|
1118
864
|
}
|
|
1119
|
-
function defaultOptionValue(option, context = {}) {
|
|
1120
|
-
switch (option.type) {
|
|
1121
|
-
case "dateRange":
|
|
1122
|
-
return resolveDateRange(option.default, context);
|
|
1123
|
-
case "sort":
|
|
1124
|
-
return option.default;
|
|
1125
|
-
case "columns":
|
|
1126
|
-
return [...option.default];
|
|
1127
|
-
default:
|
|
1128
|
-
return option.default;
|
|
1129
|
-
}
|
|
865
|
+
function defaultOptionValue(option, context = {}) {
|
|
866
|
+
switch (option.type) {
|
|
867
|
+
case "dateRange":
|
|
868
|
+
return resolveDateRange(option.default, context);
|
|
869
|
+
case "sort":
|
|
870
|
+
return option.default;
|
|
871
|
+
case "columns":
|
|
872
|
+
return [...option.default];
|
|
873
|
+
default:
|
|
874
|
+
return option.default;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
var NO_VALUES = Object.freeze({});
|
|
878
|
+
function resolveOptionValues(definition, chosen, context = {}) {
|
|
879
|
+
const options = definition.options;
|
|
880
|
+
if (options === void 0 || options.length === 0) return NO_VALUES;
|
|
881
|
+
const values = {};
|
|
882
|
+
for (const option of options) {
|
|
883
|
+
const picked = chosen?.[option.key];
|
|
884
|
+
const value = (picked === void 0 ? void 0 : coerceOptionValue(option, picked, context)) ?? defaultOptionValue(option, context);
|
|
885
|
+
if (value !== void 0) values[option.key] = value;
|
|
886
|
+
}
|
|
887
|
+
return values;
|
|
888
|
+
}
|
|
889
|
+
function sameValue(a, b) {
|
|
890
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
891
|
+
return a.length === b.length && a.every((item, i) => item === b[i]);
|
|
892
|
+
}
|
|
893
|
+
return a === b;
|
|
894
|
+
}
|
|
895
|
+
function sameOptionValues(a, b) {
|
|
896
|
+
const keys = Object.keys({ ...a, ...b });
|
|
897
|
+
return keys.every((key) => sameValue(a[key], b[key]));
|
|
898
|
+
}
|
|
899
|
+
function optionsCacheSuffix(definition, values, context = {}) {
|
|
900
|
+
const parts = [];
|
|
901
|
+
for (const option of definition.options ?? []) {
|
|
902
|
+
if (isClientApplied(option)) continue;
|
|
903
|
+
const value = values[option.key];
|
|
904
|
+
if (sameValue(value, defaultOptionValue(option, context))) continue;
|
|
905
|
+
parts.push(
|
|
906
|
+
`${option.key}=${encodeURIComponent(JSON.stringify(value ?? null))}`
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
parts.sort();
|
|
910
|
+
return parts.length === 0 ? "" : `?${parts.join("&")}`;
|
|
911
|
+
}
|
|
912
|
+
function tableColumnIndex(key) {
|
|
913
|
+
const match = /^c(\d+)$/.exec(key);
|
|
914
|
+
return match === null ? void 0 : Number(match[1]);
|
|
915
|
+
}
|
|
916
|
+
function sortBarItems(items, sort) {
|
|
917
|
+
const sign = sort.direction === "desc" ? -1 : 1;
|
|
918
|
+
return items.map((item, index) => ({ item, index })).sort((a, b) => {
|
|
919
|
+
const order = sort.column === "value" ? a.item.value - b.item.value : a.item.label.localeCompare(b.item.label);
|
|
920
|
+
return order * sign || a.index - b.index;
|
|
921
|
+
}).map(({ item }) => item);
|
|
922
|
+
}
|
|
923
|
+
function applyClientOptions(definition, data, values) {
|
|
924
|
+
const options = definition.options ?? [];
|
|
925
|
+
const sortOption = options.find(
|
|
926
|
+
(o) => o.type === "sort" && o.apply === "client"
|
|
927
|
+
);
|
|
928
|
+
const columnsOption = options.find(
|
|
929
|
+
(o) => o.type === "columns" && o.apply === "client"
|
|
930
|
+
);
|
|
931
|
+
const sortValue = sortOption === void 0 ? void 0 : values[sortOption.key];
|
|
932
|
+
const columnsValue = columnsOption === void 0 ? void 0 : values[columnsOption.key];
|
|
933
|
+
let next = data;
|
|
934
|
+
if (typeof sortValue === "string") {
|
|
935
|
+
const sort = parseSortValue(sortValue);
|
|
936
|
+
if (sort !== void 0 && next.kind === "BAR_LIST") {
|
|
937
|
+
if (sort.column === "label" || sort.column === "value") {
|
|
938
|
+
next = { ...next, items: sortBarItems(next.items, sort) };
|
|
939
|
+
}
|
|
940
|
+
} else if (sort !== void 0 && next.kind === "TABLE") {
|
|
941
|
+
const index = tableColumnIndex(sort.column);
|
|
942
|
+
if (index !== void 0 && index < next.columns.length) {
|
|
943
|
+
const sorted = queryRows(tableDetailData(next), {
|
|
944
|
+
page: 1,
|
|
945
|
+
pageSize: Math.max(1, next.rows.length),
|
|
946
|
+
sort
|
|
947
|
+
});
|
|
948
|
+
if (sorted.ok) next = { ...next, rows: sorted.value.rows };
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
if (Array.isArray(columnsValue) && next.kind === "TABLE") {
|
|
953
|
+
const table = next;
|
|
954
|
+
const indexes = columnsValue.map(tableColumnIndex).filter(
|
|
955
|
+
(index) => index !== void 0 && index < table.columns.length
|
|
956
|
+
);
|
|
957
|
+
if (indexes.length > 0) {
|
|
958
|
+
next = {
|
|
959
|
+
...table,
|
|
960
|
+
columns: indexes.map((i) => table.columns[i]),
|
|
961
|
+
rows: table.rows.map((row) => indexes.map((i) => row[i]))
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
return next;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// src/core/settings.ts
|
|
969
|
+
var MAX_WIDGET_TITLE_LENGTH = 80;
|
|
970
|
+
var MAX_PARSED_SETTINGS = 500;
|
|
971
|
+
var MAX_COLUMN_LIST = 100;
|
|
972
|
+
var MAX_VALUE_LENGTH = 500;
|
|
973
|
+
function isRecord(value) {
|
|
974
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
975
|
+
}
|
|
976
|
+
function parseOptionValue(value) {
|
|
977
|
+
if (typeof value === "string") {
|
|
978
|
+
return value.length <= MAX_VALUE_LENGTH ? value : void 0;
|
|
979
|
+
}
|
|
980
|
+
if (typeof value === "number")
|
|
981
|
+
return Number.isFinite(value) ? value : void 0;
|
|
982
|
+
if (typeof value === "boolean") return value;
|
|
983
|
+
if (Array.isArray(value) && value.length <= MAX_COLUMN_LIST) {
|
|
984
|
+
return value.every(
|
|
985
|
+
(entry) => typeof entry === "string" && entry.length <= MAX_VALUE_LENGTH
|
|
986
|
+
) ? value : void 0;
|
|
987
|
+
}
|
|
988
|
+
return void 0;
|
|
989
|
+
}
|
|
990
|
+
function parseOne(raw) {
|
|
991
|
+
if (!isRecord(raw)) return void 0;
|
|
992
|
+
const title = raw["title"];
|
|
993
|
+
const width = raw["width"];
|
|
994
|
+
const flexible = raw["flexible"];
|
|
995
|
+
const options = raw["options"];
|
|
996
|
+
const cleanTitle = typeof title === "string" ? title.trim().slice(0, MAX_WIDGET_TITLE_LENGTH) : "";
|
|
997
|
+
const chosen = {};
|
|
998
|
+
if (isRecord(options)) {
|
|
999
|
+
for (const [key, value] of Object.entries(options)) {
|
|
1000
|
+
const parsed = parseOptionValue(value);
|
|
1001
|
+
if (parsed !== void 0) chosen[key] = parsed;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
const settings = {
|
|
1005
|
+
...cleanTitle === "" ? {} : { title: cleanTitle },
|
|
1006
|
+
...isWidgetWidth(width) ? { width } : {},
|
|
1007
|
+
...typeof flexible === "boolean" ? { flexible } : {},
|
|
1008
|
+
...Object.keys(chosen).length === 0 ? {} : { options: chosen }
|
|
1009
|
+
};
|
|
1010
|
+
return Object.keys(settings).length === 0 ? void 0 : settings;
|
|
1011
|
+
}
|
|
1012
|
+
function parseSettings(raw) {
|
|
1013
|
+
if (!isRecord(raw)) return void 0;
|
|
1014
|
+
const parsed = {};
|
|
1015
|
+
for (const [key, value] of Object.entries(raw).slice(
|
|
1016
|
+
0,
|
|
1017
|
+
MAX_PARSED_SETTINGS
|
|
1018
|
+
)) {
|
|
1019
|
+
if (key === "") continue;
|
|
1020
|
+
const one = parseOne(value);
|
|
1021
|
+
if (one !== void 0) parsed[key] = one;
|
|
1022
|
+
}
|
|
1023
|
+
return Object.keys(parsed).length === 0 ? void 0 : parsed;
|
|
1024
|
+
}
|
|
1025
|
+
function serializeSettings(settings) {
|
|
1026
|
+
if (settings === void 0) return void 0;
|
|
1027
|
+
const out = {};
|
|
1028
|
+
for (const key of Object.keys(settings)) {
|
|
1029
|
+
const one = settings[key];
|
|
1030
|
+
if (one === void 0) continue;
|
|
1031
|
+
const piece = {
|
|
1032
|
+
...one.title === void 0 ? {} : { title: one.title },
|
|
1033
|
+
...one.width === void 0 ? {} : { width: one.width },
|
|
1034
|
+
...one.flexible === void 0 ? {} : { flexible: one.flexible },
|
|
1035
|
+
...one.options === void 0 || Object.keys(one.options).length === 0 ? {} : { options: { ...one.options } }
|
|
1036
|
+
};
|
|
1037
|
+
if (Object.keys(piece).length > 0) out[key] = piece;
|
|
1038
|
+
}
|
|
1039
|
+
return Object.keys(out).length === 0 ? void 0 : out;
|
|
1040
|
+
}
|
|
1041
|
+
function widthRange(item) {
|
|
1042
|
+
const min = isWidgetWidth(item.minWidth) ? item.minWidth : MIN_WIDGET_WIDTH;
|
|
1043
|
+
const max = isWidgetWidth(item.maxWidth) ? item.maxWidth : MAX_WIDGET_WIDTH;
|
|
1044
|
+
return { min, max: Math.max(min, max) };
|
|
1045
|
+
}
|
|
1046
|
+
var WIDTH_SHARES = {
|
|
1047
|
+
2: "1/6",
|
|
1048
|
+
3: "1/4",
|
|
1049
|
+
4: "1/3",
|
|
1050
|
+
5: "5/12",
|
|
1051
|
+
6: "1/2",
|
|
1052
|
+
7: "7/12",
|
|
1053
|
+
8: "2/3",
|
|
1054
|
+
9: "3/4",
|
|
1055
|
+
10: "5/6",
|
|
1056
|
+
11: "11/12",
|
|
1057
|
+
12: "Full"
|
|
1058
|
+
};
|
|
1059
|
+
function widthShareLabel(width) {
|
|
1060
|
+
return WIDTH_SHARES[width] ?? `${width}/12`;
|
|
1061
|
+
}
|
|
1062
|
+
function isOptionsLocked(item) {
|
|
1063
|
+
return resolveWidgetLock(item).options;
|
|
1064
|
+
}
|
|
1065
|
+
function fillWith(fill, flexible) {
|
|
1066
|
+
const heightToo = fill === "height" || fill === "both";
|
|
1067
|
+
if (flexible) return heightToo ? "both" : "width";
|
|
1068
|
+
return heightToo ? "height" : void 0;
|
|
1069
|
+
}
|
|
1070
|
+
function isFlexible(item, settings) {
|
|
1071
|
+
if (settings?.flexible !== void 0) return settings.flexible;
|
|
1072
|
+
return item.fill === "width" || item.fill === "both";
|
|
1073
|
+
}
|
|
1074
|
+
function applySettings(definition, settings) {
|
|
1075
|
+
if (settings === void 0) return definition;
|
|
1076
|
+
const range = widthRange(definition);
|
|
1077
|
+
const width = settings.width === void 0 ? void 0 : Math.min(range.max, Math.max(range.min, settings.width));
|
|
1078
|
+
const title = settings.title === void 0 || settings.title.trim() === "" ? void 0 : settings.title;
|
|
1079
|
+
const flexibleChanged = settings.flexible !== void 0 && settings.flexible !== isFlexible(definition, void 0);
|
|
1080
|
+
if (title === void 0 && width === void 0 && !flexibleChanged) {
|
|
1081
|
+
return definition;
|
|
1082
|
+
}
|
|
1083
|
+
const next = { ...definition };
|
|
1084
|
+
if (title !== void 0) next.title = title;
|
|
1085
|
+
if (width !== void 0) next.width = width;
|
|
1086
|
+
if (flexibleChanged) {
|
|
1087
|
+
const fill = fillWith(
|
|
1088
|
+
definition.fill,
|
|
1089
|
+
settings.flexible === true
|
|
1090
|
+
);
|
|
1091
|
+
if (fill === void 0) {
|
|
1092
|
+
delete next.fill;
|
|
1093
|
+
} else {
|
|
1094
|
+
next.fill = fill;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
return next;
|
|
1098
|
+
}
|
|
1099
|
+
function effectiveDefinitions(definitions, layout, options = {}) {
|
|
1100
|
+
return definitions.map(
|
|
1101
|
+
(definition) => options.overrideLocks !== true && isOptionsLocked(definition) ? definition : applySettings(definition, layout.settings?.[definition.key])
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
function widgetOptionValues(definition, settings) {
|
|
1105
|
+
const declared = definition.options ?? [];
|
|
1106
|
+
const values = {};
|
|
1107
|
+
for (const option of declared) {
|
|
1108
|
+
const picked = settings?.options?.[option.key];
|
|
1109
|
+
const value = (picked === void 0 ? void 0 : coerceOptionValue(option, picked)) ?? defaultOptionValue(option);
|
|
1110
|
+
if (value !== void 0) values[option.key] = value;
|
|
1111
|
+
}
|
|
1112
|
+
return values;
|
|
1113
|
+
}
|
|
1114
|
+
function optionValuesFromLayout(definitions, layout, options = {}) {
|
|
1115
|
+
const out = {};
|
|
1116
|
+
for (const definition of definitions) {
|
|
1117
|
+
if (definition.options === void 0 || definition.options.length === 0) {
|
|
1118
|
+
continue;
|
|
1119
|
+
}
|
|
1120
|
+
const locked = options.overrideLocks !== true && isOptionsLocked(definition);
|
|
1121
|
+
out[definition.key] = locked ? {} : layout.settings?.[definition.key]?.options ?? {};
|
|
1122
|
+
}
|
|
1123
|
+
return out;
|
|
1124
|
+
}
|
|
1125
|
+
function sameSettings(a, b) {
|
|
1126
|
+
if (a === b) return true;
|
|
1127
|
+
return a?.title === b?.title && a?.width === b?.width && a?.flexible === b?.flexible && sameOptionValues(a?.options ?? {}, b?.options ?? {});
|
|
1128
|
+
}
|
|
1129
|
+
function setWidgetSettings(layout, key, patch) {
|
|
1130
|
+
const before = layout.settings?.[key];
|
|
1131
|
+
const draft = { ...before };
|
|
1132
|
+
for (const field of ["title", "width", "flexible", "options"]) {
|
|
1133
|
+
if (!(field in patch)) continue;
|
|
1134
|
+
const value = patch[field];
|
|
1135
|
+
if (value === void 0) {
|
|
1136
|
+
delete draft[field];
|
|
1137
|
+
} else {
|
|
1138
|
+
draft[field] = value;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
const merged = parseOne(draft);
|
|
1142
|
+
if (sameSettings(before, merged)) return layout;
|
|
1143
|
+
const rest = { ...layout.settings };
|
|
1144
|
+
if (merged === void 0) {
|
|
1145
|
+
delete rest[key];
|
|
1146
|
+
} else {
|
|
1147
|
+
rest[key] = merged;
|
|
1148
|
+
}
|
|
1149
|
+
const { settings: _previous, ...others } = layout;
|
|
1150
|
+
return Object.keys(rest).length === 0 ? others : { ...others, settings: rest };
|
|
1151
|
+
}
|
|
1152
|
+
function clearWidgetSettings(layout, key) {
|
|
1153
|
+
if (layout.settings?.[key] === void 0) return layout;
|
|
1154
|
+
return setWidgetSettings(layout, key, {
|
|
1155
|
+
title: void 0,
|
|
1156
|
+
width: void 0,
|
|
1157
|
+
flexible: void 0,
|
|
1158
|
+
options: void 0
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
function normalizeTitle(title) {
|
|
1162
|
+
return title.normalize("NFKC").trim().toLowerCase();
|
|
1163
|
+
}
|
|
1164
|
+
function isTitleInUse(definitions, layout, key, title) {
|
|
1165
|
+
const wanted = normalizeTitle(title);
|
|
1166
|
+
if (wanted === "") return false;
|
|
1167
|
+
return effectiveDefinitions(definitions, layout, {
|
|
1168
|
+
overrideLocks: true
|
|
1169
|
+
}).some(
|
|
1170
|
+
(other) => other.key !== key && normalizeTitle(other.title ?? other.key) === wanted
|
|
1171
|
+
);
|
|
1172
|
+
}
|
|
1173
|
+
function uniqueTitle(taken, title) {
|
|
1174
|
+
if (!taken.has(normalizeTitle(title))) return title;
|
|
1175
|
+
for (let n = 2; ; n += 1) {
|
|
1176
|
+
const suffix = ` (#${n})`;
|
|
1177
|
+
const candidate = `${title.slice(0, MAX_WIDGET_TITLE_LENGTH - suffix.length)}${suffix}`;
|
|
1178
|
+
if (!taken.has(normalizeTitle(candidate))) return candidate;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
function normalizeSettings(definitions, layout, options = {}) {
|
|
1182
|
+
const settings = layout.settings;
|
|
1183
|
+
if (settings === void 0) return layout;
|
|
1184
|
+
const cleaned = {};
|
|
1185
|
+
const taken = /* @__PURE__ */ new Set();
|
|
1186
|
+
for (const definition of definitions) {
|
|
1187
|
+
const renamed = settings[definition.key]?.title !== void 0 && (options.overrideLocks === true || !isOptionsLocked(definition));
|
|
1188
|
+
if (!renamed) taken.add(normalizeTitle(definition.title ?? definition.key));
|
|
1189
|
+
}
|
|
1190
|
+
for (const definition of definitions) {
|
|
1191
|
+
const one = settings[definition.key];
|
|
1192
|
+
if (one === void 0) continue;
|
|
1193
|
+
if (options.overrideLocks !== true && isOptionsLocked(definition)) continue;
|
|
1194
|
+
const range = widthRange(definition);
|
|
1195
|
+
const chosen = {};
|
|
1196
|
+
for (const option of definition.options ?? []) {
|
|
1197
|
+
const picked = one.options?.[option.key];
|
|
1198
|
+
if (picked === void 0) continue;
|
|
1199
|
+
const value = coerceOptionValue(option, picked);
|
|
1200
|
+
if (value !== void 0) {
|
|
1201
|
+
chosen[option.key] = option.type === "dateRange" ? picked : value;
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
let title = one.title;
|
|
1205
|
+
if (title !== void 0) {
|
|
1206
|
+
title = title.trim().slice(0, MAX_WIDGET_TITLE_LENGTH);
|
|
1207
|
+
if (title === "") {
|
|
1208
|
+
title = void 0;
|
|
1209
|
+
} else {
|
|
1210
|
+
title = uniqueTitle(taken, title);
|
|
1211
|
+
taken.add(normalizeTitle(title));
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
const next = {
|
|
1215
|
+
...title === void 0 ? {} : { title },
|
|
1216
|
+
...one.width === void 0 ? {} : { width: Math.min(range.max, Math.max(range.min, one.width)) },
|
|
1217
|
+
...one.flexible === void 0 ? {} : { flexible: one.flexible },
|
|
1218
|
+
...Object.keys(chosen).length === 0 ? {} : { options: chosen }
|
|
1219
|
+
};
|
|
1220
|
+
if (Object.keys(next).length > 0) {
|
|
1221
|
+
cleaned[definition.key] = sameSettings(one, next) ? one : next;
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
const keys = Object.keys(cleaned);
|
|
1225
|
+
const unchanged = keys.length === Object.keys(settings).length && keys.every((key) => cleaned[key] === settings[key]);
|
|
1226
|
+
if (unchanged) return layout;
|
|
1227
|
+
const { settings: _previous, ...others } = layout;
|
|
1228
|
+
return keys.length === 0 ? others : { ...others, settings: cleaned };
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
// src/core/layout.ts
|
|
1232
|
+
var MAX_PAGE_TITLE_LENGTH = 30;
|
|
1233
|
+
var MAX_PARSED_PAGES = 50;
|
|
1234
|
+
var MAX_PAGE_ROWS = 20;
|
|
1235
|
+
var EMPTY_LAYOUT = Object.freeze({
|
|
1236
|
+
order: Object.freeze([]),
|
|
1237
|
+
hidden: Object.freeze([]),
|
|
1238
|
+
minimized: Object.freeze([])
|
|
1239
|
+
});
|
|
1240
|
+
function uniqueStrings(value) {
|
|
1241
|
+
if (!Array.isArray(value)) return [];
|
|
1242
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1243
|
+
for (const entry of value) {
|
|
1244
|
+
if (typeof entry === "string" && entry !== "") seen.add(entry);
|
|
1245
|
+
}
|
|
1246
|
+
return [...seen];
|
|
1247
|
+
}
|
|
1248
|
+
function parseLayout(raw) {
|
|
1249
|
+
let value = raw;
|
|
1250
|
+
if (typeof raw === "string") {
|
|
1251
|
+
try {
|
|
1252
|
+
value = JSON.parse(raw);
|
|
1253
|
+
} catch {
|
|
1254
|
+
return EMPTY_LAYOUT;
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1258
|
+
return EMPTY_LAYOUT;
|
|
1259
|
+
}
|
|
1260
|
+
const record = value;
|
|
1261
|
+
const order = uniqueStrings(record["order"]);
|
|
1262
|
+
const hidden = uniqueStrings(record["hidden"]);
|
|
1263
|
+
const minimized = uniqueStrings(record["minimized"]);
|
|
1264
|
+
const pages = parsePages(record["pages"]);
|
|
1265
|
+
const settings = parseSettings(
|
|
1266
|
+
record["settings"]
|
|
1267
|
+
);
|
|
1268
|
+
const withSettings = settings === void 0 ? {} : { settings };
|
|
1269
|
+
if (pages.length > 0) {
|
|
1270
|
+
return {
|
|
1271
|
+
order: EMPTY_LAYOUT.order,
|
|
1272
|
+
hidden: EMPTY_LAYOUT.hidden,
|
|
1273
|
+
minimized: EMPTY_LAYOUT.minimized,
|
|
1274
|
+
pages,
|
|
1275
|
+
...withSettings
|
|
1276
|
+
};
|
|
1277
|
+
}
|
|
1278
|
+
if (order.length === 0 && hidden.length === 0 && minimized.length === 0) {
|
|
1279
|
+
return settings === void 0 ? EMPTY_LAYOUT : { ...EMPTY_LAYOUT, settings };
|
|
1280
|
+
}
|
|
1281
|
+
return { order, hidden, minimized, ...withSettings };
|
|
1282
|
+
}
|
|
1283
|
+
function parsePages(value) {
|
|
1284
|
+
if (!Array.isArray(value)) return [];
|
|
1285
|
+
const pages = [];
|
|
1286
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1287
|
+
for (const entry of value.slice(0, MAX_PARSED_PAGES)) {
|
|
1288
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
1289
|
+
continue;
|
|
1290
|
+
}
|
|
1291
|
+
const record = entry;
|
|
1292
|
+
const key = record["key"];
|
|
1293
|
+
if (typeof key !== "string" || key === "" || keys.has(key)) continue;
|
|
1294
|
+
keys.add(key);
|
|
1295
|
+
const rawTitle = record["title"];
|
|
1296
|
+
const title = typeof rawTitle === "string" && rawTitle.trim() !== "" ? rawTitle.trim().slice(0, MAX_PAGE_TITLE_LENGTH) : `Page ${pages.length + 1}`;
|
|
1297
|
+
const maxRows = record["maxRows"];
|
|
1298
|
+
const held = /* @__PURE__ */ new Set();
|
|
1299
|
+
for (const earlier of pages) {
|
|
1300
|
+
for (const k of [...earlier.order, ...earlier.hidden]) held.add(k);
|
|
1301
|
+
}
|
|
1302
|
+
const keep = (list) => uniqueStrings(list).filter((k) => !held.has(k));
|
|
1303
|
+
pages.push({
|
|
1304
|
+
key,
|
|
1305
|
+
title,
|
|
1306
|
+
order: keep(record["order"]),
|
|
1307
|
+
hidden: keep(record["hidden"]),
|
|
1308
|
+
minimized: keep(record["minimized"]),
|
|
1309
|
+
...typeof maxRows === "number" && Number.isInteger(maxRows) && maxRows >= 1 && maxRows <= MAX_PAGE_ROWS ? { maxRows } : {}
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
return pages;
|
|
1313
|
+
}
|
|
1314
|
+
function serializeLayout(layout) {
|
|
1315
|
+
return JSON.stringify({
|
|
1316
|
+
order: [...layout.order],
|
|
1317
|
+
hidden: [...layout.hidden],
|
|
1318
|
+
minimized: [...layout.minimized],
|
|
1319
|
+
...layout.pages === void 0 || layout.pages.length === 0 ? {} : {
|
|
1320
|
+
pages: layout.pages.map((page) => ({
|
|
1321
|
+
key: page.key,
|
|
1322
|
+
title: page.title,
|
|
1323
|
+
order: [...page.order],
|
|
1324
|
+
hidden: [...page.hidden],
|
|
1325
|
+
minimized: [...page.minimized],
|
|
1326
|
+
...page.maxRows === void 0 ? {} : { maxRows: page.maxRows }
|
|
1327
|
+
}))
|
|
1328
|
+
},
|
|
1329
|
+
...serializeSettings(layout.settings) === void 0 ? {} : { settings: serializeSettings(layout.settings) }
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
function bySortOrder(items) {
|
|
1333
|
+
return items.map((item, index) => ({ item, index })).sort(
|
|
1334
|
+
(a, b) => (a.item.sortOrder ?? 0) - (b.item.sortOrder ?? 0) || a.index - b.index
|
|
1335
|
+
).map(({ item }) => item);
|
|
1336
|
+
}
|
|
1337
|
+
function isPinned(item) {
|
|
1338
|
+
return resolveWidgetLock(item).move;
|
|
1339
|
+
}
|
|
1340
|
+
function viewerOrder(visible, layout) {
|
|
1341
|
+
const byKey = new Map(visible.map((d) => [d.key, d]));
|
|
1342
|
+
const ordered = [];
|
|
1343
|
+
for (const key of layout.order) {
|
|
1344
|
+
const definition = byKey.get(key);
|
|
1345
|
+
if (definition !== void 0) {
|
|
1346
|
+
ordered.push(definition);
|
|
1347
|
+
byKey.delete(key);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
for (const definition of bySortOrder(visible)) {
|
|
1351
|
+
if (byKey.has(definition.key)) ordered.push(definition);
|
|
1352
|
+
}
|
|
1353
|
+
return ordered;
|
|
1354
|
+
}
|
|
1355
|
+
function placeAroundPinned(visible, free) {
|
|
1356
|
+
const slots = bySortOrder(visible).map(
|
|
1357
|
+
(item) => isPinned(item) ? item : void 0
|
|
1358
|
+
);
|
|
1359
|
+
let next = 0;
|
|
1360
|
+
return slots.map((pinned) => pinned ?? free[next++]);
|
|
1361
|
+
}
|
|
1362
|
+
function visibleWidgets(definitions, layout) {
|
|
1363
|
+
const hidden = new Set(layout.hidden);
|
|
1364
|
+
const visible = definitions.filter((d) => !hidden.has(d.key));
|
|
1365
|
+
const ordered = viewerOrder(visible, layout);
|
|
1366
|
+
if (!visible.some(isPinned)) return ordered;
|
|
1367
|
+
return placeAroundPinned(
|
|
1368
|
+
visible,
|
|
1369
|
+
ordered.filter((item) => !isPinned(item))
|
|
1370
|
+
);
|
|
1371
|
+
}
|
|
1372
|
+
function hiddenWidgets(definitions, layout) {
|
|
1373
|
+
const hidden = new Set(layout.hidden);
|
|
1374
|
+
return bySortOrder(definitions.filter((d) => hidden.has(d.key)));
|
|
1375
|
+
}
|
|
1376
|
+
function moveWidget(definitions, layout, key, toIndex) {
|
|
1377
|
+
const current = visibleWidgets(definitions, layout);
|
|
1378
|
+
const fromIndex = current.findIndex((d) => d.key === key);
|
|
1379
|
+
const mover = current[fromIndex];
|
|
1380
|
+
if (mover === void 0 || !Number.isInteger(toIndex) || toIndex < 0 || toIndex >= current.length || toIndex === fromIndex || isPinned(mover) || isPinned(current[toIndex])) {
|
|
1381
|
+
return layout;
|
|
1382
|
+
}
|
|
1383
|
+
const freeSlots = freeSlotIndexes(current);
|
|
1384
|
+
const free = current.filter((item) => !isPinned(item));
|
|
1385
|
+
const fromFree = freeSlots.indexOf(fromIndex);
|
|
1386
|
+
const toFree = freeSlots.indexOf(toIndex);
|
|
1387
|
+
free.splice(fromFree, 1);
|
|
1388
|
+
free.splice(toFree, 0, mover);
|
|
1389
|
+
const next = placeAroundPinned(current, free);
|
|
1390
|
+
return { ...layout, order: next.map((d) => d.key) };
|
|
1391
|
+
}
|
|
1392
|
+
function freeSlotIndexes(ordered) {
|
|
1393
|
+
const slots = [];
|
|
1394
|
+
ordered.forEach((item, index) => {
|
|
1395
|
+
if (!isPinned(item)) slots.push(index);
|
|
1396
|
+
});
|
|
1397
|
+
return slots;
|
|
1398
|
+
}
|
|
1399
|
+
function moveWidgetBy(definitions, layout, key, offset) {
|
|
1400
|
+
const current = visibleWidgets(definitions, layout);
|
|
1401
|
+
const fromIndex = current.findIndex((d) => d.key === key);
|
|
1402
|
+
if (fromIndex === -1) return layout;
|
|
1403
|
+
const freeSlots = freeSlotIndexes(current);
|
|
1404
|
+
const fromFree = freeSlots.indexOf(fromIndex);
|
|
1405
|
+
if (fromFree === -1) return layout;
|
|
1406
|
+
const toFree = Math.max(
|
|
1407
|
+
0,
|
|
1408
|
+
Math.min(freeSlots.length - 1, fromFree + Math.trunc(offset))
|
|
1409
|
+
);
|
|
1410
|
+
return moveWidget(definitions, layout, key, freeSlots[toFree]);
|
|
1411
|
+
}
|
|
1412
|
+
function moveWidgetUp(definitions, layout, key) {
|
|
1413
|
+
return moveWidgetBy(definitions, layout, key, -1);
|
|
1130
1414
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
const options = definition.options;
|
|
1134
|
-
if (options === void 0 || options.length === 0) return NO_VALUES;
|
|
1135
|
-
const values = {};
|
|
1136
|
-
for (const option of options) {
|
|
1137
|
-
const picked = chosen?.[option.key];
|
|
1138
|
-
const value = (picked === void 0 ? void 0 : coerceOptionValue(option, picked, context)) ?? defaultOptionValue(option, context);
|
|
1139
|
-
if (value !== void 0) values[option.key] = value;
|
|
1140
|
-
}
|
|
1141
|
-
return values;
|
|
1415
|
+
function moveWidgetDown(definitions, layout, key) {
|
|
1416
|
+
return moveWidgetBy(definitions, layout, key, 1);
|
|
1142
1417
|
}
|
|
1143
|
-
function
|
|
1144
|
-
|
|
1145
|
-
return a.length === b.length && a.every((item, i) => item === b[i]);
|
|
1146
|
-
}
|
|
1147
|
-
return a === b;
|
|
1418
|
+
function isHidden(layout, key) {
|
|
1419
|
+
return layout.hidden.includes(key);
|
|
1148
1420
|
}
|
|
1149
|
-
function
|
|
1150
|
-
|
|
1151
|
-
return keys.every((key) => sameValue(a[key], b[key]));
|
|
1421
|
+
function isMinimized(layout, key) {
|
|
1422
|
+
return layout.minimized.includes(key);
|
|
1152
1423
|
}
|
|
1153
|
-
function
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
if (isClientApplied(option)) continue;
|
|
1157
|
-
const value = values[option.key];
|
|
1158
|
-
if (sameValue(value, defaultOptionValue(option, context))) continue;
|
|
1159
|
-
parts.push(
|
|
1160
|
-
`${option.key}=${encodeURIComponent(JSON.stringify(value ?? null))}`
|
|
1161
|
-
);
|
|
1162
|
-
}
|
|
1163
|
-
parts.sort();
|
|
1164
|
-
return parts.length === 0 ? "" : `?${parts.join("&")}`;
|
|
1424
|
+
function hideWidget(layout, key) {
|
|
1425
|
+
if (isHidden(layout, key)) return layout;
|
|
1426
|
+
return { ...layout, hidden: [...layout.hidden, key] };
|
|
1165
1427
|
}
|
|
1166
|
-
function
|
|
1167
|
-
|
|
1168
|
-
return
|
|
1428
|
+
function showWidget(layout, key) {
|
|
1429
|
+
if (!isHidden(layout, key)) return layout;
|
|
1430
|
+
return {
|
|
1431
|
+
...layout,
|
|
1432
|
+
hidden: layout.hidden.filter((k) => k !== key),
|
|
1433
|
+
minimized: layout.minimized.filter((k) => k !== key)
|
|
1434
|
+
};
|
|
1169
1435
|
}
|
|
1170
|
-
function
|
|
1171
|
-
|
|
1172
|
-
return items.map((item, index) => ({ item, index })).sort((a, b) => {
|
|
1173
|
-
const order = sort.column === "value" ? a.item.value - b.item.value : a.item.label.localeCompare(b.item.label);
|
|
1174
|
-
return order * sign || a.index - b.index;
|
|
1175
|
-
}).map(({ item }) => item);
|
|
1436
|
+
function toggleHidden(layout, key) {
|
|
1437
|
+
return isHidden(layout, key) ? showWidget(layout, key) : hideWidget(layout, key);
|
|
1176
1438
|
}
|
|
1177
|
-
function
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1439
|
+
function minimizeWidget(layout, key) {
|
|
1440
|
+
if (isMinimized(layout, key)) return layout;
|
|
1441
|
+
return { ...layout, minimized: [...layout.minimized, key] };
|
|
1442
|
+
}
|
|
1443
|
+
function restoreWidget(layout, key) {
|
|
1444
|
+
if (!isMinimized(layout, key)) return layout;
|
|
1445
|
+
return { ...layout, minimized: layout.minimized.filter((k) => k !== key) };
|
|
1446
|
+
}
|
|
1447
|
+
function toggleMinimized(layout, key) {
|
|
1448
|
+
return isMinimized(layout, key) ? restoreWidget(layout, key) : minimizeWidget(layout, key);
|
|
1449
|
+
}
|
|
1450
|
+
function pruneLayout(definitions, layout) {
|
|
1451
|
+
const known = new Set(definitions.map((d) => d.key));
|
|
1452
|
+
const keep = (keys) => keys.every((k) => known.has(k)) ? keys : keys.filter((k) => known.has(k));
|
|
1453
|
+
return mapLayoutLists(layout, (lists) => ({
|
|
1454
|
+
order: keep(lists.order),
|
|
1455
|
+
hidden: keep(lists.hidden),
|
|
1456
|
+
minimized: keep(lists.minimized)
|
|
1457
|
+
}));
|
|
1458
|
+
}
|
|
1459
|
+
function mapLayoutLists(layout, change) {
|
|
1460
|
+
const top = change(layout);
|
|
1461
|
+
const pages = layout.pages?.map((page) => {
|
|
1462
|
+
const next = change(page);
|
|
1463
|
+
return next.order === page.order && next.hidden === page.hidden && next.minimized === page.minimized ? page : { ...page, ...next };
|
|
1464
|
+
});
|
|
1465
|
+
const pagesChanged = pages !== void 0 && pages.some((page, i) => page !== layout.pages?.[i]);
|
|
1466
|
+
if (top.order === layout.order && top.hidden === layout.hidden && top.minimized === layout.minimized && !pagesChanged) {
|
|
1467
|
+
return layout;
|
|
1205
1468
|
}
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1469
|
+
return {
|
|
1470
|
+
...layout,
|
|
1471
|
+
...top,
|
|
1472
|
+
...pages === void 0 ? {} : { pages }
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
function enforceLocks(definitions, layout, options = {}) {
|
|
1476
|
+
if (options.overrideLocks === true) return layout;
|
|
1477
|
+
const noMove = /* @__PURE__ */ new Set();
|
|
1478
|
+
const noHide = /* @__PURE__ */ new Set();
|
|
1479
|
+
const noMinimize = /* @__PURE__ */ new Set();
|
|
1480
|
+
for (const definition of definitions) {
|
|
1481
|
+
const lock = resolveWidgetLock(definition);
|
|
1482
|
+
if (lock.move) noMove.add(definition.key);
|
|
1483
|
+
if (lock.hide) noHide.add(definition.key);
|
|
1484
|
+
if (lock.minimize) noMinimize.add(definition.key);
|
|
1218
1485
|
}
|
|
1219
|
-
|
|
1486
|
+
const without2 = (keys, drop) => keys.some((key) => drop.has(key)) ? keys.filter((key) => !drop.has(key)) : keys;
|
|
1487
|
+
return mapLayoutLists(layout, (lists) => ({
|
|
1488
|
+
order: without2(lists.order, noMove),
|
|
1489
|
+
hidden: without2(lists.hidden, noHide),
|
|
1490
|
+
minimized: without2(lists.minimized, noMinimize)
|
|
1491
|
+
}));
|
|
1220
1492
|
}
|
|
1221
1493
|
|
|
1222
1494
|
// src/core/validate.ts
|
|
@@ -1229,7 +1501,7 @@ function describeType2(value) {
|
|
|
1229
1501
|
}
|
|
1230
1502
|
return typeof value;
|
|
1231
1503
|
}
|
|
1232
|
-
function
|
|
1504
|
+
function isRecord2(value) {
|
|
1233
1505
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1234
1506
|
}
|
|
1235
1507
|
function join(path, field) {
|
|
@@ -1311,7 +1583,7 @@ function requireOneOf(record, field, options, path, problems) {
|
|
|
1311
1583
|
function eachRecord(items, path, problems, check) {
|
|
1312
1584
|
items.forEach((item, index) => {
|
|
1313
1585
|
const itemPath = `${path}[${index}]`;
|
|
1314
|
-
if (
|
|
1586
|
+
if (isRecord2(item)) {
|
|
1315
1587
|
check(item, itemPath);
|
|
1316
1588
|
} else {
|
|
1317
1589
|
problems.push(
|
|
@@ -1460,7 +1732,7 @@ function checkPayloadFields(kind, data, problems) {
|
|
|
1460
1732
|
}
|
|
1461
1733
|
function checkDetailOption(detail, problems) {
|
|
1462
1734
|
if (detail === void 0 || typeof detail === "boolean") return;
|
|
1463
|
-
if (!
|
|
1735
|
+
if (!isRecord2(detail)) {
|
|
1464
1736
|
problems.push(
|
|
1465
1737
|
`detail must be true, false or an options object, got ${describeType2(detail)}.`
|
|
1466
1738
|
);
|
|
@@ -1479,13 +1751,13 @@ function checkDetailOption(detail, problems) {
|
|
|
1479
1751
|
}
|
|
1480
1752
|
function checkTableControlsOption(controls, kind, problems) {
|
|
1481
1753
|
if (controls === void 0 || controls === false) return;
|
|
1482
|
-
if (typeof controls !== "boolean" && !
|
|
1754
|
+
if (typeof controls !== "boolean" && !isRecord2(controls)) {
|
|
1483
1755
|
problems.push(
|
|
1484
1756
|
`tableControls must be true, false or an options object, got ${describeType2(controls)}.`
|
|
1485
1757
|
);
|
|
1486
1758
|
return;
|
|
1487
1759
|
}
|
|
1488
|
-
if (
|
|
1760
|
+
if (isRecord2(controls)) {
|
|
1489
1761
|
optionalBoolean(controls, "search", "tableControls", problems);
|
|
1490
1762
|
optionalBoolean(controls, "sort", "tableControls", problems);
|
|
1491
1763
|
}
|
|
@@ -1495,9 +1767,39 @@ function checkTableControlsOption(controls, kind, problems) {
|
|
|
1495
1767
|
);
|
|
1496
1768
|
}
|
|
1497
1769
|
}
|
|
1770
|
+
function checkWidthLimits(value, problems) {
|
|
1771
|
+
for (const field of ["minWidth", "maxWidth"]) {
|
|
1772
|
+
const limit = value[field];
|
|
1773
|
+
if (limit !== void 0 && !isWidgetWidth(limit)) {
|
|
1774
|
+
problems.push(
|
|
1775
|
+
`${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)}.`
|
|
1776
|
+
);
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
const { minWidth, maxWidth, width } = value;
|
|
1781
|
+
if (isWidgetWidth(minWidth) && isWidgetWidth(maxWidth) && minWidth > maxWidth) {
|
|
1782
|
+
problems.push(
|
|
1783
|
+
`minWidth (${minWidth}) must not be more than maxWidth (${maxWidth}).`
|
|
1784
|
+
);
|
|
1785
|
+
return;
|
|
1786
|
+
}
|
|
1787
|
+
if (isWidgetWidth(width)) {
|
|
1788
|
+
if (isWidgetWidth(minWidth) && width < minWidth) {
|
|
1789
|
+
problems.push(
|
|
1790
|
+
`width (${width}) must not be less than minWidth (${minWidth}).`
|
|
1791
|
+
);
|
|
1792
|
+
}
|
|
1793
|
+
if (isWidgetWidth(maxWidth) && width > maxWidth) {
|
|
1794
|
+
problems.push(
|
|
1795
|
+
`width (${width}) must not be more than maxWidth (${maxWidth}).`
|
|
1796
|
+
);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1498
1800
|
function checkLockedOption(locked, problems) {
|
|
1499
1801
|
if (locked === void 0 || typeof locked === "boolean") return;
|
|
1500
|
-
if (!
|
|
1802
|
+
if (!isRecord2(locked)) {
|
|
1501
1803
|
problems.push(
|
|
1502
1804
|
`locked must be true, false or an object, got ${describeType2(locked)}.`
|
|
1503
1805
|
);
|
|
@@ -1506,6 +1808,7 @@ function checkLockedOption(locked, problems) {
|
|
|
1506
1808
|
optionalBoolean(locked, "move", "locked", problems);
|
|
1507
1809
|
optionalBoolean(locked, "hide", "locked", problems);
|
|
1508
1810
|
optionalBoolean(locked, "minimize", "locked", problems);
|
|
1811
|
+
optionalBoolean(locked, "options", "locked", problems);
|
|
1509
1812
|
}
|
|
1510
1813
|
function checkOptionColumns(option, path, kind, problems) {
|
|
1511
1814
|
const columns = option["columns"];
|
|
@@ -1518,7 +1821,7 @@ function checkOptionColumns(option, path, kind, problems) {
|
|
|
1518
1821
|
const keys = [];
|
|
1519
1822
|
columns.forEach((entry, index) => {
|
|
1520
1823
|
const entryPath = `${path}.columns[${index}]`;
|
|
1521
|
-
if (!
|
|
1824
|
+
if (!isRecord2(entry)) {
|
|
1522
1825
|
problems.push(
|
|
1523
1826
|
`${entryPath} must be an object, got ${describeType2(entry)}.`
|
|
1524
1827
|
);
|
|
@@ -1543,7 +1846,7 @@ function checkOptionColumns(option, path, kind, problems) {
|
|
|
1543
1846
|
return keys;
|
|
1544
1847
|
}
|
|
1545
1848
|
function checkOption(option, path, kind, seenKeys, seenTypes, problems) {
|
|
1546
|
-
if (!
|
|
1849
|
+
if (!isRecord2(option)) {
|
|
1547
1850
|
problems.push(`${path} must be an object, got ${describeType2(option)}.`);
|
|
1548
1851
|
return;
|
|
1549
1852
|
}
|
|
@@ -1575,7 +1878,7 @@ function checkOption(option, path, kind, seenKeys, seenTypes, problems) {
|
|
|
1575
1878
|
} else {
|
|
1576
1879
|
choices.forEach((choice, index) => {
|
|
1577
1880
|
const choicePath = `${path}.choices[${index}]`;
|
|
1578
|
-
if (!
|
|
1881
|
+
if (!isRecord2(choice)) {
|
|
1579
1882
|
problems.push(
|
|
1580
1883
|
`${choicePath} must be an object, got ${describeType2(choice)}.`
|
|
1581
1884
|
);
|
|
@@ -1665,7 +1968,7 @@ function checkOption(option, path, kind, seenKeys, seenTypes, problems) {
|
|
|
1665
1968
|
} else {
|
|
1666
1969
|
presets.forEach((preset, index) => {
|
|
1667
1970
|
const presetPath = `${path}.presets[${index}]`;
|
|
1668
|
-
if (!
|
|
1971
|
+
if (!isRecord2(preset)) {
|
|
1669
1972
|
problems.push(
|
|
1670
1973
|
`${presetPath} must be an object, got ${describeType2(preset)}.`
|
|
1671
1974
|
);
|
|
@@ -1792,7 +2095,7 @@ function formatProblems(prefix, problems) {
|
|
|
1792
2095
|
}
|
|
1793
2096
|
function validateWidgetData(value, options = {}) {
|
|
1794
2097
|
const subject = options.widgetKey === void 0 ? "Widget data" : `Widget ${JSON.stringify(options.widgetKey)}`;
|
|
1795
|
-
if (!
|
|
2098
|
+
if (!isRecord2(value)) {
|
|
1796
2099
|
return err(`${subject}: must be an object, got ${describeType2(value)}.`);
|
|
1797
2100
|
}
|
|
1798
2101
|
if (value["empty"] === true) {
|
|
@@ -1822,7 +2125,7 @@ function validateWidgetData(value, options = {}) {
|
|
|
1822
2125
|
return ok(value);
|
|
1823
2126
|
}
|
|
1824
2127
|
function validateWidgetDefinition(value) {
|
|
1825
|
-
if (!
|
|
2128
|
+
if (!isRecord2(value)) {
|
|
1826
2129
|
return err(
|
|
1827
2130
|
`Widget definition: must be an object, got ${describeType2(value)}.`
|
|
1828
2131
|
);
|
|
@@ -1856,6 +2159,7 @@ function validateWidgetDefinition(value) {
|
|
|
1856
2159
|
`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"])}.`
|
|
1857
2160
|
);
|
|
1858
2161
|
}
|
|
2162
|
+
checkWidthLimits(value, problems);
|
|
1859
2163
|
checkDetailOption(value["detail"], problems);
|
|
1860
2164
|
checkTableControlsOption(value["tableControls"], value["kind"], problems);
|
|
1861
2165
|
checkLockedOption(value["locked"], problems);
|
|
@@ -2517,7 +2821,7 @@ function withoutLockField(definition) {
|
|
|
2517
2821
|
delete copy.locked;
|
|
2518
2822
|
return copy;
|
|
2519
2823
|
}
|
|
2520
|
-
function
|
|
2824
|
+
function uniqueTitle2(taken, title, index) {
|
|
2521
2825
|
let base = title.trim().slice(0, MAX_PAGE_TITLE_LENGTH);
|
|
2522
2826
|
if (base === "") base = `Page ${index + 1}`;
|
|
2523
2827
|
let candidate = base;
|
|
@@ -2538,9 +2842,18 @@ function samePages(a, b) {
|
|
|
2538
2842
|
}
|
|
2539
2843
|
function normalizeLayout(definitions, layout, options = {}) {
|
|
2540
2844
|
const known = pruneLayout(definitions, layout);
|
|
2541
|
-
const
|
|
2845
|
+
const enforced = enforceLocks(definitions, known, options);
|
|
2846
|
+
const locked = normalizeSettings(
|
|
2847
|
+
definitions,
|
|
2848
|
+
enforced,
|
|
2849
|
+
options
|
|
2850
|
+
);
|
|
2542
2851
|
if (!hasPages(locked)) return locked === layout ? layout : locked;
|
|
2543
|
-
const defs =
|
|
2852
|
+
const defs = effectiveDefinitions(
|
|
2853
|
+
options.overrideLocks === true ? definitions.map(withoutLockField) : definitions,
|
|
2854
|
+
locked,
|
|
2855
|
+
options
|
|
2856
|
+
);
|
|
2544
2857
|
const taken = /* @__PURE__ */ new Set();
|
|
2545
2858
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
2546
2859
|
const held = /* @__PURE__ */ new Set();
|
|
@@ -2553,7 +2866,7 @@ function normalizeLayout(definitions, layout, options = {}) {
|
|
|
2553
2866
|
];
|
|
2554
2867
|
const next = {
|
|
2555
2868
|
...page,
|
|
2556
|
-
title:
|
|
2869
|
+
title: uniqueTitle2(taken, page.title, index),
|
|
2557
2870
|
order: keep(page.order),
|
|
2558
2871
|
hidden: keep(page.hidden),
|
|
2559
2872
|
minimized: keep(page.minimized)
|
|
@@ -2641,7 +2954,7 @@ function normalizeLayout(definitions, layout, options = {}) {
|
|
|
2641
2954
|
}
|
|
2642
2955
|
if (!placed) {
|
|
2643
2956
|
const key = nextPageKey(work());
|
|
2644
|
-
const title =
|
|
2957
|
+
const title = uniqueTitle2(
|
|
2645
2958
|
new Set(pages.map((p) => normalizedTitle(p.title))),
|
|
2646
2959
|
`Page ${pages.length + 1}`,
|
|
2647
2960
|
pages.length
|
|
@@ -2653,7 +2966,7 @@ function normalizeLayout(definitions, layout, options = {}) {
|
|
|
2653
2966
|
}
|
|
2654
2967
|
}
|
|
2655
2968
|
const result = work();
|
|
2656
|
-
return samePages(layout.pages ?? [], pages) && sameKeys(layout.order, result.order) ? layout : result;
|
|
2969
|
+
return samePages(layout.pages ?? [], pages) && sameKeys(layout.order, result.order) && locked.settings === layout.settings ? layout : result;
|
|
2657
2970
|
}
|
|
2658
2971
|
|
|
2659
2972
|
// src/core/scale.ts
|
|
@@ -3236,6 +3549,255 @@ function createLayoutPersistence(adapter, options = {}) {
|
|
|
3236
3549
|
};
|
|
3237
3550
|
}
|
|
3238
3551
|
|
|
3552
|
+
// src/core/theme.ts
|
|
3553
|
+
var THEME_TOKEN_VARIABLES = {
|
|
3554
|
+
font: "--dwt-font",
|
|
3555
|
+
surface: "--dwt-surface",
|
|
3556
|
+
page: "--dwt-page",
|
|
3557
|
+
text: "--dwt-text",
|
|
3558
|
+
textSecondary: "--dwt-text-secondary",
|
|
3559
|
+
textMuted: "--dwt-text-muted",
|
|
3560
|
+
border: "--dwt-border",
|
|
3561
|
+
grid: "--dwt-grid",
|
|
3562
|
+
baseline: "--dwt-baseline",
|
|
3563
|
+
crosshair: "--dwt-crosshair",
|
|
3564
|
+
link: "--dwt-link",
|
|
3565
|
+
focus: "--dwt-focus",
|
|
3566
|
+
good: "--dwt-good",
|
|
3567
|
+
bad: "--dwt-bad",
|
|
3568
|
+
meterTrack: "--dwt-meter-track",
|
|
3569
|
+
barTrack: "--dwt-bar-track",
|
|
3570
|
+
buttonHover: "--dwt-button-hover",
|
|
3571
|
+
radius: "--dwt-radius",
|
|
3572
|
+
gap: "--dwt-gap",
|
|
3573
|
+
padding: "--dwt-padding",
|
|
3574
|
+
minColumn: "--dwt-min-column"
|
|
3575
|
+
};
|
|
3576
|
+
var LENGTH_KEYS = /* @__PURE__ */ new Set([
|
|
3577
|
+
"radius",
|
|
3578
|
+
"gap",
|
|
3579
|
+
"padding",
|
|
3580
|
+
"minColumn"
|
|
3581
|
+
]);
|
|
3582
|
+
function kindOf(key) {
|
|
3583
|
+
if (key === "font") {
|
|
3584
|
+
return "font";
|
|
3585
|
+
}
|
|
3586
|
+
return LENGTH_KEYS.has(key) ? "length" : "color";
|
|
3587
|
+
}
|
|
3588
|
+
var NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
3589
|
+
var SCOPE_PATTERN = /^[A-Za-z0-9_\-[\]=.# '"]+$/;
|
|
3590
|
+
var HEX_COLOR = /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
3591
|
+
var COLOR_FUNCTION = /^(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color|color-mix|light-dark)\(.+\)$/i;
|
|
3592
|
+
var VAR_REFERENCE = /^var\(\s*--[A-Za-z0-9_-]+\s*(?:,.+)?\)$/;
|
|
3593
|
+
var COLOR_KEYWORD = /^[A-Za-z]+$/;
|
|
3594
|
+
var LENGTH_VALUE = /^(?:0|-?(?:\d+\.?\d*|\.\d+)(?:px|rem|em|%|ch|vw|vh|cqw|cqh))$/;
|
|
3595
|
+
var LENGTH_FUNCTION = /^(?:calc|min|max|clamp)\(.+\)$/i;
|
|
3596
|
+
var FONT_VALUE = /^[A-Za-z0-9 ,'"_-]+$/;
|
|
3597
|
+
function unsafeReason(value) {
|
|
3598
|
+
if ([...value].some(
|
|
3599
|
+
(character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127
|
|
3600
|
+
)) {
|
|
3601
|
+
return "contains a control character or line break";
|
|
3602
|
+
}
|
|
3603
|
+
if (/[;{}<>\\@]/.test(value)) {
|
|
3604
|
+
return "contains one of ; { } < > \\ @";
|
|
3605
|
+
}
|
|
3606
|
+
const lower = value.toLowerCase();
|
|
3607
|
+
if (lower.includes("/*") || lower.includes("*/")) {
|
|
3608
|
+
return "contains a comment marker";
|
|
3609
|
+
}
|
|
3610
|
+
if (lower.includes("url(") || lower.includes("expression(")) {
|
|
3611
|
+
return "contains url( or expression(";
|
|
3612
|
+
}
|
|
3613
|
+
let depth = 0;
|
|
3614
|
+
for (const character of value) {
|
|
3615
|
+
if (character === "(") {
|
|
3616
|
+
depth += 1;
|
|
3617
|
+
} else if (character === ")") {
|
|
3618
|
+
depth -= 1;
|
|
3619
|
+
if (depth < 0) {
|
|
3620
|
+
return "has unbalanced parentheses";
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
return depth === 0 ? null : "has unbalanced parentheses";
|
|
3625
|
+
}
|
|
3626
|
+
function countOf(value, character) {
|
|
3627
|
+
return value.split(character).length - 1;
|
|
3628
|
+
}
|
|
3629
|
+
function invalidReason(kind, value) {
|
|
3630
|
+
if (value !== value.trim() || value === "") {
|
|
3631
|
+
return "must not be empty or have leading or trailing spaces";
|
|
3632
|
+
}
|
|
3633
|
+
if (kind === "font") {
|
|
3634
|
+
if (!FONT_VALUE.test(value)) {
|
|
3635
|
+
return "allows only letters, digits, spaces, commas, hyphens, underscores and quotes";
|
|
3636
|
+
}
|
|
3637
|
+
if (countOf(value, "'") % 2 !== 0 || countOf(value, '"') % 2 !== 0) {
|
|
3638
|
+
return "has an unclosed quote";
|
|
3639
|
+
}
|
|
3640
|
+
return null;
|
|
3641
|
+
}
|
|
3642
|
+
const unsafe = unsafeReason(value);
|
|
3643
|
+
if (unsafe !== null) {
|
|
3644
|
+
return unsafe;
|
|
3645
|
+
}
|
|
3646
|
+
if (value.includes("'") || value.includes('"')) {
|
|
3647
|
+
return "must not contain quotes";
|
|
3648
|
+
}
|
|
3649
|
+
if (kind === "color") {
|
|
3650
|
+
const valid2 = HEX_COLOR.test(value) || COLOR_FUNCTION.test(value) || VAR_REFERENCE.test(value) || COLOR_KEYWORD.test(value);
|
|
3651
|
+
return valid2 ? null : "is not a color (use #hex, rgb(), hsl(), oklch() and similar, var(--name) or a keyword)";
|
|
3652
|
+
}
|
|
3653
|
+
const valid = LENGTH_VALUE.test(value) || LENGTH_FUNCTION.test(value) || VAR_REFERENCE.test(value);
|
|
3654
|
+
return valid ? null : "is not a length (use a number with px, rem, em, %, ch, vw or vh, 0, calc(), min(), max(), clamp() or var(--name))";
|
|
3655
|
+
}
|
|
3656
|
+
function collectVariables(themeName, mode, tokens) {
|
|
3657
|
+
const variables = {};
|
|
3658
|
+
if (tokens === void 0) {
|
|
3659
|
+
return ok(variables);
|
|
3660
|
+
}
|
|
3661
|
+
const label = (key) => `createTheme "${themeName}": ${mode}.${key}`;
|
|
3662
|
+
const knownKeys = [
|
|
3663
|
+
...Object.keys(THEME_TOKEN_VARIABLES),
|
|
3664
|
+
"series"
|
|
3665
|
+
];
|
|
3666
|
+
for (const key of Object.keys(tokens)) {
|
|
3667
|
+
if (!knownKeys.includes(key)) {
|
|
3668
|
+
return err(
|
|
3669
|
+
`${label(key)} is not a theme token. Known tokens: ${knownKeys.join(", ")}.`
|
|
3670
|
+
);
|
|
3671
|
+
}
|
|
3672
|
+
}
|
|
3673
|
+
for (const key of Object.keys(THEME_TOKEN_VARIABLES)) {
|
|
3674
|
+
const value = tokens[key];
|
|
3675
|
+
if (value === void 0) {
|
|
3676
|
+
continue;
|
|
3677
|
+
}
|
|
3678
|
+
if (typeof value !== "string") {
|
|
3679
|
+
return err(`${label(key)} must be a string, got ${typeof value}.`);
|
|
3680
|
+
}
|
|
3681
|
+
const reason = invalidReason(kindOf(key), value);
|
|
3682
|
+
if (reason !== null) {
|
|
3683
|
+
return err(`${label(key)} "${value}" ${reason}.`);
|
|
3684
|
+
}
|
|
3685
|
+
variables[THEME_TOKEN_VARIABLES[key]] = value;
|
|
3686
|
+
}
|
|
3687
|
+
const series = tokens.series;
|
|
3688
|
+
if (series !== void 0) {
|
|
3689
|
+
if (!Array.isArray(series) || series.length === 0) {
|
|
3690
|
+
return err(`${label("series")} must be a non-empty array of colors.`);
|
|
3691
|
+
}
|
|
3692
|
+
if (series.length > MAX_GRAPH_SERIES) {
|
|
3693
|
+
return err(
|
|
3694
|
+
`${label("series")} has ${series.length} colors; charts draw at most ${MAX_GRAPH_SERIES} series.`
|
|
3695
|
+
);
|
|
3696
|
+
}
|
|
3697
|
+
for (const [index, value] of series.entries()) {
|
|
3698
|
+
const reason = typeof value === "string" ? invalidReason("color", value) : "must be a string";
|
|
3699
|
+
if (reason !== null) {
|
|
3700
|
+
return err(
|
|
3701
|
+
`${label(`series[${index}]`)} "${String(value)}" ${reason}.`
|
|
3702
|
+
);
|
|
3703
|
+
}
|
|
3704
|
+
variables[`--dwt-series-${index + 1}`] = value;
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
3707
|
+
return ok(variables);
|
|
3708
|
+
}
|
|
3709
|
+
function declarations(variables) {
|
|
3710
|
+
return Object.entries(variables).map(([variable, value]) => ` ${variable}: ${value};`).join("\n");
|
|
3711
|
+
}
|
|
3712
|
+
function targets(prefix, scope) {
|
|
3713
|
+
const lead = prefix === "" ? "" : `${prefix} `;
|
|
3714
|
+
return `${lead}${scope} .dwt-dashboard,
|
|
3715
|
+
${lead}${scope} .dwt-card`;
|
|
3716
|
+
}
|
|
3717
|
+
function rule(prefix, scope, variables) {
|
|
3718
|
+
return `${targets(prefix, scope)} {
|
|
3719
|
+
${declarations(variables)}
|
|
3720
|
+
}`;
|
|
3721
|
+
}
|
|
3722
|
+
function createTheme(options) {
|
|
3723
|
+
const name = options.name;
|
|
3724
|
+
if (typeof name !== "string" || !NAME_PATTERN.test(name)) {
|
|
3725
|
+
return err(
|
|
3726
|
+
`createTheme: name "${String(name)}" must start with a lower-case letter and contain only lower-case letters, digits and hyphens.`
|
|
3727
|
+
);
|
|
3728
|
+
}
|
|
3729
|
+
const scope = (options.scope ?? `[data-dwt-theme='${name}']`).trim();
|
|
3730
|
+
if (scope === "" || !SCOPE_PATTERN.test(scope)) {
|
|
3731
|
+
return err(
|
|
3732
|
+
`createTheme "${name}": scope "${scope}" must be a selector of letters, digits and _ - [ ] = . # ' " or spaces (no commas, colons or combinators).`
|
|
3733
|
+
);
|
|
3734
|
+
}
|
|
3735
|
+
const modes = ["base", "light", "dark"];
|
|
3736
|
+
const variables = {
|
|
3737
|
+
base: {},
|
|
3738
|
+
light: {},
|
|
3739
|
+
dark: {}
|
|
3740
|
+
};
|
|
3741
|
+
for (const mode of modes) {
|
|
3742
|
+
const collected = collectVariables(
|
|
3743
|
+
name,
|
|
3744
|
+
mode,
|
|
3745
|
+
options[mode]
|
|
3746
|
+
);
|
|
3747
|
+
if (!collected.ok) {
|
|
3748
|
+
return collected;
|
|
3749
|
+
}
|
|
3750
|
+
variables[mode] = collected.value;
|
|
3751
|
+
}
|
|
3752
|
+
if (modes.every((mode) => Object.keys(variables[mode]).length === 0)) {
|
|
3753
|
+
return err(
|
|
3754
|
+
`createTheme "${name}": no tokens were set. Pass at least one value in base, light or dark.`
|
|
3755
|
+
);
|
|
3756
|
+
}
|
|
3757
|
+
const blocks = [];
|
|
3758
|
+
if (Object.keys(variables.base).length > 0) {
|
|
3759
|
+
blocks.push(rule("", scope, variables.base));
|
|
3760
|
+
}
|
|
3761
|
+
if (Object.keys(variables.light).length > 0) {
|
|
3762
|
+
blocks.push(
|
|
3763
|
+
rule(":root:where([data-theme='light'])", scope, variables.light)
|
|
3764
|
+
);
|
|
3765
|
+
blocks.push(
|
|
3766
|
+
`@media (prefers-color-scheme: light) {
|
|
3767
|
+
${indent(
|
|
3768
|
+
rule(
|
|
3769
|
+
":root:where(:not([data-theme='dark'], .dark))",
|
|
3770
|
+
scope,
|
|
3771
|
+
variables.light
|
|
3772
|
+
)
|
|
3773
|
+
)}
|
|
3774
|
+
}`
|
|
3775
|
+
);
|
|
3776
|
+
}
|
|
3777
|
+
if (Object.keys(variables.dark).length > 0) {
|
|
3778
|
+
blocks.push(
|
|
3779
|
+
rule(":root:where([data-theme='dark'], .dark)", scope, variables.dark)
|
|
3780
|
+
);
|
|
3781
|
+
blocks.push(
|
|
3782
|
+
`@media (prefers-color-scheme: dark) {
|
|
3783
|
+
${indent(
|
|
3784
|
+
rule(":root:where(:not([data-theme='light']))", scope, variables.dark)
|
|
3785
|
+
)}
|
|
3786
|
+
}`
|
|
3787
|
+
);
|
|
3788
|
+
}
|
|
3789
|
+
return ok({
|
|
3790
|
+
name,
|
|
3791
|
+
scope,
|
|
3792
|
+
css: `${blocks.join("\n\n")}
|
|
3793
|
+
`,
|
|
3794
|
+
variables
|
|
3795
|
+
});
|
|
3796
|
+
}
|
|
3797
|
+
function indent(block) {
|
|
3798
|
+
return block.split("\n").map((line) => ` ${line}`).join("\n");
|
|
3799
|
+
}
|
|
3800
|
+
|
|
3239
3801
|
exports.DATE_RANGE_PRESETS = DATE_RANGE_PRESETS;
|
|
3240
3802
|
exports.DEFAULT_COMPARISON_LABEL = DEFAULT_COMPARISON_LABEL;
|
|
3241
3803
|
exports.DEFAULT_CURRENCY = DEFAULT_CURRENCY;
|
|
@@ -3252,12 +3814,15 @@ exports.MAX_GRAPH_SERIES = MAX_GRAPH_SERIES;
|
|
|
3252
3814
|
exports.MAX_PAGE_ROWS = MAX_PAGE_ROWS;
|
|
3253
3815
|
exports.MAX_PAGE_TITLE_LENGTH = MAX_PAGE_TITLE_LENGTH;
|
|
3254
3816
|
exports.MAX_PARSED_PAGES = MAX_PARSED_PAGES;
|
|
3817
|
+
exports.MAX_PARSED_SETTINGS = MAX_PARSED_SETTINGS;
|
|
3255
3818
|
exports.MAX_REPORTED_PROBLEMS = MAX_REPORTED_PROBLEMS;
|
|
3256
3819
|
exports.MAX_TEXT_OPTION_LENGTH = MAX_TEXT_OPTION_LENGTH;
|
|
3257
3820
|
exports.MAX_WIDGET_OPTIONS = MAX_WIDGET_OPTIONS;
|
|
3821
|
+
exports.MAX_WIDGET_TITLE_LENGTH = MAX_WIDGET_TITLE_LENGTH;
|
|
3258
3822
|
exports.MAX_WIDGET_WIDTH = MAX_WIDGET_WIDTH;
|
|
3259
3823
|
exports.MIN_WIDGET_WIDTH = MIN_WIDGET_WIDTH;
|
|
3260
3824
|
exports.OPTION_KEY_PATTERN = OPTION_KEY_PATTERN;
|
|
3825
|
+
exports.THEME_TOKEN_VARIABLES = THEME_TOKEN_VARIABLES;
|
|
3261
3826
|
exports.VALUE_FORMATS = VALUE_FORMATS;
|
|
3262
3827
|
exports.WIDGET_FILLS = WIDGET_FILLS;
|
|
3263
3828
|
exports.WIDGET_KINDS = WIDGET_KINDS;
|
|
@@ -3266,17 +3831,21 @@ exports.WIDGET_SIZES = WIDGET_SIZES;
|
|
|
3266
3831
|
exports.WIDTH_COLUMNS = WIDTH_COLUMNS;
|
|
3267
3832
|
exports.addPage = addPage;
|
|
3268
3833
|
exports.applyClientOptions = applyClientOptions;
|
|
3834
|
+
exports.applySettings = applySettings;
|
|
3269
3835
|
exports.assignPages = assignPages;
|
|
3270
3836
|
exports.baseColumnSpan = baseColumnSpan;
|
|
3271
3837
|
exports.cacheKeyFor = cacheKeyFor;
|
|
3838
|
+
exports.clearWidgetSettings = clearWidgetSettings;
|
|
3272
3839
|
exports.coerceOptionValue = coerceOptionValue;
|
|
3273
3840
|
exports.createLayoutPersistence = createLayoutPersistence;
|
|
3841
|
+
exports.createTheme = createTheme;
|
|
3274
3842
|
exports.createWidgetLoader = createWidgetLoader;
|
|
3275
3843
|
exports.defaultDetailQuery = defaultDetailQuery;
|
|
3276
3844
|
exports.defaultOptionValue = defaultOptionValue;
|
|
3277
3845
|
exports.defineWidget = defineWidget;
|
|
3278
3846
|
exports.deriveDetailData = deriveDetailData;
|
|
3279
3847
|
exports.describeUnknownError = describeUnknownError;
|
|
3848
|
+
exports.effectiveDefinitions = effectiveDefinitions;
|
|
3280
3849
|
exports.emptyTextFor = emptyTextFor;
|
|
3281
3850
|
exports.emptyWidget = emptyWidget;
|
|
3282
3851
|
exports.enforceLocks = enforceLocks;
|
|
@@ -3297,11 +3866,14 @@ exports.hiddenWidgets = hiddenWidgets;
|
|
|
3297
3866
|
exports.hideWidget = hideWidget;
|
|
3298
3867
|
exports.isClientApplied = isClientApplied;
|
|
3299
3868
|
exports.isColumnKeyFor = isColumnKeyFor;
|
|
3869
|
+
exports.isFlexible = isFlexible;
|
|
3300
3870
|
exports.isHidden = isHidden;
|
|
3301
3871
|
exports.isIsoInterval = isIsoInterval;
|
|
3302
3872
|
exports.isMinimized = isMinimized;
|
|
3873
|
+
exports.isOptionsLocked = isOptionsLocked;
|
|
3303
3874
|
exports.isSafeHref = isSafeHref;
|
|
3304
3875
|
exports.isSafeImageUrl = isSafeImageUrl;
|
|
3876
|
+
exports.isTitleInUse = isTitleInUse;
|
|
3305
3877
|
exports.isWidgetEmptyState = isWidgetEmptyState;
|
|
3306
3878
|
exports.isWidgetKind = isWidgetKind;
|
|
3307
3879
|
exports.isWidgetVisibleTo = isWidgetVisibleTo;
|
|
@@ -3322,8 +3894,11 @@ exports.nextPageKey = nextPageKey;
|
|
|
3322
3894
|
exports.niceDomain = niceDomain;
|
|
3323
3895
|
exports.niceStep = niceStep;
|
|
3324
3896
|
exports.normalizeLayout = normalizeLayout;
|
|
3897
|
+
exports.normalizeSettings = normalizeSettings;
|
|
3898
|
+
exports.normalizeTitle = normalizeTitle;
|
|
3325
3899
|
exports.ok = ok;
|
|
3326
3900
|
exports.optionValuesFor = optionValuesFor;
|
|
3901
|
+
exports.optionValuesFromLayout = optionValuesFromLayout;
|
|
3327
3902
|
exports.optionsCacheSuffix = optionsCacheSuffix;
|
|
3328
3903
|
exports.pageLayout = pageLayout;
|
|
3329
3904
|
exports.pageList = pageList;
|
|
@@ -3332,6 +3907,7 @@ exports.pageRoom = pageRoom;
|
|
|
3332
3907
|
exports.pageWidgets = pageWidgets;
|
|
3333
3908
|
exports.parseDetailQuery = parseDetailQuery;
|
|
3334
3909
|
exports.parseLayout = parseLayout;
|
|
3910
|
+
exports.parseSettings = parseSettings;
|
|
3335
3911
|
exports.parseSortValue = parseSortValue;
|
|
3336
3912
|
exports.percentChange = percentChange;
|
|
3337
3913
|
exports.placeRows = placeRows;
|
|
@@ -3354,6 +3930,8 @@ exports.restoreWidget = restoreWidget;
|
|
|
3354
3930
|
exports.sameOptionValues = sameOptionValues;
|
|
3355
3931
|
exports.serializeDetailQuery = serializeDetailQuery;
|
|
3356
3932
|
exports.serializeLayout = serializeLayout;
|
|
3933
|
+
exports.serializeSettings = serializeSettings;
|
|
3934
|
+
exports.setWidgetSettings = setWidgetSettings;
|
|
3357
3935
|
exports.shareOf = shareOf;
|
|
3358
3936
|
exports.showWidget = showWidget;
|
|
3359
3937
|
exports.sortDefinitions = sortDefinitions;
|
|
@@ -3368,8 +3946,11 @@ exports.validateDetailData = validateDetailData;
|
|
|
3368
3946
|
exports.validateWidgetData = validateWidgetData;
|
|
3369
3947
|
exports.validateWidgetDefinition = validateWidgetDefinition;
|
|
3370
3948
|
exports.visibleWidgets = visibleWidgets;
|
|
3949
|
+
exports.widgetOptionValues = widgetOptionValues;
|
|
3371
3950
|
exports.widgetsFor = widgetsFor;
|
|
3372
3951
|
exports.widthForSize = widthForSize;
|
|
3952
|
+
exports.widthRange = widthRange;
|
|
3953
|
+
exports.widthShareLabel = widthShareLabel;
|
|
3373
3954
|
exports.withEncoding = withEncoding;
|
|
3374
3955
|
exports.withFallback = withFallback;
|
|
3375
3956
|
exports.withLogging = withLogging;
|