@ramathibodi/nuxt-commons 0.1.37 → 0.1.39

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.
@@ -1,113 +1,56 @@
1
1
  <script lang="ts" setup>
2
2
  import {VAutocomplete} from 'vuetify/components/VAutocomplete'
3
- import {concat, isEmpty, sortBy} from 'lodash-es'
4
- import {computed, ref, watch,watchEffect} from 'vue'
5
- import {watchDebounced} from '@vueuse/core'
6
- import {useFuzzy} from '../../composables/utils/fuzzy'
7
- import {useGraphQl} from '../../composables/graphql'
3
+ import {concat} from 'lodash-es'
4
+ import {computed} from 'vue'
8
5
 
9
6
  interface Props extends /* @vue-ignore */ InstanceType<typeof VAutocomplete['$props']> {
10
- fuzzy?: boolean
7
+ sortBy?: 'itemCode' | 'itemValue'
8
+ showCode?: boolean
9
+
11
10
  groupKey: string
11
+ itemCodes?: string[]
12
12
  lang?: 'TH' | 'EN'
13
- fields?: string[]
13
+ fields?: Array<string | object>
14
14
  noDataText?: string
15
15
  filterText?: string
16
16
  waitForFilter?: boolean
17
17
  waitForFilterText?: string
18
- modelValue?: string
19
- sortBy?: 'itemCode' | 'itemValue'
20
- showCode?: boolean
21
18
  }
22
19
 
23
20
  const props = withDefaults(defineProps<Props>(), {
24
- fuzzy: false,
25
- noDataText: 'ไม่พบข้อมูล',
21
+ sortBy: 'itemValue',
22
+ showCode: false,
23
+
26
24
  lang: 'TH',
25
+ noDataText: 'ไม่พบข้อมูล',
27
26
  waitForFilter: false,
28
- sortBy: 'itemValue',
29
- showCode: false
30
27
  })
31
28
 
