@ramathibodi/nuxt-commons 0.1.56 → 0.1.58
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 +1 -1
- package/dist/runtime/components/document/TemplateBuilder.vue +133 -122
- package/dist/runtime/components/form/Pad.vue +39 -0
- package/dist/runtime/components/form/TableData.vue +180 -41
- package/dist/runtime/components/label/DateCount.vue +40 -0
- package/dist/runtime/components/label/Object.vue +14 -0
- package/dist/runtime/components/master/Autocomplete.vue +25 -108
- package/dist/runtime/components/master/Combobox.vue +59 -83
- package/dist/runtime/components/model/Autocomplete.vue +23 -234
- package/dist/runtime/components/model/Combobox.vue +70 -0
- package/dist/runtime/composables/document/template.d.ts +4 -1
- package/dist/runtime/composables/document/template.js +22 -15
- package/dist/runtime/composables/document/templateFormHidden.d.ts +1 -1
- package/dist/runtime/composables/document/templateFormHidden.js +5 -2
- package/dist/runtime/composables/document/templateFormTable.d.ts +3 -2
- package/dist/runtime/composables/document/templateFormTable.js +36 -40
- package/dist/runtime/composables/document/templateFormTableData.d.ts +2 -0
- package/dist/runtime/composables/document/templateFormTableData.js +21 -0
- package/dist/runtime/composables/graphqlModel.d.ts +6 -6
- package/dist/runtime/composables/graphqlModelItem.d.ts +4 -4
- package/dist/runtime/composables/graphqlModelOperation.d.ts +6 -6
- package/dist/runtime/composables/lookupList.d.ts +44 -0
- package/dist/runtime/composables/lookupList.js +194 -0
- package/dist/runtime/composables/lookupListMaster.d.ts +28 -0
- package/dist/runtime/composables/lookupListMaster.js +61 -0
- package/dist/runtime/types/menu.d.ts +7 -4
- package/dist/runtime/utils/formatter.d.ts +1 -0
- package/dist/runtime/utils/formatter.js +19 -0
- package/dist/runtime/utils/object.d.ts +1 -0
- package/dist/runtime/utils/object.js +46 -1
- package/package.json +2 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { VCombobox } from 'vuetify/components/VCombobox'
|
|
3
|
+
import { defineModel, withDefaults } from 'vue'
|
|
4
|
+
import { useLookupList, type LookupProps } from '../../composables/lookupList'
|
|
5
|
+
|
|
6
|
+
interface Props extends /* @vue-ignore */ InstanceType<typeof VCombobox['$props']> {}
|
|
7
|
+
|
|
8
|
+
const props = withDefaults(defineProps<Props & LookupProps>(), {
|
|
9
|
+
fuzzy: false,
|
|
10
|
+
showCode: false,
|
|
11
|
+
cache: false,
|
|
12
|
+
serverSearch: false,
|
|
13
|
+
searchSearchSort: false,
|
|
14
|
+
serverSearchText: 'Type to search…',
|
|
15
|
+
serverSearchLoadingText: 'Searching…',
|
|
16
|
+
serverSearchDebounce: 500,
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const emit = defineEmits<{
|
|
20
|
+
(e: 'update:selectedObject', object: any | any[]): void
|
|
21
|
+
}>()
|
|
22
|
+
|
|
23
|
+
const selectedItems = defineModel<any>()
|
|
24
|
+
|
|
25
|
+
const {
|
|
26
|
+
searchData,
|
|
27
|
+
computedItems,
|
|
28
|
+
computedFilterKeys,
|
|
29
|
+
computedPlaceholder,
|
|
30
|
+
computedNoDataText,
|
|
31
|
+
isLoading,
|
|
32
|
+
isErrorLoading,
|
|
33
|
+
} = useLookupList(props, emit, selectedItems)
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<template>
|
|
37
|
+
<v-combobox
|
|
38
|
+
v-model="selectedItems"
|
|
39
|
+
v-model:search="searchData"
|
|
40
|
+
v-bind="$attrs"
|
|
41
|
+
:items="computedItems"
|
|
42
|
+
:filter-keys="computedFilterKeys"
|
|
43
|
+
:no-filter="props.fuzzy || props.serverSearch"
|
|
44
|
+
:item-title="props.itemTitle"
|
|
45
|
+
:item-value="props.itemValue"
|
|
46
|
+
:loading="isLoading"
|
|
47
|
+
:placeholder="computedPlaceholder"
|
|
48
|
+
:no-data-text="computedNoDataText"
|
|
49
|
+
:multiple="props.multiple"
|
|
50
|
+
:return-object="false"
|
|
51
|
+
>
|
|
52
|
+
<!-- passthrough slots -->
|
|
53
|
+
<!-- @ts-ignore -->
|
|
54
|
+
<template v-for="(_, name, index) in ($slots as {})" :key="index" #[name]="slotData">
|
|
55
|
+
<slot :name="name" v-bind="((slotData || {}) as object)" />
|
|
56
|
+
</template>
|
|
57
|
+
|
|
58
|
+
<template v-if="!$slots.item" #item="{ props: itemProps, item }">
|
|
59
|
+
<v-list-item v-bind="itemProps" :title="(props.showCode ? item.value + '-' : '') + item.title" />
|
|
60
|
+
</template>
|
|
61
|
+
|
|
62
|
+
<template v-if="!$slots.selection && props.showCode" #selection="{ item }">
|
|
63
|
+
{{ item.value + '-' + item.title }}
|
|
64
|
+
</template>
|
|
65
|
+
|
|
66
|
+
<template v-if="isErrorLoading" #append>
|
|
67
|
+
<v-icon color="error">mdi mdi-alert</v-icon>
|
|
68
|
+
</template>
|
|
69
|
+
</v-combobox>
|
|
70
|
+
</template>
|
|
@@ -11,6 +11,8 @@ export interface DocumentTemplateItem {
|
|
|
11
11
|
conditionalDisplay?: string;
|
|
12
12
|
computedValue?: string;
|
|
13
13
|
retrievedValue?: string;
|
|
14
|
+
customClass?: string;
|
|
15
|
+
customStyle?: string;
|
|
14
16
|
}
|
|
15
17
|
export interface ChoiceItem {
|
|
16
18
|
label: string;
|
|
@@ -18,7 +20,8 @@ export interface ChoiceItem {
|
|
|
18
20
|
}
|
|
19
21
|
export declare const validationRulesRegex: RegExp;
|
|
20
22
|
export declare function useDocumentTemplate(items: string | object, parentTemplates?: string | string[]): string;
|
|
23
|
+
export declare function templateItemToString(inputItem: DocumentTemplateItem, parentTemplates: string | string[], dataVariable?: string): string;
|
|
21
24
|
export declare function optionStringToChoiceObject(option: string | object): ChoiceItem[];
|
|
22
25
|
export declare function escapeObjectForInlineBinding(obj: object): string;
|
|
23
26
|
export declare function buildValidationRules(validationString: string): string;
|
|
24
|
-
export declare function processDefaultTemplate(item: DocumentTemplateItem, insideTemplate?: string, optionString?: string, validationRules?: string): string;
|
|
27
|
+
export declare function processDefaultTemplate(item: DocumentTemplateItem, insideTemplate?: string, optionString?: string, validationRules?: string, dataVariable?: string): string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { processTemplateFormTable
|
|
1
|
+
import { processTemplateFormTable } from "./templateFormTable.js";
|
|
2
|
+
import { processTemplateFormTableData } from "./templateFormTableData.js";
|
|
2
3
|
import { processTemplateFormHidden } from "./templateFormHidden.js";
|
|
3
|
-
import { some, includes } from "lodash-es";
|
|
4
|
+
import { some, includes, cloneDeep } from "lodash-es";
|
|
4
5
|
export const validationRulesRegex = /^(require(?:\([^)]*\))?|requireIf\([^)]*\)|requireTrue(?:\([^)]*\))?|requireTrueIf\([^)]*\)|numeric(?:\([^)]*\))?|range\([^)]*\)|integer(?:\([^)]*\))?|unique\([^)]*\)|length(?:\([^)]*\))?|lengthGreater\([^)]*\)|lengthLess\([^)]*\)|telephone(?:\([^)]*\))?|email(?:\([^)]*\))?|regex\([^)]*\)|idcard(?:\([^)]*\))?|DateFuture(?:\([^)]*\))?|DatetimeFuture(?:\([^)]*\))?|DateHappen(?:\([^)]*\))?|DatetimeHappen(?:\([^)]*\))?|DateAfter\([^)]*\)|DateBefore\([^)]*\)|DateEqual\([^)]*\))(,(require(?:\([^)]*\))?|requireIf\([^)]*\)|requireTrue(?:\([^)]*\))?|requireTrueIf\([^)]*\)|numeric(?:\([^)]*\))?|range\([^)]*\)|integer(?:\([^)]*\))?|unique\([^)]*\)|length(?:\([^)]*\))?|lengthGreater\([^)]*\)|lengthLess\([^)]*\)|telephone(?:\([^)]*\))?|email(?:\([^)]*\))?|regex\([^)]*\)|idcard(?:\([^)]*\))?|DateFuture(?:\([^)]*\))?|DatetimeFuture(?:\([^)]*\))?|DateHappen(?:\([^)]*\))?|DatetimeHappen(?:\([^)]*\))?|DateAfter\([^)]*\)|DateBefore\([^)]*\)|DateEqual\([^)]*\)))*$/;
|
|
5
6
|
export function useDocumentTemplate(items, parentTemplates) {
|
|
6
7
|
if (!items) return "";
|
|
@@ -14,10 +15,12 @@ export function useDocumentTemplate(items, parentTemplates) {
|
|
|
14
15
|
if (!Array.isArray(items) || !items.every((item) => typeof item === "object" && item !== null)) {
|
|
15
16
|
return "";
|
|
16
17
|
}
|
|
17
|
-
const templateString = items.map((item) =>
|
|
18
|
+
const templateString = items.map((item) => columnWrapTemplateItemString(item, parentTemplates || [])).join("");
|
|
18
19
|
return !parentTemplates || parentTemplates.length == 0 ? `<v-container fluid><v-row dense>${templateString}</v-row></v-container>` : `<v-row dense>${templateString}</v-row>`;
|
|
19
20
|
}
|
|
20
|
-
function templateItemToString(
|
|
21
|
+
export function templateItemToString(inputItem, parentTemplates, dataVariable = "data") {
|
|
22
|
+
let item = cloneDeep(inputItem);
|
|
23
|
+
item.inputAttributes = item.inputAttributes?.trim() || "";
|
|
21
24
|
let optionString = "";
|
|
22
25
|
if (item.inputOptions) {
|
|
23
26
|
if (item.inputType === "MasterAutocomplete") {
|
|
@@ -52,22 +55,22 @@ function templateItemToString(item, parentTemplates) {
|
|
|
52
55
|
templateString = item.inputCustomCode || "";
|
|
53
56
|
break;
|
|
54
57
|
case "VRadio":
|
|
55
|
-
templateString = `${item.inputLabel ? `<p class="opacity-60">${item.inputLabel}</p>` : ""} <v-radio-group ${item.inputAttributes?.trim()} v-model="
|
|
58
|
+
templateString = `${item.inputLabel ? `<p class="opacity-60">${item.inputLabel}</p>` : ""} <v-radio-group ${item.inputAttributes?.trim()} v-model="${dataVariable}.${item.variableName || ""}"${validationRules ? " " + validationRules.trim() : ""}>${optionString ? " " + optionString.trim() : ""}</v-radio-group>`;
|
|
56
59
|
break;
|
|
57
60
|
case "VRadioInline":
|
|
58
|
-
templateString = `<v-radio-group ${item.inputAttributes?.trim()} v-model="
|
|
61
|
+
templateString = `<v-radio-group ${item.inputAttributes?.trim()} v-model="${dataVariable}.${item.variableName || ""}" inline ${validationRules ? " " + validationRules.trim() : ""}>${item.inputLabel ? `<template #prepend><span class="opacity-60">${item.inputLabel}</span></template>` : ""}${optionString ? " " + optionString.trim() : ""}</v-radio-group>`;
|
|
59
62
|
break;
|
|
60
63
|
case "Separator":
|
|
61
|
-
templateString =
|
|
64
|
+
templateString = `</v-row><v-row dense${item.customClass ? ` class="${item.customClass}"` : ""}${item.customStyle ? ` class="${item.customStyle}"` : ""}>`;
|
|
62
65
|
break;
|
|
63
66
|
case "Header":
|
|
64
67
|
templateString = `<div class="${optionString || "text-h4 font-weight-medium"}">${item.inputLabel || item.variableName || ""}</div>`;
|
|
65
68
|
break;
|
|
66
69
|
case "FormTable":
|
|
67
|
-
templateString = processTemplateFormTable(item, parentTemplates);
|
|
70
|
+
templateString = processTemplateFormTable(item, parentTemplates, dataVariable);
|
|
68
71
|
break;
|
|
69
72
|
case "FormTableData":
|
|
70
|
-
templateString = processTemplateFormTableData(item, parentTemplates);
|
|
73
|
+
templateString = processTemplateFormTableData(item, parentTemplates, dataVariable);
|
|
71
74
|
break;
|
|
72
75
|
case "FormHidden":
|
|
73
76
|
templateString = processTemplateFormHidden(item, parentTemplates);
|
|
@@ -77,15 +80,19 @@ function templateItemToString(item, parentTemplates) {
|
|
|
77
80
|
templateString = `<document-form :model-value="data" templateCode="${item.inputOptions || ""}" parent-templates="${parentTemplatesString}"></document-form>`;
|
|
78
81
|
break;
|
|
79
82
|
default:
|
|
80
|
-
templateString = processDefaultTemplate(item, void 0, optionString);
|
|
83
|
+
templateString = processDefaultTemplate(item, void 0, optionString, void 0, dataVariable);
|
|
81
84
|
}
|
|
82
|
-
if (!["Separator"].includes(item.inputType)) templateString = `<v-col${item.width ? ` cols="${item.width}"` : ""}${item.columnAttributes ? " " + item.columnAttributes : ""}>${templateString}</v-col>`;
|
|
83
|
-
if (["Header", "DocumentForm"].includes(item.inputType)) templateString = `</v-row><v-row dense>${templateString}</v-row><v-row dense>`;
|
|
84
85
|
if (item.computedValue && item.variableName) {
|
|
85
|
-
templateString = `${templateString || ""}<FormHidden v-model="
|
|
86
|
+
templateString = `${templateString || ""}<FormHidden v-model="${dataVariable}.${item.variableName}" :item-value="data" :hook="()=>${item.computedValue}"/>`.trim();
|
|
86
87
|
}
|
|
87
88
|
return templateString || "";
|
|
88
89
|
}
|
|
90
|
+
function columnWrapTemplateItemString(item, parentTemplates, dataVariable = "data") {
|
|
91
|
+
let templateString = templateItemToString(item, parentTemplates, dataVariable);
|
|
92
|
+
if (!["Separator", "FormHidden"].includes(item.inputType)) templateString = `<v-col${item.width ? ` cols="${item.width}"` : ""}${item.columnAttributes ? " " + item.columnAttributes : ""}${item.customClass ? ` class="${item.customClass}"` : ""}${item.customStyle ? ` class="${item.customStyle}"` : ""}>${templateString}</v-col>`;
|
|
93
|
+
if (["Header", "DocumentForm"].includes(item.inputType)) templateString = `</v-row><v-row dense${item.customClass ? ` class="${item.customClass}"` : ""}${item.customStyle ? ` class="${item.customStyle}"` : ""}>${templateString}</v-row><v-row dense>`;
|
|
94
|
+
return templateString || "";
|
|
95
|
+
}
|
|
89
96
|
export function optionStringToChoiceObject(option) {
|
|
90
97
|
if (typeof option === "string") {
|
|
91
98
|
if (!/^[^'",]+(,[^'",]+)*$/.test(option.trim())) return [];
|
|
@@ -137,9 +144,9 @@ export function buildValidationRules(validationString) {
|
|
|
137
144
|
});
|
|
138
145
|
return `:rules="[${formatted.join(",")}]"`;
|
|
139
146
|
}
|
|
140
|
-
export function processDefaultTemplate(item, insideTemplate, optionString, validationRules) {
|
|
147
|
+
export function processDefaultTemplate(item, insideTemplate, optionString, validationRules, dataVariable = "data") {
|
|
141
148
|
if (!validationRules) validationRules = item.validationRules ? buildValidationRules(item.validationRules) || "" : "";
|
|
142
149
|
const hasVariant = some([item.inputAttributes], (str) => includes(str, "variant"));
|
|
143
150
|
const defaultAttributes = `${!hasVariant ? `variant="underlined"` : ""} :active=isReadonly`;
|
|
144
|
-
return `<${item.inputType} ${defaultAttributes} v-model="
|
|
151
|
+
return `<${item.inputType} ${defaultAttributes} v-model="${dataVariable}.${item.variableName || ""}" label="${item.inputLabel || item.variableName || ""}"${item.inputAttributes ? " " + item.inputAttributes : ""}${optionString ? " " + optionString.trim() : ""}${validationRules ? " " + validationRules.trim() : ""}>${insideTemplate || ""}</${item.inputType}>`;
|
|
145
152
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { type DocumentTemplateItem } from './template.js';
|
|
2
|
-
export declare function processTemplateFormHidden(item: DocumentTemplateItem, parentTemplates: string | string[]): string;
|
|
2
|
+
export declare function processTemplateFormHidden(item: DocumentTemplateItem, parentTemplates: string | string[], dataVariable?: string): string;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
export function processTemplateFormHidden(item, parentTemplates) {
|
|
1
|
+
export function processTemplateFormHidden(item, parentTemplates, dataVariable = "data") {
|
|
2
2
|
let formHiddenOptions = Object.assign({ itemValue: "", hook: "" }, item.inputOptions);
|
|
3
3
|
if (formHiddenOptions.hook.trim()) {
|
|
4
4
|
formHiddenOptions.hook = formHiddenOptions.hook.replace(/\n/g, ";").trim();
|
|
5
5
|
if (!/^return\s/.test(formHiddenOptions.hook)) {
|
|
6
6
|
formHiddenOptions.hook = `return ${formHiddenOptions.hook}`;
|
|
7
7
|
}
|
|
8
|
+
formHiddenOptions.hook = ` :hook="(data,modelValue) => {${formHiddenOptions.hook}}"`;
|
|
9
|
+
} else {
|
|
10
|
+
formHiddenOptions.hook = "";
|
|
8
11
|
}
|
|
9
|
-
return `<form-hidden v-model="
|
|
12
|
+
return `<form-hidden v-model="${dataVariable}.${item.variableName || ""}" :item-value="${formHiddenOptions.itemValue || "data"}"${formHiddenOptions.hook}></form-hidden>`;
|
|
10
13
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { type DocumentTemplateItem } from './template.js';
|
|
2
|
-
export declare function
|
|
3
|
-
export declare function
|
|
2
|
+
export declare function autoActionHeader(tableHeader: Record<string, any>[]): Record<string, any>[];
|
|
3
|
+
export declare function templateToHeader(template: string | object | undefined): Record<string, any>[];
|
|
4
|
+
export declare function processTemplateFormTable(item: DocumentTemplateItem, parentTemplates: string | string[], dataVariable: string): string;
|
|
@@ -3,48 +3,44 @@ import {
|
|
|
3
3
|
processDefaultTemplate,
|
|
4
4
|
useDocumentTemplate
|
|
5
5
|
} from "./template.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
let
|
|
9
|
-
if (!
|
|
10
|
-
return
|
|
6
|
+
import { cloneDeep, isString, isArray } from "lodash-es";
|
|
7
|
+
export function autoActionHeader(tableHeader) {
|
|
8
|
+
let autoTableHeader = cloneDeep(tableHeader);
|
|
9
|
+
if (!autoTableHeader.some((h) => h.key === "action")) autoTableHeader.push({ title: "Action", key: "action", width: "100px" });
|
|
10
|
+
return autoTableHeader;
|
|
11
|
+
}
|
|
12
|
+
export function templateToHeader(template) {
|
|
13
|
+
if (isString(template)) {
|
|
14
|
+
try {
|
|
15
|
+
template = JSON.parse(template);
|
|
16
|
+
} catch (e) {
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (!isArray(template)) return [];
|
|
20
|
+
return template.map((item) => {
|
|
21
|
+
return {
|
|
22
|
+
title: item.inputLabel || item.label || item.title || item.variableName || item.key,
|
|
23
|
+
key: item.variableName || item.key,
|
|
24
|
+
width: item.width && item.width > 12 ? guessWidth(item.width) : void 0
|
|
25
|
+
};
|
|
26
|
+
});
|
|
11
27
|
}
|
|
12
|
-
|
|
28
|
+
function guessWidth(width) {
|
|
29
|
+
if (width == 0) return void 0;
|
|
30
|
+
if (width >= 1 && width < 12) return (width * 100 / 12).toFixed(2) + "%";
|
|
31
|
+
if (width > 12 && width < 100) return width + "%";
|
|
32
|
+
else return width + "px";
|
|
33
|
+
}
|
|
34
|
+
export function processTemplateFormTable(item, parentTemplates, dataVariable) {
|
|
13
35
|
let tableOptions = Object.assign({ title: item.inputLabel || "", formTemplate: "" }, item.inputOptions);
|
|
14
36
|
let tableHeader = tableOptions.headers || [];
|
|
15
|
-
|
|
16
|
-
let
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const inputAttributes = header.conditionalDisplay ? `v-if="${header.conditionalDisplay}"`.trim() : "";
|
|
22
|
-
if (header.template) {
|
|
23
|
-
tableTemplate += `<template #item.${header.key}="{item}">
|
|
24
|
-
<form-pad ${inputAttributes} template='${header.template}' v-model="item.${header.key}">
|
|
25
|
-
</form-pad>
|
|
26
|
-
</template>`;
|
|
27
|
-
}
|
|
28
|
-
if (header.headerTemplate) {
|
|
29
|
-
tableTemplate += `<template #header.${header.key}>
|
|
30
|
-
<form-pad ${inputAttributes} template='${header.headerTemplate}'
|
|
31
|
-
@update:model-value='(value) =>{
|
|
32
|
-
for (const t of data.${item.variableName}) {
|
|
33
|
-
t.${header.key} = {...value}
|
|
34
|
-
}
|
|
35
|
-
}'>
|
|
36
|
-
</form-pad>
|
|
37
|
-
</template>`;
|
|
37
|
+
if (!tableHeader.some((h) => h.key === "action")) tableHeader.push({ title: "Action", key: "action", width: "100px" });
|
|
38
|
+
let tableItemTemplate = "";
|
|
39
|
+
tableHeader.forEach((h) => {
|
|
40
|
+
if (h.template) {
|
|
41
|
+
tableItemTemplate += `\r
|
|
42
|
+
<template #item.${h.key}="props">${h.template}</template>`;
|
|
38
43
|
}
|
|
39
|
-
}
|
|
40
|
-
item.
|
|
41
|
-
return processDefaultTemplate(item, tableTemplate, `title="${tableOptions.title}"
|
|
42
|
-
:headers='${escapeObjectForInlineBinding(tableHeader)}'
|
|
43
|
-
:items='${escapeObjectForInlineBinding(tableItems)}'
|
|
44
|
-
:insertable="false"
|
|
45
|
-
:importable="false"
|
|
46
|
-
:exportable="false"
|
|
47
|
-
:searchable="false"
|
|
48
|
-
hide-default-footer
|
|
49
|
-
`);
|
|
44
|
+
});
|
|
45
|
+
return processDefaultTemplate(item, `<template #form="{data,rules}">${useDocumentTemplate(tableOptions.formTemplate)}</template>${tableItemTemplate}`, `title="${tableOptions.title}" :headers='${escapeObjectForInlineBinding(tableHeader)}'`, void 0, dataVariable);
|
|
50
46
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
escapeObjectForInlineBinding,
|
|
3
|
+
processDefaultTemplate
|
|
4
|
+
} from "./template.js";
|
|
5
|
+
import { templateToHeader } from "./templateFormTable.js";
|
|
6
|
+
export function processTemplateFormTableData(item, parentTemplates, dataVariable) {
|
|
7
|
+
let tableOptions = Object.assign({ title: item.inputLabel || "", dataTemplate: {}, itemTemplate: {} }, item.inputOptions);
|
|
8
|
+
let tableHeader = tableOptions.headers || [];
|
|
9
|
+
if (tableHeader.length === 0) {
|
|
10
|
+
tableHeader = templateToHeader(tableOptions.itemTemplate);
|
|
11
|
+
}
|
|
12
|
+
let tableItemTemplate = "";
|
|
13
|
+
tableHeader.forEach((h) => {
|
|
14
|
+
if (h.template) {
|
|
15
|
+
tableItemTemplate += `\r
|
|
16
|
+
<template #item.${h.key}="props">${h.template}</template>`;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
let disableApplyToAll = tableOptions.disableApplyToAll && tableOptions.disableApplyToAll == "partial" ? escapeObjectForInlineBinding(tableOptions.disableApplyToAllPartial?.split(",").map((h) => h.trim()) || []) : !!(tableOptions.disableApplyToAll && tableOptions.disableApplyToAll == "true");
|
|
20
|
+
return processDefaultTemplate(item, void 0, `title="${tableOptions.title}" :headers='${escapeObjectForInlineBinding(tableHeader)}' :itemsInitial='${escapeObjectForInlineBinding(tableOptions.items || [])}' :dataTemplate='${escapeObjectForInlineBinding(tableOptions.dataTemplate)}' :disableApplyToAll='${disableApplyToAll}'`, void 0, dataVariable);
|
|
21
|
+
}
|
|
@@ -13,12 +13,12 @@ export declare function useGraphqlModel<T extends GraphqlModelProps>(props: T):
|
|
|
13
13
|
search: import("vue").Ref<string | undefined, string | undefined>;
|
|
14
14
|
setSearch: (keyword: string) => void;
|
|
15
15
|
currentOptions: import("vue").Ref<any, any>;
|
|
16
|
-
operationCreate: import("vue").ComputedRef<import("
|
|
17
|
-
operationUpdate: import("vue").ComputedRef<import("
|
|
18
|
-
operationDelete: import("vue").ComputedRef<import("
|
|
19
|
-
operationRead: import("vue").ComputedRef<import("
|
|
20
|
-
operationReadPageable: import("vue").ComputedRef<import("
|
|
21
|
-
operationSearch: import("vue").ComputedRef<import("
|
|
16
|
+
operationCreate: import("vue").ComputedRef<import("~/.nuxt/types/graphqlOperation").graphqlOperationObject<any, any>>;
|
|
17
|
+
operationUpdate: import("vue").ComputedRef<import("~/.nuxt/types/graphqlOperation").graphqlOperationObject<any, any>>;
|
|
18
|
+
operationDelete: import("vue").ComputedRef<import("~/.nuxt/types/graphqlOperation").graphqlOperationObject<any, any>>;
|
|
19
|
+
operationRead: import("vue").ComputedRef<import("~/.nuxt/types/graphqlOperation").graphqlOperationObject<any, any>>;
|
|
20
|
+
operationReadPageable: import("vue").ComputedRef<import("~/.nuxt/types/graphqlOperation").graphqlOperationObject<any, any>>;
|
|
21
|
+
operationSearch: import("vue").ComputedRef<import("~/.nuxt/types/graphqlOperation").graphqlOperationObject<any, any>>;
|
|
22
22
|
fields: import("vue").ComputedRef<(string | object)[]>;
|
|
23
23
|
canServerPageable: import("vue").ComputedRef<boolean>;
|
|
24
24
|
canServerSearch: import("vue").ComputedRef<boolean>;
|
|
@@ -4,10 +4,10 @@ export type GraphqlModelItemProps = Omit<GraphqlModelConfigProps, 'operationSear
|
|
|
4
4
|
export declare function useGraphqlModelItem<T extends GraphqlModelItemProps>(props: T): {
|
|
5
5
|
modelBy: import("vue").Ref<object | undefined, object | undefined>;
|
|
6
6
|
item: import("vue").Ref<Record<string, any> | undefined, Record<string, any> | undefined>;
|
|
7
|
-
operationCreate: import("vue").ComputedRef<import("
|
|
8
|
-
operationUpdate: import("vue").ComputedRef<import("
|
|
9
|
-
operationDelete: import("vue").ComputedRef<import("
|
|
10
|
-
operationRead: import("vue").ComputedRef<import("
|
|
7
|
+
operationCreate: import("vue").ComputedRef<import("~/.nuxt/types/graphqlOperation").graphqlOperationObject<any, any>>;
|
|
8
|
+
operationUpdate: import("vue").ComputedRef<import("~/.nuxt/types/graphqlOperation").graphqlOperationObject<any, any>>;
|
|
9
|
+
operationDelete: import("vue").ComputedRef<import("~/.nuxt/types/graphqlOperation").graphqlOperationObject<any, any>>;
|
|
10
|
+
operationRead: import("vue").ComputedRef<import("~/.nuxt/types/graphqlOperation").graphqlOperationObject<any, any>>;
|
|
11
11
|
fields: import("vue").ComputedRef<(string | object)[]>;
|
|
12
12
|
canCreate: import("vue").ComputedRef<boolean>;
|
|
13
13
|
canUpdate: import("vue").ComputedRef<boolean>;
|
|
@@ -12,10 +12,10 @@ export interface GraphqlModelConfigProps {
|
|
|
12
12
|
fields?: Array<string | object>;
|
|
13
13
|
}
|
|
14
14
|
export declare function useGraphqlModelOperation<T extends GraphqlModelConfigProps>(props: T): {
|
|
15
|
-
operationCreate: import("vue").ComputedRef<graphqlOperationObject<any, any>>;
|
|
16
|
-
operationUpdate: import("vue").ComputedRef<graphqlOperationObject<any, any>>;
|
|
17
|
-
operationDelete: import("vue").ComputedRef<graphqlOperationObject<any, any>>;
|
|
18
|
-
operationRead: import("vue").ComputedRef<graphqlOperationObject<any, any>>;
|
|
19
|
-
operationReadPageable: import("vue").ComputedRef<graphqlOperationObject<any, any>>;
|
|
20
|
-
operationSearch: import("vue").ComputedRef<graphqlOperationObject<any, any>>;
|
|
15
|
+
operationCreate: import("vue").ComputedRef<import("~/.nuxt/types/graphqlOperation").graphqlOperationObject<any, any>>;
|
|
16
|
+
operationUpdate: import("vue").ComputedRef<import("~/.nuxt/types/graphqlOperation").graphqlOperationObject<any, any>>;
|
|
17
|
+
operationDelete: import("vue").ComputedRef<import("~/.nuxt/types/graphqlOperation").graphqlOperationObject<any, any>>;
|
|
18
|
+
operationRead: import("vue").ComputedRef<import("~/.nuxt/types/graphqlOperation").graphqlOperationObject<any, any>>;
|
|
19
|
+
operationReadPageable: import("vue").ComputedRef<import("~/.nuxt/types/graphqlOperation").graphqlOperationObject<any, any>>;
|
|
20
|
+
operationSearch: import("vue").ComputedRef<import("~/.nuxt/types/graphqlOperation").graphqlOperationObject<any, any>>;
|
|
21
21
|
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type Ref } from 'vue';
|
|
2
|
+
export interface LookupProps {
|
|
3
|
+
fuzzy?: boolean;
|
|
4
|
+
sortBy?: string | string[];
|
|
5
|
+
showCode?: boolean;
|
|
6
|
+
modelName: string;
|
|
7
|
+
modelBy?: Record<string, any>;
|
|
8
|
+
itemTitle: string;
|
|
9
|
+
itemValue: string;
|
|
10
|
+
fields?: Array<string | object>;
|
|
11
|
+
cache?: boolean | number;
|
|
12
|
+
serverSearch?: boolean;
|
|
13
|
+
serverSearchKey?: string;
|
|
14
|
+
searchSearchSort?: boolean;
|
|
15
|
+
serverSearchDebounce?: number;
|
|
16
|
+
serverSearchText?: string;
|
|
17
|
+
serverSearchLoadingText?: string;
|
|
18
|
+
filterKeys?: string | string[];
|
|
19
|
+
noDataText?: string;
|
|
20
|
+
placeholder?: string;
|
|
21
|
+
multiple?: boolean;
|
|
22
|
+
modelSelectedItem?: string;
|
|
23
|
+
modelSelectedItemBy?: Record<string, any>;
|
|
24
|
+
modelSelectedItemKey?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface LookupEmits {
|
|
27
|
+
(e: 'update:selectedObject', payload: any | any[]): void;
|
|
28
|
+
}
|
|
29
|
+
export declare function useLookupList(props: LookupProps, emit: LookupEmits, selectedItems: Ref<any>): {
|
|
30
|
+
modelItems: Ref<any[], any[]>;
|
|
31
|
+
items: Ref<any[], any[]>;
|
|
32
|
+
searchData: Ref<string, string>;
|
|
33
|
+
isLoading: Ref<boolean, boolean>;
|
|
34
|
+
isErrorLoading: Ref<boolean, boolean>;
|
|
35
|
+
queryFields: import("vue").ComputedRef<any[]>;
|
|
36
|
+
computedFilterKeys: import("vue").ComputedRef<string | string[]>;
|
|
37
|
+
computedPlaceholder: import("vue").ComputedRef<string | undefined>;
|
|
38
|
+
computedNoDataText: import("vue").ComputedRef<string | undefined>;
|
|
39
|
+
computedItems: import("vue").ComputedRef<any[]>;
|
|
40
|
+
selectedItemsObject: Ref<any[], any[]>;
|
|
41
|
+
syncSelectedFromModelValue: () => Promise<void>;
|
|
42
|
+
loadItems: () => Promise<void>;
|
|
43
|
+
applyFuzzy: () => void;
|
|
44
|
+
};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { computed, ref, watch } from "vue";
|
|
2
|
+
import { watchDebounced } from "@vueuse/core";
|
|
3
|
+
import { union, isEmpty, isArray, sortBy, castArray } from "lodash-es";
|
|
4
|
+
import { useFuzzy } from "./utils/fuzzy.js";
|
|
5
|
+
import { useGraphQlOperation } from "./graphqlOperation.js";
|
|
6
|
+
export function useLookupList(props, emit, selectedItems) {
|
|
7
|
+
const modelItems = ref([]);
|
|
8
|
+
const items = ref([]);
|
|
9
|
+
const searchData = ref("");
|
|
10
|
+
const isLoading = ref(false);
|
|
11
|
+
const isErrorLoading = ref(false);
|
|
12
|
+
const queryFields = computed(() => {
|
|
13
|
+
let fieldsArray = [props.itemTitle, props.itemValue];
|
|
14
|
+
if (props.fields) fieldsArray = union(fieldsArray, props.fields);
|
|
15
|
+
return fieldsArray;
|
|
16
|
+
});
|
|
17
|
+
const computedFilterKeys = computed(() => {
|
|
18
|
+
if (props.filterKeys) return props.filterKeys;
|
|
19
|
+
return ["title", "raw." + props.itemValue];
|
|
20
|
+
});
|
|
21
|
+
const computedPlaceholder = computed(() => {
|
|
22
|
+
if (!props.serverSearch) return props.placeholder;
|
|
23
|
+
return isLoading.value ? props.serverSearchLoadingText : props.serverSearchText;
|
|
24
|
+
});
|
|
25
|
+
const computedNoDataText = computed(() => {
|
|
26
|
+
if (!props.serverSearch) return props.noDataText;
|
|
27
|
+
if (isLoading.value) return props.serverSearchLoadingText;
|
|
28
|
+
if (!searchData.value) return props.serverSearchText;
|
|
29
|
+
return props.noDataText;
|
|
30
|
+
});
|
|
31
|
+
let requestId = 0;
|
|
32
|
+
async function loadItems() {
|
|
33
|
+
if (!props.modelName) {
|
|
34
|
+
modelItems.value = [];
|
|
35
|
+
items.value = [];
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const id = ++requestId;
|
|
39
|
+
isLoading.value = true;
|
|
40
|
+
const variables = { ...props.modelBy || {} };
|
|
41
|
+
if (props.serverSearch && props.serverSearchKey) {
|
|
42
|
+
variables[props.serverSearchKey] = searchData.value;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const result = await useGraphQlOperation(
|
|
46
|
+
"Query",
|
|
47
|
+
props.modelName,
|
|
48
|
+
queryFields.value,
|
|
49
|
+
variables,
|
|
50
|
+
props.cache
|
|
51
|
+
);
|
|
52
|
+
if (id !== requestId) return;
|
|
53
|
+
if (isArray(result)) {
|
|
54
|
+
modelItems.value = result;
|
|
55
|
+
items.value = result;
|
|
56
|
+
isErrorLoading.value = false;
|
|
57
|
+
} else {
|
|
58
|
+
isErrorLoading.value = true;
|
|
59
|
+
modelItems.value = [];
|
|
60
|
+
items.value = [];
|
|
61
|
+
}
|
|
62
|
+
} catch (e) {
|
|
63
|
+
if (id !== requestId) return;
|
|
64
|
+
isErrorLoading.value = true;
|
|
65
|
+
modelItems.value = [];
|
|
66
|
+
items.value = [];
|
|
67
|
+
} finally {
|
|
68
|
+
if (id === requestId) isLoading.value = false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function applyFuzzy() {
|
|
72
|
+
if (!props.fuzzy) return;
|
|
73
|
+
if (isEmpty(searchData.value)) {
|
|
74
|
+
items.value = modelItems.value;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const results = useFuzzy(searchData.value, modelItems.value, queryFields.value);
|
|
78
|
+
const output = [];
|
|
79
|
+
if (results.value?.length) {
|
|
80
|
+
for (let index = 0; index < results.value.length; index++) {
|
|
81
|
+
const result = results.value[index];
|
|
82
|
+
if (result?.item) output.push(result.item);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
items.value = output;
|
|
86
|
+
}
|
|
87
|
+
const selectedItemsObject = ref([]);
|
|
88
|
+
async function syncSelectedFromModelValue() {
|
|
89
|
+
const modelValueData = selectedItems.value;
|
|
90
|
+
const values = castArray(modelValueData).filter(
|
|
91
|
+
(value) => value !== void 0 && value !== null && value !== ""
|
|
92
|
+
);
|
|
93
|
+
if (!values.length) {
|
|
94
|
+
emit("update:selectedObject", props.multiple ? [] : void 0);
|
|
95
|
+
selectedItemsObject.value = [];
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
let alreadyInObject = selectedItemsObject.value?.filter((item) => values.includes(item?.[props.itemValue])) || [];
|
|
99
|
+
const haveSet = /* @__PURE__ */ new Set([...alreadyInObject.map((item) => item?.[props.itemValue])]);
|
|
100
|
+
const missingValues = values.filter((value) => !haveSet.has(value));
|
|
101
|
+
const stillMissing = [];
|
|
102
|
+
for (const value of missingValues) {
|
|
103
|
+
const localHit = modelItems.value.find((item) => item?.[props.itemValue] === value);
|
|
104
|
+
if (localHit) {
|
|
105
|
+
alreadyInObject.push(localHit);
|
|
106
|
+
haveSet.add(value);
|
|
107
|
+
} else {
|
|
108
|
+
stillMissing.push(value);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (stillMissing.length && props.modelSelectedItem) {
|
|
112
|
+
try {
|
|
113
|
+
const key = props.modelSelectedItemKey || "id";
|
|
114
|
+
const variables = { ...props.modelSelectedItemBy || {} };
|
|
115
|
+
variables[key] = values;
|
|
116
|
+
const result = await useGraphQlOperation(
|
|
117
|
+
"Query",
|
|
118
|
+
props.modelSelectedItem,
|
|
119
|
+
queryFields.value,
|
|
120
|
+
variables,
|
|
121
|
+
props.cache
|
|
122
|
+
);
|
|
123
|
+
selectedItemsObject.value = castArray(result);
|
|
124
|
+
} catch {
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
selectedItemsObject.value = alreadyInObject;
|
|
128
|
+
}
|
|
129
|
+
emit("update:selectedObject", props.multiple ? selectedItemsObject.value : selectedItemsObject.value[0]);
|
|
130
|
+
}
|
|
131
|
+
watch(
|
|
132
|
+
() => [
|
|
133
|
+
props.modelName,
|
|
134
|
+
props.serverSearch,
|
|
135
|
+
props.serverSearchKey,
|
|
136
|
+
props.cache,
|
|
137
|
+
props.modelBy,
|
|
138
|
+
queryFields.value
|
|
139
|
+
],
|
|
140
|
+
() => loadItems(),
|
|
141
|
+
{ immediate: true, deep: true }
|
|
142
|
+
);
|
|
143
|
+
watchDebounced(
|
|
144
|
+
searchData,
|
|
145
|
+
() => {
|
|
146
|
+
if (props.serverSearch) loadItems();
|
|
147
|
+
else applyFuzzy();
|
|
148
|
+
},
|
|
149
|
+
{ debounce: props.serverSearch ? props.serverSearchDebounce ?? 500 : 300, maxWait: 1500 }
|
|
150
|
+
);
|
|
151
|
+
watch(
|
|
152
|
+
() => modelItems.value,
|
|
153
|
+
() => {
|
|
154
|
+
if (!props.serverSearch) applyFuzzy();
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
watch(
|
|
158
|
+
() => selectedItems.value,
|
|
159
|
+
() => {
|
|
160
|
+
syncSelectedFromModelValue();
|
|
161
|
+
},
|
|
162
|
+
{ immediate: true, deep: true }
|
|
163
|
+
);
|
|
164
|
+
const computedItems = computed(() => {
|
|
165
|
+
const sortByField = !props.sortBy || typeof props.sortBy === "string" ? [props.sortBy || (props.showCode ? props.itemValue : props.itemTitle)] : props.sortBy;
|
|
166
|
+
const baseItems = (props.fuzzy || props.serverSearch && !props.searchSearchSort) && !isEmpty(searchData.value) ? items.value : sortBy(items.value, sortByField);
|
|
167
|
+
for (const selectedItem of selectedItemsObject.value || []) {
|
|
168
|
+
if (!baseItems.find((existingItem) => existingItem[props.itemValue] === selectedItem[props.itemValue])) {
|
|
169
|
+
baseItems.push(selectedItem);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return baseItems;
|
|
173
|
+
});
|
|
174
|
+
return {
|
|
175
|
+
// state
|
|
176
|
+
modelItems,
|
|
177
|
+
items,
|
|
178
|
+
searchData,
|
|
179
|
+
isLoading,
|
|
180
|
+
isErrorLoading,
|
|
181
|
+
// derived
|
|
182
|
+
queryFields,
|
|
183
|
+
computedFilterKeys,
|
|
184
|
+
computedPlaceholder,
|
|
185
|
+
computedNoDataText,
|
|
186
|
+
computedItems,
|
|
187
|
+
// selection
|
|
188
|
+
selectedItemsObject,
|
|
189
|
+
syncSelectedFromModelValue,
|
|
190
|
+
// actions (exposed for testing if needed)
|
|
191
|
+
loadItems,
|
|
192
|
+
applyFuzzy
|
|
193
|
+
};
|
|
194
|
+
}
|