refactor: project component
This commit is contained in:
parent
f217dd7825
commit
9c97c6aeab
1 changed files with 133 additions and 102 deletions
|
@ -1,5 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { ChevronDown, ChevronUp, ExternalLink } from 'lucide-svelte';
|
||||
import { ChevronDown, ChevronUp, ExternalLink, BookText } from 'lucide-svelte';
|
||||
|
||||
import LanguageStats from './languages.svelte';
|
||||
import TiltCard from './tiltcard.svelte';
|
||||
|
||||
import { Github, Gitlab, Code, GitBranch } from 'lucide-svelte';
|
||||
|
@ -16,7 +18,7 @@
|
|||
}
|
||||
|
||||
export let title: string;
|
||||
export let description_preview: string;
|
||||
export let headline: string;
|
||||
export let description: string;
|
||||
export let icon: string;
|
||||
export let repository: ProjectLink = { label: 'Forgejo', url: '', iconComponent: GitBranch };
|
||||
|
@ -25,88 +27,113 @@
|
|||
{ label: 'Gitlab', url: '', iconComponent: Gitlab },
|
||||
{ label: 'Codeberg', url: '', iconComponent: Code }
|
||||
];
|
||||
export let documentation: string = '';
|
||||
export let gallery: string[] = [];
|
||||
export let features: string[] = [];
|
||||
export let languages: Language[] = [];
|
||||
export let graphicsApis: string[] = [];
|
||||
export let cicd: string[] = [];
|
||||
|
||||
let expansionStage = 0; // 0: collapsed, 1: description, 2: gallery, 3: source code
|
||||
const MAX_STAGE = 3;
|
||||
const MAX_STAGE = 1;
|
||||
|
||||
function toggleExpansion() {
|
||||
expansionStage = expansionStage >= MAX_STAGE ? 0 : expansionStage + 1;
|
||||
}
|
||||
|
||||
import { slide } from 'svelte/transition';
|
||||
|
||||
let tiltX = 0;
|
||||
let tiltY = 0;
|
||||
|
||||
function handleMouseMove(event: MouseEvent) {
|
||||
const card = event.currentTarget as HTMLElement;
|
||||
const rect = card.getBoundingClientRect();
|
||||
const x = event.clientX - rect.left;
|
||||
const y = event.clientY - rect.top;
|
||||
const centerX = rect.width / 2;
|
||||
const centerY = rect.height / 2;
|
||||
const percentX = (x - centerX) / centerX;
|
||||
const percentY = (centerY - y) / centerY; // Invert Y for natural tilt
|
||||
const maxTilt = 2;
|
||||
tiltX = percentY * maxTilt;
|
||||
tiltY = percentX * maxTilt;
|
||||
}
|
||||
|
||||
function handleMouseLeave() {
|
||||
tiltX = 0;
|
||||
tiltY = 0;
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
class="bg-card border-border relative mx-auto w-full max-w-2xl rounded-lg border transition-all duration-300 ease-out hover:scale-[1.01] hover:shadow-[5px_5px_5px_#000000]"
|
||||
style="transform: perspective(1000px) rotateX({tiltX}deg) rotateY({tiltY}deg);"
|
||||
on:mousemove={handleMouseMove}
|
||||
on:mouseleave={handleMouseLeave}
|
||||
>
|
||||
<div class="p-6">
|
||||
<div class="mb-4 flex items-center gap-4">
|
||||
<div class="flex-shrink-0">
|
||||
<TiltCard imageSrc={icon} imageAlt="{title} icon" width="256px" height="256px" />
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<TiltCard imageSrc="/light_text.svg" imageAlt="{title} icon" width="auto" height="auto" />
|
||||
</div>
|
||||
</div>
|
||||
{#if icon !== ''}
|
||||
<div class="mb-4 flex items-center gap-4">
|
||||
<div class="flex-shrink-0">
|
||||
<TiltCard imageSrc={icon} imageAlt="{title} icon" width="256px" height="256px" />
|
||||
</div>
|
||||
<div class="m-4 min-w-0 flex-1">
|
||||
<div>
|
||||
<h1 class="font-mono text-4xl uppercase">{title}</h1>
|
||||
|
||||
<p class="text-muted-foreground leading-relaxed">
|
||||
{expansionStage >= 1 ? description : description_preview}
|
||||
</p>
|
||||
<div class="border-border w-full self-center border-t p-1"></div>
|
||||
<p class="text-muted-foreground leading-relaxed">
|
||||
{headline}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="m-4 min-w-0 flex-1">
|
||||
<div>
|
||||
<h1 class="font-mono text-3xl">{title}</h1>
|
||||
|
||||
{#if expansionStage >= 1}
|
||||
<div class="py-4" transition:slide={{ duration: 300 }}>
|
||||
{#if features.length > 0 || languages.length > 0 || graphicsApis.length > 0 || cicd.length > 0}
|
||||
{#if features.length > 0}
|
||||
<h3 class="text-muted-foreground mb-3 text-sm font-semibold uppercase tracking-wide">
|
||||
Features
|
||||
</h3>
|
||||
<ul class="text-muted-foreground mb-2 list-disc pl-5">
|
||||
{#each features as feature}
|
||||
<li>{feature}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
<h4 class="mb-1 font-medium">Technology behind {title}:</h4>
|
||||
{#if languages.length > 0}
|
||||
<h5 class="mb-1 text-sm">Languages:</h5>
|
||||
<div class="mb-2 flex flex-wrap gap-2">
|
||||
{#each languages as lang}
|
||||
<span class="flex items-center gap-1">
|
||||
{#if lang.icon}
|
||||
<img src={lang.icon} alt="{lang.name} icon" class="ivert h-4 w-4" />
|
||||
{/if}
|
||||
{lang.name}
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if graphicsApis.length > 0}
|
||||
<h5 class="mb-1 text-sm">Graphics APIs:</h5>
|
||||
<div class="mb-2 flex flex-wrap gap-2">
|
||||
{#each graphicsApis as api}
|
||||
<span>{api}</span>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if cicd.length > 0}
|
||||
<h5 class="mb-1 text-sm">CICD:</h5>
|
||||
<div class="mb-2 flex flex-wrap gap-2">
|
||||
{#each cicd as tool}
|
||||
<span>{tool}</span>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
<div class="border-border w-full self-center border-t p-1"></div>
|
||||
<p class="text-muted-foreground leading-relaxed">
|
||||
{headline}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if expansionStage >= 2 && gallery.length > 0}
|
||||
<div transition:slide={{ duration: 300 }}>
|
||||
{#if expansionStage >= 1}
|
||||
<div transition:slide={{ duration: 500 }} class="ease-out">
|
||||
<p class="text-muted-foreground pb-8 leading-relaxed">
|
||||
{description}
|
||||
</p>
|
||||
|
||||
<!-- LANGUAGES -->
|
||||
|
||||
<h3 class="text-muted-foreground mb-3 text-sm font-semibold uppercase tracking-wide">
|
||||
Supported Graphics-APIs
|
||||
</h3>
|
||||
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<p>Vulkan</p>
|
||||
|
||||
<p>DirectX</p>
|
||||
|
||||
<p>Metal</p>
|
||||
</div>
|
||||
|
||||
<h3 class="text-muted-foreground mb-3 text-sm font-semibold uppercase tracking-wide">
|
||||
Features
|
||||
</h3>
|
||||
|
||||
<!-- FEATURES -->
|
||||
<ul class="text-muted-foreground mb-2 list-disc pl-5">
|
||||
{#each features as feature}
|
||||
<li>{feature}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<h4 class="mb-1 font-medium">{title}'s Tech Stack':</h4>
|
||||
|
||||
<!-- GALLERY -->
|
||||
|
||||
<h3 class="text-muted-foreground mb-3 text-sm font-semibold uppercase tracking-wide">
|
||||
Gallery
|
||||
</h3>
|
||||
|
@ -115,47 +142,55 @@
|
|||
<img src={img} alt="{title} screenshot" class="rounded object-cover" />
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if expansionStage >= 3}
|
||||
<div transition:slide={{ duration: 300 }}>
|
||||
<div class="border-border border-t pt-4">
|
||||
<div class="flex items-start justify-start gap-0">
|
||||
<div class="flex-none">
|
||||
<h3 class="text-muted-foreground mb-2 text-sm font-semibold uppercase tracking-wide">
|
||||
Repository
|
||||
</h3>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<div class="mx-auto w-full py-4">
|
||||
<LanguageStats repositoryUrl="https://github.com/light7734/light" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-start justify-start gap-0">
|
||||
<div class="flex-none">
|
||||
<h3 class="text-muted-foreground mb-2 text-sm font-semibold uppercase tracking-wide">
|
||||
Source
|
||||
</h3>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<a
|
||||
href={repository.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-accent-foreground flex items-center gap-1 transition-colors duration-200 hover:underline"
|
||||
>
|
||||
<svelte:component this={repository.iconComponent} class="h-4 w-4" />
|
||||
{repository.label}
|
||||
</a>
|
||||
|
||||
<a
|
||||
href={documentation}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-accent-foreground flex items-center gap-1 transition-colors duration-200 hover:underline"
|
||||
>
|
||||
<BookText class="h-4 w-4" />
|
||||
Documentation
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-border mx-4 h-8 self-center border-l"></div>
|
||||
<div class="flex-none">
|
||||
<h3 class="text-muted-foreground mb-2 text-sm font-semibold uppercase tracking-wide">
|
||||
Mirrors
|
||||
</h3>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
{#each mirrors as link}
|
||||
<a
|
||||
href={repository.url}
|
||||
href={link.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-accent-foreground flex items-center gap-1 transition-colors duration-200 hover:underline"
|
||||
>
|
||||
<svelte:component this={repository.iconComponent} class="h-4 w-4" />
|
||||
{repository.label}
|
||||
<svelte:component this={link.iconComponent} class="h-4 w-4" />
|
||||
{link.label}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-border mx-4 h-8 self-center border-l"></div>
|
||||
<div class="flex-none">
|
||||
<h3 class="text-muted-foreground mb-2 text-sm font-semibold uppercase tracking-wide">
|
||||
Mirrors
|
||||
</h3>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
{#each mirrors as link}
|
||||
<a
|
||||
href={link.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-accent-foreground flex items-center gap-1 transition-colors duration-200 hover:underline"
|
||||
>
|
||||
<svelte:component this={link.iconComponent} class="h-4 w-4" />
|
||||
{link.label}
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -177,11 +212,7 @@
|
|||
/>
|
||||
<span class="font-bold">
|
||||
{#if expansionStage === 0}
|
||||
FEATURES
|
||||
{:else if expansionStage === 1}
|
||||
GALLERY
|
||||
{:else if expansionStage === 2}
|
||||
SOURCE
|
||||
DETAILS
|
||||
{/if}
|
||||
</span>
|
||||
<ChevronDown
|
||||
|
|
Loading…
Add table
Reference in a new issue