mirror of https://github.com/VERT-sh/VERT.git
fix: layout jump, feat: image preview
This commit is contained in:
parent
cda3825f6a
commit
db5d5414db
|
@ -21,5 +21,5 @@
|
|||
|
||||
body {
|
||||
@apply text-foreground bg-background font-body overflow-x-hidden;
|
||||
padding-left: calc(100vw - 100%);
|
||||
width: 100vw;
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
if (!event.dataTransfer) return;
|
||||
if (!files) files = Array.from(event.dataTransfer.files);
|
||||
else files.push(...Array.from(event.dataTransfer.files));
|
||||
onupload?.();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
<script lang="ts">
|
||||
type Props = {
|
||||
iterations: number;
|
||||
endIntensity: number;
|
||||
direction: "top" | "left" | "bottom" | "right";
|
||||
fadeTo?: string;
|
||||
};
|
||||
|
||||
let {
|
||||
iterations,
|
||||
endIntensity,
|
||||
direction,
|
||||
fadeTo = "transparent",
|
||||
}: Props = $props();
|
||||
|
||||
const getGradientDirection = () => {
|
||||
switch (direction) {
|
||||
case "top":
|
||||
return "to top";
|
||||
case "left":
|
||||
return "to left";
|
||||
case "bottom":
|
||||
return "to bottom";
|
||||
case "right":
|
||||
return "to right";
|
||||
}
|
||||
};
|
||||
|
||||
const blurSteps = $derived(
|
||||
Array.from({ length: iterations }, (_, i) => {
|
||||
const blurIntensity =
|
||||
(endIntensity / 2 ** (iterations - 1)) * 2 ** i;
|
||||
const gradientStart = (i / iterations) * 100;
|
||||
const gradientEnd = ((i + 1) / iterations) * 100;
|
||||
|
||||
return {
|
||||
blurIntensity,
|
||||
mask: `linear-gradient(${getGradientDirection()}, rgba(0, 0, 0, 0) ${gradientStart}%, rgba(0, 0, 0, 1) ${gradientEnd}%)`,
|
||||
};
|
||||
}),
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="w-full h-full relative">
|
||||
{#each blurSteps as { blurIntensity, mask }, index}
|
||||
<div
|
||||
class="absolute w-full h-full"
|
||||
style="
|
||||
z-index: {index + 2};
|
||||
backdrop-filter: blur({blurIntensity}px);
|
||||
mask: {mask};
|
||||
"
|
||||
></div>
|
||||
{/each}
|
||||
<div
|
||||
style="
|
||||
z-index: {iterations + 2};
|
||||
backdrop-filter: blur({endIntensity}px);
|
||||
mask: linear-gradient({getGradientDirection()}, rgba(0, 0, 0, 0) ${(iterations /
|
||||
(iterations + 1)) *
|
||||
100}%, rgba(0, 0, 0, 1) 100%);
|
||||
"
|
||||
></div>
|
||||
<div
|
||||
class="absolute top-0 left-0 w-full h-full z-50"
|
||||
style="background: linear-gradient({getGradientDirection()}, transparent 0%, {fadeTo} 100%);"
|
||||
></div>
|
||||
</div>
|
|
@ -1,7 +1,12 @@
|
|||
class Files {
|
||||
public files = $state<File[]>([]);
|
||||
public files = $state<
|
||||
{
|
||||
file: File;
|
||||
to: string;
|
||||
blobUrl: string;
|
||||
}[]
|
||||
>([]);
|
||||
public conversionTypes = $state<string[]>([]);
|
||||
public downloadFns = $state<(() => void)[]>([]);
|
||||
public beenToConverterPage = $state(false);
|
||||
public shouldShowAlert = $derived(
|
||||
!this.beenToConverterPage && this.files.length > 0,
|
||||
|
|
|
@ -25,18 +25,30 @@
|
|||
const linkIndex = $derived(
|
||||
Object.keys(links).findIndex((link) => links[link] === data.pathname),
|
||||
);
|
||||
|
||||
const maybeNavToHome = (e: DragEvent) => {
|
||||
if (e.dataTransfer?.types.includes("Files")) {
|
||||
e.preventDefault();
|
||||
goto("/");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="w-full h-full flex items-center pt-48 flex-col gap-10">
|
||||
<div
|
||||
role="main"
|
||||
class="w-full h-full flex items-center p-8 flex-col gap-8"
|
||||
ondragenter={maybeNavToHome}
|
||||
>
|
||||
<div
|
||||
class="w-full max-w-screen-lg p-1 border-solid border-2 rounded-2xl border-foreground-muted-alt grid"
|
||||
style="grid-template-columns: auto 1fr"
|
||||
class="w-full max-w-screen-lg p-1 border-solid border-2 rounded-2xl border-foreground-muted-alt flex"
|
||||
>
|
||||
<div
|
||||
class="px-4 m-1 mr-3 flex items-center bg-accent-background fill-accent-foreground rounded-xl"
|
||||
>
|
||||
<div class="h-6">
|
||||
<Logo />
|
||||
<div class="p-1">
|
||||
<div
|
||||
class="px-4 relative w-full h-full mr-3 justify-center items-center bg-accent-background fill-accent-foreground rounded-xl md:flex hidden"
|
||||
>
|
||||
<div class="h-6 items-center flex justify-center">
|
||||
<Logo />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -53,7 +65,7 @@
|
|||
></div>
|
||||
{#each Object.entries(links) as [name, link] (link)}
|
||||
<button
|
||||
class="w-1/2 h-full flex items-center justify-center rounded-xl relative font-display overflow-hidden"
|
||||
class="w-1/2 px-2 h-full flex items-center justify-center rounded-xl relative font-display overflow-hidden"
|
||||
onclick={() => {
|
||||
const keys = Object.keys(links);
|
||||
const currentIndex = keys.findIndex(
|
||||
|
@ -90,9 +102,11 @@
|
|||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full max-w-screen-lg grid grid-cols-1 grid-rows-1 relative">
|
||||
<div
|
||||
class="w-full max-w-screen-lg h-full grid grid-cols-1 grid-rows-1 relative"
|
||||
>
|
||||
{#key data.pathname}
|
||||
<div class="w-full">
|
||||
<div class="w-full h-full">
|
||||
<div
|
||||
class="absolute top-0 left-0 w-full h-full flex justify-center"
|
||||
in:blur={{
|
||||
|
|
|
@ -4,39 +4,58 @@
|
|||
import { converters } from "$lib/converters";
|
||||
import { files } from "$lib/store/index.svelte";
|
||||
|
||||
const convertAllFiles = async () => {
|
||||
const promises = files.files?.map(async (file, i) => {
|
||||
let conversionType = files.conversionTypes[i];
|
||||
const converter = converters[0];
|
||||
const convertedFile = await converter.convert(
|
||||
{
|
||||
name: file.name,
|
||||
buffer: await file.arrayBuffer(),
|
||||
},
|
||||
conversionType,
|
||||
);
|
||||
files.downloadFns[i] = () => {
|
||||
const url = URL.createObjectURL(
|
||||
new Blob([convertedFile.buffer]),
|
||||
);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
if (conversionType.startsWith("."))
|
||||
conversionType = conversionType.slice(1);
|
||||
a.download = `${file.name}.${conversionType}`;
|
||||
a.target = "_self";
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
});
|
||||
if (promises) await Promise.all(promises);
|
||||
let ourFiles = $state<File[]>();
|
||||
|
||||
const runUpload = () => {
|
||||
files.files = [
|
||||
...files.files,
|
||||
...(ourFiles || []).map((f) => ({
|
||||
file: f,
|
||||
to: converters[0].supportedFormats[0],
|
||||
blobUrl: URL.createObjectURL(f),
|
||||
})),
|
||||
];
|
||||
|
||||
ourFiles = [];
|
||||
|
||||
if (files.files.length > 0 && !files.beenToConverterPage)
|
||||
goto("/convert");
|
||||
};
|
||||
|
||||
// const convertAllFiles = async () => {
|
||||
// const promises = files.files?.map(async (file, i) => {
|
||||
// let conversionType = files.conversionTypes[i];
|
||||
// const converter = converters[0];
|
||||
// const convertedFile = await converter.convert(
|
||||
// {
|
||||
// name: file.name,
|
||||
// buffer: await file.arrayBuffer(),
|
||||
// },
|
||||
// conversionType,
|
||||
// );
|
||||
// files.downloadFns[i] = () => {
|
||||
// const url = URL.createObjectURL(
|
||||
// new Blob([convertedFile.buffer]),
|
||||
// );
|
||||
// const a = document.createElement("a");
|
||||
// a.href = url;
|
||||
// if (conversionType.startsWith("."))
|
||||
// conversionType = conversionType.slice(1);
|
||||
// a.download = `${file.name}.${conversionType}`;
|
||||
// a.target = "_self";
|
||||
// a.click();
|
||||
// URL.revokeObjectURL(url);
|
||||
// };
|
||||
// });
|
||||
// if (promises) await Promise.all(promises);
|
||||
// };
|
||||
</script>
|
||||
|
||||
<Uploader
|
||||
bind:files={files.files}
|
||||
onupload={() => files.files.length > 0 && goto("/convert")}
|
||||
/>
|
||||
<div
|
||||
class="flex w-full h-full items-center justify-center pb-32 [@media(max-height:768px)]:block"
|
||||
>
|
||||
<Uploader bind:files={ourFiles} onupload={runUpload} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* for this page specifically */
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import ProgressiveBlur from "$lib/components/visual/effects/ProgressiveBlur.svelte";
|
||||
import { converters } from "$lib/converters";
|
||||
import { files } from "$lib/store/index.svelte";
|
||||
|
||||
|
@ -11,28 +12,52 @@
|
|||
No files uploaded. Head to the Upload tab to begin!
|
||||
</p>
|
||||
{/if}
|
||||
{#each files.files as file, i}
|
||||
{#each files.files.reverse() as file, i}
|
||||
<div
|
||||
class="flex items-center w-full border-2 border-solid border-foreground-muted-alt rounded-xl pl-4 pr-2 py-2"
|
||||
class="flex relative items-center w-full border-2 border-solid border-foreground-muted-alt rounded-xl pl-4 pr-2 py-2"
|
||||
>
|
||||
<div class="flex items-center flex-grow">
|
||||
{file.name}
|
||||
<div class="flex items-center w-full z-50 relative">
|
||||
<div
|
||||
class="flex items-center flex-grow"
|
||||
style="text-shadow: 0px 0px 6px white, 0px 0px 12px white"
|
||||
>
|
||||
{file.file.name}
|
||||
</div>
|
||||
<div class="flex items-center gap-2 flex-shrink-0">
|
||||
<span>from</span>
|
||||
<span
|
||||
class="py-2 px-3 font-display bg-foreground text-background rounded-xl"
|
||||
>.{file.file.name.split(".").slice(-1)}</span
|
||||
>
|
||||
<span>to</span>
|
||||
<select
|
||||
class="font-display border-2 border-solid border-foreground-muted-alt rounded-xl p-2 focus:!outline-none"
|
||||
bind:value={files.conversionTypes[i]}
|
||||
>
|
||||
{#each converters[0].supportedFormats as conversionType}
|
||||
<option value={conversionType}
|
||||
>{conversionType}</option
|
||||
>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 flex-shrink-0">
|
||||
<span>from</span>
|
||||
<span
|
||||
class="py-2 px-3 font-display bg-foreground text-background rounded-xl"
|
||||
>.{file.name.split(".").slice(-1)}</span
|
||||
>
|
||||
<span>to</span>
|
||||
<select
|
||||
class="font-display border-2 border-solid border-foreground-muted-alt rounded-xl p-2 focus:!outline-none"
|
||||
bind:value={files.conversionTypes[i]}
|
||||
>
|
||||
{#each converters[0].supportedFormats as conversionType}
|
||||
<option value={conversionType}>{conversionType}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<!-- god knows why, but setting opacity > 0.98 causes a z-ordering issue in firefox ??? -->
|
||||
<div
|
||||
class="absolute top-0 -z-50 left-0 w-full h-full rounded-[10px] overflow-hidden opacity-[0.98]"
|
||||
>
|
||||
<div
|
||||
class="bg-cover bg-center w-full h-full"
|
||||
style="background-image: url({file.blobUrl});"
|
||||
></div>
|
||||
<div class="absolute top-0 right-0 w-5/6 h-full">
|
||||
<ProgressiveBlur
|
||||
direction="right"
|
||||
endIntensity={64}
|
||||
iterations={6}
|
||||
fadeTo="rgba(255, 255, 255, 0.8)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
|
Loading…
Reference in New Issue