@marianmeres/stuic 2.8.1 → 2.9.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.
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
addLabel?: string;
|
|
48
48
|
emptyMessage?: string;
|
|
49
49
|
onChange?: (value: string) => void;
|
|
50
|
+
strictJsonValidation?: boolean;
|
|
50
51
|
t?: TranslateFn;
|
|
51
52
|
}
|
|
52
53
|
</script>
|
|
@@ -79,6 +80,7 @@
|
|
|
79
80
|
empty_message: "No entries",
|
|
80
81
|
remove_entry: "Remove entry",
|
|
81
82
|
duplicate_keys: "Duplicate keys are not allowed",
|
|
83
|
+
invalid_json_syntax: "Invalid JSON syntax. Check for missing quotes or brackets.",
|
|
82
84
|
};
|
|
83
85
|
let out = m[k] ?? fallback ?? k;
|
|
84
86
|
return isPlainObject(values) ? replaceMap(out, values as any) : out;
|
|
@@ -118,26 +120,37 @@
|
|
|
118
120
|
addLabel,
|
|
119
121
|
emptyMessage,
|
|
120
122
|
onChange,
|
|
123
|
+
strictJsonValidation = true,
|
|
121
124
|
t = t_default,
|
|
122
125
|
}: Props = $props();
|
|
123
126
|
|
|
124
127
|
let hiddenInputEl: HTMLInputElement | undefined = $state();
|
|
125
128
|
let keyInputRefs: HTMLInputElement[] = $state([]);
|
|
129
|
+
let entryJsonErrors: boolean[] = $state([]);
|
|
126
130
|
|
|
127
131
|
// Internal state - handle undefined value from async form initialization
|
|
128
132
|
let entries: KeyValueEntry[] = $state(parseValue(value ?? SERIALIZED_DEFAULT));
|
|
129
133
|
|
|
130
134
|
// Parse JSON value with auto-detect: try JSON first, fallback to plain string
|
|
131
|
-
|
|
135
|
+
// Returns intendedJson=true if input looks like JSON (starts with { or [)
|
|
136
|
+
// Returns parseError=true if JSON parse failed
|
|
137
|
+
function parseJsonValue(input: string): {
|
|
138
|
+
value: unknown;
|
|
139
|
+
intendedJson: boolean;
|
|
140
|
+
parseError: boolean;
|
|
141
|
+
} {
|
|
132
142
|
const trimmed = input.trim();
|
|
133
|
-
if (trimmed === "") return { value: "" };
|
|
143
|
+
if (trimmed === "") return { value: "", intendedJson: false, parseError: false };
|
|
144
|
+
|
|
145
|
+
// Heuristics: input looks like it's trying to be JSON
|
|
146
|
+
const intendedJson = trimmed.startsWith("{") || trimmed.startsWith("[");
|
|
134
147
|
|
|
135
148
|
try {
|
|
136
149
|
const parsed = JSON.parse(trimmed);
|
|
137
|
-
return { value: parsed };
|
|
150
|
+
return { value: parsed, intendedJson, parseError: false };
|
|
138
151
|
} catch {
|
|
139
|
-
// If parse fails, treat as plain string
|
|
140
|
-
return { value: trimmed };
|
|
152
|
+
// If parse fails, treat as plain string but flag the error
|
|
153
|
+
return { value: trimmed, intendedJson, parseError: intendedJson };
|
|
141
154
|
}
|
|
142
155
|
}
|
|
143
156
|
|
|
@@ -183,6 +196,8 @@
|
|
|
183
196
|
const newSerialized = serializeValue(newEntries);
|
|
184
197
|
if (currentSerialized !== newSerialized) {
|
|
185
198
|
entries = newEntries;
|
|
199
|
+
// Reset JSON errors when external value changes (assume valid JSON from external source)
|
|
200
|
+
entryJsonErrors = new Array(newEntries.length).fill(false);
|
|
186
201
|
}
|
|
187
202
|
});
|
|
188
203
|
|
|
@@ -190,6 +205,7 @@
|
|
|
190
205
|
function addEntry() {
|
|
191
206
|
const newEntry: KeyValueEntry = { key: "", value: "", parsedValue: "" };
|
|
192
207
|
entries = [...entries, newEntry];
|
|
208
|
+
entryJsonErrors = [...entryJsonErrors, false];
|
|
193
209
|
syncToValue();
|
|
194
210
|
// Focus the new key input after render
|
|
195
211
|
tick().then(() => {
|
|
@@ -201,15 +217,17 @@
|
|
|
201
217
|
// Remove entry by index
|
|
202
218
|
function removeEntry(idx: number) {
|
|
203
219
|
entries = entries.filter((_, i) => i !== idx);
|
|
220
|
+
entryJsonErrors = entryJsonErrors.filter((_, i) => i !== idx);
|
|
204
221
|
syncToValue();
|
|
205
222
|
}
|
|
206
223
|
|
|
207
224
|
// Update entry field
|
|
208
225
|
function updateEntry(idx: number, field: "key" | "value", newValue: string) {
|
|
209
226
|
if (field === "value") {
|
|
210
|
-
const { value: parsed } = parseJsonValue(newValue);
|
|
227
|
+
const { value: parsed, parseError } = parseJsonValue(newValue);
|
|
211
228
|
entries[idx].value = newValue;
|
|
212
229
|
entries[idx].parsedValue = parsed;
|
|
230
|
+
entryJsonErrors[idx] = parseError;
|
|
213
231
|
} else {
|
|
214
232
|
entries[idx].key = newValue;
|
|
215
233
|
}
|
|
@@ -243,6 +261,11 @@
|
|
|
243
261
|
return t("duplicate_keys");
|
|
244
262
|
}
|
|
245
263
|
|
|
264
|
+
// Check for JSON syntax errors when strictJsonValidation is enabled
|
|
265
|
+
if (strictJsonValidation && entryJsonErrors.some(Boolean)) {
|
|
266
|
+
return t("invalid_json_syntax");
|
|
267
|
+
}
|
|
268
|
+
|
|
246
269
|
// Continue with provided validator
|
|
247
270
|
return (validate as any)?.customValidator?.(val, context, el) || "";
|
|
248
271
|
},
|
|
@@ -42,6 +42,7 @@ export interface Props extends Record<string, any> {
|
|
|
42
42
|
addLabel?: string;
|
|
43
43
|
emptyMessage?: string;
|
|
44
44
|
onChange?: (value: string) => void;
|
|
45
|
+
strictJsonValidation?: boolean;
|
|
45
46
|
t?: TranslateFn;
|
|
46
47
|
}
|
|
47
48
|
declare const FieldKeyValues: import("svelte").Component<Props, {}, "value">;
|