@ramathibodi/nuxt-commons 0.1.73 → 0.1.75

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 (111) hide show
  1. package/README.md +115 -96
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +1 -0
  4. package/dist/runtime/components/Alert.vue +58 -54
  5. package/dist/runtime/components/BarcodeReader.vue +130 -122
  6. package/dist/runtime/components/ExportCSV.vue +110 -102
  7. package/dist/runtime/components/FileBtn.vue +79 -67
  8. package/dist/runtime/components/ImportCSV.vue +151 -139
  9. package/dist/runtime/components/MrzReader.vue +168 -0
  10. package/dist/runtime/components/SplitterPanel.vue +67 -59
  11. package/dist/runtime/components/TabsGroup.vue +39 -31
  12. package/dist/runtime/components/TextBarcode.vue +66 -54
  13. package/dist/runtime/components/device/IdCardButton.vue +95 -83
  14. package/dist/runtime/components/device/IdCardWebSocket.vue +207 -195
  15. package/dist/runtime/components/device/Scanner.vue +350 -338
  16. package/dist/runtime/components/dialog/Confirm.vue +112 -100
  17. package/dist/runtime/components/dialog/Host.vue +88 -84
  18. package/dist/runtime/components/dialog/Index.vue +84 -72
  19. package/dist/runtime/components/dialog/Loading.vue +51 -39
  20. package/dist/runtime/components/dialog/default/Confirm.vue +112 -100
  21. package/dist/runtime/components/dialog/default/Loading.vue +60 -48
  22. package/dist/runtime/components/dialog/default/Notify.vue +82 -70
  23. package/dist/runtime/components/dialog/default/Printing.vue +46 -34
  24. package/dist/runtime/components/dialog/default/VerifyUser.vue +144 -132
  25. package/dist/runtime/components/document/Form.vue +50 -42
  26. package/dist/runtime/components/document/TemplateBuilder.vue +536 -524
  27. package/dist/runtime/components/form/ActionPad.vue +156 -144
  28. package/dist/runtime/components/form/Birthdate.vue +116 -104
  29. package/dist/runtime/components/form/CheckboxGroup.vue +99 -87
  30. package/dist/runtime/components/form/CodeEditor.vue +45 -37
  31. package/dist/runtime/components/form/Date.vue +270 -258
  32. package/dist/runtime/components/form/DateTime.vue +220 -208
  33. package/dist/runtime/components/form/Dialog.vue +178 -166
  34. package/dist/runtime/components/form/EditPad.vue +157 -145
  35. package/dist/runtime/components/form/File.vue +295 -283
  36. package/dist/runtime/components/form/Hidden.vue +44 -32
  37. package/dist/runtime/components/form/Iterator.vue +538 -526
  38. package/dist/runtime/components/form/Login.vue +143 -131
  39. package/dist/runtime/components/form/Pad.vue +399 -387
  40. package/dist/runtime/components/form/SignPad.vue +226 -218
  41. package/dist/runtime/components/form/System.vue +34 -26
  42. package/dist/runtime/components/form/Table.vue +391 -379
  43. package/dist/runtime/components/form/TableData.vue +236 -224
  44. package/dist/runtime/components/form/Time.vue +177 -165
  45. package/dist/runtime/components/form/images/Capture.vue +245 -237
  46. package/dist/runtime/components/form/images/Edit.vue +133 -121
  47. package/dist/runtime/components/form/images/Field.vue +331 -320
  48. package/dist/runtime/components/form/images/Pad.vue +54 -42
  49. package/dist/runtime/components/label/Date.vue +37 -29
  50. package/dist/runtime/components/label/DateAgo.vue +102 -94
  51. package/dist/runtime/components/label/DateCount.vue +152 -144
  52. package/dist/runtime/components/label/Field.vue +111 -103
  53. package/dist/runtime/components/label/FormatMoney.vue +37 -29
  54. package/dist/runtime/components/label/Mask.vue +46 -38
  55. package/dist/runtime/components/label/Object.vue +21 -13
  56. package/dist/runtime/components/master/Autocomplete.vue +89 -81
  57. package/dist/runtime/components/master/Combobox.vue +88 -80
  58. package/dist/runtime/components/master/RadioGroup.vue +90 -78
  59. package/dist/runtime/components/master/Select.vue +70 -62
  60. package/dist/runtime/components/master/label.vue +55 -47
  61. package/dist/runtime/components/model/Autocomplete.vue +91 -79
  62. package/dist/runtime/components/model/Combobox.vue +90 -78
  63. package/dist/runtime/components/model/Pad.vue +114 -102
  64. package/dist/runtime/components/model/Select.vue +78 -72
  65. package/dist/runtime/components/model/Table.vue +370 -358
  66. package/dist/runtime/components/model/iterator.vue +497 -489
  67. package/dist/runtime/components/model/label.vue +58 -50
  68. package/dist/runtime/components/pdf/Print.vue +75 -63
  69. package/dist/runtime/components/pdf/View.vue +146 -134
  70. package/dist/runtime/composables/alert.d.ts +4 -0
  71. package/dist/runtime/composables/api.d.ts +4 -0
  72. package/dist/runtime/composables/dialog.d.ts +1 -1
  73. package/dist/runtime/composables/document/templateFormHidden.d.ts +4 -0
  74. package/dist/runtime/composables/graphql.d.ts +1 -1
  75. package/dist/runtime/composables/graphqlModel.d.ts +9 -9
  76. package/dist/runtime/composables/graphqlModelItem.d.ts +7 -7
  77. package/dist/runtime/composables/graphqlModelOperation.d.ts +6 -6
  78. package/dist/runtime/composables/localStorageModel.d.ts +4 -0
  79. package/dist/runtime/composables/lookupList.d.ts +4 -0
  80. package/dist/runtime/composables/menu.d.ts +4 -0
  81. package/dist/runtime/composables/useMrzReader.d.ts +48 -0
  82. package/dist/runtime/composables/useMrzReader.js +423 -0
  83. package/dist/runtime/composables/useTesseract.d.ts +16 -0
  84. package/dist/runtime/composables/useTesseract.js +45 -0
  85. package/dist/runtime/composables/userPermission.d.ts +1 -1
  86. package/dist/runtime/labs/Calendar.vue +99 -99
  87. package/dist/runtime/labs/form/EditMobile.vue +152 -152
  88. package/dist/runtime/labs/form/TextFieldMask.vue +43 -43
  89. package/dist/runtime/plugins/clientConfig.d.ts +1 -1
  90. package/dist/runtime/plugins/default.d.ts +1 -1
  91. package/dist/runtime/plugins/dialogManager.d.ts +1 -1
  92. package/dist/runtime/plugins/permission.d.ts +1 -1
  93. package/dist/runtime/types/alert.d.ts +11 -11
  94. package/dist/runtime/types/clientConfig.d.ts +13 -13
  95. package/dist/runtime/types/dialogManager.d.ts +35 -35
  96. package/dist/runtime/types/formDialog.d.ts +5 -5
  97. package/dist/runtime/types/graphqlOperation.d.ts +23 -23
  98. package/dist/runtime/types/menu.d.ts +31 -31
  99. package/dist/runtime/types/modules.d.ts +7 -7
  100. package/dist/runtime/types/permission.d.ts +13 -13
  101. package/dist/runtime/utils/asset.d.ts +2 -0
  102. package/dist/runtime/utils/asset.js +49 -0
  103. package/package.json +131 -122
  104. package/scripts/enrich-vue-docs-from-ai.mjs +197 -0
  105. package/scripts/generate-ai-summary.mjs +321 -0
  106. package/scripts/generate-composables-md.mjs +129 -0
  107. package/scripts/postInstall.cjs +70 -70
  108. package/templates/.codegen/codegen.ts +32 -32
  109. package/templates/.codegen/plugin-schema-object.js +161 -161
  110. package/templates/public/tesseract/mrz.traineddata.gz +0 -0
  111. package/templates/public/tesseract/ocrb.traineddata.gz +0 -0
