Browse Source

copy & download qr code as png

pull/2521/head
Bernd Storath 4 months ago
parent
commit
9ad6ed45da
  1. 7
      src/app/app.css
  2. 73
      src/app/components/Clients/QRCodeDialog.vue
  3. 4
      src/app/components/Header/ChartToggle.vue
  4. 7
      src/app/components/Icons/Copy.vue
  5. 1
      src/nuxt.config.ts

7
src/app/app.css

@ -0,0 +1,7 @@
:root {
color-scheme: light;
}
.dark {
color-scheme: dark;
}

73
src/app/components/Clients/QRCodeDialog.vue

@ -5,10 +5,16 @@
</template>
<template #description>
<div class="bg-white">
<img :src="qrCode" />
<img ref="img" :src="qrCode" />
</div>
</template>
<template #actions>
<BaseSecondaryButton class="flex items-center gap-2" @click="copyPng">
<IconsCopy class="size-5" /> PNG
</BaseSecondaryButton>
<BaseSecondaryButton class="flex items-center gap-2" @click="downloadPng">
<IconsDownload class="size-5" /> PNG
</BaseSecondaryButton>
<DialogClose as-child>
<BaseSecondaryButton>{{ $t('dialog.cancel') }}</BaseSecondaryButton>
</DialogClose>
@ -18,4 +24,69 @@
<script setup lang="ts">
defineProps<{ qrCode: string }>();
const img = useTemplateRef('img');
async function svgToPng() {
if (!img.value || !img.value.complete || img.value.naturalWidth === 0) {
throw new Error('image is not loaded');
}
const width = 1000;
const height = 1000;
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
if (!ctx) {
throw new Error('was not able to create 2d context');
}
ctx.drawImage(img.value!, 0, 0, width, height);
return new Promise<Blob>((res, rej) => {
canvas.toBlob((blob) => {
if (!blob) {
return rej(new Error('was not able to create blob'));
}
return res(blob);
}, 'image/png');
});
}
async function downloadPng() {
const blob = await svgToPng();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'client-config.png';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
async function copyPng() {
const blob = await svgToPng();
try {
await navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob,
}),
]);
useToast().showToast({
type: 'success',
message: $t('copy.copied'),
});
} catch (e) {
console.error('failed to copy png', e);
useToast().showToast({
type: 'error',
message: $t('copy.failed'),
});
}
}
</script>

4
src/app/components/Header/ChartToggle.vue

@ -1,12 +1,12 @@
<template>
<Toggle
:pressed="globalStore.uiShowCharts"
class="group inline-flex h-8 w-8 cursor-pointer items-center justify-center whitespace-nowrap rounded-full bg-gray-200 transition hover:bg-gray-300 dark:bg-neutral-700 dark:hover:bg-neutral-600"
class="group flex h-8 w-8 items-center justify-center rounded-full bg-gray-200 transition hover:bg-gray-300 dark:bg-neutral-700 dark:hover:bg-neutral-600"
:title="$t('layout.toggleCharts')"
@update:pressed="globalStore.toggleCharts"
>
<IconsChart
class="h-5 w-5 fill-gray-400 transition group-data-[state=on]:fill-gray-600 dark:fill-neutral-600 dark:group-data-[state=on]:fill-neutral-400"
class="h-5 w-5 transition group-data-[state=on]:fill-gray-600 dark:text-neutral-400 dark:group-data-[state=on]:fill-gray-300"
/>
</Toggle>
</template>

7
src/app/components/Icons/Copy.vue

@ -0,0 +1,7 @@
<template>
<ClipboardDocumentIcon />
</template>
<script lang="ts" setup>
import ClipboardDocumentIcon from '@heroicons/vue/24/outline/esm/ClipboardDocumentIcon';
</script>

1
src/nuxt.config.ts

@ -23,6 +23,7 @@ export default defineNuxtConfig({
classSuffix: '',
cookieName: 'theme',
},
css: ['~/app.css'],
i18n: {
// https://i18n.nuxtjs.org/docs/guide/server-side-translations
experimental: {

Loading…
Cancel
Save