| 
									
										
										
										
											2023-08-21 10:00:37 -05:00
										 |  |  | <template> | 
					
						
							| 
									
										
										
										
											2025-06-29 20:17:49 +02:00
										 |  |  |   <v-card class="ma-0 pt-2" :elevation="4"> | 
					
						
							|  |  |  |     <v-card-text> | 
					
						
							|  |  |  |       <!-- Controls Row (Menu) --> | 
					
						
							|  |  |  |       <v-row class="mb-2 mx-1"> | 
					
						
							|  |  |  |         <v-btn | 
					
						
							|  |  |  |           color="error" | 
					
						
							|  |  |  |           :icon="$globals.icons.delete" | 
					
						
							|  |  |  |           :disabled="submitted" | 
					
						
							|  |  |  |           @click="$emit('delete')" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         /> | 
					
						
							| 
									
										
										
										
											2025-06-29 20:17:49 +02:00
										 |  |  |         <v-spacer /> | 
					
						
							|  |  |  |         <v-btn | 
					
						
							|  |  |  |           v-if="changed" | 
					
						
							|  |  |  |           class="mr-2" | 
					
						
							|  |  |  |           color="success" | 
					
						
							|  |  |  |           :icon="$globals.icons.save" | 
					
						
							|  |  |  |           :disabled="submitted" | 
					
						
							|  |  |  |           @click="() => save()" | 
					
						
							|  |  |  |         /> | 
					
						
							|  |  |  |         <v-menu offset-y :close-on-content-click="false" location="bottom center"> | 
					
						
							|  |  |  |           <template #activator="{ props }"> | 
					
						
							|  |  |  |             <v-btn color="info" v-bind="props" :icon="$globals.icons.edit" :disabled="submitted" /> | 
					
						
							|  |  |  |           </template> | 
					
						
							|  |  |  |           <v-list class="mt-1"> | 
					
						
							|  |  |  |             <template v-for="(row, keyRow) in controls" :key="keyRow"> | 
					
						
							|  |  |  |               <v-list-item-group> | 
					
						
							|  |  |  |                 <v-list-item | 
					
						
							|  |  |  |                   v-for="(control, keyControl) in row" | 
					
						
							|  |  |  |                   :key="keyControl" | 
					
						
							|  |  |  |                   :disabled="submitted" | 
					
						
							|  |  |  |                   @click="control.callback()" | 
					
						
							|  |  |  |                 > | 
					
						
							|  |  |  |                   <v-list-item-icon> | 
					
						
							|  |  |  |                     <v-icon :color="control.color" :icon="control.icon" /> | 
					
						
							|  |  |  |                   </v-list-item-icon> | 
					
						
							|  |  |  |                 </v-list-item> | 
					
						
							|  |  |  |               </v-list-item-group> | 
					
						
							|  |  |  |             </template> | 
					
						
							|  |  |  |           </v-list> | 
					
						
							|  |  |  |         </v-menu> | 
					
						
							|  |  |  |       </v-row> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       <!-- Image Row --> | 
					
						
							|  |  |  |       <Cropper | 
					
						
							|  |  |  |         ref="cropper" | 
					
						
							|  |  |  |         class="cropper" | 
					
						
							|  |  |  |         :src="img" | 
					
						
							|  |  |  |         :default-size="defaultSize" | 
					
						
							|  |  |  |         :style="`height: ${cropperHeight}; width: ${cropperWidth};`" | 
					
						
							|  |  |  |         @change="changed = changed + 1" | 
					
						
							|  |  |  |         @ready="changed = -1" | 
					
						
							|  |  |  |       /> | 
					
						
							|  |  |  |     </v-card-text> | 
					
						
							|  |  |  |   </v-card> | 
					
						
							| 
									
										
										
										
											2023-08-21 10:00:37 -05:00
										 |  |  | </template> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <script lang="ts"> | 
					
						
							|  |  |  | import { Cropper } from "vue-advanced-cropper"; | 
					
						
							|  |  |  | import "vue-advanced-cropper/dist/style.css"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  | export default defineNuxtComponent({ | 
					
						
							|  |  |  |   components: { Cropper }, | 
					
						
							|  |  |  |   props: { | 
					
						
							|  |  |  |     img: { | 
					
						
							|  |  |  |       type: String, | 
					
						
							|  |  |  |       required: true, | 
					
						
							| 
									
										
										
										
											2023-08-21 10:00:37 -05:00
										 |  |  |     }, | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     cropperHeight: { | 
					
						
							|  |  |  |       type: String, | 
					
						
							|  |  |  |       default: undefined, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     cropperWidth: { | 
					
						
							|  |  |  |       type: String, | 
					
						
							|  |  |  |       default: undefined, | 
					
						
							|  |  |  |     }, | 
					
						
							| 
									
										
										
										
											2025-06-29 20:17:49 +02:00
										 |  |  |     submitted: { | 
					
						
							|  |  |  |       type: Boolean, | 
					
						
							|  |  |  |       default: false, | 
					
						
							|  |  |  |     }, | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |   }, | 
					
						
							| 
									
										
										
										
											2025-06-29 20:17:49 +02:00
										 |  |  |   emits: ["save", "delete"], | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |   setup(_, context) { | 
					
						
							| 
									
										
										
										
											2025-06-29 20:17:49 +02:00
										 |  |  |     const cropper = ref<any>(); | 
					
						
							|  |  |  |     const changed = ref(0); | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     const { $globals } = useNuxtApp(); | 
					
						
							| 
									
										
										
										
											2023-08-21 10:00:37 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     interface Control { | 
					
						
							|  |  |  |       color: string; | 
					
						
							|  |  |  |       icon: string; | 
					
						
							|  |  |  |       callback: CallableFunction; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-21 10:00:37 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     const controls = ref<Control[][]>([ | 
					
						
							|  |  |  |       [ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           color: "info", | 
					
						
							|  |  |  |           icon: $globals.icons.flipHorizontal, | 
					
						
							|  |  |  |           callback: () => flip(true, false), | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           color: "info", | 
					
						
							|  |  |  |           icon: $globals.icons.flipVertical, | 
					
						
							|  |  |  |           callback: () => flip(false, true), | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |       [ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           color: "info", | 
					
						
							|  |  |  |           icon: $globals.icons.rotateLeft, | 
					
						
							|  |  |  |           callback: () => rotate(-90), | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           color: "info", | 
					
						
							|  |  |  |           icon: $globals.icons.rotateRight, | 
					
						
							|  |  |  |           callback: () => rotate(90), | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |     ]); | 
					
						
							| 
									
										
										
										
											2023-08-21 10:00:37 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     function flip(hortizontal: boolean, vertical?: boolean) { | 
					
						
							|  |  |  |       if (!cropper.value) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       cropper.value.flip(hortizontal, vertical); | 
					
						
							| 
									
										
										
										
											2025-06-29 20:17:49 +02:00
										 |  |  |       changed.value = changed.value + 1; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-21 10:00:37 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     function rotate(angle: number) { | 
					
						
							|  |  |  |       if (!cropper.value) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       cropper.value.rotate(angle); | 
					
						
							| 
									
										
										
										
											2025-06-29 20:17:49 +02:00
										 |  |  |       changed.value = changed.value + 1; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-21 10:00:37 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     function save() { | 
					
						
							|  |  |  |       if (!cropper.value) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       const { canvas } = cropper.value.getResult(); | 
					
						
							|  |  |  |       if (!canvas) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       canvas.toBlob((blob) => { | 
					
						
							|  |  |  |         if (blob) { | 
					
						
							|  |  |  |           context.emit("save", blob); | 
					
						
							| 
									
										
										
										
											2023-08-21 10:00:37 -05:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-21 10:00:37 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     return { | 
					
						
							|  |  |  |       cropper, | 
					
						
							|  |  |  |       controls, | 
					
						
							|  |  |  |       flip, | 
					
						
							|  |  |  |       rotate, | 
					
						
							|  |  |  |       save, | 
					
						
							| 
									
										
										
										
											2025-06-29 20:17:49 +02:00
										 |  |  |       changed, | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     }; | 
					
						
							|  |  |  |   }, | 
					
						
							| 
									
										
										
										
											2023-08-21 10:00:37 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |   methods: { | 
					
						
							|  |  |  |     defaultSize({ imageSize, visibleArea }) { | 
					
						
							|  |  |  |       return { | 
					
						
							|  |  |  |         width: (visibleArea || imageSize).width, | 
					
						
							|  |  |  |         height: (visibleArea || imageSize).height, | 
					
						
							|  |  |  |       }; | 
					
						
							| 
									
										
										
										
											2023-08-21 10:00:37 -05:00
										 |  |  |     }, | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |   }, | 
					
						
							| 
									
										
										
										
											2023-08-21 10:00:37 -05:00
										 |  |  | }); | 
					
						
							|  |  |  | </script> |