@@ -1,238 +1,246 @@
1
- <script lang="ts" setup>
2
- import {computed, defineExpose, onBeforeUnmount, onMounted, ref, useTemplateRef, watchEffect} from "vue"
3
- import {useDevicesList, useUserMedia} from "@vueuse/core"
4
- import {getBase64Strings, type ImageFormat} from 'exif-rotate-js'
5
- import {useAlert} from '../../../composables/alert'
6
- import {VInput} from 'vuetify/components/VInput'
7
-
8
- interface Props {
9
- imageFormat?: ImageFormat
10
- autoStart?: boolean
11
- fileOnly?: boolean
12
- readonly?: boolean
13
- disabled?: boolean
14
- buttonText?: string
15
- maxHeight?: string | number
16
- maxWidth?: string | number
17
- aspectRatio?: number
18
- }
19
-
20
- const props = withDefaults(defineProps<Props>(), {
21
- imageFormat: "image/jpeg",
22
- autoStart: true,
23
- fileOnly: false,
24
- readonly: false,
25
- disabled: false,
26
- buttonText: "Add Photo",
27
- maxWidth: 1024
28
- })
29
-
30
- const alert = useAlert()
31
-
32
- const imageData = defineModel<string>()
33
- const inputRef = useTemplateRef<VInput>("inputRef")
34
-
35
- const isLoading = ref<boolean>(false)
36
- const isCaptured = computed(()=>!!imageData.value)
37
- const isEditing = ref<boolean>(false)
38
-
39
- const videoScreen = ref<HTMLVideoElement>()
40
-
41
- const currentCameraId = ref<ConstrainDOMString | undefined>()
42
- const { videoInputs: cameras } = useDevicesList({
43
- requestPermissions: true,
44
- constraints: { audio: false, video: true },
45
- onUpdated() {
46
- if (!cameras.value.find(camera => camera.deviceId === currentCameraId.value))
47
- currentCameraId.value = cameras.value[0]?.deviceId
48
- },
49
- })
50
- const hasCamera = computed(()=>{ return !!currentCameraId.value })
51
-
52
- const { stream, start: cameraStart, stop: cameraStop, enabled: cameraEnabled } = useUserMedia({
53
- constraints: { video: { deviceId: currentCameraId.value, width: {min: 1280}, aspectRatio: props.aspectRatio}},
54
- })
55
-
56
- watchEffect(() => {
57
- if (videoScreen.value) videoScreen.value.srcObject = (stream.value) ? stream.value! : null
58
- })
59
-
60
- function startCamera() {
61
- imageData.value = undefined
62
- if (!cameraEnabled.value) {
63
- isLoading.value = true
64
- cameraStart().finally(()=>{
65
- isLoading.value = false
66
- })
67
- }
68
- }
69
-
70
- function stopCamera() {
71
- if (cameraEnabled.value) cameraStop()
72
- }
73
-
74
- function captureImage() {
75
- if (videoScreen.value) {
76
- const canvas = document.createElement('canvas')
77
- canvas.width = videoScreen.value.videoWidth
78
- canvas.height = videoScreen.value.videoHeight
79
- const context = canvas.getContext('2d')
80
- if (context) {
81
- context.drawImage(videoScreen.value, 0, 0, canvas.width, canvas.height)
82
- isEditing.value = false
83
- imageData.value = canvas.toDataURL(props.imageFormat)
84
- stopCamera()
85
- }
86
- }
87
- }
88
-
89
- function captureImageFile(selectedFile: File | File[] | undefined) {
90
- if (!selectedFile) {
91
- alert?.addAlert({ message: 'No file selected.', alertType: 'error' })
92
- return
93
- }
94
-
95
- const scanImageSingleFile: File = Array.isArray(selectedFile) ? selectedFile[0] : selectedFile
96
-
97
- getBase64Strings([scanImageSingleFile], { maxSize: computedMaxSize.value,type: props.imageFormat,quality: 1 }).then((returnData) => {
98
- isEditing.value = false
99
- imageData.value = returnData[0]
100
- stopCamera()
101
- }).catch((e) => void e)
102
- }
103
-
104
- onMounted(() => {
105
- if (!isCaptured.value && props.autoStart && !props.fileOnly) startCamera()
106
- })
107
-
108
- onBeforeUnmount(() => {
109
- stopCamera()
110
- })
111
-
112
- function reset() {
113
- inputRef.value?.reset()
114
- stopCamera()
115
- if (props.autoStart && !props.fileOnly) startCamera()
116
- }
117
-
118
- const computedMaxWidth = computed(() => {
119
- if (typeof props.maxWidth === 'number') {
120
- return `${props.maxWidth}px`
121
- } else if (!isNaN(Number(props.maxWidth))) {
122
- return `${props.maxWidth}px`
123
- }
124
- return props.maxWidth
125
- })
126
-
127
- const computedMaxHeight = computed(() => {
128
- if (typeof props.maxHeight === 'number') {
129
- return `${props.maxHeight}px`
130
- } else if (!isNaN(Number(props.maxHeight))) {
131
- return `${props.maxHeight}px`
132
- }
133
- return props.maxHeight
134
- })
135
-
136
- const computedMaxSize = computed(() => {
137
- let tmpMaxHeight : number = 1024
138
- let tmpMaxWidth : number = 1024
139
-
140
- if (typeof props.maxWidth === 'number') {
141
- tmpMaxWidth = <number>props.maxWidth
142
- } else if (!isNaN(Number(props.maxWidth))) {
143
- tmpMaxWidth = Number(props.maxWidth)
144
- }
145
-
146
- if (typeof props.maxHeight === 'number') {
147
- tmpMaxHeight = <number>props.maxHeight
148
- } else if (!isNaN(Number(props.maxHeight))) {
149
- tmpMaxHeight = Number(props.maxHeight)
150
- }
151
-
152
- return (tmpMaxWidth>tmpMaxHeight) ? tmpMaxWidth : tmpMaxHeight
153
- })
154
-
155
- const operation = ref({startCamera,stopCamera,reset,captureImage,captureImageFile,isLoading,isCaptured,hasCamera})
156
-
157
- const isValid = computed(()=>{
158
- return inputRef.value?.isValid
159
- })
160
-
161
- const errorMessages = computed(()=>{
162
- return inputRef.value?.errorMessages
163
- })
164
-
165
- defineExpose({
166
- errorMessages,
167
- isValid,
168
- reset,
169
- resetValidation : ()=>inputRef.value?.resetValidation(),
170
- validate : ()=>inputRef.value?.validate(),
171
- operation
172
- })
173
- </script>
174
-
175
- <template>
176
- <v-input v-model="imageData" v-bind="$attrs" ref="inputRef">
177
- <template #default="{isReadonly,isDisabled}">
178
- <v-container fluid class="ma-0 pa-0">
179
- <v-card>
180
- <v-card-text class="d-flex justify-center text-center" v-if="!isLoading && !isCaptured">
181
- <template v-if="!hasCamera || fileOnly">
182
- <FileBtn
183
- color="primary"
184
- variant="flat"
185
- accept="image/*"
186
- @update:model-value="captureImageFile"
187
- :disabled="disabled || readonly || (isReadonly.value || isDisabled.value)"
188
- >
189
- <v-icon>mdi mdi-image-plus</v-icon>
190
- {{ buttonText }}
191
- </FileBtn>
192
- </template>
193
- <template v-else>
194
- <div style="position: relative; display: inline-block; width: 100%;" :style="{maxWidth:computedMaxWidth,maxHeight:computedMaxHeight}">
195
- <video autoplay ref="videoScreen" width="100%" :style="{maxWidth:computedMaxWidth,maxHeight:computedMaxHeight}"></video>
196
- <div style="position: absolute; bottom: 10px; right: 10px; z-index: 2000;">
197
- <FileBtn
198
- accept="image/*"
199
- icon="mdi mdi-image-plus"
200
- icon-only
201
- @update:model-value="captureImageFile"
202
- :disabled="disabled || readonly || (isReadonly.value || isDisabled.value)"
203
- />
204
- </div>
205
- </div>
206
- </template>
207
- </v-card-text>
208
- <v-card-text class="d-flex justify-center" v-if="isCaptured">
209
- <div style="position: relative; display: inline-block; width: 100%;" :style="{maxWidth:computedMaxWidth,maxHeight:computedMaxHeight}" v-if="!isEditing">
210
- <v-img :src="imageData" :max-height="maxHeight" :max-width="maxWidth" contain></v-img>
211
- <div style="position: absolute; bottom: 10px; right: 10px; z-index: 2000;">
212
- <v-btn
213
- icon="mdi mdi-image-edit"
214
- icon-only
215
- @click="isEditing=true"
216
- :disabled="disabled || readonly || (isReadonly.value || isDisabled.value)"
217
- />
218
- </div>
219
- </div>
220
- <form-images-edit v-model="imageData" :aspect-ratio="aspectRatio" :image-format="imageFormat" @update:model-value="isEditing=false" @close="isEditing=false" v-else></form-images-edit>
221
- </v-card-text>
222
- <v-card-text class="d-flex justify-center" v-if="isLoading">
223
- <v-progress-circular indeterminate></v-progress-circular>
224
- </v-card-text>
225
- <v-card-actions v-if="!readonly && (!fileOnly || isCaptured) && !isEditing && !(isReadonly.value || isDisabled.value)">
226
- <slot name="actions" :operation="operation">
227
- <v-spacer></v-spacer>
228
- <v-btn color="primary" variant="flat" @click="startCamera" v-if="!cameraEnabled && hasCamera && !fileOnly" :disabled="disabled">{{ (isCaptured) ? "Retake" : "Start" }}</v-btn>
229
- <v-btn color="primary" variant="flat" @click="captureImage" v-if="cameraEnabled" :disabled="disabled">Capture</v-btn>
230
- <v-btn color="primary" variant="flat" @click="reset" :disabled="disabled">Reset</v-btn>
231
- <v-spacer></v-spacer>
232
- </slot>
233
- </v-card-actions>
234
- </v-card>
235
- </v-container>
236
- </template>
237
- </v-input>
1
+ <script lang="ts" setup>
2
+ /**
3
+ * FormImagesCapture handles image capture, editing, preview, and value synchronization for schema-driven form image fields.
4
+ * This doc block is consumed by vue-docgen for generated API documentation.
5
+ */
6
+ import {computed, defineExpose, onBeforeUnmount, onMounted, ref, useTemplateRef, watchEffect} from "vue"
7
+ import {useDevicesList, useUserMedia} from "@vueuse/core"
8
+ import {getBase64Strings, type ImageFormat} from 'exif-rotate-js'
9
+ import {useAlert} from '../../../composables/alert'
10
+ import {VInput} from 'vuetify/components/VInput'
11
+
12
+ interface Props {
13
+ imageFormat?: ImageFormat // Output image MIME/encoding format for capture or editing.
14
+ autoStart?: boolean // Starts camera stream automatically when dialog opens.
15
+ fileOnly?: boolean // Emits file object only, without base64 payload conversion.
16
+ readonly?: boolean // renders as read-only while keeping value visible
17
+ disabled?: boolean // disables user interaction for this field
18
+ buttonText?: string // Label text displayed on the primary action button.
19
+ maxHeight?: string | number // Maximum height constraint for previews, dialogs, or editors.
20
+ maxWidth?: string | number // Maximum width constraint for previews, dialogs, or editors.
21
+ aspectRatio?: number // Target aspect ratio used while cropping or capturing images.
22
+ }
23
+
24
+ /**
25
+ * Public props accepted by FormImagesCapture.
26
+ * Document each prop field with intent, defaults, and side effects for clear generated docs.
27
+ */
28
+ const props = withDefaults(defineProps<Props>(), {
29
+ imageFormat: "image/jpeg",
30
+ autoStart: true,
31
+ fileOnly: false,
32
+ readonly: false,
33
+ disabled: false,
34
+ buttonText: "Add Photo",
35
+ maxWidth: 1024
36
+ })
37
+
38
+ const alert = useAlert()
39
+
40
+ const imageData = defineModel<string>()
41
+ const inputRef = useTemplateRef<VInput>("inputRef")
42
+
43
+ const isLoading = ref<boolean>(false)
44
+ const isCaptured = computed(()=>!!imageData.value)
45
+ const isEditing = ref<boolean>(false)
46
+
47
+ const videoScreen = ref<HTMLVideoElement>()
48
+
49
+ const currentCameraId = ref<ConstrainDOMString | undefined>()
50
+ const { videoInputs: cameras } = useDevicesList({
51
+ requestPermissions: true,
52
+ constraints: { audio: false, video: true },
53
+ onUpdated() {
54
+ if (!cameras.value.find(camera => camera.deviceId === currentCameraId.value))
55
+ currentCameraId.value = cameras.value[0]?.deviceId
56
+ },
57
+ })
58
+ const hasCamera = computed(()=>{ return !!currentCameraId.value })
59
+
60
+ const { stream, start: cameraStart, stop: cameraStop, enabled: cameraEnabled } = useUserMedia({
61
+ constraints: { video: { deviceId: currentCameraId.value, width: {min: 1280}, aspectRatio: props.aspectRatio}},
62
+ })
63
+
64
+ watchEffect(() => {
65
+ if (videoScreen.value) videoScreen.value.srcObject = (stream.value) ? stream.value! : null
66
+ })
67
+
68
+ function startCamera() {
69
+ imageData.value = undefined
70
+ if (!cameraEnabled.value) {
71
+ isLoading.value = true
72
+ cameraStart().finally(()=>{
73
+ isLoading.value = false
74
+ })
75
+ }
76
+ }
77
+
78
+ function stopCamera() {
79
+ if (cameraEnabled.value) cameraStop()
80
+ }
81
+
82
+ function captureImage() {
83
+ if (videoScreen.value) {
84
+ const canvas = document.createElement('canvas')
85
+ canvas.width = videoScreen.value.videoWidth
86
+ canvas.height = videoScreen.value.videoHeight
87
+ const context = canvas.getContext('2d')
88
+ if (context) {
89
+ context.drawImage(videoScreen.value, 0, 0, canvas.width, canvas.height)
90
+ isEditing.value = false
91
+ imageData.value = canvas.toDataURL(props.imageFormat)
92
+ stopCamera()
93
+ }
94
+ }
95
+ }
96
+
97
+ function captureImageFile(selectedFile: File | File[] | undefined) {
98
+ if (!selectedFile) {
99
+ alert?.addAlert({ message: 'No file selected.', alertType: 'error' })
100
+ return
101
+ }
102
+
103
+ const scanImageSingleFile: File = Array.isArray(selectedFile) ? selectedFile[0] : selectedFile
104
+
105
+ getBase64Strings([scanImageSingleFile], { maxSize: computedMaxSize.value,type: props.imageFormat,quality: 1 }).then((returnData) => {
106
+ isEditing.value = false
107
+ imageData.value = returnData[0]
108
+ stopCamera()
109
+ }).catch((e) => void e)
110
+ }
111
+
112
+ onMounted(() => {
113
+ if (!isCaptured.value && props.autoStart && !props.fileOnly) startCamera()
114
+ })
115
+
116
+ onBeforeUnmount(() => {
117
+ stopCamera()
118
+ })
119
+
120
+ function reset() {
121
+ inputRef.value?.reset()
122
+ stopCamera()
123
+ if (props.autoStart && !props.fileOnly) startCamera()
124
+ }
125
+
126
+ const computedMaxWidth = computed(() => {
127
+ if (typeof props.maxWidth === 'number') {
128
+ return `${props.maxWidth}px`
129
+ } else if (!isNaN(Number(props.maxWidth))) {
130
+ return `${props.maxWidth}px`
131
+ }
132
+ return props.maxWidth
133
+ })
134
+
135
+ const computedMaxHeight = computed(() => {
136
+ if (typeof props.maxHeight === 'number') {
137
+ return `${props.maxHeight}px`
138
+ } else if (!isNaN(Number(props.maxHeight))) {
139
+ return `${props.maxHeight}px`
140
+ }
141
+ return props.maxHeight
142
+ })
143
+
144
+ const computedMaxSize = computed(() => {
145
+ let tmpMaxHeight : number = 1024
146
+ let tmpMaxWidth : number = 1024
147
+
148
+ if (typeof props.maxWidth === 'number') {
149
+ tmpMaxWidth = <number>props.maxWidth
150
+ } else if (!isNaN(Number(props.maxWidth))) {
151
+ tmpMaxWidth = Number(props.maxWidth)
152
+ }
153
+
154
+ if (typeof props.maxHeight === 'number') {
155
+ tmpMaxHeight = <number>props.maxHeight
156
+ } else if (!isNaN(Number(props.maxHeight))) {
157
+ tmpMaxHeight = Number(props.maxHeight)
158
+ }
159
+
160
+ return (tmpMaxWidth>tmpMaxHeight) ? tmpMaxWidth : tmpMaxHeight
161
+ })
162
+
163
+ const operation = ref({startCamera,stopCamera,reset,captureImage,captureImageFile,isLoading,isCaptured,hasCamera})
164
+
165
+ const isValid = computed(()=>{
166
+ return inputRef.value?.isValid
167
+ })
168
+
169
+ const errorMessages = computed(()=>{
170
+ return inputRef.value?.errorMessages
171
+ })
172
+
173
+ defineExpose({
174
+ errorMessages,
175
+ isValid,
176
+ reset,
177
+ resetValidation : ()=>inputRef.value?.resetValidation(),
178
+ validate : ()=>inputRef.value?.validate(),
179
+ operation
180
+ })
181
+ </script>
182
+
183
+ <template>
184
+ <v-input v-model="imageData" v-bind="$attrs" ref="inputRef">
185
+ <template #default="{isReadonly,isDisabled}">
186
+ <v-container fluid class="ma-0 pa-0">
187
+ <v-card>
188
+ <v-card-text class="d-flex justify-center text-center" v-if="!isLoading && !isCaptured">
189
+ <template v-if="!hasCamera || fileOnly">
190
+ <FileBtn
191
+ color="primary"
192
+ variant="flat"
193
+ accept="image/*"
194
+ @update:model-value="captureImageFile"
195
+ :disabled="disabled || readonly || (isReadonly.value || isDisabled.value)"
196
+ >
197
+ <v-icon>mdi mdi-image-plus</v-icon>
198
+ {{ buttonText }}
199
+ </FileBtn>
200
+ </template>
201
+ <template v-else>
202
+ <div style="position: relative; display: inline-block; width: 100%;" :style="{maxWidth:computedMaxWidth,maxHeight:computedMaxHeight}">
203
+ <video autoplay ref="videoScreen" width="100%" :style="{maxWidth:computedMaxWidth,maxHeight:computedMaxHeight}"></video>
204
+ <div style="position: absolute; bottom: 10px; right: 10px; z-index: 2000;">
205
+ <FileBtn
206
+ accept="image/*"
207
+ icon="mdi mdi-image-plus"
208
+ icon-only
209
+ @update:model-value="captureImageFile"
210
+ :disabled="disabled || readonly || (isReadonly.value || isDisabled.value)"
211
+ />
212
+ </div>
213
+ </div>
214
+ </template>
215
+ </v-card-text>
216
+ <v-card-text class="d-flex justify-center" v-if="isCaptured">
217
+ <div style="position: relative; display: inline-block; width: 100%;" :style="{maxWidth:computedMaxWidth,maxHeight:computedMaxHeight}" v-if="!isEditing">
218
+ <v-img :src="imageData" :max-height="maxHeight" :max-width="maxWidth" contain></v-img>
219
+ <div style="position: absolute; bottom: 10px; right: 10px; z-index: 2000;">
220
+ <v-btn
221
+ icon="mdi mdi-image-edit"
222
+ icon-only
223
+ @click="isEditing=true"
224
+ :disabled="disabled || readonly || (isReadonly.value || isDisabled.value)"
225
+ />
226
+ </div>
227
+ </div>
228
+ <form-images-edit v-model="imageData" :aspect-ratio="aspectRatio" :image-format="imageFormat" @update:model-value="isEditing=false" @close="isEditing=false" v-else></form-images-edit>
229
+ </v-card-text>
230
+ <v-card-text class="d-flex justify-center" v-if="isLoading">
231
+ <v-progress-circular indeterminate></v-progress-circular>
232
+ </v-card-text>
233
+ <v-card-actions v-if="!readonly && (!fileOnly || isCaptured) && !isEditing && !(isReadonly.value || isDisabled.value)">
234
+ <slot name="actions" :operation="operation">
235
+ <v-spacer></v-spacer>
236
+ <v-btn color="primary" variant="flat" @click="startCamera" v-if="!cameraEnabled && hasCamera && !fileOnly" :disabled="disabled">{{ (isCaptured) ? "Retake" : "Start" }}</v-btn>
237
+ <v-btn color="primary" variant="flat" @click="captureImage" v-if="cameraEnabled" :disabled="disabled">Capture</v-btn>
238
+ <v-btn color="primary" variant="flat" @click="reset" :disabled="disabled">Reset</v-btn>
239
+ <v-spacer></v-spacer>
240
+ </slot>
241
+ </v-card-actions>
242
+ </v-card>
243
+ </v-container>
244
+ </template>
245
+ </v-input>
238
246
  </template>