@ramathibodi/nuxt-commons 0.1.12 → 0.1.14

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.
Files changed (39) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/ExportCSV.vue +4 -4
  3. package/dist/runtime/components/FileBtn.vue +3 -3
  4. package/dist/runtime/components/ImportCSV.vue +1 -1
  5. package/dist/runtime/components/TabsGroup.vue +12 -8
  6. package/dist/runtime/components/form/Birthdate.vue +216 -0
  7. package/dist/runtime/components/form/CodeEditor.vue +3 -3
  8. package/dist/runtime/components/form/Dialog.vue +3 -2
  9. package/dist/runtime/components/form/Pad.vue +31 -9
  10. package/dist/runtime/components/form/Table.vue +5 -5
  11. package/dist/runtime/components/form/images/Capture.vue +231 -0
  12. package/dist/runtime/components/form/images/Edit.vue +95 -121
  13. package/dist/runtime/components/label/Field.vue +21 -8
  14. package/dist/runtime/components/master/Autocomplete.vue +7 -7
  15. package/dist/runtime/components/master/Combobox.vue +29 -25
  16. package/dist/runtime/components/master/RadioGroup.vue +40 -30
  17. package/dist/runtime/components/master/Select.vue +34 -23
  18. package/dist/runtime/components/model/Pad.vue +122 -0
  19. package/dist/runtime/components/model/Table.vue +126 -103
  20. package/dist/runtime/components/model/iterator.vue +312 -0
  21. package/dist/runtime/composables/graphql.mjs +1 -1
  22. package/dist/runtime/composables/graphqlModel.d.ts +7 -17
  23. package/dist/runtime/composables/graphqlModel.mjs +19 -89
  24. package/dist/runtime/composables/graphqlModelItem.d.ts +20 -0
  25. package/dist/runtime/composables/graphqlModelItem.mjs +103 -0
  26. package/dist/runtime/composables/graphqlModelOperation.d.ts +21 -0
  27. package/dist/runtime/composables/graphqlModelOperation.mjs +80 -0
  28. package/dist/runtime/composables/graphqlOperation.d.ts +2 -2
  29. package/dist/runtime/composables/graphqlOperation.mjs +9 -9
  30. package/dist/runtime/labs/form/TextFieldMask.vue +4 -4
  31. package/dist/runtime/types/formDialog.d.ts +3 -3
  32. package/dist/runtime/types/graphqlOperation.d.ts +14 -14
  33. package/package.json +5 -4
  34. package/scripts/postInstall.cjs +38 -35
  35. package/templates/.codegen/codegen.ts +8 -8
  36. package/templates/.codegen/plugin-schema-object.js +97 -97
  37. package/dist/runtime/components/Camera.vue +0 -129
  38. package/dist/runtime/components/form/images/CameraCrop.vue +0 -58
  39. package/dist/runtime/components/form/images/Preview.vue +0 -48
package/dist/module.json CHANGED
@@ -4,5 +4,5 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^3.0.0"
6
6
  },
7
- "version": "0.1.12"
7
+ "version": "0.1.14"
8
8
  }
@@ -1,8 +1,8 @@
1
1
  <script lang="ts" setup>
2
- import { ref, withDefaults } from 'vue'
2
+ import {ref, withDefaults} from 'vue'
3
3
  import * as XLSX from 'xlsx'
4
- import { VBtn } from 'vuetify/components/VBtn'
5
- import { useAlert } from '../composables/alert'
4
+ import {VBtn} from 'vuetify/components/VBtn'
5
+ import {useAlert} from '../composables/alert'
6
6
 
