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