32
- const emit = defineEmits(['update:modelValue'])
33
-
34
- const masterItems = ref<Array<any>>([])
35
- const items = ref<Array<any>>([])
36
- const selectedItem = ref<any>()
37
-
38
- const searchData = ref<string>('')
39
-
40
- const isLoading = ref(false)
41
- const isErrorLoading = ref(false)
42
-
43
- function query(groupKey: string, filterText: string | undefined) {
29
+ const computedModelName = computed(()=>{
44
30
  let operation = 'masterItemByGroupKey'
31
+ if (props.filterText) operation = 'masterItemByGroupKeyAndFilterText'
32
+ if (props.itemCodes && props.itemCodes.length>0) operation = 'masterItemByGroupKeyAndItemCodeIn'
33
+ if (props.waitForFilter && !props.filterText) operation = ""
34
+ return operation
35
+ })
45
36
 
46
- const variables: Record<string, any> = { groupKey: { value: groupKey, required: true } }
47
- if (filterText) {
48
- variables['filterText'] = { value: filterText }
49
- operation = 'masterItemByGroupKeyAndFilterText'
37
+ const computedModelBy = computed(()=>{
38
+ let modelBy : Record<string,any> = {groupKey: props.groupKey}
39
+ if (props.filterText) {
40
+ modelBy["filterText"] = props.filterText
50
41
  }
51
-
52
- let fields: any[] = ['itemCode', 'itemValue', 'itemValueAlternative']
53
- if (props.fields) fields = concat(fields, props.fields)
54
-
55
- isLoading.value = true
56
-
57
- useGraphQl().queryPromise(operation, fields, variables).then((result: any) => {
58
- masterItems.value = result
59
- items.value = result
60
- isErrorLoading.value = false
61
- }).catch((_error) => {
62
- isErrorLoading.value = true
63
- }).finally(() => {
64
- isLoading.value = false
65
- })
66
- }
67
-
68
- watchEffect(()=>{
69
- if (props.waitForFilter && !props.filterText) {
70
- masterItems.value = []
71
- items.value = []
72
- selectedItem.value = undefined
73
- } else {
74
- query(props.groupKey, props.filterText)
42
+ if (props.itemCodes && props.itemCodes.length>0) {
43
+ modelBy["itemCodes"] = props.itemCodes
75
44
  }
45
+ return modelBy
76
46
  })
77
47
 
78
- watch(() => props.modelValue, (newValue) => {
79
- selectedItem.value = newValue
80
- }, { immediate: true })
81
-
82
- watch(selectedItem, (newValue) => {
83
- emit('update:modelValue', newValue)
48
+ const computedFields = computed(()=>{
49
+ return concat(['itemCode', 'itemValue', 'itemValueAlternative'],props.fields)
84
50
  })
85
51
 
86
- async function fuzzySearch() {
87
- if (props.fuzzy) {
88
- if (isEmpty(searchData.value)) {
89
- items.value = masterItems.value
90
- }
91
- else {
92
- let fields: any[] = ['itemCode', 'itemValue', 'itemValueAlternative']
93
- if (props.fields) fields = concat(fields, props.fields)
94
-
95
- const results: any = useFuzzy(searchData.value, masterItems.value, fields)
96
- items.value = []
97
- if (results.value.length) {
98
- for (let i = 0; results.value.length > i; i++) {
99
- if (results.value[i].item) items.value.push(results.value[i].item)
100
- }
101
- }
102
- }
103
- }
104
- }
105
-
106
- watchDebounced(searchData, fuzzySearch, { debounce: 1000, maxWait: 5000 })
107
-
108
52
  const itemTitleField = computed(() => {
109
- if (props.lang == 'TH') return 'itemValue'
110
- else return 'itemValueAlternative'
53
+ return (props.lang == 'TH') ? 'itemValue' : 'itemValueAlternative'
111
54
  })
112
55
 
113
56
  const computedNoDataText = computed(() => {
@@ -116,64 +59,34 @@ const computedNoDataText = computed(() => {
116
59
  })
117
60
 
118
61
  const computedSortBy = computed(()=>{
119
- let sortByField = props.sortBy
120
- if (sortByField == 'itemValue') {
62
+ if (props.sortBy == 'itemValue') {
121
63
  if (props.showCode) return ['itemCode']
122
64
  else return [itemTitleField.value,'itemValue','itemCode']
123
65
  } else {
124
- return [sortByField]
125
- }
126
- })
127
-
128
- const computedItems = computed(()=>{
129
- if (props.fuzzy && !isEmpty(searchData.value)) {
130
- return items.value
131
- } else {
132
- return sortBy(items.value, computedSortBy.value)
66
+ return [props.sortBy]
133
67
  }
134
68
  })
135
69
  </script>
136
70
 
137
71
  <template>
138
- <v-autocomplete
139
- v-model="selectedItem"
140
- v-model:search="searchData"
141
- v-bind="$attrs"
142
- :items="computedItems"
143
- :no-filter="props.fuzzy"
144
- :item-title="itemTitleField"
145
- item-value="itemCode"
146
- :no-data-text="computedNoDataText"
147
- :loading="isLoading"
72
+ <model-autocomplete
73
+ :model-name="computedModelName"
74
+ :model-by="computedModelBy"
75
+ :fields="computedFields"
76
+ :sort-by="computedSortBy"
77
+ :item-title="itemTitleField"
78
+ item-value="itemCode"
79
+ :no-data-text="computedNoDataText"
80
+ :show-code="props.showCode"
148
81
  >
149
- <!-- @ts-ignore -->
150
82
  <template
151
- v-for="(_, name, index) in ($slots as {})"
152
- :key="index"
153
- #[name]="slotData"
154
- >
155
- <slot
156
- :name="name"
157
- v-bind="((slotData || {}) as object)"
158
- :operation="operation"
159
- />
160
- </template>
161
- <template
162
- v-if="!$slots.item"
163
- #item="{ props, item }"
83
+ v-if="!$slots.item"
84
+ #item="{ props, item }"
164
85
  >
165
86
  <v-list-item
166
- v-bind="props"
167
- :title="(showCode ? item.raw.itemCode+'-' : '')+(item.title || item.raw.itemValue || item.raw.itemCode)"
87
+ v-bind="props"
88
+ :title="(showCode ? item.raw.itemCode+'-' : '')+(item.title || item.raw.itemValue || item.raw.itemCode)"
168
89
  />
169
90
  </template>
170
- <template
171
- v-if="isErrorLoading"
172
- #append
173
- >
174
- <v-icon color="error">
175
- mdi mdi-alert
176
- </v-icon>
177
- </template>
178
- </v-autocomplete>
91
+ </model-autocomplete>
179
92
  </template>
@@ -1,11 +1,9 @@
1
1
  <script lang="ts" setup>
2
- import { computedAsync } from '@vueuse/core'
3
- import { useGraphQl } from '../../composables/graphql'
4
-
5
2
  interface Props {
6
3
  groupKey?: string | null
7
4
  itemCode?: string | null
8
5
  locale?: string
6
+
9
7
  notFoundText?: string
10
8
  placeholder?: string
11
9
  }
@@ -14,24 +12,27 @@ const props = withDefaults(defineProps<Props>(), {
14
12
  locale: 'TH',
15
13
  })
16
14
 
17
- const masterItemValue = computedAsync<string>(async () => {
18
- if (props.groupKey && props.itemCode) {
19
- try {
20
- const result = await useGraphQl().queryPromise<any>("masterItemByGroupKeyAndItemCode",['itemValue', 'itemValueAlternative'],{ groupKey: props.groupKey, itemCode: props.itemCode })
21
-
22
- if (result) {
23
- return props.locale === 'TH'
24
- ? result.itemValue
25
- : result.itemValueAlternative || result.itemValue
26
- }
27
- } catch (e) {
28
- console.error(e)
29
- }
30
- }
15
+ const computedNotFoundText = computed(()=>{
31
16
  return props.notFoundText || `${props.itemCode}`
32
- }, props.placeholder || `${props.groupKey}(${props.itemCode})`)
17
+ })
18
+
19
+ const computedPlaceholder = computed(()=>{
20
+ return props.placeholder || `${props.groupKey}(${props.itemCode})`
21
+ })
22
+
23
+ const computedItemTitle = computed(()=>{
24
+ return (props.locale == 'TH')
25
+ ? "itemValue"
26
+ : (result : Record<string,any>) => result["itemValueAlternative"] || result["itemValue"]
27
+ })
33
28
  </script>
34
29
 
35
30
  <template>
36
- {{ masterItemValue }}
31
+ <model-label
32
+ model-name="masterItemByGroupKeyAndItemCode"
33
+ :model-by="{groupKey: props.groupKey, itemCode: props.itemCode}"
34
+ :item-title="computedItemTitle"
35
+ :not-found-text="computedNotFoundText"
36
+ :placeholder="computedPlaceholder"
37
+ ></model-label>
37
38
  </template>
@@ -8,7 +8,7 @@ import {useGraphQlOperation} from "../../composables/graphqlOperation";
8
8
 
9
9
  interface Props extends /* @vue-ignore */ InstanceType<typeof VAutocomplete['$props']> {
10
10
  fuzzy?: boolean
11
- sortBy?: string
11
+ sortBy?: string | string[]
12
12
  showCode?: boolean
13
13
 
14
14
  modelName: string
@@ -48,21 +48,28 @@ watchEffect(()=>{
48
48
  variables[props.serverSearchKey] = searchData.value
49
49
  }
50
50
 
51
- isLoading.value = true
51
+ if (props.modelName) {
52
+ isLoading.value = true
52
53
 
53
- useGraphQlOperation('Query',props.modelName,fields,variables,props.cache).then((result: any) => {
54
- if (isArray(result)) {
55
- modelItems.value = result
56
- items.value = result
57
- isErrorLoading.value = false
58
- } else {
54
+ useGraphQlOperation('Query',props.modelName,fields,variables,props.cache).then((result: any) => {
55
+ if (isArray(result)) {
56
+ modelItems.value = result
57
+ items.value = result
58
+ isErrorLoading.value = false
59
+ } else {
60
+ isErrorLoading.value = true
61
+ }
62
+ }).catch((_error) => {
63
+ modelItems.value = []
64
+ items.value = []
59
65
  isErrorLoading.value = true
60
- }
61
- }).catch((_error) => {
62
- isErrorLoading.value = true
63
- }).finally(() => {
64
- isLoading.value = false
65
- })
66
+ }).finally(() => {
67
+ isLoading.value = false
68
+ })
69
+ } else {
70
+ modelItems.value = []
71
+ items.value = []
72
+ }
66
73
  })
67
74
 
68
75
  async function fuzzySearch() {
@@ -88,12 +95,12 @@ async function fuzzySearch() {
88
95
  watchDebounced(searchData, fuzzySearch, { debounce: 1000, maxWait: 5000 })
89
96
 
90
97
  const computedItems = computed(()=>{
91
- let sortByField = props.sortBy || ((props.showCode) ? props.itemValue : props.itemTitle)
98
+ let sortByField = (!props.sortBy || typeof props.sortBy === "string") ? [props.sortBy || ((props.showCode) ? props.itemValue : props.itemTitle)] : props.sortBy
92
99
 
93
100
  if (props.fuzzy && !isEmpty(searchData.value)) {
94
101
  return items.value
95
102
  } else {
96
- return sortBy(items.value, [sortByField])
103
+ return sortBy(items.value, sortByField)
97
104
  }
98
105
  })
99
106
  </script>
@@ -117,7 +124,6 @@ const computedItems = computed(()=>{
117
124
  <slot
118
125
  :name="name"
119
126
  v-bind="((slotData || {}) as object)"
120
- :operation="operation"
121
127
  />
122
128
  </template>
123
129
  <template
@@ -1,11 +1,12 @@
1
1
  <script lang="ts" setup>
2
2
  import { computedAsync } from '@vueuse/core'
3
3
  import { useGraphQlOperation } from '../../composables/graphqlOperation'
4
+ import {concat} from "lodash-es";
4
5
 
5
6
  interface Props {
6
7
  modelName: string
7
8
  modelBy?: object
8
- itemTitle: string
9
+ itemTitle: string | ((result:Record<string,any>)=>void)
9
10
  fields?: Array<string | object>
10
11
  cache?: boolean
11
12
 
@@ -19,14 +20,16 @@ const props = withDefaults(defineProps<Props>(), {
19
20
 
20
21
  const modelItemValue = computedAsync<string>(async () => {
21
22
  if (props.modelName && props.itemTitle) {
22
- let fields: any[] = [props.itemTitle]
23
+ let fields: any[] = (typeof props.itemTitle === "string") ? [props.itemTitle] : []
24
+ if (props.fields) fields = concat(fields, props.fields)
25
+
23
26
  const variables: Record<string, any> = Object.assign({},props.modelBy)
27
+ const result : Record<string, any> = await useGraphQlOperation('Query',props.modelName,fields,variables,props.cache)
24
28
 
25
29
  try {
26
- const result : Record<string, any> = await useGraphQlOperation('Query',props.modelName,fields,variables,props.cache)
27
-
28
30
  if (result) {
29
- return result[props.itemTitle]
31
+ if (typeof props.itemTitle === "string") return result[props.itemTitle]
32
+ else return props.itemTitle(result)
30
33
  }
31
34
  } catch (e) {
32
35
  console.error(e)
@@ -1,2 +1,24 @@
1
+ export interface DocumentTemplateItem {
2
+ inputType: string;
3
+ width?: string | number;
4
+ inputLabel?: string;
5
+ variableName?: string;
6
+ validationRules?: string;
7
+ inputOptions?: string | object;
8
+ inputAttributes?: string;
9
+ inputCustomCode?: string;
10
+ columnAttributes?: string;
11
+ conditionalDisplay?: string;
12
+ computedValue?: string;
13
+ retrievedValue?: string;
14
+ }
15
+ export interface ChoiceItem {
16
+ label: string;
17
+ value: string;
18
+ }
1
19
  export declare const validationRulesRegex: RegExp;
2
- export declare function useDocumentTemplate(items: string | object): string;
20
+ export declare function useDocumentTemplate(items: string | object, parentTemplates?: string | string[]): string;
21
+ export declare function optionStringToChoiceObject(option: string | object): ChoiceItem[];
22
+ export declare function escapeObjectForInlineBinding(obj: object): string;
23
+ export declare function buildValidationRules(validationString: string): string;
24
+ export declare function processDefaultTemplate(item: DocumentTemplateItem, insideTemplate?: string, optionString?: string, validationRules?: string): string;
@@ -1,5 +1,7 @@
1
+ import { processTemplateFormTable } from "./templateFormTable.js";
2
+ import { processTemplateFormHidden } from "./templateFormHidden.js";
1
3
  export const validationRulesRegex = /^(require(?:\([^)]*\))?|requireIf(?:\([^)]*\))?|requireTrue(?:\([^)]*\))?|requireTrueIf(?:\([^)]*\))?|numeric(?:\([^)]*\))?|range(?:\([^)]*\))?|integer(?:\([^)]*\))?|unique(?:\([^)]*\))?|length(?:\([^)]*\))?|lengthGreater(?:\([^)]*\))?|lengthLess(?:\([^)]*\))?|telephone(?:\([^)]*\))?|email(?:\([^)]*\))?|regex(?:\([^)]*\))?)(,(require(?:\([^)]*\))?|requireIf(?:\([^)]*\))?|requireTrue(?:\([^)]*\))?|requireTrueIf(?:\([^)]*\))?|numeric(?:\([^)]*\))?|range(?:\([^)]*\))?|integer(?:\([^)]*\))?|unique(?:\([^)]*\))?|length(?:\([^)]*\))?|lengthGreater(?:\([^)]*\))?|lengthLess(?:\([^)]*\))?|telephone(?:\([^)]*\))?|email(?:\([^)]*\))?|regex(?:\([^)]*\))?))*$/;
