mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-05-05 05:47:10 -04:00
feat(TextareaCopyable/FormatTransformer): add download button prop
This commit is contained in:
parent
135b31190d
commit
052ddebc34
3 changed files with 106 additions and 23 deletions
|
@ -1,7 +1,9 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { Base64 } from 'js-base64';
|
||||||
import type { UseValidationRule } from '@/composable/validation';
|
import type { UseValidationRule } from '@/composable/validation';
|
||||||
import CInputText from '@/ui/c-input-text/c-input-text.vue';
|
import CInputText from '@/ui/c-input-text/c-input-text.vue';
|
||||||
|
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -12,6 +14,8 @@ const props = withDefaults(
|
||||||
inputDefault?: string
|
inputDefault?: string
|
||||||
outputLabel?: string
|
outputLabel?: string
|
||||||
outputLanguage?: string
|
outputLanguage?: string
|
||||||
|
downloadFileName?: string
|
||||||
|
downloadButtonText?: string
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
transformer: _.identity,
|
transformer: _.identity,
|
||||||
|
@ -21,16 +25,27 @@ const props = withDefaults(
|
||||||
inputPlaceholder: 'Input...',
|
inputPlaceholder: 'Input...',
|
||||||
outputLabel: 'Output',
|
outputLabel: 'Output',
|
||||||
outputLanguage: '',
|
outputLanguage: '',
|
||||||
|
downloadFileName: '',
|
||||||
|
downloadButtonText: 'Download',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const { transformer, inputValidationRules, inputLabel, outputLabel, outputLanguage, inputPlaceholder, inputDefault }
|
const {
|
||||||
= toRefs(props);
|
transformer, inputValidationRules, inputLabel, outputLabel, outputLanguage,
|
||||||
|
inputPlaceholder, inputDefault, downloadFileName, downloadButtonText,
|
||||||
|
} = toRefs(props);
|
||||||
|
|
||||||
const inputElement = ref<typeof CInputText>();
|
const inputElement = ref<typeof CInputText>();
|
||||||
|
|
||||||
const input = ref(inputDefault.value);
|
const input = ref(inputDefault.value);
|
||||||
const output = computed(() => transformer.value(input.value));
|
const output = computed(() => transformer.value(input.value));
|
||||||
|
|
||||||
|
const outputBase64 = computed(() => Base64.encode(output.value));
|
||||||
|
const { download } = useDownloadFileFromBase64(
|
||||||
|
{
|
||||||
|
source: outputBase64,
|
||||||
|
filename: downloadFileName,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -53,5 +68,11 @@ const output = computed(() => transformer.value(input.value));
|
||||||
{{ outputLabel }}
|
{{ outputLabel }}
|
||||||
</div>
|
</div>
|
||||||
<textarea-copyable :value="output" :language="outputLanguage" :follow-height-of="inputElement?.inputWrapperRef" />
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -13,7 +13,9 @@ import jsHljs from 'highlight.js/lib/languages/javascript';
|
||||||
import cssHljs from 'highlight.js/lib/languages/css';
|
import cssHljs from 'highlight.js/lib/languages/css';
|
||||||
import goHljs from 'highlight.js/lib/languages/go';
|
import goHljs from 'highlight.js/lib/languages/go';
|
||||||
import csharpHljs from 'highlight.js/lib/languages/csharp';
|
import csharpHljs from 'highlight.js/lib/languages/csharp';
|
||||||
|
import { Base64 } from 'js-base64';
|
||||||
import { useCopy } from '@/composable/copy';
|
import { useCopy } from '@/composable/copy';
|
||||||
|
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -23,12 +25,16 @@ const props = withDefaults(
|
||||||
copyPlacement?: 'top-right' | 'bottom-right' | 'outside' | 'none'
|
copyPlacement?: 'top-right' | 'bottom-right' | 'outside' | 'none'
|
||||||
copyMessage?: string
|
copyMessage?: string
|
||||||
wordWrap?: boolean
|
wordWrap?: boolean
|
||||||
|
downloadFileName?: string
|
||||||
|
downloadButtonText?: string
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
followHeightOf: null,
|
followHeightOf: null,
|
||||||
language: 'txt',
|
language: 'txt',
|
||||||
copyPlacement: 'top-right',
|
copyPlacement: 'top-right',
|
||||||
copyMessage: 'Copy to clipboard',
|
copyMessage: 'Copy to clipboard',
|
||||||
|
downloadFileName: '',
|
||||||
|
downloadButtonText: 'Download',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
hljs.registerLanguage('sql', sqlHljs);
|
hljs.registerLanguage('sql', sqlHljs);
|
||||||
|
@ -44,11 +50,18 @@ hljs.registerLanguage('javascript', jsHljs);
|
||||||
hljs.registerLanguage('go', goHljs);
|
hljs.registerLanguage('go', goHljs);
|
||||||
hljs.registerLanguage('csharp', csharpHljs);
|
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 { height } = followHeightOf.value ? useElementSize(followHeightOf) : { height: ref(null) };
|
||||||
|
|
||||||
const { copy, isJustCopied } = useCopy({ source: value, createToast: false });
|
const { copy, isJustCopied } = useCopy({ source: value, createToast: false });
|
||||||
const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage.value);
|
const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage.value);
|
||||||
|
|
||||||
|
const valueBase64 = computed(() => Base64.encode(value.value));
|
||||||
|
const { download } = useDownloadFileFromBase64(
|
||||||
|
{
|
||||||
|
source: valueBase64,
|
||||||
|
filename: downloadFileName,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -81,6 +94,11 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage.
|
||||||
{{ tooltipText }}
|
{{ tooltipText }}
|
||||||
</c-button>
|
</c-button>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="downloadFileName !== '' && value !== ''" mt-5 flex justify-center>
|
||||||
|
<c-button secondary @click="download">
|
||||||
|
{{ downloadButtonText }}
|
||||||
|
</c-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
import { extension as getExtensionFromMime } from 'mime-types';
|
import { extension as getExtensionFromMimeType, extension as getMimeTypeFromExtension } from 'mime-types';
|
||||||
import type { Ref } from 'vue';
|
import type { MaybeRef } from 'vue';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { get } from '@vueuse/core';
|
||||||
|
|
||||||
export { getMimeTypeFromBase64, useDownloadFileFromBase64 };
|
export {
|
||||||
|
getMimeTypeFromBase64,
|
||||||
|
getMimeTypeFromExtension, getExtensionFromMimeType,
|
||||||
|
useDownloadFileFromBase64,
|
||||||
|
previewImageFromBase64,
|
||||||
|
};
|
||||||
|
|
||||||
const commonMimeTypesSignatures = {
|
const commonMimeTypesSignatures = {
|
||||||
'JVBERi0': 'application/pdf',
|
'JVBERi0': 'application/pdf',
|
||||||
|
@ -36,30 +42,68 @@ function getFileExtensionFromMimeType({
|
||||||
defaultExtension?: string
|
defaultExtension?: string
|
||||||
}) {
|
}) {
|
||||||
if (mimeType) {
|
if (mimeType) {
|
||||||
return getExtensionFromMime(mimeType) ?? defaultExtension;
|
return getExtensionFromMimeType(mimeType) ?? defaultExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 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 {
|
return {
|
||||||
download() {
|
download() {
|
||||||
if (source.value === '') {
|
downloadFromBase64({ sourceValue: get(source), filename: get(filename), extension: get(extension) });
|
||||||
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();
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue