fix: layout jump, feat: image preview

This commit is contained in:
not-nullptr 2024-11-12 00:23:51 +00:00
parent cda3825f6a
commit db5d5414db
7 changed files with 195 additions and 63 deletions

View File

@ -21,5 +21,5 @@
body { body {
@apply text-foreground bg-background font-body overflow-x-hidden; @apply text-foreground bg-background font-body overflow-x-hidden;
padding-left: calc(100vw - 100%); width: 100vw;
} }

View File

@ -55,6 +55,7 @@
if (!event.dataTransfer) return; if (!event.dataTransfer) return;
if (!files) files = Array.from(event.dataTransfer.files); if (!files) files = Array.from(event.dataTransfer.files);
else files.push(...Array.from(event.dataTransfer.files)); else files.push(...Array.from(event.dataTransfer.files));
onupload?.();
return true; return true;
} }

View File

@ -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>

View File

@ -1,7 +1,12 @@
class Files { class Files {
public files = $state<File[]>([]); public files = $state<
{
file: File;
to: string;
blobUrl: string;
}[]
>([]);
public conversionTypes = $state<string[]>([]); public conversionTypes = $state<string[]>([]);
public downloadFns = $state<(() => void)[]>([]);
public beenToConverterPage = $state(false); public beenToConverterPage = $state(false);
public shouldShowAlert = $derived( public shouldShowAlert = $derived(
!this.beenToConverterPage && this.files.length > 0, !this.beenToConverterPage && this.files.length > 0,

View File

@ -25,18 +25,30 @@
const linkIndex = $derived( const linkIndex = $derived(
Object.keys(links).findIndex((link) => links[link] === data.pathname), Object.keys(links).findIndex((link) => links[link] === data.pathname),
); );
const maybeNavToHome = (e: DragEvent) => {
if (e.dataTransfer?.types.includes("Files")) {
e.preventDefault();
goto("/");
}
};
</script> </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 <div
class="w-full max-w-screen-lg p-1 border-solid border-2 rounded-2xl border-foreground-muted-alt grid" class="w-full max-w-screen-lg p-1 border-solid border-2 rounded-2xl border-foreground-muted-alt flex"
style="grid-template-columns: auto 1fr"
> >
<div <div class="p-1">
class="px-4 m-1 mr-3 flex items-center bg-accent-background fill-accent-foreground rounded-xl" <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"> >
<Logo /> <div class="h-6 items-center flex justify-center">
<Logo />
</div>
</div> </div>
</div> </div>
@ -53,7 +65,7 @@
></div> ></div>
{#each Object.entries(links) as [name, link] (link)} {#each Object.entries(links) as [name, link] (link)}
<button <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={() => { onclick={() => {
const keys = Object.keys(links); const keys = Object.keys(links);
const currentIndex = keys.findIndex( const currentIndex = keys.findIndex(
@ -90,9 +102,11 @@
{/each} {/each}
</div> </div>
</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} {#key data.pathname}
<div class="w-full"> <div class="w-full h-full">
<div <div
class="absolute top-0 left-0 w-full h-full flex justify-center" class="absolute top-0 left-0 w-full h-full flex justify-center"
in:blur={{ in:blur={{

View File

@ -4,39 +4,58 @@
import { converters } from "$lib/converters"; import { converters } from "$lib/converters";
import { files } from "$lib/store/index.svelte"; import { files } from "$lib/store/index.svelte";
const convertAllFiles = async () => { let ourFiles = $state<File[]>();
const promises = files.files?.map(async (file, i) => {
let conversionType = files.conversionTypes[i]; const runUpload = () => {
const converter = converters[0]; files.files = [
const convertedFile = await converter.convert( ...files.files,
{ ...(ourFiles || []).map((f) => ({
name: file.name, file: f,
buffer: await file.arrayBuffer(), to: converters[0].supportedFormats[0],
}, blobUrl: URL.createObjectURL(f),
conversionType, })),
); ];
files.downloadFns[i] = () => {
const url = URL.createObjectURL( ourFiles = [];
new Blob([convertedFile.buffer]),
); if (files.files.length > 0 && !files.beenToConverterPage)
const a = document.createElement("a"); goto("/convert");
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);
}; };
// 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> </script>
<Uploader <div
bind:files={files.files} class="flex w-full h-full items-center justify-center pb-32 [@media(max-height:768px)]:block"
onupload={() => files.files.length > 0 && goto("/convert")} >
/> <Uploader bind:files={ourFiles} onupload={runUpload} />
</div>
<style> <style>
/* for this page specifically */ /* for this page specifically */

View File

@ -1,4 +1,5 @@
<script> <script>
import ProgressiveBlur from "$lib/components/visual/effects/ProgressiveBlur.svelte";
import { converters } from "$lib/converters"; import { converters } from "$lib/converters";
import { files } from "$lib/store/index.svelte"; import { files } from "$lib/store/index.svelte";
@ -11,28 +12,52 @@
No files uploaded. Head to the Upload tab to begin! No files uploaded. Head to the Upload tab to begin!
</p> </p>
{/if} {/if}
{#each files.files as file, i} {#each files.files.reverse() as file, i}
<div <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"> <div class="flex items-center w-full z-50 relative">
{file.name} <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>
<div class="flex items-center gap-2 flex-shrink-0"> <!-- god knows why, but setting opacity > 0.98 causes a z-ordering issue in firefox ??? -->
<span>from</span> <div
<span class="absolute top-0 -z-50 left-0 w-full h-full rounded-[10px] overflow-hidden opacity-[0.98]"
class="py-2 px-3 font-display bg-foreground text-background rounded-xl" >
>.{file.name.split(".").slice(-1)}</span <div
> class="bg-cover bg-center w-full h-full"
<span>to</span> style="background-image: url({file.blobUrl});"
<select ></div>
class="font-display border-2 border-solid border-foreground-muted-alt rounded-xl p-2 focus:!outline-none" <div class="absolute top-0 right-0 w-5/6 h-full">
bind:value={files.conversionTypes[i]} <ProgressiveBlur
> direction="right"
{#each converters[0].supportedFormats as conversionType} endIntensity={64}
<option value={conversionType}>{conversionType}</option> iterations={6}
{/each} fadeTo="rgba(255, 255, 255, 0.8)"
</select> />
</div>
</div> </div>
</div> </div>
{/each} {/each}