feat(TextareaCopyable/FormatTransformer): add download button prop

This commit is contained in:
sharevb 2024-06-16 12:34:41 +02:00
parent 135b31190d
commit 052ddebc34
3 changed files with 106 additions and 23 deletions

View file

@ -1,7 +1,9 @@
<script setup lang="ts">
import _ from 'lodash';
import { Base64 } from 'js-base64';
import type { UseValidationRule } from '@/composable/validation';
import CInputText from '@/ui/c-input-text/c-input-text.vue';
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
const props = withDefaults(
defineProps<{
@ -12,6 +14,8 @@ const props = withDefaults(
inputDefault?: string
outputLabel?: string
outputLanguage?: string
downloadFileName?: string
downloadButtonText?: string
}>(),
{
transformer: _.identity,
@ -21,16 +25,27 @@ const props = withDefaults(
inputPlaceholder: 'Input...',
outputLabel: 'Output',
outputLanguage: '',
downloadFileName: '',
downloadButtonText: 'Download',
},
);
const { transformer, inputValidationRules, inputLabel, outputLabel, outputLanguage, inputPlaceholder, inputDefault }
= toRefs(props);
const {
transformer, inputValidationRules, inputLabel, outputLabel, outputLanguage,
inputPlaceholder, inputDefault, downloadFileName, downloadButtonText,
} = toRefs(props);
const inputElement = ref<typeof CInputText>();
const input = ref(inputDefault.value);
const output = computed(() => transformer.value(input.value));
const outputBase64 = computed(() => Base64.encode(output.value));
const { download } = useDownloadFileFromBase64(
{
source: outputBase64,
filename: downloadFileName,
});
</script>
<template>
@ -53,5 +68,11 @@ const output = computed(() => transformer.value(input.value));
{{ outputLabel }}
</div>
<textarea-copyable :value="output" :language="outputLanguage" :follow-height-of="inputElement?.inputWrapperRef" />
<div v-if="downloadFileName !== '' && output !== ''" mt-5 flex justify-center>
<c-button secondary @click="download">
{{ downloadButtonText }}
</c-button>
</div>
</div>
</template>

View file

@ -13,7 +13,9 @@ import jsHljs from 'highlight.js/lib/languages/javascript';
import cssHljs from 'highlight.js/lib/languages/css';
import goHljs from 'highlight.js/lib/languages/go';
import csharpHljs from 'highlight.js/lib/languages/csharp';
import { Base64 } from 'js-base64';
import { useCopy } from '@/composable/copy';
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
const props = withDefaults(
defineProps<{
@ -23,12 +25,16 @@ const props = withDefaults(
copyPlacement?: 'top-right' | 'bottom-right' | 'outside' | 'none'
copyMessage?: string
wordWrap?: boolean
downloadFileName?: string
downloadButtonText?: string
}>(),
{
followHeightOf: null,
language: 'txt',
copyPlacement: 'top-right',
copyMessage: 'Copy to clipboard',
downloadFileName: '',
downloadButtonText: 'Download',
},
);
hljs.registerLanguage('sql', sqlHljs);
@ -44,11 +50,18 @@ hljs.registerLanguage('javascript', jsHljs);
hljs.registerLanguage('go', goHljs);
hljs.registerLanguage('csharp', csharpHljs);
const { value, language, followHeightOf, copyPlacement, copyMessage } = toRefs(props);
const { value, language, followHeightOf, copyPlacement, copyMessage, downloadFileName, downloadButtonText } = toRefs(props);
const { height } = followHeightOf.value ? useElementSize(followHeightOf) : { height: ref(null) };
const { copy, isJustCopied } = useCopy({ source: value, createToast: false });
const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage.value);
const valueBase64 = computed(() => Base64.encode(value.value));
const { download } = useDownloadFileFromBase64(
{
source: valueBase64,
filename: downloadFileName,
});
</script>
<template>
@ -81,6 +94,11 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage.
{{ tooltipText }}
</c-button>
</div>
<div v-if="downloadFileName !== '' && value !== ''" mt-5 flex justify-center>
<c-button secondary @click="download">
{{ downloadButtonText }}
</c-button>
</div>
</div>
</template>

View file

@ -1,8 +1,14 @@
import { extension as getExtensionFromMime } from 'mime-types';
import type { Ref } from 'vue';
import { extension as getExtensionFromMimeType, extension as getMimeTypeFromExtension } from 'mime-types';
import type { MaybeRef } from 'vue';
import _ from 'lodash';
import { get } from '@vueuse/core';
export { getMimeTypeFromBase64, useDownloadFileFromBase64 };
export {
getMimeTypeFromBase64,
getMimeTypeFromExtension, getExtensionFromMimeType,
useDownloadFileFromBase64,
previewImageFromBase64,
};
const commonMimeTypesSignatures = {
'JVBERi0': 'application/pdf',
@ -36,30 +42,68 @@ function getFileExtensionFromMimeType({
defaultExtension?: string
}) {
if (mimeType) {
return getExtensionFromMime(mimeType) ?? defaultExtension;
return getExtensionFromMimeType(mimeType) ?? defaultExtension;
}
return defaultExtension;
}
function useDownloadFileFromBase64({ source, filename }: { source: Ref<string>; filename?: string }) {
function downloadFromBase64({ sourceValue, filename, extension, fileMimeType }:
{ sourceValue: string; filename?: string; extension?: string; fileMimeType?: string }) {
if (sourceValue === '') {
throw new Error('Base64 string is empty');
}
const defaultExtension = extension ?? 'txt';
const { mimeType } = getMimeTypeFromBase64({ base64String: sourceValue });
let base64String = sourceValue;
if (!mimeType) {
const targetMimeType = fileMimeType ?? getMimeTypeFromExtension(defaultExtension);
base64String = `data:${targetMimeType};base64,${sourceValue}`;
}
const cleanExtension = extension ?? getFileExtensionFromMimeType(
{ mimeType, defaultExtension });
let cleanFileName = filename ?? `file.${cleanExtension}`;
if (extension && !cleanFileName.endsWith(`.${extension}`)) {
cleanFileName = `${cleanFileName}.${cleanExtension}`;
}
const a = document.createElement('a');
a.href = base64String;
a.download = cleanFileName;
a.click();
}
function useDownloadFileFromBase64(
{ source, filename, extension }:
{ source: MaybeRef<string>; filename?: MaybeRef<string>; extension?: MaybeRef<string> }) {
return {
download() {
if (source.value === '') {
throw new Error('Base64 string is empty');
}
const { mimeType } = getMimeTypeFromBase64({ base64String: source.value });
const base64String = mimeType
? source.value
: `data:text/plain;base64,${source.value}`;
const cleanFileName = filename ?? `file.${getFileExtensionFromMimeType({ mimeType })}`;
const a = document.createElement('a');
a.href = base64String;
a.download = cleanFileName;
a.click();
downloadFromBase64({ sourceValue: get(source), filename: get(filename), extension: get(extension) });
},
};
}
function previewImageFromBase64(base64String: string): HTMLImageElement {
if (base64String === '') {
throw new Error('Base64 string is empty');
}
const img = document.createElement('img');
img.src = base64String;
const container = document.createElement('div');
container.appendChild(img);
const previewContainer = document.getElementById('previewContainer');
if (previewContainer) {
previewContainer.innerHTML = '';
previewContainer.appendChild(container);
}
else {
throw new Error('Preview container element not found');
}
return img;
}