mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-11-04 03:03:18 -05:00 
			
		
		
		
	Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com> Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
		
			
				
	
	
		
			166 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			166 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
<template>
 | 
						|
  <v-container class="pa-0">
 | 
						|
    <v-row no-gutters>
 | 
						|
      <v-col
 | 
						|
        cols="8"
 | 
						|
        align-self="center"
 | 
						|
      >
 | 
						|
        <Cropper
 | 
						|
          ref="cropper"
 | 
						|
          class="cropper"
 | 
						|
          :src="img"
 | 
						|
          :default-size="defaultSize"
 | 
						|
          :style="`height: ${cropperHeight}; width: ${cropperWidth};`"
 | 
						|
        />
 | 
						|
      </v-col>
 | 
						|
      <v-spacer />
 | 
						|
      <v-col
 | 
						|
        cols="2"
 | 
						|
        align-self="center"
 | 
						|
      >
 | 
						|
        <v-container class="pa-0 mx-0">
 | 
						|
          <v-row
 | 
						|
            v-for="(row, keyRow) in controls"
 | 
						|
            :key="keyRow"
 | 
						|
          >
 | 
						|
            <v-col
 | 
						|
              v-for="(control, keyControl) in row"
 | 
						|
              :key="keyControl"
 | 
						|
              :cols="12 / row.length"
 | 
						|
              class="py-2 mx-0"
 | 
						|
              style="display: flex; align-items: center; justify-content: center;"
 | 
						|
            >
 | 
						|
              <v-btn
 | 
						|
                icon
 | 
						|
                :color="control.color"
 | 
						|
                @click="control.callback()"
 | 
						|
              >
 | 
						|
                <v-icon> {{ control.icon }} </v-icon>
 | 
						|
              </v-btn>
 | 
						|
            </v-col>
 | 
						|
          </v-row>
 | 
						|
        </v-container>
 | 
						|
      </v-col>
 | 
						|
    </v-row>
 | 
						|
  </v-container>
 | 
						|
</template>
 | 
						|
 | 
						|
<script lang="ts">
 | 
						|
import { Cropper } from "vue-advanced-cropper";
 | 
						|
import "vue-advanced-cropper/dist/style.css";
 | 
						|
 | 
						|
export default defineNuxtComponent({
 | 
						|
  components: { Cropper },
 | 
						|
  props: {
 | 
						|
    img: {
 | 
						|
      type: String,
 | 
						|
      required: true,
 | 
						|
    },
 | 
						|
    cropperHeight: {
 | 
						|
      type: String,
 | 
						|
      default: undefined,
 | 
						|
    },
 | 
						|
    cropperWidth: {
 | 
						|
      type: String,
 | 
						|
      default: undefined,
 | 
						|
    },
 | 
						|
  },
 | 
						|
  emits: ["save"],
 | 
						|
  setup(_, context) {
 | 
						|
    const cropper = ref<Cropper>();
 | 
						|
    const { $globals } = useNuxtApp();
 | 
						|
 | 
						|
    interface Control {
 | 
						|
      color: string;
 | 
						|
      icon: string;
 | 
						|
      callback: CallableFunction;
 | 
						|
    }
 | 
						|
 | 
						|
    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),
 | 
						|
        },
 | 
						|
      ],
 | 
						|
      [
 | 
						|
        {
 | 
						|
          color: "success",
 | 
						|
          icon: $globals.icons.save,
 | 
						|
          callback: () => save(),
 | 
						|
        },
 | 
						|
      ],
 | 
						|
    ]);
 | 
						|
 | 
						|
    function flip(hortizontal: boolean, vertical?: boolean) {
 | 
						|
      if (!cropper.value) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      cropper.value.flip(hortizontal, vertical);
 | 
						|
    }
 | 
						|
 | 
						|
    function rotate(angle: number) {
 | 
						|
      if (!cropper.value) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      cropper.value.rotate(angle);
 | 
						|
    }
 | 
						|
 | 
						|
    function save() {
 | 
						|
      if (!cropper.value) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      const { canvas } = cropper.value.getResult();
 | 
						|
      if (!canvas) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      canvas.toBlob((blob) => {
 | 
						|
        if (blob) {
 | 
						|
          context.emit("save", blob);
 | 
						|
        }
 | 
						|
      });
 | 
						|
    }
 | 
						|
 | 
						|
    return {
 | 
						|
      cropper,
 | 
						|
      controls,
 | 
						|
      flip,
 | 
						|
      rotate,
 | 
						|
      save,
 | 
						|
    };
 | 
						|
  },
 | 
						|
 | 
						|
  methods: {
 | 
						|
    // @ts-expect-error https://advanced-cropper.github.io/vue-advanced-cropper/guides/advanced-recipes.html
 | 
						|
    defaultSize({ imageSize, visibleArea }) {
 | 
						|
      return {
 | 
						|
        width: (visibleArea || imageSize).width,
 | 
						|
        height: (visibleArea || imageSize).height,
 | 
						|
      };
 | 
						|
    },
 | 
						|
  },
 | 
						|
});
 | 
						|
</script>
 |