7
7
  interface ExportButtonProps extends /* @vue-ignore */ InstanceType<typeof VBtn['$props']> {
8
8
  fileName?: string
@@ -48,7 +48,7 @@ function exportFile() {
48
48
  >
49
49
  <slot
50
50
  :name="name"
51
- v-bind="(slotData as object)"
51
+ v-bind="((slotData || {}) as object)"
52
52
  />
53
53
  </template>
54
54
  </VBtn>
@@ -1,6 +1,6 @@
1
1
  <script lang="ts" setup>
2
- import { ref, watch } from 'vue'
3
- import { VBtn } from 'vuetify/components/VBtn'
2
+ import {ref, watch} from 'vue'
3
+ import {VBtn} from 'vuetify/components/VBtn'
4
4
 
5
5
  interface Props extends /* @vue-ignore */ InstanceType<typeof VBtn['$props']> {
6
6
  accept?: string
@@ -48,7 +48,7 @@ defineExpose({ reset })
48
48
  >
49
49
  <slot
50
50
  :name="name"
51
- v-bind="(slotData as object)"
51
+ v-bind="((slotData || {}) as object)"
52
52
  />
53
53
  </template>
54
54
  </v-btn>
@@ -57,7 +57,7 @@ function uploadedFile(files: File[] | File | undefined) {
57
57
  >
58
58
  <slot
59
59
  :name="name"
60
- v-bind="(slotData as object)"
60
+ v-bind="((slotData || {}) as object)"
61
61
  />
62
62
  </template>
63
63
  </FileBtn>
@@ -1,28 +1,32 @@
1
1
  <script lang="ts" setup>
2
- import { ref } from 'vue'
3
- import { VTabs } from 'vuetify/components'
2
+ import {ref} from 'vue'
3
+ import {VTabs} from 'vuetify/components'
4
4
 
5
5
  interface Props extends /* @vue-ignore */ InstanceType<typeof VTabs['$props']> {
6
6
  flat?: boolean
7
7
  }
8
8
 
9
+ defineOptions({
10
+ inheritAttrs: false,
11
+ })
12
+
9
13
  const props = defineProps<Props>()
10
- const currentTab = ref<number | null>(null)
14
+ const currentTab = ref<string | number>()
11
15
  </script>
12
16
 
13
17
  <template>
14
18
  <v-card :flat="props.flat">
15
19
  <v-tabs
16
- v-model="currentTab"
17
20
  v-bind="$attrs"
21
+ v-model="currentTab"
18
22
  show-arrows
19
23
  >
20
24
  <slot name="tabs" />
21
25
  </v-tabs>
22
- <v-card>
23
- <v-window v-model="currentTab">
26
+ <v-card-text>
27
+ <v-tabs-window v-model="currentTab">
24
28
  <slot name="items" />
25
- </v-window>
26
- </v-card>
29
+ </v-tabs-window>
30
+ </v-card-text>
27
31
  </v-card>
28
32
  </template>
@@ -0,0 +1,216 @@
1
+ <script lang="ts" setup>
2
+ import Datepicker from '@vuepic/vue-datepicker'
3
+ import '@vuepic/vue-datepicker/dist/main.css'
4
+ import {nextTick, ref, watch, watchEffect,computed} from 'vue'
5
+ import {type dateFormat, Datetime} from '../../utils/datetime'
6
+
7
+ interface IDobPrecision {
8
+ yearMonthDay: string
9
+ yearMonth: string
10
+ year: string
11
+ }
12
+ interface Props {
13
+ readonly?: boolean
14
+ locale?: 'TH' | 'EN'
15
+ format?: dateFormat | string
16
+ modelValue?: string | null
17
+ holiday?: object[] | undefined
18
+ minDate?: Date | string
19
+ maxDate?: Date | string
20
+ pickerOnly?: boolean
21
+ flow?: ('month' | 'year' | 'calendar' | 'time' | 'minutes' | 'hours' | 'seconds')[]
22
+ dobPrecisionText?: IDobPrecision
23
+ }
24
+
25
+ const props = withDefaults(defineProps<Props>(), {
26
+ readonly: false,
27
+ locale: 'TH',
28
+ format: 'shortDate',
29
+ pickerOnly: false,
30
+ flow: () => ['year', 'month', 'calendar'],
31
+ })
32
+
33
+ const emit = defineEmits(['update:modelValue'])
34
+
35
+ const dobPrecisionText = computed(() => {
36
+ if (props.dobPrecisionText) return props.dobPrecisionText
37
+ else return {
38
+ yearMonthDay: 'YMD',
39
+ yearMonth: 'YM',
40
+ year: 'Y',
41
+ }
42
+ })
43
+ const dobPrecisionChoice = ref<('yearMonthDay' | 'yearMonth' | 'year')[]>(['yearMonthDay', 'yearMonth', 'year'])
44
+ const dobPrecisionSelected = defineModel<'yearMonthDay' | 'yearMonth' | 'year'>('dobPrecision', { default: 'yearMonthDay' })
45
+
46
+ const dobPrecisionDisplay = computed(() => {
47
+ return (dobPrecisionSelected.value) ? dobPrecisionText.value[dobPrecisionSelected.value] : dobPrecisionText.value['yearMonthDay']
48
+ })
49
+
50
+ const changeDobPrecision = () => {
51
+ if (dobPrecisionSelected.value && dobPrecisionChoice.value.includes(dobPrecisionSelected.value)) {
52
+ dobPrecisionSelected.value = dobPrecisionChoice.value[(dobPrecisionChoice.value.indexOf(dobPrecisionSelected.value) + 1) % dobPrecisionChoice.value.length]
53
+ }
54
+ else {
55
+ dobPrecisionSelected.value = dobPrecisionChoice.value[0]
56
+ }
57
+ }
58
+
59
+ const selectedDate = ref<string | null>(null)
60
+ const displayedDate = ref<string | null>(null)
61
+
62
+ const isMenuOpen = ref(false)
63
+ const isTextFieldFocused = ref(false)
64
+ const hasTextFieldInput = ref(false)
65
+
66
+ function handleTextFieldFocus(event: Event) {
67
+ isTextFieldFocused.value = true
68
+ nextTick(() => {
69
+ (event.target as HTMLInputElement).select()
70
+ })
71
+ }
72
+
73
+ function handleTextFieldInput() {
74
+ if (!hasTextFieldInput.value) {
75
+ hasTextFieldInput.value = true
76
+ isMenuOpen.value = false
77
+ }
78
+ }
79
+
80
+ function handleTextFieldBlur() {
81
+ if (hasTextFieldInput.value) {
82
+ updateDate(displayedDate.value)
83
+ hasTextFieldInput.value = false
84
+ }
85
+ isTextFieldFocused.value = false
86
+ }
87
+
88
+ function handleTextFieldEnterKey() {
89
+ handleTextFieldBlur()
90
+ }
91
+
92
+ function updateDatePicker(dateString: string | null) {
93
+ isMenuOpen.value = false
94
+ updateDate(dateString)
95
+ }
96
+
97
+ function updateDate(dateString: string | null) {
98
+ const dateTime = Datetime().fromString(dateString, undefined, props.locale)
99
+ if (!dateTime.luxonDateTime.isValid) {
100
+ displayedDate.value = null
101
+ selectedDate.value = null
102
+ }
103
+ else {
104
+ if (dobPrecisionSelected.value=="yearMonth") {
105
+ dateTime.luxonDateTime = dateTime.luxonDateTime.startOf('month')
106
+ }
107
+ if (dobPrecisionSelected.value=="year") {
108
+ dateTime.luxonDateTime = dateTime.luxonDateTime.startOf('year')
109
+ }
110
+ selectedDate.value = dateTime.toFormat('yyyy-MM-dd', 'EN')
111
+ displayedDate.value = selectedDate.value
112
+ }
113
+
114
+ if (!isTextFieldFocused.value) displayedDate.value = formatDate(displayedDate.value)
115
+ }
116
+
117
+ function formatDate(dateString: string | null) {
118
+ if (!dateString) return null
119
+
120
+ let displayFormat = props.format
121
+ if (dobPrecisionSelected.value=="yearMonth") {
122
+ displayFormat = "MMM yyyy"
123
+ }
124
+ if (dobPrecisionSelected.value=="year") {
125
+ displayFormat = "yyyy"
126
+ }
127
+
128
+ const dateTime = Datetime().fromString(dateString, undefined, props.locale)
129
+ return dateTime.toFormat(displayFormat, props.locale)
130
+ }
131
+
132
+ function handleTextFieldClear() {
133
+ resetDatePicker()
134
+ }
135
+
136
+ function resetDatePicker() {
137
+ selectedDate.value = null
138
+ displayedDate.value = null
139
+ }
140
+
141
+ watchEffect(() => {
142
+ if (!isTextFieldFocused.value && selectedDate.value) {
143
+ displayedDate.value = formatDate(selectedDate.value)
144
+ }
145
+ else {
146
+ displayedDate.value = selectedDate.value
147
+ }
148
+ })
149
+
150
+ watch(selectedDate, (newValue) => {
151
+ emit('update:modelValue', newValue)
152
+ })
153
+
154
+ watch(() => props.modelValue, () => {
155
+ updateDate(props.modelValue || null)
156
+ }, { immediate: true })
157
+
158
+ watch(dobPrecisionSelected,()=>{
159
+ updateDate(selectedDate.value)
160
+ })
161
+
162
+ function toggleMenuOpen(trigger: string) {
163
+ if ((trigger === 'textField' && props.pickerOnly) || (trigger === 'icon' && !props.pickerOnly)) {
164
+ isMenuOpen.value = true
165
+ }
166
+ }
167
+ </script>
168
+
169
+ <template>
170
+ <v-menu
171
+ v-model="isMenuOpen"
172
+ :open-on-click="false"
173
+ >
174
+ <template #activator="{ props: activatorProps }">
175
+ <v-text-field
176
+ ref="textField"
177
+ v-model="displayedDate"
178
+ :readonly="readonly"
179
+ v-bind="$attrs"
180
+ @focus="handleTextFieldFocus"
181
+ @blur="handleTextFieldBlur"
182
+ @keydown="handleTextFieldInput"
183
+ @keyup.enter="handleTextFieldEnterKey"
184
+ @click:clear="handleTextFieldClear"
185
+ @click="toggleMenuOpen('textField')"
186
+ >
187
+ <template #append-inner>
188
+ <span
189
+ style="cursor: pointer;"
190
+ class="font-weight-medium"
191
+ @click="changeDobPrecision"
192
+ >{{ dobPrecisionDisplay }}</span>
193
+ &nbsp;
194
+ <v-icon
195
+ v-bind="activatorProps"
196
+ @click="toggleMenuOpen('icon')"
197
+ >
198
+ fa:fa-regular fa-calendar
199
+ </v-icon>
200
+ </template>
201
+ </v-text-field>
202
+ </template>
203
+ <Datepicker
204
+ v-model="selectedDate"
205
+ model-type="yyyy-MM-dd"
206
+ :enable-time-picker="false"
207
+ :flow="flow"
208
+ :min-date="props.minDate"
209
+ :max-date="props.maxDate"
210
+ auto-apply
211
+ inline
212
+ :locale="locale"
213
+ @update:model-value="updateDatePicker"
214
+ />
215
+ </v-menu>
216
+ </template>
@@ -30,8 +30,8 @@ const model = defineModel<string>()
30
30
 
31
31
  <template>
32
32
  <codemirror
33
- v-model="model"
34
- :extensions="extensions"
35
- :style="{ height: height, minHeight: minHeight }"
33
+ v-model="model"
34
+ :extensions="extensions"
35
+ :style="{ height: height, minHeight: minHeight }"
36
36
  />
37
37
  </template>
@@ -28,7 +28,7 @@ const emit = defineEmits(['create', 'update'])
28
28
  function save() {
29
29
  if (formPadRef.value.isValid) {
30
30
  isSaving.value = true
31
- emit((isCreating.value) ? 'create' : 'update', cloneDeep(formData.value) , callback)
31
+ emit((isCreating.value) ? 'create' : 'update', cloneDeep(formData.value), callback)
32
32
  }
33
33
  }
34
34
 
@@ -61,7 +61,8 @@ const createOriginalValue = computed(() => {
61
61
  const loadFormData = () => {
62
62
  if (props.formData) {
63
63
  formData.value = cloneDeep(props.formData)
64
- } else {
64
+ }
65
+ else {
65
66
  formData.value = Object.assign({}, props.initialData)
66
67
  }
67
68
  }
@@ -1,10 +1,21 @@
1
1
  <script lang="ts" setup>
2
2
  /* eslint-disable @typescript-eslint/no-explicit-any,import/no-self-import */
3
- import { compile, defineComponent, inject, onMounted, ref, shallowRef, watch, computed, withDefaults, defineOptions } from 'vue'
4
- import { isObject } from 'lodash-es'
5
- import { watchDebounced } from '@vueuse/core'
6
- import { useRules } from '../../composables/utils/validation'
7
- import { useDocumentTemplate } from '../../composables/document/template'
3
+ import {
4
+ compile,
5
+ computed,
6
+ defineComponent,
7
+ defineOptions,
8
+ inject,
9
+ onMounted,
10
+ ref,
11
+ shallowRef,
12
+ watch,
13
+ withDefaults
14
+ } from 'vue'
15
+ import {isObject} from 'lodash-es'
16
+ import {watchDebounced} from '@vueuse/core'
17
+ import {useRules} from '../../composables/utils/validation'
18
+ import {useDocumentTemplate} from '../../composables/document/template'
8
19
  import FormPad from './Pad.vue'
9
20
 
10
21
  defineOptions({
@@ -18,18 +29,21 @@ interface Props {
18
29
  disabled?: boolean
19
30
  readonly?: boolean
20
31
  isolated?: boolean
32
+ decoration?: object
21
33
  }
22
34
 
23
35
  const props = withDefaults(defineProps<Props>(), {
24
36
  disabled: false,
25
37
  readonly: false,
26
38
  isolated: false,
39
+ decoration: () => { return {} },
27
40
  })
28
41
 
29
42
  const emit = defineEmits(['update:modelValue'])
30
43
 
31
44
  const disabled = ref(props.disabled)
32
45
  const readonly = ref(props.readonly)
46
+ const decoration = ref(props.decoration)
33
47
 
34
48
  watch(() => props.disabled, (newValue) => {
35
49
  disabled.value = newValue
@@ -39,6 +53,10 @@ watch(() => props.readonly, (newValue) => {
39
53
  readonly.value = newValue
40
54
  })
41
55
 
56
+ watch(() => props.decoration, (newValue) => {
57
+ decoration.value = newValue
58
+ }, { deep: true })
59
+
42
60
  const { rules } = useRules()
43
61
 
44
62
  const trimmedTemplate = ref<string>('')
@@ -73,7 +91,7 @@ function buildFormComponent() {
73
91
  const originalConsoleError = console.warn
74
92
  console.warn = (error) => { throw new Error(error) } // eslint-disable-line
75
93
  try {
76
- const componentTemplate = '<form-pad ref="formPadTemplate" v-model="formComponentData" :disabled="disabled" :readonly="readonly" :isolated="isolated"><template v-slot="{ data,isDisabled,isReadonly,rules,formProvided }">' + trimmedTemplate.value + '</template></form-pad>'
94
+ const componentTemplate = '<form-pad ref="formPadTemplate" v-model="formComponentData" :disabled="disabled" :readonly="readonly" :decoration="decoration" :isolated="isolated"><template v-slot="{ data,isDisabled,isReadonly,rules,formProvided,decoration }">' + trimmedTemplate.value + '</template></form-pad>'
77
95
  compile(componentTemplate)
78
96
  formComponent.value = defineComponent({
79
97
  components: { FormPad },
@@ -81,6 +99,7 @@ function buildFormComponent() {
81
99
  modelValue: { type: Object, default: undefined },
82
100
  disabled: { type: Boolean, default: false },
83
101
  readonly: { type: Boolean, default: false },
102
+ decoration: { type: Object, default: () => { return {} } },
84
103
  isolated: { type: Boolean, default: false },
85
104
  },
86
105
  emits: ['update:modelValue'],
@@ -116,17 +135,17 @@ function buildFormComponent() {
116
135
 
117
136
  function reset() {
118
137
  if (!formInjected.value) formPad.value.reset()
119
- else formInjected.value.items.forEach((item: any)=>item.reset())
138
+ else formInjected.value.items.forEach((item: any) => item.reset())
120
139
  }
121
140
 
122
141
  function validate() {
123
142
  if (!formInjected.value) formPad.value.validate()
124
- else formInjected.value.items.forEach((item: any)=>item.validate())
143
+ else formInjected.value.items.forEach((item: any) => item.validate())
125
144
  }
126
145
 
127
146
  function resetValidate() {
128
147
  if (!formInjected.value) formPad.value.resetValidate()
129
- else formInjected.value.items.forEach((item: any)=>item.resetValidate())
148
+ else formInjected.value.items.forEach((item: any) => item.resetValidate())
130
149
  }
131
150
 
132
151
  const isValid = computed(() => {
@@ -170,6 +189,7 @@ defineExpose({
170
189
  :is-disabled="disabled"
171
190
  :is-readonly="readonly"
172
191
  :rules="rules"
192
+ :decoration="decoration"
173
193
  />
174
194
  </template>
175
195
  </v-form>
@@ -180,6 +200,7 @@ defineExpose({
180
200
  :is-disabled="disabled"
181
201
  :is-readonly="readonly"
182
202
  :rules="rules"
203
+ :decoration="decoration"
183
204
  />
184
205
  </template>
185
206
  <component
@@ -189,6 +210,7 @@ defineExpose({
189
210
  v-model="formData"
190
211
  :disabled="disabled"
191
212
  :readonly="readonly"
213
+ :decoration="decoration"
192
214
  :isolated="isolated"
193
215
  :class="$attrs.class"
194
216
  />
@@ -1,8 +1,8 @@
1
1
  <script lang="ts" setup>
2
2
  import {VDataTable} from 'vuetify/components/VDataTable'
3
3
  import {computed, defineOptions, nextTick, ref, useAttrs, watch} from 'vue'
4
- import type {FormDialogCallback} from '../../types/formDialog'
5
4
  import {omit} from 'lodash-es'
5
+ import type {FormDialogCallback} from '../../types/formDialog'
6
6
 
7
7
  defineOptions({
8
8
  inheritAttrs: false,
@@ -65,7 +65,7 @@ watch(items, (newValue) => {
65
65
  }, { deep: true })
66
66
 
67
67
  function createItem(item: Record<string, any>, callback?: FormDialogCallback) {
68
- if (items.value.length>0) item[props.modelKey] = Math.max(...items.value.map(i => i[props.modelKey] || 0)) + 1
68
+ if (items.value.length > 0) item[props.modelKey] = Math.max(...items.value.map(i => i[props.modelKey] || 0)) + 1
69
69
  else item[props.modelKey] = 1
70
70
 
71
71
  items.value.push(item)
@@ -124,14 +124,14 @@ function deleteItem(deleteItem: Record<string, any>, callback?: FormDialogCallba
124
124
  if (callback) callback.done()
125
125
  }
126
126
 
127
- const operation = ref({ createItem, updateItem, deleteItem, moveUpItem, moveDownItem })
128
-
129
127
  function openDialog(item?: object) {
130
128
  currentItem.value = item
131
129
  nextTick(() => {
132
130
  isDialogOpen.value = true
133
131
  })
134
132
  }
133
+
134
+ const operation = ref({ openDialog, createItem, updateItem, deleteItem, moveUpItem, moveDownItem })
135
135
  </script>
136
136
 
137
137
  <template>
@@ -205,7 +205,7 @@ function openDialog(item?: object) {
205
205
  >
206
206
  <slot
207
207
  :name="name"
208
- v-bind="(slotData as object)"
208
+ v-bind="((slotData || {}) as object)"
209
209
  :operation="operation"
210
210
  />
211
211
  </template>