@richardmcquiston01/dashboard-widgets-toolkit 0.13.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 +713 -383
- 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 +697 -384
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +713 -383
- 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 +697 -384
- 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-DNB7gjvT.d.cts → storage-_Eb8Y98-.d.cts} +144 -3
- package/dist/{storage-DNB7gjvT.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,6 +688,546 @@ function placeAndFillRows(items, columns, spanOf) {
|
|
|
685
688
|
return { spans, rows };
|
|
686
689
|
}
|
|
687
690
|
|
|
691
|
+
// src/core/options.ts
|
|
692
|
+
var DATE_RANGE_PRESETS = [
|
|
693
|
+
"today",
|
|
694
|
+
"yesterday",
|
|
695
|
+
"last7",
|
|
696
|
+
"last30",
|
|
697
|
+
"last90",
|
|
698
|
+
"thisMonth",
|
|
699
|
+
"lastMonth",
|
|
700
|
+
"thisYear",
|
|
701
|
+
"lastYear"
|
|
702
|
+
];
|
|
703
|
+
var WIDGET_OPTION_TYPES = [
|
|
704
|
+
"choice",
|
|
705
|
+
"number",
|
|
706
|
+
"boolean",
|
|
707
|
+
"text",
|
|
708
|
+
"dateRange",
|
|
709
|
+
"color",
|
|
710
|
+
"sort",
|
|
711
|
+
"columns"
|
|
712
|
+
];
|
|
713
|
+
var MAX_WIDGET_OPTIONS = 20;
|
|
714
|
+
var MAX_TEXT_OPTION_LENGTH = 500;
|
|
715
|
+
var OPTION_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_-]{0,39}$/;
|
|
716
|
+
var COLOR_PATTERN = /^#[0-9a-fA-F]{6}$/;
|
|
717
|
+
var INTERVAL_PATTERN = /^(\d{4}-\d{2}-\d{2})\/(\d{4}-\d{2}-\d{2})$/;
|
|
718
|
+
var CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/;
|
|
719
|
+
function isColumnKeyFor(kind, key) {
|
|
720
|
+
if (kind === "TABLE") return /^c\d+$/.test(key);
|
|
721
|
+
if (kind === "BAR_LIST") return key === "label" || key === "value";
|
|
722
|
+
return false;
|
|
723
|
+
}
|
|
724
|
+
function isClientApplied(option) {
|
|
725
|
+
return (option.type === "sort" || option.type === "columns") && option.apply === "client";
|
|
726
|
+
}
|
|
727
|
+
function civilToday(context) {
|
|
728
|
+
const now = context.now ?? Date.now();
|
|
729
|
+
let timeZone = context.timeZone ?? "UTC";
|
|
730
|
+
try {
|
|
731
|
+
new Intl.DateTimeFormat("en-US", { timeZone });
|
|
732
|
+
} catch {
|
|
733
|
+
timeZone = "UTC";
|
|
734
|
+
}
|
|
735
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
736
|
+
timeZone,
|
|
737
|
+
year: "numeric",
|
|
738
|
+
month: "2-digit",
|
|
739
|
+
day: "2-digit"
|
|
740
|
+
}).formatToParts(new Date(now));
|
|
741
|
+
const pick = (type) => Number(parts.find((part) => part.type === type)?.value ?? "0");
|
|
742
|
+
return { year: pick("year"), month: pick("month"), day: pick("day") };
|
|
743
|
+
}
|
|
744
|
+
function toIso(epochMs) {
|
|
745
|
+
return new Date(epochMs).toISOString().slice(0, 10);
|
|
746
|
+
}
|
|
747
|
+
var DAY_MS = 864e5;
|
|
748
|
+
function parseIsoDay(text) {
|
|
749
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(text);
|
|
750
|
+
if (match === null) return void 0;
|
|
751
|
+
const epoch = Date.UTC(
|
|
752
|
+
Number(match[1]),
|
|
753
|
+
Number(match[2]) - 1,
|
|
754
|
+
Number(match[3])
|
|
755
|
+
);
|
|
756
|
+
return toIso(epoch) === text ? epoch : void 0;
|
|
757
|
+
}
|
|
758
|
+
function isIsoInterval(value) {
|
|
759
|
+
const match = INTERVAL_PATTERN.exec(value);
|
|
760
|
+
if (match === null) return false;
|
|
761
|
+
const start = parseIsoDay(match[1]);
|
|
762
|
+
const end = parseIsoDay(match[2]);
|
|
763
|
+
return start !== void 0 && end !== void 0 && start <= end;
|
|
764
|
+
}
|
|
765
|
+
function isPreset(value) {
|
|
766
|
+
return DATE_RANGE_PRESETS.includes(value);
|
|
767
|
+
}
|
|
768
|
+
function resolveDateRange(value, context = {}) {
|
|
769
|
+
if (isIsoInterval(value)) return value;
|
|
770
|
+
if (!isPreset(value)) return void 0;
|
|
771
|
+
const today = civilToday(context);
|
|
772
|
+
const todayMs = Date.UTC(today.year, today.month - 1, today.day);
|
|
773
|
+
const range = (startMs, endMs) => `${toIso(startMs)}/${toIso(endMs)}`;
|
|
774
|
+
switch (value) {
|
|
775
|
+
case "today":
|
|
776
|
+
return range(todayMs, todayMs);
|
|
777
|
+
case "yesterday":
|
|
778
|
+
return range(todayMs - DAY_MS, todayMs - DAY_MS);
|
|
779
|
+
case "last7":
|
|
780
|
+
return range(todayMs - 6 * DAY_MS, todayMs);
|
|
781
|
+
case "last30":
|
|
782
|
+
return range(todayMs - 29 * DAY_MS, todayMs);
|
|
783
|
+
case "last90":
|
|
784
|
+
return range(todayMs - 89 * DAY_MS, todayMs);
|
|
785
|
+
case "thisMonth":
|
|
786
|
+
return range(Date.UTC(today.year, today.month - 1, 1), todayMs);
|
|
787
|
+
case "lastMonth":
|
|
788
|
+
return range(
|
|
789
|
+
Date.UTC(today.year, today.month - 2, 1),
|
|
790
|
+
Date.UTC(today.year, today.month - 1, 1) - DAY_MS
|
|
791
|
+
);
|
|
792
|
+
case "thisYear":
|
|
793
|
+
return range(Date.UTC(today.year, 0, 1), todayMs);
|
|
794
|
+
case "lastYear":
|
|
795
|
+
return range(
|
|
796
|
+
Date.UTC(today.year - 1, 0, 1),
|
|
797
|
+
Date.UTC(today.year, 0, 1) - DAY_MS
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
function onStepGrid(option, value) {
|
|
802
|
+
if (option.step === void 0) return true;
|
|
803
|
+
const steps = (value - option.min) / option.step;
|
|
804
|
+
return Math.abs(steps - Math.round(steps)) < 1e-9;
|
|
805
|
+
}
|
|
806
|
+
function parseSortValue(value) {
|
|
807
|
+
const separator = value.lastIndexOf(":");
|
|
808
|
+
if (separator <= 0) return void 0;
|
|
809
|
+
const column = value.slice(0, separator);
|
|
810
|
+
const direction = value.slice(separator + 1);
|
|
811
|
+
if (direction !== "asc" && direction !== "desc") return void 0;
|
|
812
|
+
return { column, direction };
|
|
813
|
+
}
|
|
814
|
+
function sortFromOptions(definition, values) {
|
|
815
|
+
if (values === void 0) return void 0;
|
|
816
|
+
const sortOption = definition.options?.find(
|
|
817
|
+
(option) => option.type === "sort"
|
|
818
|
+
);
|
|
819
|
+
if (sortOption === void 0) return void 0;
|
|
820
|
+
const chosen = values[sortOption.key];
|
|
821
|
+
return typeof chosen === "string" ? parseSortValue(chosen) : void 0;
|
|
822
|
+
}
|
|
823
|
+
function coerceOptionValue(option, value, context = {}) {
|
|
824
|
+
switch (option.type) {
|
|
825
|
+
case "choice":
|
|
826
|
+
return typeof value === "string" && option.choices.some((choice) => choice.value === value) ? value : void 0;
|
|
827
|
+
case "number":
|
|
828
|
+
return typeof value === "number" && Number.isFinite(value) && value >= option.min && value <= option.max && onStepGrid(option, value) ? value : void 0;
|
|
829
|
+
case "boolean":
|
|
830
|
+
return typeof value === "boolean" ? value : void 0;
|
|
831
|
+
case "text": {
|
|
832
|
+
if (typeof value !== "string") return void 0;
|
|
833
|
+
const trimmed = value.trim();
|
|
834
|
+
return trimmed.length <= option.maxLength && !CONTROL_CHARACTERS.test(trimmed) ? trimmed : void 0;
|
|
835
|
+
}
|
|
836
|
+
case "dateRange": {
|
|
837
|
+
if (typeof value !== "string") return void 0;
|
|
838
|
+
if (isPreset(value) && option.presets !== void 0 && !option.presets.some((preset) => preset.value === value)) {
|
|
839
|
+
return void 0;
|
|
840
|
+
}
|
|
841
|
+
return resolveDateRange(value, context);
|
|
842
|
+
}
|
|
843
|
+
case "color":
|
|
844
|
+
return typeof value === "string" && COLOR_PATTERN.test(value) ? value.toLowerCase() : void 0;
|
|
845
|
+
case "sort": {
|
|
846
|
+
if (typeof value !== "string") return void 0;
|
|
847
|
+
const sort = parseSortValue(value);
|
|
848
|
+
return sort !== void 0 && option.columns.some((column) => column.key === sort.column) ? value : void 0;
|
|
849
|
+
}
|
|
850
|
+
case "columns": {
|
|
851
|
+
if (!Array.isArray(value) || value.length === 0) return void 0;
|
|
852
|
+
const keys = value;
|
|
853
|
+
const allowed = new Set(option.columns.map((c) => c.key));
|
|
854
|
+
const seen = /* @__PURE__ */ new Set();
|
|
855
|
+
for (const key of keys) {
|
|
856
|
+
if (typeof key !== "string" || !allowed.has(key) || seen.has(key)) {
|
|
857
|
+
return void 0;
|
|
858
|
+
}
|
|
859
|
+
seen.add(key);
|
|
860
|
+
}
|
|
861
|
+
return [...seen];
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
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
|
+
|
|
688
1231
|
// src/core/layout.ts
|
|
689
1232
|
var MAX_PAGE_TITLE_LENGTH = 30;
|
|
690
1233
|
var MAX_PARSED_PAGES = 50;
|
|
@@ -719,18 +1262,23 @@ function parseLayout(raw) {
|
|
|
719
1262
|
const hidden = uniqueStrings(record["hidden"]);
|
|
720
1263
|
const minimized = uniqueStrings(record["minimized"]);
|
|
721
1264
|
const pages = parsePages(record["pages"]);
|
|
1265
|
+
const settings = parseSettings(
|
|
1266
|
+
record["settings"]
|
|
1267
|
+
);
|
|
1268
|
+
const withSettings = settings === void 0 ? {} : { settings };
|
|
722
1269
|
if (pages.length > 0) {
|
|
723
1270
|
return {
|
|
724
1271
|
order: EMPTY_LAYOUT.order,
|
|
725
1272
|
hidden: EMPTY_LAYOUT.hidden,
|
|
726
1273
|
minimized: EMPTY_LAYOUT.minimized,
|
|
727
|
-
pages
|
|
1274
|
+
pages,
|
|
1275
|
+
...withSettings
|
|
728
1276
|
};
|
|
729
1277
|
}
|
|
730
1278
|
if (order.length === 0 && hidden.length === 0 && minimized.length === 0) {
|
|
731
|
-
return EMPTY_LAYOUT;
|
|
1279
|
+
return settings === void 0 ? EMPTY_LAYOUT : { ...EMPTY_LAYOUT, settings };
|
|
732
1280
|
}
|
|
733
|
-
return { order, hidden, minimized };
|
|
1281
|
+
return { order, hidden, minimized, ...withSettings };
|
|
734
1282
|
}
|
|
735
1283
|
function parsePages(value) {
|
|
736
1284
|
if (!Array.isArray(value)) return [];
|
|
@@ -777,7 +1325,8 @@ function serializeLayout(layout) {
|
|
|
777
1325
|
minimized: [...page.minimized],
|
|
778
1326
|
...page.maxRows === void 0 ? {} : { maxRows: page.maxRows }
|
|
779
1327
|
}))
|
|
780
|
-
}
|
|
1328
|
+
},
|
|
1329
|
+
...serializeSettings(layout.settings) === void 0 ? {} : { settings: serializeSettings(layout.settings) }
|
|
781
1330
|
});
|
|
782
1331
|
}
|
|
783
1332
|
function bySortOrder(items) {
|
|
@@ -845,378 +1394,101 @@ function freeSlotIndexes(ordered) {
|
|
|
845
1394
|
ordered.forEach((item, index) => {
|
|
846
1395
|
if (!isPinned(item)) slots.push(index);
|
|
847
1396
|
});
|
|
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
|
-
// src/core/options.ts
|
|
946
|
-
var DATE_RANGE_PRESETS = [
|
|
947
|
-
"today",
|
|
948
|
-
"yesterday",
|
|
949
|
-
"last7",
|
|
950
|
-
"last30",
|
|
951
|
-
"last90",
|
|
952
|
-
"thisMonth",
|
|
953
|
-
"lastMonth",
|
|
954
|
-
"thisYear",
|
|
955
|
-
"lastYear"
|
|
956
|
-
];
|
|
957
|
-
var WIDGET_OPTION_TYPES = [
|
|
958
|
-
"choice",
|
|
959
|
-
"number",
|
|
960
|
-
"boolean",
|
|
961
|
-
"text",
|
|
962
|
-
"dateRange",
|
|
963
|
-
"color",
|
|
964
|
-
"sort",
|
|
965
|
-
"columns"
|
|
966
|
-
];
|
|
967
|
-
var MAX_WIDGET_OPTIONS = 20;
|
|
968
|
-
var MAX_TEXT_OPTION_LENGTH = 500;
|
|
969
|
-
var OPTION_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_-]{0,39}$/;
|
|
970
|
-
var COLOR_PATTERN = /^#[0-9a-fA-F]{6}$/;
|
|
971
|
-
var INTERVAL_PATTERN = /^(\d{4}-\d{2}-\d{2})\/(\d{4}-\d{2}-\d{2})$/;
|
|
972
|
-
var CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/;
|
|
973
|
-
function isColumnKeyFor(kind, key) {
|
|
974
|
-
if (kind === "TABLE") return /^c\d+$/.test(key);
|
|
975
|
-
if (kind === "BAR_LIST") return key === "label" || key === "value";
|
|
976
|
-
return false;
|
|
977
|
-
}
|
|
978
|
-
function isClientApplied(option) {
|
|
979
|
-
return (option.type === "sort" || option.type === "columns") && option.apply === "client";
|
|
980
|
-
}
|
|
981
|
-
function civilToday(context) {
|
|
982
|
-
const now = context.now ?? Date.now();
|
|
983
|
-
let timeZone = context.timeZone ?? "UTC";
|
|
984
|
-
try {
|
|
985
|
-
new Intl.DateTimeFormat("en-US", { timeZone });
|
|
986
|
-
} catch {
|
|
987
|
-
timeZone = "UTC";
|
|
988
|
-
}
|
|
989
|
-
const parts = new Intl.DateTimeFormat("en-US", {
|
|
990
|
-
timeZone,
|
|
991
|
-
year: "numeric",
|
|
992
|
-
month: "2-digit",
|
|
993
|
-
day: "2-digit"
|
|
994
|
-
}).formatToParts(new Date(now));
|
|
995
|
-
const pick = (type) => Number(parts.find((part) => part.type === type)?.value ?? "0");
|
|
996
|
-
return { year: pick("year"), month: pick("month"), day: pick("day") };
|
|
997
|
-
}
|
|
998
|
-
function toIso(epochMs) {
|
|
999
|
-
return new Date(epochMs).toISOString().slice(0, 10);
|
|
1000
|
-
}
|
|
1001
|
-
var DAY_MS = 864e5;
|
|
1002
|
-
function parseIsoDay(text) {
|
|
1003
|
-
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(text);
|
|
1004
|
-
if (match === null) return void 0;
|
|
1005
|
-
const epoch = Date.UTC(
|
|
1006
|
-
Number(match[1]),
|
|
1007
|
-
Number(match[2]) - 1,
|
|
1008
|
-
Number(match[3])
|
|
1009
|
-
);
|
|
1010
|
-
return toIso(epoch) === text ? epoch : void 0;
|
|
1011
|
-
}
|
|
1012
|
-
function isIsoInterval(value) {
|
|
1013
|
-
const match = INTERVAL_PATTERN.exec(value);
|
|
1014
|
-
if (match === null) return false;
|
|
1015
|
-
const start = parseIsoDay(match[1]);
|
|
1016
|
-
const end = parseIsoDay(match[2]);
|
|
1017
|
-
return start !== void 0 && end !== void 0 && start <= end;
|
|
1018
|
-
}
|
|
1019
|
-
function isPreset(value) {
|
|
1020
|
-
return DATE_RANGE_PRESETS.includes(value);
|
|
1021
|
-
}
|
|
1022
|
-
function resolveDateRange(value, context = {}) {
|
|
1023
|
-
if (isIsoInterval(value)) return value;
|
|
1024
|
-
if (!isPreset(value)) return void 0;
|
|
1025
|
-
const today = civilToday(context);
|
|
1026
|
-
const todayMs = Date.UTC(today.year, today.month - 1, today.day);
|
|
1027
|
-
const range = (startMs, endMs) => `${toIso(startMs)}/${toIso(endMs)}`;
|
|
1028
|
-
switch (value) {
|
|
1029
|
-
case "today":
|
|
1030
|
-
return range(todayMs, todayMs);
|
|
1031
|
-
case "yesterday":
|
|
1032
|
-
return range(todayMs - DAY_MS, todayMs - DAY_MS);
|
|
1033
|
-
case "last7":
|
|
1034
|
-
return range(todayMs - 6 * DAY_MS, todayMs);
|
|
1035
|
-
case "last30":
|
|
1036
|
-
return range(todayMs - 29 * DAY_MS, todayMs);
|
|
1037
|
-
case "last90":
|
|
1038
|
-
return range(todayMs - 89 * DAY_MS, todayMs);
|
|
1039
|
-
case "thisMonth":
|
|
1040
|
-
return range(Date.UTC(today.year, today.month - 1, 1), todayMs);
|
|
1041
|
-
case "lastMonth":
|
|
1042
|
-
return range(
|
|
1043
|
-
Date.UTC(today.year, today.month - 2, 1),
|
|
1044
|
-
Date.UTC(today.year, today.month - 1, 1) - DAY_MS
|
|
1045
|
-
);
|
|
1046
|
-
case "thisYear":
|
|
1047
|
-
return range(Date.UTC(today.year, 0, 1), todayMs);
|
|
1048
|
-
case "lastYear":
|
|
1049
|
-
return range(
|
|
1050
|
-
Date.UTC(today.year - 1, 0, 1),
|
|
1051
|
-
Date.UTC(today.year, 0, 1) - DAY_MS
|
|
1052
|
-
);
|
|
1053
|
-
}
|
|
1397
|
+
return slots;
|
|
1054
1398
|
}
|
|
1055
|
-
function
|
|
1056
|
-
|
|
1057
|
-
const
|
|
1058
|
-
|
|
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]);
|
|
1059
1411
|
}
|
|
1060
|
-
function
|
|
1061
|
-
|
|
1062
|
-
if (separator <= 0) return void 0;
|
|
1063
|
-
const column = value.slice(0, separator);
|
|
1064
|
-
const direction = value.slice(separator + 1);
|
|
1065
|
-
if (direction !== "asc" && direction !== "desc") return void 0;
|
|
1066
|
-
return { column, direction };
|
|
1412
|
+
function moveWidgetUp(definitions, layout, key) {
|
|
1413
|
+
return moveWidgetBy(definitions, layout, key, -1);
|
|
1067
1414
|
}
|
|
1068
|
-
function
|
|
1069
|
-
|
|
1070
|
-
const sortOption = definition.options?.find(
|
|
1071
|
-
(option) => option.type === "sort"
|
|
1072
|
-
);
|
|
1073
|
-
if (sortOption === void 0) return void 0;
|
|
1074
|
-
const chosen = values[sortOption.key];
|
|
1075
|
-
return typeof chosen === "string" ? parseSortValue(chosen) : void 0;
|
|
1415
|
+
function moveWidgetDown(definitions, layout, key) {
|
|
1416
|
+
return moveWidgetBy(definitions, layout, key, 1);
|
|
1076
1417
|
}
|
|
1077
|
-
function
|
|
1078
|
-
|
|
1079
|
-
case "choice":
|
|
1080
|
-
return typeof value === "string" && option.choices.some((choice) => choice.value === value) ? value : void 0;
|
|
1081
|
-
case "number":
|
|
1082
|
-
return typeof value === "number" && Number.isFinite(value) && value >= option.min && value <= option.max && onStepGrid(option, value) ? value : void 0;
|
|
1083
|
-
case "boolean":
|
|
1084
|
-
return typeof value === "boolean" ? value : void 0;
|
|
1085
|
-
case "text": {
|
|
1086
|
-
if (typeof value !== "string") return void 0;
|
|
1087
|
-
const trimmed = value.trim();
|
|
1088
|
-
return trimmed.length <= option.maxLength && !CONTROL_CHARACTERS.test(trimmed) ? trimmed : void 0;
|
|
1089
|
-
}
|
|
1090
|
-
case "dateRange": {
|
|
1091
|
-
if (typeof value !== "string") return void 0;
|
|
1092
|
-
if (isPreset(value) && option.presets !== void 0 && !option.presets.some((preset) => preset.value === value)) {
|
|
1093
|
-
return void 0;
|
|
1094
|
-
}
|
|
1095
|
-
return resolveDateRange(value, context);
|
|
1096
|
-
}
|
|
1097
|
-
case "color":
|
|
1098
|
-
return typeof value === "string" && COLOR_PATTERN.test(value) ? value.toLowerCase() : void 0;
|
|
1099
|
-
case "sort": {
|
|
1100
|
-
if (typeof value !== "string") return void 0;
|
|
1101
|
-
const sort = parseSortValue(value);
|
|
1102
|
-
return sort !== void 0 && option.columns.some((column) => column.key === sort.column) ? value : void 0;
|
|
1103
|
-
}
|
|
1104
|
-
case "columns": {
|
|
1105
|
-
if (!Array.isArray(value) || value.length === 0) return void 0;
|
|
1106
|
-
const keys = value;
|
|
1107
|
-
const allowed = new Set(option.columns.map((c) => c.key));
|
|
1108
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1109
|
-
for (const key of keys) {
|
|
1110
|
-
if (typeof key !== "string" || !allowed.has(key) || seen.has(key)) {
|
|
1111
|
-
return void 0;
|
|
1112
|
-
}
|
|
1113
|
-
seen.add(key);
|
|
1114
|
-
}
|
|
1115
|
-
return [...seen];
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1418
|
+
function isHidden(layout, key) {
|
|
1419
|
+
return layout.hidden.includes(key);
|
|
1118
1420
|
}
|
|
1119
|
-
function
|
|
1120
|
-
|
|
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
|
-
}
|
|
1421
|
+
function isMinimized(layout, key) {
|
|
1422
|
+
return layout.minimized.includes(key);
|
|
1130
1423
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
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;
|
|
1424
|
+
function hideWidget(layout, key) {
|
|
1425
|
+
if (isHidden(layout, key)) return layout;
|
|
1426
|
+
return { ...layout, hidden: [...layout.hidden, key] };
|
|
1142
1427
|
}
|
|
1143
|
-
function
|
|
1144
|
-
if (
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
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
|
+
};
|
|
1148
1435
|
}
|
|
1149
|
-
function
|
|
1150
|
-
|
|
1151
|
-
return keys.every((key) => sameValue(a[key], b[key]));
|
|
1436
|
+
function toggleHidden(layout, key) {
|
|
1437
|
+
return isHidden(layout, key) ? showWidget(layout, key) : hideWidget(layout, key);
|
|
1152
1438
|
}
|
|
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("&")}`;
|
|
1439
|
+
function minimizeWidget(layout, key) {
|
|
1440
|
+
if (isMinimized(layout, key)) return layout;
|
|
1441
|
+
return { ...layout, minimized: [...layout.minimized, key] };
|
|
1165
1442
|
}
|
|
1166
|
-
function
|
|
1167
|
-
|
|
1168
|
-
return
|
|
1443
|
+
function restoreWidget(layout, key) {
|
|
1444
|
+
if (!isMinimized(layout, key)) return layout;
|
|
1445
|
+
return { ...layout, minimized: layout.minimized.filter((k) => k !== key) };
|
|
1169
1446
|
}
|
|
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);
|
|
1447
|
+
function toggleMinimized(layout, key) {
|
|
1448
|
+
return isMinimized(layout, key) ? restoreWidget(layout, key) : minimizeWidget(layout, key);
|
|
1176
1449
|
}
|
|
1177
|
-
function
|
|
1178
|
-
const
|
|
1179
|
-
const
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
(
|
|
1184
|
-
);
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
const
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
const index = tableColumnIndex(sort.column);
|
|
1196
|
-
if (index !== void 0 && index < next.columns.length) {
|
|
1197
|
-
const sorted = queryRows(tableDetailData(next), {
|
|
1198
|
-
page: 1,
|
|
1199
|
-
pageSize: Math.max(1, next.rows.length),
|
|
1200
|
-
sort
|
|
1201
|
-
});
|
|
1202
|
-
if (sorted.ok) next = { ...next, rows: sorted.value.rows };
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
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
|
|
@@ -3501,9 +3814,11 @@ exports.MAX_GRAPH_SERIES = MAX_GRAPH_SERIES;
|
|
|
3501
3814
|
exports.MAX_PAGE_ROWS = MAX_PAGE_ROWS;
|
|
3502
3815
|
exports.MAX_PAGE_TITLE_LENGTH = MAX_PAGE_TITLE_LENGTH;
|
|
3503
3816
|
exports.MAX_PARSED_PAGES = MAX_PARSED_PAGES;
|
|
3817
|
+
exports.MAX_PARSED_SETTINGS = MAX_PARSED_SETTINGS;
|
|
3504
3818
|
exports.MAX_REPORTED_PROBLEMS = MAX_REPORTED_PROBLEMS;
|
|
3505
3819
|
exports.MAX_TEXT_OPTION_LENGTH = MAX_TEXT_OPTION_LENGTH;
|
|
3506
3820
|
exports.MAX_WIDGET_OPTIONS = MAX_WIDGET_OPTIONS;
|
|
3821
|
+
exports.MAX_WIDGET_TITLE_LENGTH = MAX_WIDGET_TITLE_LENGTH;
|
|
3507
3822
|
exports.MAX_WIDGET_WIDTH = MAX_WIDGET_WIDTH;
|
|
3508
3823
|
exports.MIN_WIDGET_WIDTH = MIN_WIDGET_WIDTH;
|
|
3509
3824
|
exports.OPTION_KEY_PATTERN = OPTION_KEY_PATTERN;
|
|
@@ -3516,9 +3831,11 @@ exports.WIDGET_SIZES = WIDGET_SIZES;
|
|
|
3516
3831
|
exports.WIDTH_COLUMNS = WIDTH_COLUMNS;
|
|
3517
3832
|
exports.addPage = addPage;
|
|
3518
3833
|
exports.applyClientOptions = applyClientOptions;
|
|
3834
|
+
exports.applySettings = applySettings;
|
|
3519
3835
|
exports.assignPages = assignPages;
|
|
3520
3836
|
exports.baseColumnSpan = baseColumnSpan;
|
|
3521
3837
|
exports.cacheKeyFor = cacheKeyFor;
|
|
3838
|
+
exports.clearWidgetSettings = clearWidgetSettings;
|
|
3522
3839
|
exports.coerceOptionValue = coerceOptionValue;
|
|
3523
3840
|
exports.createLayoutPersistence = createLayoutPersistence;
|
|
3524
3841
|
exports.createTheme = createTheme;
|
|
@@ -3528,6 +3845,7 @@ exports.defaultOptionValue = defaultOptionValue;
|
|
|
3528
3845
|
exports.defineWidget = defineWidget;
|
|
3529
3846
|
exports.deriveDetailData = deriveDetailData;
|
|
3530
3847
|
exports.describeUnknownError = describeUnknownError;
|
|
3848
|
+
exports.effectiveDefinitions = effectiveDefinitions;
|
|
3531
3849
|
exports.emptyTextFor = emptyTextFor;
|
|
3532
3850
|
exports.emptyWidget = emptyWidget;
|
|
3533
3851
|
exports.enforceLocks = enforceLocks;
|
|
@@ -3548,11 +3866,14 @@ exports.hiddenWidgets = hiddenWidgets;
|
|
|
3548
3866
|
exports.hideWidget = hideWidget;
|
|
3549
3867
|
exports.isClientApplied = isClientApplied;
|
|
3550
3868
|
exports.isColumnKeyFor = isColumnKeyFor;
|
|
3869
|
+
exports.isFlexible = isFlexible;
|
|
3551
3870
|
exports.isHidden = isHidden;
|
|
3552
3871
|
exports.isIsoInterval = isIsoInterval;
|
|
3553
3872
|
exports.isMinimized = isMinimized;
|
|
3873
|
+
exports.isOptionsLocked = isOptionsLocked;
|
|
3554
3874
|
exports.isSafeHref = isSafeHref;
|
|
3555
3875
|
exports.isSafeImageUrl = isSafeImageUrl;
|
|
3876
|
+
exports.isTitleInUse = isTitleInUse;
|
|
3556
3877
|
exports.isWidgetEmptyState = isWidgetEmptyState;
|
|
3557
3878
|
exports.isWidgetKind = isWidgetKind;
|
|
3558
3879
|
exports.isWidgetVisibleTo = isWidgetVisibleTo;
|
|
@@ -3573,8 +3894,11 @@ exports.nextPageKey = nextPageKey;
|
|
|
3573
3894
|
exports.niceDomain = niceDomain;
|
|
3574
3895
|
exports.niceStep = niceStep;
|
|
3575
3896
|
exports.normalizeLayout = normalizeLayout;
|
|
3897
|
+
exports.normalizeSettings = normalizeSettings;
|
|
3898
|
+
exports.normalizeTitle = normalizeTitle;
|
|
3576
3899
|
exports.ok = ok;
|
|
3577
3900
|
exports.optionValuesFor = optionValuesFor;
|
|
3901
|
+
exports.optionValuesFromLayout = optionValuesFromLayout;
|
|
3578
3902
|
exports.optionsCacheSuffix = optionsCacheSuffix;
|
|
3579
3903
|
exports.pageLayout = pageLayout;
|
|
3580
3904
|
exports.pageList = pageList;
|
|
@@ -3583,6 +3907,7 @@ exports.pageRoom = pageRoom;
|
|
|
3583
3907
|
exports.pageWidgets = pageWidgets;
|
|
3584
3908
|
exports.parseDetailQuery = parseDetailQuery;
|
|
3585
3909
|
exports.parseLayout = parseLayout;
|
|
3910
|
+
exports.parseSettings = parseSettings;
|
|
3586
3911
|
exports.parseSortValue = parseSortValue;
|
|
3587
3912
|
exports.percentChange = percentChange;
|
|
3588
3913
|
exports.placeRows = placeRows;
|
|
@@ -3605,6 +3930,8 @@ exports.restoreWidget = restoreWidget;
|
|
|
3605
3930
|
exports.sameOptionValues = sameOptionValues;
|
|
3606
3931
|
exports.serializeDetailQuery = serializeDetailQuery;
|
|
3607
3932
|
exports.serializeLayout = serializeLayout;
|
|
3933
|
+
exports.serializeSettings = serializeSettings;
|
|
3934
|
+
exports.setWidgetSettings = setWidgetSettings;
|
|
3608
3935
|
exports.shareOf = shareOf;
|
|
3609
3936
|
exports.showWidget = showWidget;
|
|
3610
3937
|
exports.sortDefinitions = sortDefinitions;
|
|
@@ -3619,8 +3946,11 @@ exports.validateDetailData = validateDetailData;
|
|
|
3619
3946
|
exports.validateWidgetData = validateWidgetData;
|
|
3620
3947
|
exports.validateWidgetDefinition = validateWidgetDefinition;
|
|
3621
3948
|
exports.visibleWidgets = visibleWidgets;
|
|
3949
|
+
exports.widgetOptionValues = widgetOptionValues;
|
|
3622
3950
|
exports.widgetsFor = widgetsFor;
|
|
3623
3951
|
exports.widthForSize = widthForSize;
|
|
3952
|
+
exports.widthRange = widthRange;
|
|
3953
|
+
exports.widthShareLabel = widthShareLabel;
|
|
3624
3954
|
exports.withEncoding = withEncoding;
|
|
3625
3955
|
exports.withFallback = withFallback;
|
|
3626
3956
|
exports.withLogging = withLogging;
|