2
- export function useDocumentTemplate(items) {
4
+ export function useDocumentTemplate(items, parentTemplates) {
3
5
  if (!items) return "";
4
6
  if (typeof items === "string") {
5
7
  try {
@@ -11,94 +13,108 @@ export function useDocumentTemplate(items) {
11
13
  if (!Array.isArray(items) || !items.every((item) => typeof item === "object" && item !== null)) {
12
14
  return "";
13
15
  }
14
- let templateString = items.map((item) => templateItemToString(item)).join("");
15
- templateString = `<v-container fluid><v-row dense>${templateString}</v-row></v-container>`;
16
- return templateString;
16
+ const templateString = items.map((item) => templateItemToString(item, parentTemplates || [])).join("");
17
+ return !parentTemplates || parentTemplates.length == 0 ? `<v-container fluid><v-row dense>${templateString}</v-row></v-container>` : `<v-row dense>${templateString}</v-row>`;
17
18
  }
18
- function templateItemToString(item) {
19
+ function templateItemToString(item, parentTemplates) {
19
20
  let optionString = "";
20
- let validationRules = "";
21
21
  if (item.inputOptions) {
22
- if (item.inputType == "MasterAutocomplete") {
23
- optionString += 'groupKey="' + item.inputOptions + '" ';
22
+ if (item.inputType === "MasterAutocomplete") {
23
+ optionString += `groupKey="${item.inputOptions}" `;
24
24
  }
25
- if (item.inputType == "VSelect" || item.inputType == "VAutocomplete" || item.inputType == "VCombobox") {
25
+ if (["VSelect", "VAutocomplete", "VCombobox"].includes(item.inputType)) {
26
26
  const choice = optionStringToChoiceObject(item.inputOptions);
27
- optionString = `item-value="value" item-title="label" :items='` + JSON.stringify(choice).replaceAll("'", "&#39;") + "' ";
27
+ optionString = `item-value="value" item-title="label" :items='${escapeObjectForInlineBinding(choice)}' `;
28
28
  }
29
- if (item.inputType == "VRadio" || item.inputType == "VRadioInline") {
29
+ if (["VRadio", "VRadioInline"].includes(item.inputType)) {
30
30
  const choice = optionStringToChoiceObject(item.inputOptions);
31
- optionString = choice.map((choiceItem) => `<v-radio label="${choiceItem.label}" value="${choiceItem.value}" ${item.inputAttributes ? " " + item.inputAttributes : ""}></v-radio>`).join("");
31
+ optionString = choice.map((choiceItem) => `<v-radio label="${choiceItem.label || ""}" value="${choiceItem.value || ""}" ${item.inputAttributes ? " " + item.inputAttributes : ""}></v-radio>`).join("");
32
32
  }
33
- if (item.inputType == "Header") {
34
- optionString = item.inputOptions.split(",").join(" ");
33
+ if (item.inputType === "Header") {
34
+ if (typeof item.inputOptions === "string") optionString = item.inputOptions.split(",").join(" ");
35
35
  }
36
36
  }
37
- if (item.inputType == "FormDateTime" && !item.inputAttributes?.includes("dense")) item.inputAttributes = (item.inputAttributes?.trim() + " dense").trim();
38
- if (item.validationRules) validationRules = buildValidationRules(item.validationRules);
37
+ if (item.conditionalDisplay) {
38
+ item.inputAttributes = `${item.inputAttributes?.trim() || ""} v-if="${item.conditionalDisplay}"`.trim();
39
+ }
40
+ if (item.inputType === "FormDateTime" && !item.inputAttributes?.includes("dense")) {
41
+ item.inputAttributes = `${item.inputAttributes?.trim() || ""} dense`.trim();
42
+ }
39
43
  let templateString;
40
44
  switch (item.inputType) {
41
45
  case "CustomCode":
42
- templateString = `${item.inputCustomCode}`;
46
+ templateString = item.inputCustomCode || "";
43
47
  break;
44
48
  case "VRadio":
45
- templateString = `<v-radio-group v-model="data.${item.variableName}" label="${item.inputLabel || item.variableName}"${validationRules ? " " + validationRules.trim() : ""}>${optionString ? " " + optionString.trim() : ""}</v-radio-group>`;
46
- break;
47
49
  case "VRadioInline":
48
- templateString = `<v-radio-group v-model="data.${item.variableName}" inline${validationRules ? " " + validationRules.trim() : ""}><template #prepend>${item.inputLabel || item.variableName}</template>${optionString ? " " + optionString.trim() : ""}</v-radio-group>`;
50
+ const inlineAttr = item.inputType === "VRadioInline" ? " inline" : "";
51
+ const validationRules = item.validationRules ? buildValidationRules(item.validationRules) || "" : "";
52
+ templateString = `<v-radio-group v-model="data.${item.variableName || ""}"${inlineAttr}${validationRules ? " " + validationRules.trim() : ""}>${item.inputLabel ? `<template #prepend>${item.inputLabel}</template>` : ""}${optionString ? " " + optionString.trim() : ""}</v-radio-group>`;
49
53
  break;
50
54
  case "Separator":
51
- templateString = `</v-row><v-row dense>`;
55
+ templateString = "</v-row><v-row dense>";
52
56
  break;
53
57
  case "Header":
54
- templateString = `<div class="${optionString ? optionString.trim() : "text-h4 font-weight-medium"}">${item.inputLabel || item.variableName}</div>`;
58
+ templateString = `<div class="${optionString || "text-h4 font-weight-medium"}">${item.inputLabel || item.variableName || ""}</div>`;
55
59
  break;
56
60
  case "FormTable":
57
- templateString = `<form-table v-model="data.${item.variableName}" label="${item.inputLabel || item.variableName}"${validationRules ? " " + validationRules.trim() : ""} ${item.inputAttributes ? " " + item.inputAttributes : ""}>${optionString ? " " + optionString.trim() : ""}
58
- <template #form>${item.inputFormTable}</template>
59
- </form-table>`;
61
+ templateString = processTemplateFormTable(item, parentTemplates);
60
62
  break;
61
63
  case "FormHidden":
62
- templateString = `${item.inputOptions} / ${item.inputFormHidden} <form-hidden v-model="data.${item.variableName}" :item-value="${item.inputOptions ? item.inputOptions : "data"}" :hook="() => {${item.inputFormHidden}}">
63
- </form-hidden>`;
64
+ templateString = processTemplateFormHidden(item, parentTemplates);
65
+ break;
66
+ case "DocumentForm":
67
+ const parentTemplatesString = typeof parentTemplates === "string" ? parentTemplates : parentTemplates.join("|");
68
+ templateString = `<document-form v-model="data" templateCode="${item.inputOptions || ""}" parent-templates="${parentTemplatesString}"></document-form>`;
64
69
  break;
65
70
  default:
66
- templateString = `<${item.inputType} v-model="data.${item.variableName}" label="${item.inputLabel || item.variableName}"${item.inputAttributes ? " " + item.inputAttributes : ""}${optionString ? " " + optionString.trim() : ""}${validationRules ? " " + validationRules.trim() : ""}></${item.inputType}>`;
71
+ templateString = processDefaultTemplate(item, void 0, optionString);
72
+ }
73
+ if (!["Separator"].includes(item.inputType)) templateString = `<v-col${item.width ? ` cols="${item.width}"` : ""}${item.columnAttributes ? " " + item.columnAttributes : ""}>${templateString}</v-col>`;
74
+ if (["Header", "DocumentForm"].includes(item.inputType)) templateString = `</v-row><v-row dense>${templateString}</v-row><v-row dense>`;
75
+ if (item.computedValue && item.variableName) {
76
+ templateString = `${templateString || ""}<FormHidden v-model="data.${item.variableName}" :item-value="data" :hook="()=>${item.computedValue}"/>`.trim();
67
77
  }
68
- if (!["Separator", "Header"].includes(item.inputType)) templateString = `<v-col${item.width ? ' cols="' + item.width + '"' : ""}${item.columnAttributes ? " " + item.columnAttributes : ""}>${templateString}</v-col>`;
69
- if (["Header"].includes(item.inputType)) templateString = `</v-row><v-row dense><v-col${item.columnAttributes ? " " + item.columnAttributes : ""}>${templateString}</v-col></v-row><v-row dense>`;
70
- return templateString;
78
+ return templateString || "";
71
79
  }
72
- function optionStringToChoiceObject(option) {
73
- let returnObject = [];
74
- if (/^[^'",]+(,[^'",]+)*$/.test(option.trim())) {
75
- returnObject = option.split(",").map((item) => {
80
+ export function optionStringToChoiceObject(option) {
81
+ if (typeof option === "string") {
82
+ if (!/^[^'",]+(,[^'",]+)*$/.test(option.trim())) return [];
83
+ return option.split(",").map((item) => {
76
84
  item = item.trim();
77
85
  if (item.includes("|")) {
78
86
  const [label, value] = item.split("|").map((str) => str.trim().replace(/^['"]|['"]$/g, ""));
79
- return { label, value };
80
- } else {
81
- item = item.replace(/^['"]|['"]$/g, "");
82
- return { label: item, value: item };
87
+ return { label: label || "", value: value || "" };
83
88
  }
89
+ item = item.replace(/^['"]|['"]$/g, "");
90
+ return { label: item, value: item };
84
91
  });
85
92
  }
86
- return returnObject;
93
+ if (Array.isArray(option) && option.every((item) => typeof item === "object" && item !== null)) {
94
+ return option.sort((a, b) => (a.id ?? Infinity) - (b.id ?? Infinity)).map((item) => ({
95
+ label: item.label || item.value || "",
96
+ // Default to empty string if label is undefined
97
+ value: item.value || ""
98
+ // Default to empty string if value is undefined
99
+ }));
100
+ }
101
+ return [];
102
+ }
103
+ export function escapeObjectForInlineBinding(obj) {
104
+ return JSON.stringify(obj).replace(/'/g, "&#39;");
87
105
  }
88
- function buildValidationRules(validationString) {
89
- validationString = validationString.trim();
90
- if (validationString.startsWith("[")) validationString = validationString.substring(1);
91
- if (validationString.endsWith("]")) validationString = validationString.substring(0, validationString.length - 1);
106
+ export function buildValidationRules(validationString) {
107
+ validationString = validationString.replace(/^\[|]$/g, "").trim();
92
108
  if (!validationRulesRegex.test(validationString)) return "";
93
- validationString = validationString.split(",").map((rule) => {
109
+ const rules = validationString.split(",").map((rule) => {
94
110
  rule = rule.trim();
95
- if (!rule.startsWith("rules.")) {
96
- rule = "rules." + rule;
97
- }
98
- if (!/\(.+\)$/.test(rule)) {
99
- rule = rule + "()";
100
- }
101
- return rule.replaceAll('"', "'");
102
- }).join(",");
103
- return `:rules="[${validationString}]"`;
111
+ if (!rule.startsWith("rules.")) rule = `rules.${rule}`;
112
+ if (!/\(.+\)$/.test(rule)) rule += "()";
113
+ return rule.replace(/"/g, "'");
114
+ });
115
+ return `:rules="[${rules.join(",")}]"`;
116
+ }
117
+ export function processDefaultTemplate(item, insideTemplate, optionString, validationRules) {
118
+ if (!validationRules) validationRules = item.validationRules ? buildValidationRules(item.validationRules) || "" : "";
119
+ return `<${item.inputType} v-model="data.${item.variableName || ""}" label="${item.inputLabel || item.variableName || ""}"${item.inputAttributes ? " " + item.inputAttributes : ""}${optionString ? " " + optionString.trim() : ""}${validationRules ? " " + validationRules.trim() : ""}>${insideTemplate || ""}</${item.inputType}>`;
104
120
  }
@@ -0,0 +1,2 @@
1
+ import { type DocumentTemplateItem } from './template.js';
2
+ export declare function processTemplateFormHidden(item: DocumentTemplateItem, parentTemplates: string | string[]): string;
@@ -0,0 +1,10 @@
1
+ export function processTemplateFormHidden(item, parentTemplates) {
2
+ let formHiddenOptions = Object.assign({ itemValue: "", hook: "" }, item.inputOptions);
3
+ if (formHiddenOptions.hook.trim()) {
4
+ formHiddenOptions.hook = formHiddenOptions.hook.replace(/\n/g, ";").trim();
5
+ if (!/^return\s/.test(formHiddenOptions.hook)) {
6
+ formHiddenOptions.hook = `return ${formHiddenOptions.hook}`;
7
+ }
8
+ }
9
+ return `<form-hidden v-model="data.${item.variableName || ""}" :item-value="${formHiddenOptions.itemValue || "data"}" :hook="(data,modelValue) => {${formHiddenOptions.hook}}"></form-hidden>`;
10
+ }
@@ -0,0 +1,2 @@
1
+ import { type DocumentTemplateItem } from './template.js';
2
+ export declare function processTemplateFormTable(item: DocumentTemplateItem, parentTemplates: string | string[]): string;
@@ -0,0 +1,9 @@
1
+ import {
2
+ escapeObjectForInlineBinding,
3
+ processDefaultTemplate,
4
+ useDocumentTemplate
5
+ } from "./template.js";
6
+ export function processTemplateFormTable(item, parentTemplates) {
7
+ let tableOptions = Object.assign({ title: item.inputLabel || "", formTemplate: "" }, item.inputOptions);
8
+ return processDefaultTemplate(item, `<template #form="{data,rules}">${useDocumentTemplate(tableOptions.formTemplate)}</template>`, `title="${tableOptions.title}" :headers='${escapeObjectForInlineBinding(tableOptions.headers || {})}'`);
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ramathibodi/nuxt-commons",
3
- "version": "0.1.37",
3
+ "version": "0.1.39",
4
4
  "description": "Ramathibodi Nuxt modules for common components",
5
5
  "repository": {
6
6
  "type": "git",
@@ -115,5 +115,5 @@
115
115
  "vitest": "^1.6.0",
116
116
  "vue-tsc": "2.0.29"
117
117
  },
118
- "packageManager": "pnpm@9.15.0+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c"
118
+ "packageManager": "pnpm@9.15.4+sha512.b2dc20e2fc72b3e18848459b37359a32064663e5627a51e4c74b2c29dd8e8e0491483c3abb40789cfd578bf362fb6ba8261b05f0387d76792ed6e23ea3b1b6a0"
119
119
  }