@pure-ds/core 0.6.9 → 0.6.10
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/custom-elements.json +71 -28
- package/dist/types/pds.d.ts +30 -0
- package/dist/types/public/assets/js/pds-manager.d.ts +2 -1
- package/dist/types/public/assets/js/pds-manager.d.ts.map +1 -1
- package/dist/types/public/assets/js/pds.d.ts.map +1 -1
- package/dist/types/public/assets/pds/components/pds-form.d.ts.map +1 -1
- package/dist/types/public/assets/pds/components/pds-live-edit.d.ts +1 -195
- package/dist/types/public/assets/pds/components/pds-live-edit.d.ts.map +1 -1
- package/dist/types/public/assets/pds/components/pds-omnibox.d.ts +0 -2
- package/dist/types/public/assets/pds/components/pds-omnibox.d.ts.map +1 -1
- package/dist/types/src/js/pds-core/pds-config.d.ts +1306 -13
- package/dist/types/src/js/pds-core/pds-config.d.ts.map +1 -1
- package/dist/types/src/js/pds-core/pds-enhancers-meta.d.ts.map +1 -1
- package/dist/types/src/js/pds-core/pds-enhancers.d.ts.map +1 -1
- package/dist/types/src/js/pds-core/pds-generator.d.ts.map +1 -1
- package/dist/types/src/js/pds-core/pds-live.d.ts.map +1 -1
- package/dist/types/src/js/pds-core/pds-ontology.d.ts.map +1 -1
- package/dist/types/src/js/pds-core/pds-start-helpers.d.ts +1 -4
- package/dist/types/src/js/pds-core/pds-start-helpers.d.ts.map +1 -1
- package/dist/types/src/js/pds.d.ts.map +1 -1
- package/package.json +2 -2
- package/packages/pds-cli/bin/pds-static.js +16 -1
- package/public/assets/js/app.js +21 -21
- package/public/assets/js/pds-manager.js +291 -161
- package/public/assets/js/pds.js +16 -16
- package/public/assets/pds/components/pds-form.js +124 -27
- package/public/assets/pds/components/pds-live-edit.js +820 -122
- package/public/assets/pds/components/pds-omnibox.js +10 -18
- package/public/assets/pds/custom-elements.json +71 -28
- package/public/assets/pds/pds-css-complete.json +1 -6
- package/public/assets/pds/pds.css-data.json +5 -35
- package/src/js/pds-core/pds-config.js +822 -31
- package/src/js/pds-core/pds-enhancers-meta.js +11 -0
- package/src/js/pds-core/pds-enhancers.js +113 -5
- package/src/js/pds-core/pds-generator.js +183 -23
- package/src/js/pds-core/pds-live.js +177 -2
- package/src/js/pds-core/pds-ontology.js +6 -0
- package/src/js/pds-core/pds-start-helpers.js +14 -6
- package/src/js/pds.d.ts +30 -0
- package/src/js/pds.js +36 -60
|
@@ -188,14 +188,22 @@ ${EDITOR_TAG} {
|
|
|
188
188
|
.${DROPDOWN_CLASS} menu {
|
|
189
189
|
min-width: max-content;
|
|
190
190
|
max-width: 350px;
|
|
191
|
+
margin: 0;
|
|
192
|
+
padding: 0;
|
|
193
|
+
list-style: none;
|
|
194
|
+
overflow: visible;
|
|
191
195
|
}
|
|
192
196
|
.${DROPDOWN_CLASS} .pds-live-editor-menu {
|
|
197
|
+
display: block;
|
|
198
|
+
background-color: var(--color-surface-base);
|
|
193
199
|
padding: var(--spacing-1);
|
|
194
200
|
max-width: 350px;
|
|
195
201
|
padding-bottom: 0;
|
|
202
|
+
overflow: visible;
|
|
196
203
|
}
|
|
197
204
|
.${DROPDOWN_CLASS} .pds-live-editor-form-container {
|
|
198
205
|
padding-bottom: var(--spacing-2);
|
|
206
|
+
overflow: visible;
|
|
199
207
|
}
|
|
200
208
|
.${DROPDOWN_CLASS} .pds-live-editor-title {
|
|
201
209
|
display: block;
|
|
@@ -589,6 +597,62 @@ function toColorInputValue(value) {
|
|
|
589
597
|
return hexValue || value;
|
|
590
598
|
}
|
|
591
599
|
|
|
600
|
+
function isColorPath(path) {
|
|
601
|
+
return String(path || "").toLowerCase().startsWith("colors.");
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function inferColorVariableCandidates(path) {
|
|
605
|
+
const normalizedPath = String(path || "").toLowerCase();
|
|
606
|
+
const key = normalizedPath.replace(/^colors\./, "").replace(/^darkmode\./, "");
|
|
607
|
+
const tail = key.split(".").pop();
|
|
608
|
+
|
|
609
|
+
const directMap = {
|
|
610
|
+
primary: ["--color-primary-500"],
|
|
611
|
+
secondary: ["--color-secondary-500", "--color-gray-500"],
|
|
612
|
+
accent: ["--color-accent-500"],
|
|
613
|
+
background: ["--color-surface-base"],
|
|
614
|
+
success: ["--color-success-500"],
|
|
615
|
+
warning: ["--color-warning-500"],
|
|
616
|
+
danger: ["--color-danger-500"],
|
|
617
|
+
info: ["--color-info-500"],
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
const candidates = new Set();
|
|
621
|
+
if (tail && directMap[tail]) {
|
|
622
|
+
directMap[tail].forEach((item) => candidates.add(item));
|
|
623
|
+
}
|
|
624
|
+
if (tail) {
|
|
625
|
+
candidates.add(`--color-${tail}-500`);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
return Array.from(candidates);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function resolveColorValueForPath(path, value, hintValue) {
|
|
632
|
+
if (!isColorPath(path)) return null;
|
|
633
|
+
|
|
634
|
+
const fromValue = toColorInputValue(value);
|
|
635
|
+
if (normalizeHexColor(fromValue)) return fromValue;
|
|
636
|
+
|
|
637
|
+
const fromHint = toColorInputValue(hintValue);
|
|
638
|
+
if (normalizeHexColor(fromHint)) return fromHint;
|
|
639
|
+
|
|
640
|
+
if (typeof window === "undefined" || typeof document === "undefined") return null;
|
|
641
|
+
const root = document.documentElement;
|
|
642
|
+
if (!root) return null;
|
|
643
|
+
|
|
644
|
+
const style = window.getComputedStyle(root);
|
|
645
|
+
const candidates = inferColorVariableCandidates(path);
|
|
646
|
+
for (const varName of candidates) {
|
|
647
|
+
const raw = style.getPropertyValue(varName).trim();
|
|
648
|
+
if (!raw) continue;
|
|
649
|
+
const resolved = toColorInputValue(raw);
|
|
650
|
+
if (normalizeHexColor(resolved)) return resolved;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
return null;
|
|
654
|
+
}
|
|
655
|
+
|
|
592
656
|
function getCustomPropertyNames(style) {
|
|
593
657
|
const names = [];
|
|
594
658
|
if (!style) return names;
|
|
@@ -929,7 +993,92 @@ function splitFontFamilyStack(value) {
|
|
|
929
993
|
return parts;
|
|
930
994
|
}
|
|
931
995
|
|
|
932
|
-
|
|
996
|
+
const GENERIC_FONT_FAMILIES = new Set([
|
|
997
|
+
"serif",
|
|
998
|
+
"sans-serif",
|
|
999
|
+
"monospace",
|
|
1000
|
+
"cursive",
|
|
1001
|
+
"fantasy",
|
|
1002
|
+
"system-ui",
|
|
1003
|
+
"ui-serif",
|
|
1004
|
+
"ui-sans-serif",
|
|
1005
|
+
"ui-monospace",
|
|
1006
|
+
"ui-rounded",
|
|
1007
|
+
"emoji",
|
|
1008
|
+
"math",
|
|
1009
|
+
"fangsong",
|
|
1010
|
+
]);
|
|
1011
|
+
|
|
1012
|
+
let loadGoogleFontFnPromise = null;
|
|
1013
|
+
|
|
1014
|
+
function normalizeFontName(fontFamily) {
|
|
1015
|
+
return String(fontFamily || "")
|
|
1016
|
+
.trim()
|
|
1017
|
+
.replace(/^['"]+|['"]+$/g, "")
|
|
1018
|
+
.trim();
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
function isLikelyLoadableFont(fontFamily) {
|
|
1022
|
+
const normalized = normalizeFontName(fontFamily).toLowerCase();
|
|
1023
|
+
if (!normalized) return false;
|
|
1024
|
+
return !GENERIC_FONT_FAMILIES.has(normalized);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
async function getLoadGoogleFontFn() {
|
|
1028
|
+
if (typeof PDS?.loadGoogleFont === "function") {
|
|
1029
|
+
return PDS.loadGoogleFont;
|
|
1030
|
+
}
|
|
1031
|
+
if (loadGoogleFontFnPromise) return loadGoogleFontFnPromise;
|
|
1032
|
+
loadGoogleFontFnPromise = (async () => {
|
|
1033
|
+
const candidates = [
|
|
1034
|
+
PDS?.currentConfig?.managerURL,
|
|
1035
|
+
"../core/pds-manager.js",
|
|
1036
|
+
"/assets/pds/core/pds-manager.js",
|
|
1037
|
+
].filter(Boolean);
|
|
1038
|
+
|
|
1039
|
+
const attempted = new Set();
|
|
1040
|
+
for (const candidate of candidates) {
|
|
1041
|
+
try {
|
|
1042
|
+
const resolved = new URL(candidate, import.meta.url).href;
|
|
1043
|
+
if (attempted.has(resolved)) continue;
|
|
1044
|
+
attempted.add(resolved);
|
|
1045
|
+
const mod = await import(resolved);
|
|
1046
|
+
if (typeof mod?.loadGoogleFont === "function") {
|
|
1047
|
+
return mod.loadGoogleFont;
|
|
1048
|
+
}
|
|
1049
|
+
} catch (e) {}
|
|
1050
|
+
}
|
|
1051
|
+
return null;
|
|
1052
|
+
})();
|
|
1053
|
+
return loadGoogleFontFnPromise;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
async function loadTypographyFontsForDesign(typography) {
|
|
1057
|
+
if (!typography || typeof typography !== "object") return;
|
|
1058
|
+
|
|
1059
|
+
const loadGoogleFont = await getLoadGoogleFontFn();
|
|
1060
|
+
if (typeof loadGoogleFont !== "function") return;
|
|
1061
|
+
|
|
1062
|
+
const families = [
|
|
1063
|
+
typography.fontFamilyHeadings,
|
|
1064
|
+
typography.fontFamilyBody,
|
|
1065
|
+
typography.fontFamilyMono,
|
|
1066
|
+
];
|
|
1067
|
+
|
|
1068
|
+
const fontNames = new Set();
|
|
1069
|
+
families.forEach((stack) => {
|
|
1070
|
+
splitFontFamilyStack(stack).forEach((item) => {
|
|
1071
|
+
const fontName = normalizeFontName(item);
|
|
1072
|
+
if (isLikelyLoadableFont(fontName)) {
|
|
1073
|
+
fontNames.add(fontName);
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
await Promise.allSettled(Array.from(fontNames).map((name) => loadGoogleFont(name)));
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
function getPresetFontFamilyVariations(previewFontSize = resolveFontFamilyPreviewFontSize()) {
|
|
933
1082
|
const presets = Object.values(PDS?.presets || {});
|
|
934
1083
|
const seen = new Set();
|
|
935
1084
|
const items = [];
|
|
@@ -938,9 +1087,11 @@ function getPresetFontFamilyVariations() {
|
|
|
938
1087
|
if (!normalized || seen.has(normalized)) return;
|
|
939
1088
|
seen.add(normalized);
|
|
940
1089
|
items.push({
|
|
1090
|
+
//index: items.length,
|
|
941
1091
|
id: normalized,
|
|
1092
|
+
value: normalized,
|
|
942
1093
|
text: normalized,
|
|
943
|
-
style: `font-family: ${normalized}
|
|
1094
|
+
style: `font-family: ${normalized}; font-size: ${previewFontSize};`,
|
|
944
1095
|
});
|
|
945
1096
|
};
|
|
946
1097
|
|
|
@@ -959,35 +1110,95 @@ function getPresetFontFamilyVariations() {
|
|
|
959
1110
|
});
|
|
960
1111
|
});
|
|
961
1112
|
|
|
962
|
-
return items
|
|
1113
|
+
return items.sort((a, b) =>
|
|
1114
|
+
String(b?.text || "").localeCompare(String(a?.text || ""), undefined, {
|
|
1115
|
+
sensitivity: "base",
|
|
1116
|
+
})
|
|
1117
|
+
);
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
function resolveFontFamilyPreviewFontSize(control) {
|
|
1121
|
+
const controlInput = control?.querySelector?.(".ac-input");
|
|
1122
|
+
if (controlInput) {
|
|
1123
|
+
const fontSize = getComputedStyle(controlInput).fontSize;
|
|
1124
|
+
if (fontSize) return fontSize;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
const selectors = [
|
|
1128
|
+
"[name='/typography/fontFamilyBody']",
|
|
1129
|
+
"[name='/typography/fontFamilyHeadings']",
|
|
1130
|
+
"[name='/typography/fontFamilyMono']",
|
|
1131
|
+
];
|
|
1132
|
+
|
|
1133
|
+
for (const selector of selectors) {
|
|
1134
|
+
const omnibox = document.querySelector(selector);
|
|
1135
|
+
const input = omnibox?.shadowRoot?.querySelector?.(".ac-input");
|
|
1136
|
+
if (!input) continue;
|
|
1137
|
+
const fontSize = getComputedStyle(input).fontSize;
|
|
1138
|
+
if (fontSize) return fontSize;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
return "var(--font-size-md)";
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
async function loadGoogleFontsForFontFamilyItems(items) {
|
|
1145
|
+
if (!Array.isArray(items) || !items.length) return;
|
|
1146
|
+
|
|
1147
|
+
const loadGoogleFont = await getLoadGoogleFontFn();
|
|
1148
|
+
if (typeof loadGoogleFont !== "function") return;
|
|
1149
|
+
|
|
1150
|
+
const fontNames = new Set();
|
|
1151
|
+
items.forEach((item) => {
|
|
1152
|
+
const stack = item?.value || item?.text;
|
|
1153
|
+
splitFontFamilyStack(stack).forEach((entry) => {
|
|
1154
|
+
const fontName = normalizeFontName(entry);
|
|
1155
|
+
if (isLikelyLoadableFont(fontName)) {
|
|
1156
|
+
fontNames.add(fontName);
|
|
1157
|
+
}
|
|
1158
|
+
});
|
|
1159
|
+
});
|
|
1160
|
+
|
|
1161
|
+
if (!fontNames.size) return;
|
|
1162
|
+
await Promise.allSettled(Array.from(fontNames).map((name) => loadGoogleFont(name)));
|
|
963
1163
|
}
|
|
964
1164
|
|
|
965
1165
|
function buildFontFamilyOmniboxSettings() {
|
|
966
|
-
const
|
|
967
|
-
const filterItems = (search) => {
|
|
1166
|
+
const filterItems = (items, search) => {
|
|
968
1167
|
const query = String(search || "").trim().toLowerCase();
|
|
969
|
-
if (!query) return
|
|
970
|
-
return
|
|
1168
|
+
if (!query) return items;
|
|
1169
|
+
return items.filter((item) => {
|
|
1170
|
+
const text = String(
|
|
1171
|
+
item?.text || item?.id || item?.element?.textContent || ""
|
|
1172
|
+
).toLowerCase();
|
|
1173
|
+
return text.includes(query);
|
|
1174
|
+
});
|
|
971
1175
|
};
|
|
972
1176
|
|
|
973
|
-
|
|
1177
|
+
|
|
1178
|
+
return {
|
|
1179
|
+
//debug: true,
|
|
1180
|
+
itemGrid: "0 1fr 0",
|
|
974
1181
|
hideCategory: true,
|
|
975
|
-
iconHandler: (item) => {
|
|
976
|
-
|
|
1182
|
+
iconHandler: (item) => {
|
|
977
1183
|
return "";
|
|
978
1184
|
},
|
|
979
1185
|
categories: {
|
|
980
1186
|
FontFamilies: {
|
|
981
1187
|
trigger: () => true,
|
|
982
|
-
getItems: (options) =>
|
|
983
|
-
|
|
984
|
-
const
|
|
1188
|
+
getItems: async (options) => {
|
|
1189
|
+
const previewFontSize = resolveFontFamilyPreviewFontSize(options?.control);
|
|
1190
|
+
const allItems = getPresetFontFamilyVariations(previewFontSize);
|
|
1191
|
+
const items = filterItems(allItems, options?.search);
|
|
985
1192
|
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1193
|
+
await loadGoogleFontsForFontFamilyItems(items);
|
|
1194
|
+
|
|
1195
|
+
return items;
|
|
1196
|
+
},
|
|
1197
|
+
action: (options) => {
|
|
1198
|
+
const input = document.querySelector("pds-omnibox");
|
|
1199
|
+
if (input) {
|
|
1200
|
+
input.value = options.text;
|
|
989
1201
|
}
|
|
990
|
-
return options?.text || options?.id;
|
|
991
1202
|
},
|
|
992
1203
|
},
|
|
993
1204
|
},
|
|
@@ -1034,7 +1245,7 @@ function buildSchemaFromPaths(paths, design, hints = {}) {
|
|
|
1034
1245
|
};
|
|
1035
1246
|
|
|
1036
1247
|
const isColorValue = (value, path) => {
|
|
1037
|
-
if (
|
|
1248
|
+
if (isColorPath(path)) return true;
|
|
1038
1249
|
if (typeof value !== "string") return false;
|
|
1039
1250
|
return /^#([0-9a-f]{3,8})$/i.test(value) || /^rgba?\(/i.test(value) || /^hsla?\(/i.test(value);
|
|
1040
1251
|
};
|
|
@@ -1064,8 +1275,12 @@ function buildSchemaFromPaths(paths, design, hints = {}) {
|
|
|
1064
1275
|
const value = getValueAtPath(design, [category, ...rest]);
|
|
1065
1276
|
const hintValue = hints[path];
|
|
1066
1277
|
const enumOptions = getEnumOptions(path);
|
|
1067
|
-
const
|
|
1068
|
-
const
|
|
1278
|
+
const resolvedColorValue = resolveColorValueForPath(path, value, hintValue);
|
|
1279
|
+
const normalizedValue = normalizeEnumValue(path, resolvedColorValue ?? value);
|
|
1280
|
+
const normalizedHint = normalizeEnumValue(
|
|
1281
|
+
path,
|
|
1282
|
+
resolvedColorValue ?? hintValue,
|
|
1283
|
+
);
|
|
1069
1284
|
const inferredType = Array.isArray(value)
|
|
1070
1285
|
? "array"
|
|
1071
1286
|
: value === null
|
|
@@ -1232,6 +1447,10 @@ async function applyDesignPatch(patch) {
|
|
|
1232
1447
|
const nextOptions = { ...currentOptions, design: nextDesign };
|
|
1233
1448
|
if (resolvedPresetId) nextOptions.preset = resolvedPresetId;
|
|
1234
1449
|
|
|
1450
|
+
try {
|
|
1451
|
+
await loadTypographyFontsForDesign(nextDesign?.typography);
|
|
1452
|
+
} catch (e) {}
|
|
1453
|
+
|
|
1235
1454
|
const nextGenerator = new Generator(nextOptions);
|
|
1236
1455
|
if (PDS?.applyStyles) {
|
|
1237
1456
|
await PDS.applyStyles(nextGenerator);
|
|
@@ -1307,6 +1526,17 @@ function getActivePresetId() {
|
|
|
1307
1526
|
return stored?.preset || PDS?.currentConfig?.preset || PDS?.currentPreset || null;
|
|
1308
1527
|
}
|
|
1309
1528
|
|
|
1529
|
+
function getPresetNameById(presetId) {
|
|
1530
|
+
if (!presetId) return "";
|
|
1531
|
+
const presets = PDS?.presets || {};
|
|
1532
|
+
const preset =
|
|
1533
|
+
presets?.[presetId] ||
|
|
1534
|
+
Object.values(presets || {}).find(
|
|
1535
|
+
(candidate) => String(candidate?.id || candidate?.name) === String(presetId)
|
|
1536
|
+
);
|
|
1537
|
+
return preset?.name || String(presetId);
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1310
1540
|
async function applyPresetSelection(presetId) {
|
|
1311
1541
|
if (!presetId) return;
|
|
1312
1542
|
setStoredConfig({
|
|
@@ -1471,13 +1701,51 @@ function setFormSchemas(form, schema, uiSchema, design) {
|
|
|
1471
1701
|
form.values = shallowClone(design);
|
|
1472
1702
|
}
|
|
1473
1703
|
|
|
1474
|
-
async function
|
|
1475
|
-
|
|
1704
|
+
async function waitForElementDefinition(tagName, timeoutMs = 4000) {
|
|
1705
|
+
if (customElements.get(tagName)) return true;
|
|
1706
|
+
|
|
1707
|
+
let probe = null;
|
|
1708
|
+
try {
|
|
1709
|
+
if (typeof document !== "undefined" && document.body) {
|
|
1710
|
+
probe = document.createElement(tagName);
|
|
1711
|
+
probe.setAttribute("hidden", "");
|
|
1712
|
+
probe.setAttribute("aria-hidden", "true");
|
|
1713
|
+
probe.style.display = "none";
|
|
1714
|
+
document.body.appendChild(probe);
|
|
1715
|
+
}
|
|
1716
|
+
} catch (e) {}
|
|
1717
|
+
|
|
1718
|
+
await Promise.race([
|
|
1719
|
+
customElements.whenDefined(tagName),
|
|
1720
|
+
new Promise((_, reject) => {
|
|
1721
|
+
setTimeout(() => reject(new Error(`Timed out waiting for <${tagName}> definition`)), timeoutMs);
|
|
1722
|
+
}),
|
|
1723
|
+
]);
|
|
1724
|
+
|
|
1725
|
+
try {
|
|
1726
|
+
if (probe && probe.parentNode) {
|
|
1727
|
+
probe.parentNode.removeChild(probe);
|
|
1728
|
+
}
|
|
1729
|
+
} catch (e) {}
|
|
1476
1730
|
|
|
1477
|
-
if (!customElements.get(
|
|
1478
|
-
|
|
1731
|
+
if (!customElements.get(tagName)) {
|
|
1732
|
+
throw new Error(`<${tagName}> is not defined`);
|
|
1479
1733
|
}
|
|
1480
1734
|
|
|
1735
|
+
return true;
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
async function createConfiguredForm({
|
|
1739
|
+
schema,
|
|
1740
|
+
uiSchema,
|
|
1741
|
+
values,
|
|
1742
|
+
onSubmit,
|
|
1743
|
+
onUndo,
|
|
1744
|
+
normalizeFlatValues,
|
|
1745
|
+
formOptions,
|
|
1746
|
+
}) {
|
|
1747
|
+
await waitForElementDefinition("pds-form");
|
|
1748
|
+
|
|
1481
1749
|
const form = document.createElement("pds-form");
|
|
1482
1750
|
const fontFamilyOmniboxSettings = buildFontFamilyOmniboxSettings();
|
|
1483
1751
|
form.setAttribute("hide-actions", "");
|
|
@@ -1488,16 +1756,38 @@ async function buildForm(paths, design, onSubmit, onUndo, hints = {}) {
|
|
|
1488
1756
|
enhancements: {
|
|
1489
1757
|
rangeOutput: true,
|
|
1490
1758
|
},
|
|
1759
|
+
...(formOptions && typeof formOptions === "object" ? formOptions : {}),
|
|
1491
1760
|
};
|
|
1761
|
+
|
|
1492
1762
|
form.defineRenderer(
|
|
1493
1763
|
"font-family-omnibox",
|
|
1494
1764
|
({ id, path, value, attrs, set }) => {
|
|
1495
|
-
const resolveSelectedValue = (options, actionResult) => {
|
|
1765
|
+
const resolveSelectedValue = (options, actionResult, selectionEvent) => {
|
|
1496
1766
|
if (typeof actionResult === "string" && actionResult.trim()) {
|
|
1497
1767
|
return actionResult;
|
|
1498
1768
|
}
|
|
1769
|
+
|
|
1770
|
+
const eventDetail = selectionEvent?.detail;
|
|
1771
|
+
const fromEventValue = String(eventDetail?.value || "").trim();
|
|
1772
|
+
if (fromEventValue) return fromEventValue;
|
|
1773
|
+
|
|
1774
|
+
const fromEventText = String(eventDetail?.text || "").trim();
|
|
1775
|
+
if (fromEventText) return fromEventText;
|
|
1776
|
+
|
|
1777
|
+
const fromEventElementText = String(
|
|
1778
|
+
eventDetail?.element?.textContent || ""
|
|
1779
|
+
).trim();
|
|
1780
|
+
if (fromEventElementText) return fromEventElementText;
|
|
1781
|
+
|
|
1499
1782
|
const fromText = String(options?.text || "").trim();
|
|
1500
1783
|
if (fromText) return fromText;
|
|
1784
|
+
|
|
1785
|
+
const fromValue = String(options?.value || "").trim();
|
|
1786
|
+
if (fromValue) return fromValue;
|
|
1787
|
+
|
|
1788
|
+
const fromElementText = String(options?.element?.textContent || "").trim();
|
|
1789
|
+
if (fromElementText) return fromElementText;
|
|
1790
|
+
|
|
1501
1791
|
return String(options?.id || "").trim();
|
|
1502
1792
|
};
|
|
1503
1793
|
|
|
@@ -1509,15 +1799,36 @@ async function buildForm(paths, design, onSubmit, onUndo, hints = {}) {
|
|
|
1509
1799
|
categoryName,
|
|
1510
1800
|
{
|
|
1511
1801
|
...categoryConfig,
|
|
1512
|
-
action: (
|
|
1802
|
+
action: (...args) => {
|
|
1803
|
+
const [options, selectionEvent] = args;
|
|
1513
1804
|
const actionResult =
|
|
1514
1805
|
typeof originalAction === "function"
|
|
1515
|
-
? originalAction(
|
|
1806
|
+
? originalAction(...args)
|
|
1516
1807
|
: undefined;
|
|
1517
|
-
|
|
1808
|
+
|
|
1809
|
+
if (actionResult && typeof actionResult.then === "function") {
|
|
1810
|
+
return actionResult.then((resolved) => {
|
|
1811
|
+
const selected = resolveSelectedValue(
|
|
1812
|
+
options,
|
|
1813
|
+
resolved,
|
|
1814
|
+
selectionEvent
|
|
1815
|
+
);
|
|
1816
|
+
if (selected) {
|
|
1817
|
+
set(selected);
|
|
1818
|
+
}
|
|
1819
|
+
return resolved;
|
|
1820
|
+
});
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
const selected = resolveSelectedValue(
|
|
1824
|
+
options,
|
|
1825
|
+
actionResult,
|
|
1826
|
+
selectionEvent
|
|
1827
|
+
);
|
|
1518
1828
|
if (selected) {
|
|
1519
1829
|
set(selected);
|
|
1520
1830
|
}
|
|
1831
|
+
|
|
1521
1832
|
return actionResult;
|
|
1522
1833
|
},
|
|
1523
1834
|
},
|
|
@@ -1529,8 +1840,11 @@ async function buildForm(paths, design, onSubmit, onUndo, hints = {}) {
|
|
|
1529
1840
|
const omnibox = document.createElement("pds-omnibox");
|
|
1530
1841
|
omnibox.id = id;
|
|
1531
1842
|
omnibox.setAttribute("name", path);
|
|
1532
|
-
omnibox.setAttribute("item-grid", "0 1fr");
|
|
1533
|
-
omnibox.setAttribute(
|
|
1843
|
+
omnibox.setAttribute("item-grid", "0 1fr 0");
|
|
1844
|
+
omnibox.setAttribute(
|
|
1845
|
+
"placeholder",
|
|
1846
|
+
attrs?.placeholder || "Select a font family"
|
|
1847
|
+
);
|
|
1534
1848
|
omnibox.value = value ?? "";
|
|
1535
1849
|
omnibox.settings = {
|
|
1536
1850
|
...fontFamilyOmniboxSettings,
|
|
@@ -1542,64 +1856,364 @@ async function buildForm(paths, design, onSubmit, onUndo, hints = {}) {
|
|
|
1542
1856
|
omnibox.addEventListener("change", (event) => {
|
|
1543
1857
|
set(event?.target?.value ?? omnibox.value ?? "");
|
|
1544
1858
|
});
|
|
1859
|
+
omnibox.addEventListener("result-selected", (event) => {
|
|
1860
|
+
const selected = resolveSelectedValue(event?.detail, undefined, event);
|
|
1861
|
+
if (!selected) return;
|
|
1862
|
+
omnibox.value = selected;
|
|
1863
|
+
set(selected);
|
|
1864
|
+
});
|
|
1545
1865
|
return omnibox;
|
|
1546
1866
|
}
|
|
1547
1867
|
);
|
|
1868
|
+
|
|
1548
1869
|
form.addEventListener("pw:submit", onSubmit);
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
setValueAtPath(values, path.split("."), normalized);
|
|
1554
|
-
}
|
|
1555
|
-
});
|
|
1556
|
-
Object.entries(hints || {}).forEach(([path, hintValue]) => {
|
|
1557
|
-
const segments = path.split(".");
|
|
1558
|
-
const currentValue = getValueAtPath(values, segments);
|
|
1559
|
-
if (currentValue === undefined || currentValue === null) {
|
|
1560
|
-
setValueAtPath(values, segments, hintValue);
|
|
1561
|
-
}
|
|
1562
|
-
});
|
|
1563
|
-
setFormSchemas(form, schema, uiSchema, values);
|
|
1870
|
+
if (typeof normalizeFlatValues === "function") {
|
|
1871
|
+
form._normalizeFlatValues = normalizeFlatValues;
|
|
1872
|
+
}
|
|
1873
|
+
setFormSchemas(form, schema, uiSchema, values || {});
|
|
1564
1874
|
|
|
1565
|
-
// Apply button (will trigger form submit programmatically)
|
|
1566
1875
|
const applyBtn = document.createElement("button");
|
|
1567
1876
|
applyBtn.className = "btn-primary btn-sm";
|
|
1568
1877
|
applyBtn.type = "button";
|
|
1569
1878
|
applyBtn.textContent = "Apply";
|
|
1570
1879
|
applyBtn.addEventListener("click", async () => {
|
|
1571
|
-
// Manually trigger pw:submit event for pds-form
|
|
1572
1880
|
if (typeof form.getValuesFlat === "function") {
|
|
1573
|
-
// Wait for form to be ready if it's still loading
|
|
1574
1881
|
if (!customElements.get("pds-form")) {
|
|
1575
1882
|
await customElements.whenDefined("pds-form");
|
|
1576
1883
|
}
|
|
1577
|
-
|
|
1578
|
-
const flatValues =
|
|
1884
|
+
|
|
1885
|
+
const flatValues =
|
|
1886
|
+
typeof form._normalizeFlatValues === "function"
|
|
1887
|
+
? form._normalizeFlatValues(form.getValuesFlat())
|
|
1888
|
+
: form.getValuesFlat();
|
|
1579
1889
|
const event = new CustomEvent("pw:submit", {
|
|
1580
1890
|
detail: {
|
|
1581
1891
|
json: flatValues,
|
|
1582
1892
|
formData: new FormData(),
|
|
1583
1893
|
valid: true,
|
|
1584
|
-
issues: []
|
|
1894
|
+
issues: [],
|
|
1585
1895
|
},
|
|
1586
1896
|
bubbles: true,
|
|
1587
|
-
cancelable: true
|
|
1897
|
+
cancelable: true,
|
|
1588
1898
|
});
|
|
1589
1899
|
form.dispatchEvent(event);
|
|
1590
1900
|
}
|
|
1591
1901
|
});
|
|
1592
1902
|
|
|
1593
|
-
// Undo button
|
|
1594
1903
|
const undoBtn = document.createElement("button");
|
|
1595
|
-
undoBtn.className = "btn-secondary btn-sm";
|
|
1904
|
+
undoBtn.className = "btn-secondary btn-sm icon-only";
|
|
1596
1905
|
undoBtn.type = "button";
|
|
1597
|
-
undoBtn.
|
|
1906
|
+
undoBtn.setAttribute("aria-label", "Undo");
|
|
1907
|
+
undoBtn.setAttribute("title", "Undo");
|
|
1908
|
+
const undoIcon = document.createElement("pds-icon");
|
|
1909
|
+
undoIcon.setAttribute("icon", "arrow-counter-clockwise");
|
|
1910
|
+
undoIcon.setAttribute("size", "sm");
|
|
1911
|
+
undoBtn.appendChild(undoIcon);
|
|
1598
1912
|
undoBtn.addEventListener("click", onUndo);
|
|
1599
1913
|
|
|
1600
1914
|
return { form, applyBtn, undoBtn };
|
|
1601
1915
|
}
|
|
1602
1916
|
|
|
1917
|
+
async function buildForm(paths, design, onSubmit, onUndo, hints = {}) {
|
|
1918
|
+
const quickPayload = buildQuickConfigPayload(paths, design, hints);
|
|
1919
|
+
const schema = quickPayload?.schema;
|
|
1920
|
+
const uiSchema = quickPayload?.uiSchema;
|
|
1921
|
+
const values = quickPayload?.values;
|
|
1922
|
+
|
|
1923
|
+
if (!schema || !uiSchema) {
|
|
1924
|
+
throw new Error("Central config form metadata is unavailable for quick edit");
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
return createConfiguredForm({
|
|
1928
|
+
schema,
|
|
1929
|
+
uiSchema,
|
|
1930
|
+
values: values || {},
|
|
1931
|
+
onSubmit,
|
|
1932
|
+
onUndo,
|
|
1933
|
+
formOptions: {
|
|
1934
|
+
layouts: {
|
|
1935
|
+
arrays: "compact",
|
|
1936
|
+
},
|
|
1937
|
+
enhancements: {
|
|
1938
|
+
rangeOutput: true,
|
|
1939
|
+
},
|
|
1940
|
+
},
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
function getConfigFormPayloadFromMetadata(design) {
|
|
1945
|
+
if (typeof PDS?.buildConfigFormSchema === "function") {
|
|
1946
|
+
return PDS.buildConfigFormSchema(design);
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
const payload = PDS?.configFormSchema;
|
|
1950
|
+
if (payload && payload.schema && payload.uiSchema) {
|
|
1951
|
+
return {
|
|
1952
|
+
schema: payload.schema,
|
|
1953
|
+
uiSchema: payload.uiSchema,
|
|
1954
|
+
values: shallowClone(design || payload.values || {}),
|
|
1955
|
+
metadata: payload.metadata || {},
|
|
1956
|
+
};
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
return null;
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
function deepClone(value) {
|
|
1963
|
+
if (typeof structuredClone === "function") {
|
|
1964
|
+
try {
|
|
1965
|
+
return structuredClone(value);
|
|
1966
|
+
} catch (e) {
|
|
1967
|
+
// Fall through to JSON clone
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
return JSON.parse(JSON.stringify(value));
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
function shouldKeepPathForSelection(selectedPaths, path) {
|
|
1974
|
+
if (!path) return true;
|
|
1975
|
+
return selectedPaths.some((selectedPath) => {
|
|
1976
|
+
if (selectedPath === path) return true;
|
|
1977
|
+
return selectedPath.startsWith(`${path}.`);
|
|
1978
|
+
});
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
function pruneSchemaForPaths(node, selectedPaths, path = "") {
|
|
1982
|
+
if (!node || typeof node !== "object") return node;
|
|
1983
|
+
if (!isObjectSchemaNode(node)) return deepClone(node);
|
|
1984
|
+
|
|
1985
|
+
if (path && !shouldKeepPathForSelection(selectedPaths, path)) {
|
|
1986
|
+
return null;
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
const properties = {};
|
|
1990
|
+
Object.entries(node.properties || {}).forEach(([key, childNode]) => {
|
|
1991
|
+
const childPath = path ? `${path}.${key}` : key;
|
|
1992
|
+
if (!shouldKeepPathForSelection(selectedPaths, childPath)) return;
|
|
1993
|
+
const prunedChild = pruneSchemaForPaths(childNode, selectedPaths, childPath);
|
|
1994
|
+
if (prunedChild) {
|
|
1995
|
+
properties[key] = prunedChild;
|
|
1996
|
+
}
|
|
1997
|
+
});
|
|
1998
|
+
|
|
1999
|
+
if (!Object.keys(properties).length) return null;
|
|
2000
|
+
|
|
2001
|
+
const clonedNode = deepClone(node);
|
|
2002
|
+
clonedNode.properties = properties;
|
|
2003
|
+
if (Array.isArray(clonedNode.required)) {
|
|
2004
|
+
clonedNode.required = clonedNode.required.filter((key) =>
|
|
2005
|
+
Object.prototype.hasOwnProperty.call(properties, key)
|
|
2006
|
+
);
|
|
2007
|
+
}
|
|
2008
|
+
return clonedNode;
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
function uiPointerToPath(pointer) {
|
|
2012
|
+
if (!pointer || pointer === "/") return "";
|
|
2013
|
+
return pointer
|
|
2014
|
+
.replace(/^\//, "")
|
|
2015
|
+
.split("/")
|
|
2016
|
+
.filter(Boolean)
|
|
2017
|
+
.join(".");
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
function filterUiSchemaForPaths(uiSchema, selectedPaths) {
|
|
2021
|
+
if (!uiSchema || typeof uiSchema !== "object") return {};
|
|
2022
|
+
const filtered = {};
|
|
2023
|
+
Object.entries(uiSchema).forEach(([pointer, value]) => {
|
|
2024
|
+
const path = uiPointerToPath(pointer);
|
|
2025
|
+
if (!path || shouldKeepPathForSelection(selectedPaths, path)) {
|
|
2026
|
+
filtered[pointer] = deepClone(value);
|
|
2027
|
+
}
|
|
2028
|
+
});
|
|
2029
|
+
return filtered;
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
function buildValuesForPaths(valuesSource, selectedPaths, hints = {}) {
|
|
2033
|
+
const values = {};
|
|
2034
|
+
selectedPaths.forEach((path) => {
|
|
2035
|
+
const segments = path.split(".");
|
|
2036
|
+
let value = getValueAtPath(valuesSource, segments);
|
|
2037
|
+
if ((value === undefined || value === null) && hints[path] !== undefined) {
|
|
2038
|
+
value = hints[path];
|
|
2039
|
+
}
|
|
2040
|
+
if (isColorPath(path)) {
|
|
2041
|
+
const resolvedColorValue = resolveColorValueForPath(path, value, hints[path]);
|
|
2042
|
+
if (resolvedColorValue) {
|
|
2043
|
+
value = resolvedColorValue;
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
if (value !== undefined) {
|
|
2047
|
+
setValueAtPath(values, segments, deepClone(value));
|
|
2048
|
+
}
|
|
2049
|
+
});
|
|
2050
|
+
return values;
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
function buildQuickConfigPayload(paths, design, hints = {}) {
|
|
2054
|
+
const payload = getConfigFormPayloadFromMetadata(design);
|
|
2055
|
+
if (!payload?.schema || !payload?.uiSchema) return null;
|
|
2056
|
+
|
|
2057
|
+
const selectedPaths = normalizePaths(paths);
|
|
2058
|
+
if (!selectedPaths.length) return null;
|
|
2059
|
+
|
|
2060
|
+
const schema = pruneSchemaForPaths(payload.schema, selectedPaths, "");
|
|
2061
|
+
if (!schema) return null;
|
|
2062
|
+
|
|
2063
|
+
const uiSchema = filterUiSchemaForPaths(payload.uiSchema, selectedPaths);
|
|
2064
|
+
const valuesSource =
|
|
2065
|
+
payload?.values && typeof payload.values === "object"
|
|
2066
|
+
? payload.values
|
|
2067
|
+
: shallowClone(design || {});
|
|
2068
|
+
const values = buildValuesForPaths(valuesSource || {}, selectedPaths, hints);
|
|
2069
|
+
|
|
2070
|
+
return { schema, uiSchema, values };
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
const FULL_CONFIG_GROUPS_KEY = "__groups";
|
|
2074
|
+
|
|
2075
|
+
function isObjectSchemaNode(node) {
|
|
2076
|
+
return !!(node && typeof node === "object" && node.type === "object" && node.properties);
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
function buildGroupedFullConfigPayload(payload, design) {
|
|
2080
|
+
const values =
|
|
2081
|
+
payload?.values && typeof payload.values === "object"
|
|
2082
|
+
? payload.values
|
|
2083
|
+
: shallowClone(design || {});
|
|
2084
|
+
|
|
2085
|
+
if (!payload?.schema || !payload?.uiSchema || !isObjectSchemaNode(payload.schema)) {
|
|
2086
|
+
return {
|
|
2087
|
+
schema: payload?.schema,
|
|
2088
|
+
uiSchema: payload?.uiSchema,
|
|
2089
|
+
values,
|
|
2090
|
+
normalizeFlatValues: null,
|
|
2091
|
+
};
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
const rootProperties = payload.schema.properties || {};
|
|
2095
|
+
const groupedKeys = [];
|
|
2096
|
+
const scalarKeys = [];
|
|
2097
|
+
|
|
2098
|
+
Object.entries(rootProperties).forEach(([key, schemaNode]) => {
|
|
2099
|
+
if (isObjectSchemaNode(schemaNode)) {
|
|
2100
|
+
groupedKeys.push(key);
|
|
2101
|
+
return;
|
|
2102
|
+
}
|
|
2103
|
+
scalarKeys.push(key);
|
|
2104
|
+
});
|
|
2105
|
+
|
|
2106
|
+
if (!groupedKeys.length || !scalarKeys.length) {
|
|
2107
|
+
return {
|
|
2108
|
+
schema: payload.schema,
|
|
2109
|
+
uiSchema: payload.uiSchema,
|
|
2110
|
+
values,
|
|
2111
|
+
normalizeFlatValues: null,
|
|
2112
|
+
};
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
const transformedSchema = {
|
|
2116
|
+
...payload.schema,
|
|
2117
|
+
properties: {
|
|
2118
|
+
...Object.fromEntries(scalarKeys.map((key) => [key, rootProperties[key]])),
|
|
2119
|
+
[FULL_CONFIG_GROUPS_KEY]: {
|
|
2120
|
+
type: "object",
|
|
2121
|
+
title: "Design Groups",
|
|
2122
|
+
properties: Object.fromEntries(
|
|
2123
|
+
groupedKeys.map((key) => [key, rootProperties[key]])
|
|
2124
|
+
),
|
|
2125
|
+
},
|
|
2126
|
+
},
|
|
2127
|
+
};
|
|
2128
|
+
|
|
2129
|
+
const transformedValues = {
|
|
2130
|
+
...Object.fromEntries(scalarKeys.map((key) => [key, values?.[key]])),
|
|
2131
|
+
[FULL_CONFIG_GROUPS_KEY]: Object.fromEntries(
|
|
2132
|
+
groupedKeys.map((key) => [key, values?.[key]])
|
|
2133
|
+
),
|
|
2134
|
+
};
|
|
2135
|
+
|
|
2136
|
+
const transformedUiSchema = { ...(payload.uiSchema || {}) };
|
|
2137
|
+
const addGroupPrefix = (path = "") => `/${FULL_CONFIG_GROUPS_KEY}${path}`;
|
|
2138
|
+
|
|
2139
|
+
groupedKeys.forEach((key) => {
|
|
2140
|
+
const originalPath = `/${key}`;
|
|
2141
|
+
|
|
2142
|
+
if (Object.prototype.hasOwnProperty.call(transformedUiSchema, originalPath)) {
|
|
2143
|
+
transformedUiSchema[addGroupPrefix(originalPath)] = transformedUiSchema[originalPath];
|
|
2144
|
+
delete transformedUiSchema[originalPath];
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
Object.keys(transformedUiSchema).forEach((path) => {
|
|
2148
|
+
if (!path.startsWith(`${originalPath}/`)) return;
|
|
2149
|
+
transformedUiSchema[addGroupPrefix(path)] = transformedUiSchema[path];
|
|
2150
|
+
delete transformedUiSchema[path];
|
|
2151
|
+
});
|
|
2152
|
+
});
|
|
2153
|
+
|
|
2154
|
+
transformedUiSchema[`/${FULL_CONFIG_GROUPS_KEY}`] = {
|
|
2155
|
+
"ui:layout": "accordion",
|
|
2156
|
+
"ui:layoutOptions": { openFirst: false },
|
|
2157
|
+
};
|
|
2158
|
+
|
|
2159
|
+
const normalizeFlatValues = (flatValues = {}) => {
|
|
2160
|
+
const normalized = {};
|
|
2161
|
+
const groupPointerPrefix = `/${FULL_CONFIG_GROUPS_KEY}/`;
|
|
2162
|
+
const groupDotPrefix = `${FULL_CONFIG_GROUPS_KEY}.`;
|
|
2163
|
+
Object.entries(flatValues || {}).forEach(([path, value]) => {
|
|
2164
|
+
const inputPath = String(path || "");
|
|
2165
|
+
if (!inputPath) return;
|
|
2166
|
+
if (inputPath === FULL_CONFIG_GROUPS_KEY || inputPath === `/${FULL_CONFIG_GROUPS_KEY}`) {
|
|
2167
|
+
return;
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
if (inputPath.startsWith(groupPointerPrefix)) {
|
|
2171
|
+
normalized[`/${inputPath.slice(groupPointerPrefix.length)}`] = value;
|
|
2172
|
+
return;
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
if (inputPath.startsWith(groupDotPrefix)) {
|
|
2176
|
+
normalized[inputPath.slice(groupDotPrefix.length)] = value;
|
|
2177
|
+
return;
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
normalized[inputPath] = value;
|
|
2181
|
+
});
|
|
2182
|
+
return normalized;
|
|
2183
|
+
};
|
|
2184
|
+
|
|
2185
|
+
return {
|
|
2186
|
+
schema: transformedSchema,
|
|
2187
|
+
uiSchema: transformedUiSchema,
|
|
2188
|
+
values: transformedValues,
|
|
2189
|
+
normalizeFlatValues,
|
|
2190
|
+
};
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
async function buildFullConfigForm(design, onSubmit, onUndo) {
|
|
2194
|
+
const payload = getConfigFormPayloadFromMetadata(design);
|
|
2195
|
+
if (!payload?.schema || !payload?.uiSchema) return null;
|
|
2196
|
+
|
|
2197
|
+
const groupedPayload = buildGroupedFullConfigPayload(payload, design);
|
|
2198
|
+
|
|
2199
|
+
return createConfiguredForm({
|
|
2200
|
+
schema: groupedPayload.schema,
|
|
2201
|
+
uiSchema: groupedPayload.uiSchema,
|
|
2202
|
+
values: groupedPayload.values,
|
|
2203
|
+
onSubmit,
|
|
2204
|
+
onUndo,
|
|
2205
|
+
normalizeFlatValues: groupedPayload.normalizeFlatValues,
|
|
2206
|
+
formOptions: {
|
|
2207
|
+
layouts: {
|
|
2208
|
+
arrays: "compact",
|
|
2209
|
+
},
|
|
2210
|
+
enhancements: {
|
|
2211
|
+
rangeOutput: true,
|
|
2212
|
+
},
|
|
2213
|
+
},
|
|
2214
|
+
});
|
|
2215
|
+
}
|
|
2216
|
+
|
|
1603
2217
|
class PdsLiveEdit extends HTMLElement {
|
|
1604
2218
|
constructor() {
|
|
1605
2219
|
super();
|
|
@@ -2017,7 +2631,7 @@ class PdsLiveEdit extends HTMLElement {
|
|
|
2017
2631
|
button.appendChild(icon);
|
|
2018
2632
|
|
|
2019
2633
|
const menu = document.createElement("menu");
|
|
2020
|
-
|
|
2634
|
+
const quickItem = document.createElement("div");
|
|
2021
2635
|
quickItem.className = "pds-live-editor-menu";
|
|
2022
2636
|
|
|
2023
2637
|
const header = document.createElement("div");
|
|
@@ -2063,13 +2677,28 @@ class PdsLiveEdit extends HTMLElement {
|
|
|
2063
2677
|
container.replaceChildren();
|
|
2064
2678
|
footer.replaceChildren();
|
|
2065
2679
|
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2680
|
+
let form;
|
|
2681
|
+
let applyBtn;
|
|
2682
|
+
let undoBtn;
|
|
2683
|
+
try {
|
|
2684
|
+
const result = await buildForm(
|
|
2685
|
+
paths,
|
|
2686
|
+
design,
|
|
2687
|
+
(event) => this._handleFormSubmit(event, form),
|
|
2688
|
+
() => this._handleUndo(),
|
|
2689
|
+
hints
|
|
2690
|
+
);
|
|
2691
|
+
form = result.form;
|
|
2692
|
+
applyBtn = result.applyBtn;
|
|
2693
|
+
undoBtn = result.undoBtn;
|
|
2694
|
+
} catch (error) {
|
|
2695
|
+
const fallback = document.createElement("p");
|
|
2696
|
+
fallback.className = "text-muted";
|
|
2697
|
+
fallback.textContent = "Editor form unavailable. Lazy component definition did not complete in time.";
|
|
2698
|
+
container.appendChild(fallback);
|
|
2699
|
+
console.warn("[PDS Live Edit] Failed to render quick form:", error);
|
|
2700
|
+
return;
|
|
2701
|
+
}
|
|
2073
2702
|
|
|
2074
2703
|
// Store reference to undo button for enabling/disabling
|
|
2075
2704
|
form._undoBtn = undoBtn;
|
|
@@ -2137,26 +2766,72 @@ class PdsLiveEdit extends HTMLElement {
|
|
|
2137
2766
|
presetText.textContent = "Choose a base style";
|
|
2138
2767
|
presetLabel.appendChild(presetText);
|
|
2139
2768
|
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2769
|
+
let presetControlRendered = false;
|
|
2770
|
+
try {
|
|
2771
|
+
await waitForElementDefinition("pds-omnibox");
|
|
2772
|
+
|
|
2773
|
+
const presetOmnibox = document.createElement("pds-omnibox");
|
|
2774
|
+
presetOmnibox.setAttribute("item-grid", "0 1fr 0");
|
|
2775
|
+
presetOmnibox.setAttribute("placeholder", "Search presets...");
|
|
2143
2776
|
|
|
2144
|
-
|
|
2145
|
-
const
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
if (String(preset.id) === String(activePreset)) {
|
|
2149
|
-
option.selected = true;
|
|
2777
|
+
const activePresetId = getActivePresetId();
|
|
2778
|
+
const activePresetName = getPresetNameById(activePresetId);
|
|
2779
|
+
if (activePresetName) {
|
|
2780
|
+
presetOmnibox.value = activePresetName;
|
|
2150
2781
|
}
|
|
2151
|
-
presetSelect.appendChild(option);
|
|
2152
|
-
});
|
|
2153
2782
|
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2783
|
+
const omniboxSettingsBuilder =
|
|
2784
|
+
typeof PDS?.buildPresetOmniboxSettings === "function"
|
|
2785
|
+
? PDS.buildPresetOmniboxSettings.bind(PDS)
|
|
2786
|
+
: null;
|
|
2787
|
+
|
|
2788
|
+
if (omniboxSettingsBuilder) {
|
|
2789
|
+
presetOmnibox.settings = omniboxSettingsBuilder({
|
|
2790
|
+
onSelect: async ({ preset, selection }) => {
|
|
2791
|
+
if (selection?.disabled) return selection?.id;
|
|
2792
|
+
const presetId = preset?.id || selection?.id;
|
|
2793
|
+
await applyPresetSelection(presetId);
|
|
2794
|
+
return presetId;
|
|
2795
|
+
},
|
|
2796
|
+
});
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2799
|
+
presetOmnibox.addEventListener("result-selected", (event) => {
|
|
2800
|
+
const selectedText = event?.detail?.text;
|
|
2801
|
+
if (typeof selectedText === "string" && selectedText.trim()) {
|
|
2802
|
+
presetOmnibox.value = selectedText;
|
|
2803
|
+
}
|
|
2804
|
+
});
|
|
2805
|
+
|
|
2806
|
+
presetLabel.appendChild(presetOmnibox);
|
|
2807
|
+
presetControlRendered = true;
|
|
2808
|
+
} catch (error) {
|
|
2809
|
+
console.warn("[PDS Live Edit] Preset omnibox unavailable, falling back to select.", error);
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
if (!presetControlRendered) {
|
|
2813
|
+
const presetSelect = document.createElement("select");
|
|
2814
|
+
const presetOptions = getPresetOptions();
|
|
2815
|
+
const activePreset = getActivePresetId();
|
|
2816
|
+
|
|
2817
|
+
presetOptions.forEach((preset) => {
|
|
2818
|
+
const option = document.createElement("option");
|
|
2819
|
+
option.value = preset.id;
|
|
2820
|
+
option.textContent = preset.name;
|
|
2821
|
+
if (String(preset.id) === String(activePreset)) {
|
|
2822
|
+
option.selected = true;
|
|
2823
|
+
}
|
|
2824
|
+
presetSelect.appendChild(option);
|
|
2825
|
+
});
|
|
2826
|
+
|
|
2827
|
+
presetSelect.addEventListener("change", async (event) => {
|
|
2828
|
+
const nextPreset = event.target?.value;
|
|
2829
|
+
await applyPresetSelection(nextPreset);
|
|
2830
|
+
});
|
|
2831
|
+
|
|
2832
|
+
presetLabel.appendChild(presetSelect);
|
|
2833
|
+
}
|
|
2158
2834
|
|
|
2159
|
-
presetLabel.appendChild(presetSelect);
|
|
2160
2835
|
presetCard.appendChild(presetLabel);
|
|
2161
2836
|
|
|
2162
2837
|
const themeCard = document.createElement("section");
|
|
@@ -2169,6 +2844,48 @@ class PdsLiveEdit extends HTMLElement {
|
|
|
2169
2844
|
const themeToggle = document.createElement("pds-theme");
|
|
2170
2845
|
themeCard.appendChild(themeToggle);
|
|
2171
2846
|
|
|
2847
|
+
const configCard = document.createElement("section");
|
|
2848
|
+
configCard.className = "card surface-elevated stack-sm";
|
|
2849
|
+
|
|
2850
|
+
const configTitle = document.createElement("h4");
|
|
2851
|
+
configTitle.textContent = "Configuration";
|
|
2852
|
+
configCard.appendChild(configTitle);
|
|
2853
|
+
|
|
2854
|
+
const configDescription = document.createElement("p");
|
|
2855
|
+
configDescription.className = "text-muted";
|
|
2856
|
+
configDescription.textContent =
|
|
2857
|
+
"Edit the full design config generated from PDS metadata.";
|
|
2858
|
+
configCard.appendChild(configDescription);
|
|
2859
|
+
|
|
2860
|
+
const configFormContainer = document.createElement("div");
|
|
2861
|
+
configFormContainer.className = "stack-sm";
|
|
2862
|
+
configCard.appendChild(configFormContainer);
|
|
2863
|
+
|
|
2864
|
+
const configFooter = document.createElement("div");
|
|
2865
|
+
configFooter.className = "flex gap-sm";
|
|
2866
|
+
configCard.appendChild(configFooter);
|
|
2867
|
+
|
|
2868
|
+
const fullDesign = shallowClone(PDS?.currentConfig?.design || {});
|
|
2869
|
+
const fullConfigFormResult = await buildFullConfigForm(
|
|
2870
|
+
fullDesign,
|
|
2871
|
+
(event) => this._handleFormSubmit(event, fullConfigFormResult?.form),
|
|
2872
|
+
() => this._handleUndo()
|
|
2873
|
+
);
|
|
2874
|
+
|
|
2875
|
+
if (fullConfigFormResult?.form) {
|
|
2876
|
+
fullConfigFormResult.form._undoBtn = fullConfigFormResult.undoBtn;
|
|
2877
|
+
fullConfigFormResult.undoBtn.disabled = this._undoStack.length === 0;
|
|
2878
|
+
configFormContainer.appendChild(fullConfigFormResult.form);
|
|
2879
|
+
configFooter.appendChild(fullConfigFormResult.applyBtn);
|
|
2880
|
+
configFooter.appendChild(fullConfigFormResult.undoBtn);
|
|
2881
|
+
} else {
|
|
2882
|
+
const unavailable = document.createElement("p");
|
|
2883
|
+
unavailable.className = "text-muted";
|
|
2884
|
+
unavailable.textContent =
|
|
2885
|
+
"Full config metadata is unavailable in this runtime.";
|
|
2886
|
+
configFormContainer.appendChild(unavailable);
|
|
2887
|
+
}
|
|
2888
|
+
|
|
2172
2889
|
const exportCard = document.createElement("section");
|
|
2173
2890
|
exportCard.className = "card surface-elevated stack-sm";
|
|
2174
2891
|
|
|
@@ -2228,54 +2945,28 @@ class PdsLiveEdit extends HTMLElement {
|
|
|
2228
2945
|
exportNav.append(exportButton, exportMenu);
|
|
2229
2946
|
exportCard.appendChild(exportNav);
|
|
2230
2947
|
|
|
2231
|
-
const
|
|
2232
|
-
|
|
2948
|
+
const resetCard = document.createElement("section");
|
|
2949
|
+
resetCard.className = "card surface-elevated stack-sm";
|
|
2233
2950
|
|
|
2234
|
-
const
|
|
2235
|
-
|
|
2236
|
-
|
|
2951
|
+
const resetTitle = document.createElement("h4");
|
|
2952
|
+
resetTitle.textContent = "Reset";
|
|
2953
|
+
resetCard.appendChild(resetTitle);
|
|
2237
2954
|
|
|
2238
|
-
const
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
getItems: async (options) => {
|
|
2248
|
-
const query = (options.search || "").trim();
|
|
2249
|
-
if (!query) return [];
|
|
2250
|
-
try {
|
|
2251
|
-
const results = await PDS.query(query);
|
|
2252
|
-
return (results || []).map((result) => ({
|
|
2253
|
-
text: result.text,
|
|
2254
|
-
id: result.value,
|
|
2255
|
-
icon: result.icon || "magnifying-glass",
|
|
2256
|
-
category: result.category,
|
|
2257
|
-
code: result.code,
|
|
2258
|
-
}));
|
|
2259
|
-
} catch (error) {
|
|
2260
|
-
console.warn("Omnibox query failed:", error);
|
|
2261
|
-
return [];
|
|
2262
|
-
}
|
|
2263
|
-
},
|
|
2264
|
-
action: async (options) => {
|
|
2265
|
-
if (options?.code && navigator.clipboard) {
|
|
2266
|
-
await navigator.clipboard.writeText(options.code);
|
|
2267
|
-
await PDS.toast("Copied token to clipboard", { type: "success" });
|
|
2268
|
-
}
|
|
2269
|
-
},
|
|
2270
|
-
},
|
|
2271
|
-
},
|
|
2272
|
-
};
|
|
2273
|
-
searchCard.appendChild(omnibox);
|
|
2955
|
+
const resetButton = document.createElement("button");
|
|
2956
|
+
resetButton.type = "button";
|
|
2957
|
+
resetButton.className = "btn-outline";
|
|
2958
|
+
resetButton.textContent = "Reset Config";
|
|
2959
|
+
resetButton.addEventListener("click", () => {
|
|
2960
|
+
window.localStorage.removeItem("pure-ds-config");
|
|
2961
|
+
window.location.reload();
|
|
2962
|
+
});
|
|
2963
|
+
resetCard.appendChild(resetButton);
|
|
2274
2964
|
|
|
2275
2965
|
content.appendChild(presetCard);
|
|
2276
2966
|
content.appendChild(themeCard);
|
|
2967
|
+
content.appendChild(configCard);
|
|
2277
2968
|
content.appendChild(exportCard);
|
|
2278
|
-
content.appendChild(
|
|
2969
|
+
content.appendChild(resetCard);
|
|
2279
2970
|
|
|
2280
2971
|
this._drawer.replaceChildren(header, content);
|
|
2281
2972
|
|
|
@@ -2308,7 +2999,14 @@ class PdsLiveEdit extends HTMLElement {
|
|
|
2308
2999
|
}
|
|
2309
3000
|
|
|
2310
3001
|
// Apply the changes
|
|
2311
|
-
const
|
|
3002
|
+
const eventJson = event?.detail?.json;
|
|
3003
|
+
const hasEventPayload =
|
|
3004
|
+
eventJson && typeof eventJson === "object" && Object.keys(eventJson).length > 0;
|
|
3005
|
+
const flatValues = hasEventPayload
|
|
3006
|
+
? eventJson
|
|
3007
|
+
: typeof form._normalizeFlatValues === "function"
|
|
3008
|
+
? form._normalizeFlatValues(form.getValuesFlat())
|
|
3009
|
+
: form.getValuesFlat();
|
|
2312
3010
|
const patch = {};
|
|
2313
3011
|
Object.entries(flatValues || {}).forEach(([path, value]) => {
|
|
2314
3012
|
setValueAtJsonPath(patch, path, value);
|