@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
- function parseJsonValue(input: string): { value: unknown } {
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">;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "2.8.1",
3
+ "version": "2.9.0",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.test.*",