refactor: try sorting components into sub-folders
Some checks failed
ci/woodpecker/push/build-and-publish Pipeline failed

This commit is contained in:
Twig 2025-07-10 22:47:52 +02:00
parent 5dbf21b0ab
commit 15e5a21277
No known key found for this signature in database
10 changed files with 0 additions and 20 deletions

View file

@ -0,0 +1,93 @@
<template>
<div id="fullscreen-container">
<div id="crop-preview">
<img ref="image" :src="imageSrc" style="min-height: 35dvh;">
<Button text="Crop" :callback="cropImage"></Button>
<Button text="Cancel" :callback="closePopup"></Button>
</div>
</div>
</template>
<script lang="ts" setup>
import Cropper from 'cropperjs';
const props = defineProps({
imageSrc: String,
onCrop: Function,
onClose: Function,
});
const image = ref<HTMLImageElement | null>(null);
const cropper = ref<Cropper | null>(null);
watch(image, (newValue) => {
if (newValue) {
cropper.value = new Cropper(newValue)
const cropperCanvas = cropper.value.getCropperCanvas()
const cropperSelection = cropper.value.getCropperSelection()
if (cropperCanvas) {
cropperCanvas.background = false
}
if (cropperSelection) {
cropperSelection.precise = true
cropperSelection.aspectRatio = 1
cropperSelection.initialCoverage = 1
}
}
});
async function cropImage() {
if (cropper) {
const selection = cropper.value?.getCropperSelection();
if (selection) {
const canvas = await selection.$toCanvas({width: 256, height: 256})
canvas.toBlob((blob) => {
if (blob && props.onCrop) {
const reader = new FileReader();
reader.addEventListener("load", () => {
if (reader.result && typeof reader.result === 'string') {
if (props.onCrop) {
props.onCrop(blob, reader.result)
}
}
});
const file = new File([blob], 'preview.png', { type: 'image/png' })
reader.readAsDataURL(file)
}
});
}
}
}
function closePopup() {
if (props.onClose) {
props.onClose();
}
}
</script>
<style scoped>
.button {
margin: 0.2em
}
#fullscreen-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 10;
background: rgba(0, 0, 0, 0.5);
}
#crop-preview {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>

View file

@ -0,0 +1,40 @@
<template>
<div id="invite-popup">
<div v-if="invite">
<p>{{ invite }}</p>
<button @click="copyInvite">Copy Link</button>
</div>
<div v-else>
<button @click="generateInvite">Generate Invite</button>
</div>
</div>
</template>
<script lang="ts" setup>
import type { InviteResponse } from '~/types/interfaces';
const invite = ref<string>();
const route = useRoute();
async function generateInvite(): Promise<void> {
const createdInvite: InviteResponse | undefined = await fetchWithApi(
`/guilds/${route.params.serverId}/invites`,
{ method: "POST", body: { custom_id: "oijewfoiewf" } }
);
invite.value = createdInvite?.id;
return;
}
function copyInvite() {
const inviteUrl = URL.parse(`invite/${invite.value}`, `${window.location.protocol}//${window.location.host}`);
navigator.clipboard.writeText(inviteUrl!.href);
}
</script>
<style>
</style>

View file

@ -0,0 +1,38 @@
<template>
<div id="loading-container">
<Icon name="lucide:loader-circle" id="loading-circle" />
</div>
</template>
<script lang="ts" setup>
</script>
<style scoped>
#loading-container {
position: fixed;
left: 50dvw;
top: 50dvh;
transform: translate(-50%, -50%);
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
#loading-circle {
animation-name: spin;
animation-duration: 1s;
animation-timing-function: linear;
animation-iteration-count: infinite;
font-size: 2rem;
}
</style>