@ramathibodi/nuxt-commons 0.1.74 → 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 (96) hide show
  1. package/README.md +115 -115
  2. package/dist/module.json +1 -1
  3. package/dist/runtime/components/Alert.vue +58 -58
  4. package/dist/runtime/components/BarcodeReader.vue +130 -130
  5. package/dist/runtime/components/ExportCSV.vue +110 -110
  6. package/dist/runtime/components/FileBtn.vue +79 -79
  7. package/dist/runtime/components/ImportCSV.vue +151 -151
  8. package/dist/runtime/components/MrzReader.vue +168 -168
  9. package/dist/runtime/components/SplitterPanel.vue +67 -67
  10. package/dist/runtime/components/TabsGroup.vue +39 -39
  11. package/dist/runtime/components/TextBarcode.vue +66 -66
  12. package/dist/runtime/components/device/IdCardButton.vue +95 -95
  13. package/dist/runtime/components/device/IdCardWebSocket.vue +207 -207
  14. package/dist/runtime/components/device/Scanner.vue +350 -350
  15. package/dist/runtime/components/dialog/Confirm.vue +112 -112
  16. package/dist/runtime/components/dialog/Host.vue +88 -88
  17. package/dist/runtime/components/dialog/Index.vue +84 -84
  18. package/dist/runtime/components/dialog/Loading.vue +51 -51
  19. package/dist/runtime/components/dialog/default/Confirm.vue +112 -112
  20. package/dist/runtime/components/dialog/default/Loading.vue +60 -60
  21. package/dist/runtime/components/dialog/default/Notify.vue +82 -82
  22. package/dist/runtime/components/dialog/default/Printing.vue +46 -46
  23. package/dist/runtime/components/dialog/default/VerifyUser.vue +144 -144
  24. package/dist/runtime/components/document/Form.vue +50 -50
  25. package/dist/runtime/components/document/TemplateBuilder.vue +536 -536
  26. package/dist/runtime/components/form/ActionPad.vue +156 -156
  27. package/dist/runtime/components/form/Birthdate.vue +116 -116
  28. package/dist/runtime/components/form/CheckboxGroup.vue +99 -99
  29. package/dist/runtime/components/form/CodeEditor.vue +45 -45
  30. package/dist/runtime/components/form/Date.vue +270 -270
  31. package/dist/runtime/components/form/DateTime.vue +220 -220
  32. package/dist/runtime/components/form/Dialog.vue +178 -178
  33. package/dist/runtime/components/form/EditPad.vue +157 -157
  34. package/dist/runtime/components/form/File.vue +295 -295
  35. package/dist/runtime/components/form/Hidden.vue +44 -44
  36. package/dist/runtime/components/form/Iterator.vue +538 -538
  37. package/dist/runtime/components/form/Login.vue +143 -143
  38. package/dist/runtime/components/form/Pad.vue +399 -399
  39. package/dist/runtime/components/form/SignPad.vue +226 -226
  40. package/dist/runtime/components/form/System.vue +34 -34
  41. package/dist/runtime/components/form/Table.vue +391 -391
  42. package/dist/runtime/components/form/TableData.vue +236 -236
  43. package/dist/runtime/components/form/Time.vue +177 -177
  44. package/dist/runtime/components/form/images/Capture.vue +245 -245
  45. package/dist/runtime/components/form/images/Edit.vue +133 -133
  46. package/dist/runtime/components/form/images/Field.vue +331 -331
  47. package/dist/runtime/components/form/images/Pad.vue +54 -54
  48. package/dist/runtime/components/label/Date.vue +37 -37
  49. package/dist/runtime/components/label/DateAgo.vue +102 -102
  50. package/dist/runtime/components/label/DateCount.vue +152 -152
  51. package/dist/runtime/components/label/Field.vue +111 -111
  52. package/dist/runtime/components/label/FormatMoney.vue +37 -37
  53. package/dist/runtime/components/label/Mask.vue +46 -46
  54. package/dist/runtime/components/label/Object.vue +21 -21
  55. package/dist/runtime/components/master/Autocomplete.vue +89 -89
  56. package/dist/runtime/components/master/Combobox.vue +88 -88
  57. package/dist/runtime/components/master/RadioGroup.vue +90 -90
  58. package/dist/runtime/components/master/Select.vue +70 -70
  59. package/dist/runtime/components/master/label.vue +55 -55
  60. package/dist/runtime/components/model/Autocomplete.vue +91 -91
  61. package/dist/runtime/components/model/Combobox.vue +90 -90
  62. package/dist/runtime/components/model/Pad.vue +114 -114
  63. package/dist/runtime/components/model/Select.vue +78 -84
  64. package/dist/runtime/components/model/Table.vue +370 -370
  65. package/dist/runtime/components/model/iterator.vue +497 -497
  66. package/dist/runtime/components/model/label.vue +58 -58
  67. package/dist/runtime/components/pdf/Print.vue +75 -75
  68. package/dist/runtime/components/pdf/View.vue +146 -146
  69. package/dist/runtime/composables/dialog.d.ts +1 -1
  70. package/dist/runtime/composables/graphql.d.ts +1 -1
  71. package/dist/runtime/composables/graphqlModel.d.ts +9 -9
  72. package/dist/runtime/composables/graphqlModelItem.d.ts +7 -7
  73. package/dist/runtime/composables/graphqlModelOperation.d.ts +6 -6
  74. package/dist/runtime/composables/userPermission.d.ts +1 -1
  75. package/dist/runtime/labs/Calendar.vue +99 -99
  76. package/dist/runtime/labs/form/EditMobile.vue +152 -152
  77. package/dist/runtime/labs/form/TextFieldMask.vue +43 -43
  78. package/dist/runtime/plugins/clientConfig.d.ts +1 -1
  79. package/dist/runtime/plugins/default.d.ts +1 -1
  80. package/dist/runtime/plugins/dialogManager.d.ts +1 -1
  81. package/dist/runtime/plugins/permission.d.ts +1 -1
  82. package/dist/runtime/types/alert.d.ts +11 -11
  83. package/dist/runtime/types/clientConfig.d.ts +13 -13
  84. package/dist/runtime/types/dialogManager.d.ts +35 -35
  85. package/dist/runtime/types/formDialog.d.ts +5 -5
  86. package/dist/runtime/types/graphqlOperation.d.ts +23 -23
  87. package/dist/runtime/types/menu.d.ts +31 -31
  88. package/dist/runtime/types/modules.d.ts +7 -7
  89. package/dist/runtime/types/permission.d.ts +13 -13
  90. package/package.json +131 -131
  91. package/scripts/enrich-vue-docs-from-ai.mjs +197 -197
  92. package/scripts/generate-ai-summary.mjs +321 -321
  93. package/scripts/generate-composables-md.mjs +129 -129
  94. package/scripts/postInstall.cjs +70 -70
  95. package/templates/.codegen/codegen.ts +32 -32
  96. package/templates/.codegen/plugin-schema-object.js +161 -161
@@ -1,246 +1,246 @@
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>
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>
246
246
  </template>