@evanschleret/formforgeclient 1.1.0 → 1.1.3

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/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=4.0.0"
6
6
  },
7
- "version": "1.1.0",
7
+ "version": "1.1.3",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
@@ -137,12 +137,55 @@ function getResolvedZodSchema() {
137
137
  }
138
138
  return internalForm.zodSchema.value;
139
139
  }
140
+ function resolveSchemaFieldNames(schema) {
141
+ const names = /* @__PURE__ */ new Set();
142
+ if (Array.isArray(schema.fields)) {
143
+ for (const field of schema.fields) {
144
+ if (typeof field.name === "string" && field.name !== "") {
145
+ names.add(field.name);
146
+ }
147
+ }
148
+ }
149
+ if (Array.isArray(schema.pages)) {
150
+ for (const page of schema.pages) {
151
+ if (!Array.isArray(page.fields)) {
152
+ continue;
153
+ }
154
+ for (const field of page.fields) {
155
+ if (typeof field.name === "string" && field.name !== "") {
156
+ names.add(field.name);
157
+ }
158
+ }
159
+ }
160
+ }
161
+ return names;
162
+ }
163
+ function sanitizePayloadWithSchema(value, schema) {
164
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
165
+ return {};
166
+ }
167
+ const allowedFieldNames = resolveSchemaFieldNames(schema);
168
+ const sanitizedPayload = {};
169
+ for (const [key, entryValue] of Object.entries(value)) {
170
+ if (!allowedFieldNames.has(key)) {
171
+ continue;
172
+ }
173
+ sanitizedPayload[key] = entryValue;
174
+ }
175
+ return sanitizedPayload;
176
+ }
140
177
  const formState = computed({
141
178
  get: () => {
142
179
  const value = usesExternalModel.value ? unwrapModelValueProp(props.modelValue) : internalForm.state.value;
143
180
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
144
181
  return {};
145
182
  }
183
+ if (usesExternalModel.value) {
184
+ const schema = getResolvedSchema();
185
+ if (schema !== null) {
186
+ return sanitizePayloadWithSchema(value, schema);
187
+ }
188
+ }
146
189
  return value;
147
190
  },
148
191
  set: (value) => {
@@ -153,6 +196,23 @@ const formState = computed({
153
196
  internalForm.replaceState(value);
154
197
  }
155
198
  });
199
+ watch(
200
+ () => [usesExternalModel.value, getResolvedSchema(), unwrapModelValueProp(props.modelValue)],
201
+ ([externalModel, schema, modelValue]) => {
202
+ if (!externalModel || schema === null) {
203
+ return;
204
+ }
205
+ const sanitizedValue = sanitizePayloadWithSchema(modelValue, schema);
206
+ if (areValuesEqual(modelValue, sanitizedValue)) {
207
+ return;
208
+ }
209
+ emit("update:modelValue", sanitizedValue);
210
+ },
211
+ {
212
+ immediate: true,
213
+ deep: true
214
+ }
215
+ );
156
216
  const displayPages = computed(() => {
157
217
  const schema = getResolvedSchema();
158
218
  if (schema === null) {
@@ -861,6 +921,20 @@ function getFieldMetaUi(field) {
861
921
  component: componentUi
862
922
  };
863
923
  }
924
+ function mergeUiClass(defaultClass, value) {
925
+ if (typeof value !== "string" || value.trim() === "") {
926
+ return defaultClass;
927
+ }
928
+ return `${defaultClass} ${value}`;
929
+ }
930
+ function getResolvedFormFieldUi(field) {
931
+ const metaUi = getFieldMetaUi(field).formField;
932
+ return {
933
+ ...metaUi,
934
+ label: mergeUiClass("text-default", metaUi?.label),
935
+ help: mergeUiClass("text-muted", metaUi?.help)
936
+ };
937
+ }
864
938
  function getFieldValue(field) {
865
939
  return formState.value[field.name];
866
940
  }
@@ -1098,7 +1172,7 @@ async function onSubmit() {
1098
1172
  >
1099
1173
  <h3
1100
1174
  v-if="page.title !== ''"
1101
- class="text-base font-semibold"
1175
+ class="text-base font-semibold text-default"
1102
1176
  >
1103
1177
  {{ page.title }}
1104
1178
  </h3>
@@ -1119,7 +1193,7 @@ async function onSubmit() {
1119
1193
  :label="field.label"
1120
1194
  :help="field.help_text"
1121
1195
  :required="isFieldRequired(field)"
1122
- :ui="getFieldMetaUi(field).formField"
1196
+ :ui="getResolvedFormFieldUi(field)"
1123
1197
  @focusout="() => onFieldBlur(field.name)"
1124
1198
  >
1125
1199
  <UInput
@@ -468,7 +468,7 @@ watch(
468
468
  <div class="w-full">
469
469
  <p
470
470
  v-if="loading"
471
- class="text-sm text-neutral-500"
471
+ class="text-sm text-default"
472
472
  >
473
473
  {{ t("response.loading") }}
474
474
  </p>
@@ -482,7 +482,7 @@ watch(
482
482
 
483
483
  <p
484
484
  v-else-if="responsePages.length === 0"
485
- class="text-sm text-neutral-500"
485
+ class="text-sm text-default"
486
486
  >
487
487
  {{ t("response.empty") }}
488
488
  </p>
@@ -504,12 +504,12 @@ watch(
504
504
  class="space-y-4"
505
505
  >
506
506
  <div class="space-y-1">
507
- <h3 class="text-base font-semibold text-neutral-900">
507
+ <h3 class="text-base font-semibold text-default">
508
508
  {{ page.title || t("response.page.fallback", { index: pageIndex + 1 }) }}
509
509
  </h3>
510
510
  <p
511
511
  v-if="typeof page.description === 'string' && page.description.trim() !== ''"
512
- class="text-sm text-neutral-500"
512
+ class="text-sm text-default"
513
513
  >
514
514
  {{ page.description }}
515
515
  </p>
@@ -525,13 +525,13 @@ watch(
525
525
  v-if="isLineLayout"
526
526
  class="grid grid-cols-1 gap-2 md:grid-cols-[minmax(0,1fr)_minmax(0,2fr)] md:gap-6"
527
527
  >
528
- <p class="whitespace-pre-wrap text-sm font-medium text-neutral-900">
528
+ <p class="whitespace-pre-wrap text-sm font-medium text-default">
529
529
  {{ item.question }}
530
530
  </p>
531
531
 
532
532
  <div
533
533
  v-if="item.answer.kind === 'text'"
534
- class="whitespace-pre-wrap text-sm text-neutral-700"
534
+ class="whitespace-pre-wrap text-sm text-default"
535
535
  >
536
536
  {{ item.answer.value }}
537
537
  </div>
@@ -561,16 +561,16 @@ watch(
561
561
  <UIcon
562
562
  v-else
563
563
  name="i-lucide-file"
564
- class="h-5 w-5 shrink-0 text-neutral-500"
564
+ class="h-5 w-5 shrink-0 text-default"
565
565
  />
566
566
 
567
567
  <div class="min-w-0 flex-1">
568
- <p class="truncate text-sm text-neutral-700">
568
+ <p class="truncate text-sm text-default">
569
569
  {{ file.name }}
570
570
  </p>
571
571
  <p
572
572
  v-if="file.mimeType"
573
- class="text-xs text-neutral-500"
573
+ class="text-xs text-default"
574
574
  >
575
575
  {{ file.mimeType }}
576
576
  </p>
@@ -622,13 +622,13 @@ watch(
622
622
  v-else
623
623
  class="space-y-1"
624
624
  >
625
- <p class="whitespace-pre-wrap text-sm font-medium text-neutral-900">
625
+ <p class="whitespace-pre-wrap text-sm font-medium text-default">
626
626
  {{ item.question }}
627
627
  </p>
628
628
 
629
629
  <p
630
630
  v-if="item.answer.kind === 'text'"
631
- class="whitespace-pre-wrap text-sm text-neutral-700"
631
+ class="whitespace-pre-wrap text-sm text-default"
632
632
  >
633
633
  {{ item.answer.value }}
634
634
  </p>
@@ -658,16 +658,16 @@ watch(
658
658
  <UIcon
659
659
  v-else
660
660
  name="i-lucide-file"
661
- class="h-5 w-5 shrink-0 text-neutral-500"
661
+ class="h-5 w-5 shrink-0 text-default"
662
662
  />
663
663
 
664
664
  <div class="min-w-0 flex-1">
665
- <p class="truncate text-sm text-neutral-700">
665
+ <p class="truncate text-sm text-default">
666
666
  {{ file.name }}
667
667
  </p>
668
668
  <p
669
669
  v-if="file.mimeType"
670
- class="text-xs text-neutral-500"
670
+ class="text-xs text-default"
671
671
  >
672
672
  {{ file.mimeType }}
673
673
  </p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evanschleret/formforgeclient",
3
- "version": "1.1.0",
3
+ "version": "1.1.3",
4
4
  "description": "Nuxt module and runtime client for FormForge",
5
5
  "license": "MIT",
6
6
  "type": "module",