@sfxcode/nuxt-ui-mongocamp 1.1.0 → 1.1.2
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/README.md +6 -6
- package/dist/module.json +1 -1
- package/dist/runtime/components/MongocampCollectionData.vue +19 -19
- package/dist/runtime/composables/useMongocampExtendedJson.d.ts +2 -0
- package/dist/runtime/composables/useMongocampExtendedJson.js +62 -0
- package/package.json +10 -28
- package/dist/runtime/composables/useMongocampDynamicForm.d.ts +0 -14
- package/dist/runtime/composables/useMongocampDynamicForm.js +0 -231
package/README.md
CHANGED
|
@@ -292,14 +292,14 @@ pnpm run docs:preview
|
|
|
292
292
|
[MIT](./LICENSE)
|
|
293
293
|
|
|
294
294
|
<!-- Badges -->
|
|
295
|
-
[npm-version-src]: https://img.shields.io/npm/v/nuxt-ui-mongocamp/latest.svg?style=flat&colorA=020420&colorB=00DC82
|
|
296
|
-
[npm-version-href]: https://npmjs.com/package/nuxt-ui-mongocamp
|
|
295
|
+
[npm-version-src]: https://img.shields.io/npm/v/@sfxcode/nuxt-ui-mongocamp/latest.svg?style=flat&colorA=020420&colorB=00DC82
|
|
296
|
+
[npm-version-href]: https://npmjs.com/package/@sfxcode/nuxt-ui-mongocamp
|
|
297
297
|
|
|
298
|
-
[npm-downloads-src]: https://img.shields.io/npm/dm/nuxt-ui-mongocamp.svg?style=flat&colorA=020420&colorB=00DC82
|
|
299
|
-
[npm-downloads-href]: https://npm.chart.dev/nuxt-ui-mongocamp
|
|
298
|
+
[npm-downloads-src]: https://img.shields.io/npm/dm/@sfxcode/nuxt-ui-mongocamp.svg?style=flat&colorA=020420&colorB=00DC82
|
|
299
|
+
[npm-downloads-href]: https://npm.chart.dev/@sfxcode/nuxt-ui-mongocamp
|
|
300
300
|
|
|
301
|
-
[license-src]: https://img.shields.io/npm/l/nuxt-ui-mongocamp.svg?style=flat&colorA=020420&colorB=00DC82
|
|
302
|
-
[license-href]: https://npmjs.com/package/nuxt-ui-mongocamp
|
|
301
|
+
[license-src]: https://img.shields.io/npm/l/@sfxcode/nuxt-ui-mongocamp.svg?style=flat&colorA=020420&colorB=00DC82
|
|
302
|
+
[license-href]: https://npmjs.com/package/@sfxcode/nuxt-ui-mongocamp
|
|
303
303
|
|
|
304
304
|
[nuxt-src]: https://img.shields.io/badge/Nuxt-020420?logo=nuxt
|
|
305
305
|
[nuxt-href]: https://nuxt.com
|
package/dist/module.json
CHANGED
|
@@ -3,8 +3,7 @@ import { h, ref, resolveComponent, watch } from "vue";
|
|
|
3
3
|
import { useMongocampApi } from "#imports";
|
|
4
4
|
import useMongocampCollection from "../composables/useMongocampCollection";
|
|
5
5
|
import { useMongocampBucket } from "../composables/useMongocampBucket";
|
|
6
|
-
import {
|
|
7
|
-
import { columnsToFormKitSchema, documentToFormData, formDataToDocument } from "../composables/useMongocampDynamicForm";
|
|
6
|
+
import { unwrapExtendedJson, wrapExtendedJson } from "../composables/useMongocampExtendedJson";
|
|
8
7
|
import useMongocampDocument from "../composables/useMongocampDocument";
|
|
9
8
|
import { useMongocampQuery } from "../composables/useMongocampQuery";
|
|
10
9
|
const props = defineProps({
|
|
@@ -13,7 +12,6 @@ const props = defineProps({
|
|
|
13
12
|
const { documentApi } = useMongocampApi();
|
|
14
13
|
const { pagination, total } = useMongocampCollection();
|
|
15
14
|
const { isBucketCollection, fileIdForRow, downloadingFileIds, uploading, downloadFile, uploadFile } = useMongocampBucket();
|
|
16
|
-
const { schemaFromSamples } = useMongocampSchema();
|
|
17
15
|
const { ensureMetaData } = useMongocampDocument();
|
|
18
16
|
const { like, or } = useMongocampQuery();
|
|
19
17
|
const fileInputRef = ref(null);
|
|
@@ -41,10 +39,14 @@ const editJson = ref("{}");
|
|
|
41
39
|
const editError = ref("");
|
|
42
40
|
const docToDelete = ref(void 0);
|
|
43
41
|
const deleteError = ref("");
|
|
44
|
-
const
|
|
45
|
-
const editSchema = ref([]);
|
|
42
|
+
const editSamples = ref([]);
|
|
46
43
|
const editFormData = ref({});
|
|
47
44
|
const originalRow = ref(void 0);
|
|
45
|
+
function omitId(row) {
|
|
46
|
+
const rest = { ...row };
|
|
47
|
+
delete rest._id;
|
|
48
|
+
return rest;
|
|
49
|
+
}
|
|
48
50
|
function getDocId(row) {
|
|
49
51
|
const id = row._id;
|
|
50
52
|
if (typeof id === "string") return id;
|
|
@@ -57,9 +59,8 @@ function openInsert() {
|
|
|
57
59
|
editDocId.value = void 0;
|
|
58
60
|
editError.value = "";
|
|
59
61
|
originalRow.value = void 0;
|
|
60
|
-
|
|
61
|
-
if (
|
|
62
|
-
editSchema.value = columnsToFormKitSchema(editColumns.value);
|
|
62
|
+
editSamples.value = documents.value.map((row) => omitId(unwrapExtendedJson(row)));
|
|
63
|
+
if (editSamples.value.length > 0) {
|
|
63
64
|
editFormData.value = {};
|
|
64
65
|
} else {
|
|
65
66
|
editJson.value = "{}";
|
|
@@ -71,10 +72,9 @@ function openEdit(row) {
|
|
|
71
72
|
editDocId.value = getDocId(row);
|
|
72
73
|
editError.value = "";
|
|
73
74
|
originalRow.value = row;
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
editFormData.value = documentToFormData(row, editColumns.value);
|
|
75
|
+
editSamples.value = documents.value.map((r) => omitId(unwrapExtendedJson(r)));
|
|
76
|
+
if (editSamples.value.length > 0) {
|
|
77
|
+
editFormData.value = omitId(unwrapExtendedJson(row));
|
|
78
78
|
} else {
|
|
79
79
|
editJson.value = JSON.stringify(row, null, 2);
|
|
80
80
|
}
|
|
@@ -88,8 +88,8 @@ function confirmDelete(row) {
|
|
|
88
88
|
async function handleSave() {
|
|
89
89
|
editError.value = "";
|
|
90
90
|
let payload;
|
|
91
|
-
if (
|
|
92
|
-
payload =
|
|
91
|
+
if (editSamples.value.length > 0) {
|
|
92
|
+
payload = wrapExtendedJson(editFormData.value, editMode.value === "update" ? originalRow.value : void 0);
|
|
93
93
|
} else {
|
|
94
94
|
try {
|
|
95
95
|
payload = JSON.parse(editJson.value);
|
|
@@ -439,10 +439,10 @@ watch(() => props.collectionName, init, { immediate: true });
|
|
|
439
439
|
:ui="{ footer: 'justify-end' }"
|
|
440
440
|
>
|
|
441
441
|
<template #body>
|
|
442
|
-
<
|
|
443
|
-
v-if="
|
|
444
|
-
|
|
445
|
-
:
|
|
442
|
+
<FUAutoForm
|
|
443
|
+
v-if="editSamples.length > 0"
|
|
444
|
+
v-model="editFormData"
|
|
445
|
+
:data="editSamples"
|
|
446
446
|
:submit-label="editMode === 'insert' ? 'Insert' : 'Save'"
|
|
447
447
|
:submit-icon="editMode === 'insert' ? 'i-lucide-plus' : 'i-lucide-save'"
|
|
448
448
|
@data-saved="handleSave"
|
|
@@ -461,7 +461,7 @@ watch(() => props.collectionName, init, { immediate: true });
|
|
|
461
461
|
</p>
|
|
462
462
|
</template>
|
|
463
463
|
<template
|
|
464
|
-
v-if="
|
|
464
|
+
v-if="editSamples.length === 0"
|
|
465
465
|
#footer
|
|
466
466
|
>
|
|
467
467
|
<UButton
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
function isPlainObject(value) {
|
|
2
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
const NUMBER_WRAPPER_KEYS = ["$numberInt", "$numberLong", "$numberDouble", "$numberDecimal"];
|
|
5
|
+
const ISO_DATE_TIME_REGEX = /^\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
6
|
+
function isIsoDateTimeString(value) {
|
|
7
|
+
return ISO_DATE_TIME_REGEX.test(value) && !Number.isNaN(Date.parse(value));
|
|
8
|
+
}
|
|
9
|
+
function unwrapDateTime(raw) {
|
|
10
|
+
const inner = raw.$date;
|
|
11
|
+
if (typeof inner === "string" || typeof inner === "number") return new Date(inner).toISOString();
|
|
12
|
+
if (isPlainObject(inner) && typeof inner.$numberLong === "string") return new Date(Number(inner.$numberLong)).toISOString();
|
|
13
|
+
return raw;
|
|
14
|
+
}
|
|
15
|
+
function unwrapNumber(raw) {
|
|
16
|
+
for (const key of NUMBER_WRAPPER_KEYS) {
|
|
17
|
+
const wrapped = raw[key];
|
|
18
|
+
if (typeof wrapped === "string" || typeof wrapped === "number") {
|
|
19
|
+
const parsed = Number(wrapped);
|
|
20
|
+
if (!Number.isNaN(parsed)) return parsed;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return void 0;
|
|
24
|
+
}
|
|
25
|
+
export function unwrapExtendedJson(value) {
|
|
26
|
+
if (Array.isArray(value)) return value.map(unwrapExtendedJson);
|
|
27
|
+
if (!isPlainObject(value)) return value;
|
|
28
|
+
if (typeof value.$oid === "string") return value.$oid;
|
|
29
|
+
if ("$date" in value) return unwrapDateTime(value);
|
|
30
|
+
const number = unwrapNumber(value);
|
|
31
|
+
if (number !== void 0) return number;
|
|
32
|
+
const result = {};
|
|
33
|
+
for (const [key, v] of Object.entries(value)) result[key] = unwrapExtendedJson(v);
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
function rewrapValue(value, originalValue) {
|
|
37
|
+
if (typeof value === "string" && isIsoDateTimeString(value)) {
|
|
38
|
+
return { $date: value };
|
|
39
|
+
}
|
|
40
|
+
if (typeof value === "string" && isPlainObject(originalValue) && typeof originalValue.$oid === "string") {
|
|
41
|
+
return { $oid: value };
|
|
42
|
+
}
|
|
43
|
+
if (Array.isArray(value)) {
|
|
44
|
+
const originalArray = Array.isArray(originalValue) ? originalValue : [];
|
|
45
|
+
return value.map((item, index) => rewrapValue(item, originalArray[index]));
|
|
46
|
+
}
|
|
47
|
+
if (isPlainObject(value)) {
|
|
48
|
+
const originalObject = isPlainObject(originalValue) ? originalValue : void 0;
|
|
49
|
+
const nested = {};
|
|
50
|
+
for (const [key, v] of Object.entries(value)) nested[key] = rewrapValue(v, originalObject?.[key]);
|
|
51
|
+
return nested;
|
|
52
|
+
}
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
export function wrapExtendedJson(formData, originalDoc) {
|
|
56
|
+
const result = originalDoc ? { ...originalDoc } : {};
|
|
57
|
+
for (const [key, value] of Object.entries(formData)) {
|
|
58
|
+
result[key] = rewrapValue(value, originalDoc?.[key]);
|
|
59
|
+
}
|
|
60
|
+
delete result._id;
|
|
61
|
+
return result;
|
|
62
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sfxcode/nuxt-ui-mongocamp",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "A Nuxt module that wraps @sfxcode/nuxt-mongocamp-server with ready-made UI components, composables, and a runtime plugin",
|
|
5
5
|
"repository": "sfxcode/nuxt-ui-mongocamp",
|
|
6
6
|
"homepage": "https://sfxcode.github.io/nuxt-ui-mongocamp/",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"dev": "npm run dev:prepare && nuxt dev playground",
|
|
45
45
|
"dev:build": "nuxt build playground",
|
|
46
46
|
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare playground",
|
|
47
|
-
"release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
|
|
47
|
+
"release": "npm run lint && npm run test && npm run prepack && changelogen --patch --release && npm publish && git push --follow-tags",
|
|
48
48
|
"lint": "eslint .",
|
|
49
49
|
"test": "vitest run",
|
|
50
50
|
"test:watch": "vitest watch",
|
|
@@ -54,12 +54,12 @@
|
|
|
54
54
|
"docs:preview": "vitepress preview docs"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@nuxt/kit": "^4.4.8",
|
|
58
57
|
"@internationalized/date": "^3.12.2",
|
|
58
|
+
"@nuxt/kit": "^4.4.8",
|
|
59
59
|
"@nuxt/ui": "4.9.0",
|
|
60
60
|
"@sfxcode/nuxt-mongocamp-server": "^1.4.4",
|
|
61
|
-
"@sfxcode/nuxt-ui-formkit": "^1.
|
|
62
|
-
"unocss-nuxt-ui": "1.2.
|
|
61
|
+
"@sfxcode/nuxt-ui-formkit": "^1.2.6",
|
|
62
|
+
"unocss-nuxt-ui": "^1.2.2"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@nuxt/devtools": "4.0.0-alpha.7",
|
|
@@ -67,33 +67,15 @@
|
|
|
67
67
|
"@nuxt/module-builder": "^1.0.2",
|
|
68
68
|
"@nuxt/schema": "^4.4.8",
|
|
69
69
|
"@nuxt/test-utils": "^4.0.3",
|
|
70
|
-
"@nuxt/ui": "4.9.0",
|
|
71
|
-
"@tiptap/core": "^3.27.1",
|
|
72
|
-
"@tiptap/extension-bubble-menu": "^3.27.1",
|
|
73
|
-
"@tiptap/extension-code": "^3.27.1",
|
|
74
|
-
"@tiptap/extension-collaboration": "^3.27.1",
|
|
75
|
-
"@tiptap/extension-drag-handle": "^3.27.1",
|
|
76
|
-
"@tiptap/extension-drag-handle-vue-3": "^3.27.1",
|
|
77
|
-
"@tiptap/extension-floating-menu": "^3.27.1",
|
|
78
|
-
"@tiptap/extension-horizontal-rule": "^3.27.1",
|
|
79
|
-
"@tiptap/extension-image": "^3.27.1",
|
|
80
|
-
"@tiptap/extension-mention": "^3.27.1",
|
|
81
|
-
"@tiptap/extension-node-range": "^3.27.1",
|
|
82
|
-
"@tiptap/extension-placeholder": "^3.27.1",
|
|
83
|
-
"@tiptap/markdown": "^3.27.1",
|
|
84
|
-
"@tiptap/pm": "^3.27.1",
|
|
85
|
-
"@tiptap/starter-kit": "^3.27.1",
|
|
86
|
-
"@tiptap/suggestion": "^3.27.1",
|
|
87
|
-
"@tiptap/vue-3": "^3.27.1",
|
|
88
70
|
"@types/node": "latest",
|
|
89
71
|
"changelogen": "^0.6.2",
|
|
90
|
-
"eslint": "^10.
|
|
72
|
+
"eslint": "^10.7.0",
|
|
91
73
|
"nuxt": "^4.4.8",
|
|
92
74
|
"tailwindcss": "^4.3.2",
|
|
93
75
|
"typescript": "~6.0.3",
|
|
94
|
-
"vitepress": "
|
|
95
|
-
"vitest": "^4.1.
|
|
96
|
-
"vue-tsc": "^3.3.
|
|
76
|
+
"vitepress": "2.0.0-alpha.18",
|
|
77
|
+
"vitest": "^4.1.10",
|
|
78
|
+
"vue-tsc": "^3.3.7"
|
|
97
79
|
},
|
|
98
|
-
"packageManager": "pnpm@11.
|
|
80
|
+
"packageManager": "pnpm@11.10.0+sha512.0b7f8b98060031904c017e3a41eb187a16d40eeb829b95c4f8cb03681761fc4ab53dd219115b9b447f4dce1a05a214764461e7d3703392a9f32f9511ce8c86c8"
|
|
99
81
|
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { ColumnDefinition } from './useMongocampSchema.js';
|
|
2
|
-
export interface FormKitSchemaNode {
|
|
3
|
-
$formkit?: string;
|
|
4
|
-
$el?: string;
|
|
5
|
-
name?: string;
|
|
6
|
-
label?: string;
|
|
7
|
-
validation?: string;
|
|
8
|
-
attrs?: Record<string, unknown>;
|
|
9
|
-
children?: FormKitSchemaNode[];
|
|
10
|
-
[key: string]: unknown;
|
|
11
|
-
}
|
|
12
|
-
export declare function columnsToFormKitSchema(columns: ColumnDefinition[]): FormKitSchemaNode[];
|
|
13
|
-
export declare function documentToFormData(doc: Record<string, unknown>, columns: ColumnDefinition[]): Record<string, unknown>;
|
|
14
|
-
export declare function formDataToDocument(formData: Record<string, unknown>, columns: ColumnDefinition[], originalDoc?: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import { parseAbsoluteToLocal } from "@internationalized/date";
|
|
2
|
-
const SCALAR_FORMKIT_TYPES = {
|
|
3
|
-
"string": "nuxtUIInput",
|
|
4
|
-
"number": "nuxtUIInputNumber",
|
|
5
|
-
"boolean": "nuxtUISwitch",
|
|
6
|
-
"date-time": "nuxtUIInputDate"
|
|
7
|
-
};
|
|
8
|
-
function scalarNode(column, formkitType) {
|
|
9
|
-
const node = {
|
|
10
|
-
$formkit: formkitType,
|
|
11
|
-
name: column.columnKey,
|
|
12
|
-
label: column.columnName
|
|
13
|
-
};
|
|
14
|
-
if (column.required) {
|
|
15
|
-
node.validation = "required";
|
|
16
|
-
}
|
|
17
|
-
return node;
|
|
18
|
-
}
|
|
19
|
-
function groupNode(column) {
|
|
20
|
-
return {
|
|
21
|
-
$formkit: "group",
|
|
22
|
-
name: column.columnKey,
|
|
23
|
-
label: column.columnName,
|
|
24
|
-
children: columnsToFormKitSchema(column.children ?? [])
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
function wrapGroup(node) {
|
|
28
|
-
return {
|
|
29
|
-
$el: "div",
|
|
30
|
-
attrs: { class: "pt-4 flex flex-col gap-3" },
|
|
31
|
-
children: [node]
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
function scalarArrayNode(column) {
|
|
35
|
-
const node = {
|
|
36
|
-
$formkit: "nuxtUIInputTags",
|
|
37
|
-
name: column.columnKey,
|
|
38
|
-
label: column.columnName
|
|
39
|
-
};
|
|
40
|
-
if (column.required) {
|
|
41
|
-
node.validation = "required";
|
|
42
|
-
}
|
|
43
|
-
return node;
|
|
44
|
-
}
|
|
45
|
-
function defaultScalarValue(formkitType) {
|
|
46
|
-
if (formkitType === "nuxtUIInputNumber") return 0;
|
|
47
|
-
if (formkitType === "nuxtUISwitch") return false;
|
|
48
|
-
return "";
|
|
49
|
-
}
|
|
50
|
-
function defaultValueForColumn(column) {
|
|
51
|
-
if (column.columnType === "object") return defaultObjectValue(column.children ?? []);
|
|
52
|
-
if (column.columnType === "array") return [];
|
|
53
|
-
const formkitType = SCALAR_FORMKIT_TYPES[column.columnType];
|
|
54
|
-
return formkitType ? defaultScalarValue(formkitType) : "";
|
|
55
|
-
}
|
|
56
|
-
function defaultObjectValue(children) {
|
|
57
|
-
const value = {};
|
|
58
|
-
children.forEach((child) => {
|
|
59
|
-
value[child.columnKey] = defaultValueForColumn(child);
|
|
60
|
-
});
|
|
61
|
-
return value;
|
|
62
|
-
}
|
|
63
|
-
const REPEATER_BUTTON_PROPS = {
|
|
64
|
-
alwaysDisplayInsertButton: true,
|
|
65
|
-
displayAddButton: true,
|
|
66
|
-
displayCloneButton: true,
|
|
67
|
-
displayDeleteButton: true
|
|
68
|
-
};
|
|
69
|
-
const REPEATER_LAYOUT_PROPS = {
|
|
70
|
-
listClass: "flex flex-col gap-3",
|
|
71
|
-
listItemClass: "flex flex-wrap items-end gap-3 p-3 border border-gray-100 dark:border-gray-800 rounded-lg shadow-sm",
|
|
72
|
-
buttonGroupClass: "flex items-center gap-1 ml-auto",
|
|
73
|
-
displayDragHandle: true,
|
|
74
|
-
draggable: true,
|
|
75
|
-
hideMoveButtons: true,
|
|
76
|
-
dragHandleClass: "cursor-grab active:cursor-grabbing text-dimmed"
|
|
77
|
-
};
|
|
78
|
-
function repeaterArrayNode(column, itemFormkitType) {
|
|
79
|
-
const node = {
|
|
80
|
-
$formkit: "nuxtUIRepeater",
|
|
81
|
-
name: column.columnKey,
|
|
82
|
-
label: column.columnName,
|
|
83
|
-
newItem: { value: defaultScalarValue(itemFormkitType) },
|
|
84
|
-
...REPEATER_BUTTON_PROPS,
|
|
85
|
-
...REPEATER_LAYOUT_PROPS,
|
|
86
|
-
children: [
|
|
87
|
-
{ $formkit: itemFormkitType, name: "value", label: "Value" }
|
|
88
|
-
]
|
|
89
|
-
};
|
|
90
|
-
if (column.required) {
|
|
91
|
-
node.validation = "required";
|
|
92
|
-
}
|
|
93
|
-
return node;
|
|
94
|
-
}
|
|
95
|
-
function objectArrayNode(column) {
|
|
96
|
-
const node = {
|
|
97
|
-
$formkit: "nuxtUIRepeater",
|
|
98
|
-
name: column.columnKey,
|
|
99
|
-
label: column.columnName,
|
|
100
|
-
newItem: defaultObjectValue(column.children ?? []),
|
|
101
|
-
...REPEATER_BUTTON_PROPS,
|
|
102
|
-
...REPEATER_LAYOUT_PROPS,
|
|
103
|
-
children: columnsToFormKitSchema(column.children ?? [])
|
|
104
|
-
};
|
|
105
|
-
if (column.required) {
|
|
106
|
-
node.validation = "required";
|
|
107
|
-
}
|
|
108
|
-
return node;
|
|
109
|
-
}
|
|
110
|
-
function arrayNode(column) {
|
|
111
|
-
if (column.arrayItemType === "object") {
|
|
112
|
-
return objectArrayNode(column);
|
|
113
|
-
}
|
|
114
|
-
if (column.arrayItemType === "string" || column.arrayItemType === "unknown") {
|
|
115
|
-
return scalarArrayNode(column);
|
|
116
|
-
}
|
|
117
|
-
const itemFormkitType = column.arrayItemType ? SCALAR_FORMKIT_TYPES[column.arrayItemType] : void 0;
|
|
118
|
-
if (itemFormkitType) {
|
|
119
|
-
return repeaterArrayNode(column, itemFormkitType);
|
|
120
|
-
}
|
|
121
|
-
return void 0;
|
|
122
|
-
}
|
|
123
|
-
export function columnsToFormKitSchema(columns) {
|
|
124
|
-
const schema = [];
|
|
125
|
-
columns.forEach((column) => {
|
|
126
|
-
if (column.columnType === "object") {
|
|
127
|
-
schema.push(wrapGroup(groupNode(column)));
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
if (column.columnType === "array") {
|
|
131
|
-
const node = arrayNode(column);
|
|
132
|
-
if (node) schema.push(node);
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
const formkitType = SCALAR_FORMKIT_TYPES[column.columnType];
|
|
136
|
-
if (formkitType) {
|
|
137
|
-
schema.push(scalarNode(column, formkitType));
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
return schema;
|
|
141
|
-
}
|
|
142
|
-
function isPlainObject(value) {
|
|
143
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
144
|
-
}
|
|
145
|
-
function unwrapOid(raw) {
|
|
146
|
-
if (isPlainObject(raw) && typeof raw.$oid === "string") return raw.$oid;
|
|
147
|
-
return raw;
|
|
148
|
-
}
|
|
149
|
-
const NUMBER_WRAPPER_KEYS = ["$numberInt", "$numberLong", "$numberDouble", "$numberDecimal"];
|
|
150
|
-
function unwrapNumber(raw) {
|
|
151
|
-
if (!isPlainObject(raw)) return raw;
|
|
152
|
-
for (const key of NUMBER_WRAPPER_KEYS) {
|
|
153
|
-
const wrapped = raw[key];
|
|
154
|
-
if (typeof wrapped === "string" || typeof wrapped === "number") {
|
|
155
|
-
const parsed = Number(wrapped);
|
|
156
|
-
if (!Number.isNaN(parsed)) return parsed;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return raw;
|
|
160
|
-
}
|
|
161
|
-
function unwrapDateTime(raw) {
|
|
162
|
-
if (!isPlainObject(raw) || !("$date" in raw)) return raw;
|
|
163
|
-
const inner = raw.$date;
|
|
164
|
-
if (typeof inner === "string" || typeof inner === "number") {
|
|
165
|
-
return new Date(inner).toISOString();
|
|
166
|
-
}
|
|
167
|
-
if (isPlainObject(inner) && typeof inner.$numberLong === "string") {
|
|
168
|
-
return new Date(Number(inner.$numberLong)).toISOString();
|
|
169
|
-
}
|
|
170
|
-
return raw;
|
|
171
|
-
}
|
|
172
|
-
function toZonedDateTime(value) {
|
|
173
|
-
if (typeof value !== "string") return value;
|
|
174
|
-
try {
|
|
175
|
-
return parseAbsoluteToLocal(value);
|
|
176
|
-
} catch {
|
|
177
|
-
return value;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
function isZonedDateTimeLike(value) {
|
|
181
|
-
return typeof value === "object" && value !== null && typeof value.toAbsoluteString === "function";
|
|
182
|
-
}
|
|
183
|
-
export function documentToFormData(doc, columns) {
|
|
184
|
-
const formData = {};
|
|
185
|
-
columns.forEach((column) => {
|
|
186
|
-
if (!(column.columnKey in doc)) return;
|
|
187
|
-
const raw = doc[column.columnKey];
|
|
188
|
-
if (column.columnType === "object") {
|
|
189
|
-
if (isPlainObject(raw)) {
|
|
190
|
-
formData[column.columnKey] = documentToFormData(raw, column.children ?? []);
|
|
191
|
-
}
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
if (column.columnType === "array") {
|
|
195
|
-
if (!Array.isArray(raw)) return;
|
|
196
|
-
formData[column.columnKey] = column.arrayItemType === "object" ? raw.map((item) => isPlainObject(item) ? documentToFormData(item, column.children ?? []) : item) : raw;
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
if (column.columnType === "date-time") {
|
|
200
|
-
formData[column.columnKey] = toZonedDateTime(unwrapDateTime(raw));
|
|
201
|
-
} else if (column.columnType === "string") {
|
|
202
|
-
formData[column.columnKey] = unwrapOid(raw);
|
|
203
|
-
} else if (column.columnType === "number") {
|
|
204
|
-
formData[column.columnKey] = unwrapNumber(raw);
|
|
205
|
-
} else {
|
|
206
|
-
formData[column.columnKey] = raw;
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
return formData;
|
|
210
|
-
}
|
|
211
|
-
function wasOriginallyOid(originalValue) {
|
|
212
|
-
return isPlainObject(originalValue) && typeof originalValue.$oid === "string";
|
|
213
|
-
}
|
|
214
|
-
export function formDataToDocument(formData, columns, originalDoc) {
|
|
215
|
-
const result = originalDoc ? { ...originalDoc } : {};
|
|
216
|
-
columns.forEach((column) => {
|
|
217
|
-
if (!(column.columnKey in formData)) return;
|
|
218
|
-
const value = formData[column.columnKey];
|
|
219
|
-
if (column.columnType === "date-time" && isZonedDateTimeLike(value)) {
|
|
220
|
-
result[column.columnKey] = { $date: value.toAbsoluteString() };
|
|
221
|
-
} else if (column.columnType === "date-time" && typeof value === "string") {
|
|
222
|
-
result[column.columnKey] = { $date: value };
|
|
223
|
-
} else if (column.columnType === "string" && wasOriginallyOid(originalDoc?.[column.columnKey])) {
|
|
224
|
-
result[column.columnKey] = { $oid: value };
|
|
225
|
-
} else {
|
|
226
|
-
result[column.columnKey] = value;
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
delete result._id;
|
|
230
|
-
return result;
|
|
231
|
-
}
|