mirror of
https://github.com/hyprwm/hyprland-website.git
synced 2026-05-05 15:48:00 +02:00
update to Svelte 5
This commit is contained in:
parent
29b7c040d3
commit
bc6151c57e
50 changed files with 1073 additions and 864 deletions
20
package.json
20
package.json
|
|
@ -3,6 +3,7 @@
|
|||
"version": "1.1.0",
|
||||
"description": "Website for Hyprland - Hyprland - A wayland compositor with the looks.",
|
||||
"repository": "github:hyprwm/hyprland-website",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
|
|
@ -13,7 +14,7 @@
|
|||
"keywords": [
|
||||
"hyprland"
|
||||
],
|
||||
"author": "",
|
||||
"author": "VDawg",
|
||||
"license": "BSD-3-Clause",
|
||||
"private": "true",
|
||||
"os": [
|
||||
|
|
@ -27,26 +28,25 @@
|
|||
"@iconify/json": "^2.2.220",
|
||||
"@interactjs/types": "^1.10.27",
|
||||
"@sveltejs/adapter-static": "^3.0.2",
|
||||
"@sveltejs/kit": "^2.5.17",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
||||
"@sveltejs/kit": "^2.39.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^4.0.4",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"eslint": "^9.5.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-svelte": "^2.40.0",
|
||||
"eslint-plugin-svelte": "^2.46.1",
|
||||
"mdsvex": "^0.11.2",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.3.2",
|
||||
"prettier-plugin-svelte": "^3.2.4",
|
||||
"prettier-plugin-svelte": "^3.4.0",
|
||||
"simplex-noise": "^4.0.1",
|
||||
"svelte": "^4.2.18",
|
||||
"svelte-check": "^3.8.1",
|
||||
"svelte": "^5.38.10",
|
||||
"svelte-check": "^4.3.1",
|
||||
"tailwindcss": "^3.4.4",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"typescript": "^5.4.5",
|
||||
"typescript": "^5.9.2",
|
||||
"unplugin-icons": "^0.19.0",
|
||||
"vite": "^5.3.1"
|
||||
"vite": "^5.4.20"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@fontsource-variable/inter": "^5.2.5",
|
||||
"@fontsource/ibm-plex-mono": "^5.0.13",
|
||||
|
|
|
|||
906
pnpm-lock.yaml
generated
906
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -407,7 +407,7 @@
|
|||
"size": 85
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/321614457623019520/abd926b4f0c2ba37d6ee9359ad0599d2.webp",
|
||||
"image": "https://xcdn.discordapp.com/avatars/321614457623019520/abd926b4f0c2ba37d6ee9359ad0599d2.webp",
|
||||
"class": "outline-orange-500",
|
||||
"coordinates": [
|
||||
962,
|
||||
|
|
|
|||
|
|
@ -1,52 +1,61 @@
|
|||
<script>
|
||||
<script lang="ts">
|
||||
import {
|
||||
BehaviorSubject,
|
||||
Subject,
|
||||
auditTime,
|
||||
combineLatest,
|
||||
debounceTime,
|
||||
delay,
|
||||
distinctUntilChanged,
|
||||
filter,
|
||||
map,
|
||||
of,
|
||||
startWith,
|
||||
switchMap,
|
||||
timer
|
||||
timer,
|
||||
type Observable
|
||||
} from 'rxjs'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import { spring } from 'svelte/motion'
|
||||
import { Spring } from 'svelte/motion'
|
||||
|
||||
/** The start position of the gradient. */
|
||||
export let startPosition = [-1000, -1000]
|
||||
type Props = {
|
||||
/** The start position of the gradient. */
|
||||
startPosition?: [number, number]
|
||||
class?: string
|
||||
}
|
||||
|
||||
let { class: className, startPosition = [-1000, -1000] }: Props = $props()
|
||||
|
||||
let wrapperElement: HTMLDivElement | undefined = $state(undefined)
|
||||
|
||||
/** @type {HTMLDivElement}*/
|
||||
let wrapperElement = undefined
|
||||
/** @type {import('rxjs').BehaviorSubject<boolean>}*/
|
||||
const isMouseOver$ = new BehaviorSubject(false).pipe(
|
||||
// Do not harshly stop updating the gradient when the mouse leaves, but wait a bit
|
||||
switchMap((isTrue) => (isTrue ? of(isTrue) : timer(1500).pipe(map(() => false)))),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
) as BehaviorSubject<boolean>
|
||||
|
||||
/** @type {import('rxjs').BehaviorSubject<number>}*/
|
||||
const gradientSize$ = new BehaviorSubject().pipe(
|
||||
const gradientSize$: BehaviorSubject<number> = new BehaviorSubject(undefined).pipe(
|
||||
// Debounce resize events with some high number for performance
|
||||
debounceTime(1),
|
||||
map(() => wrapperElement.getBoundingClientRect().width * 3),
|
||||
auditTime(16),
|
||||
map(() => (wrapperElement?.getBoundingClientRect().width ?? 0) * 3),
|
||||
startWith(800)
|
||||
)
|
||||
/** @type {import('rxjs').Subject< {clientX: number, clientY: number} >}*/
|
||||
const mousePosition$ = new Subject()
|
||||
) as BehaviorSubject<number>
|
||||
const mousePosition$: Subject<{ clientX: number; clientY: number }> = new Subject()
|
||||
|
||||
const gradientPosition$ = combineLatest([mousePosition$, gradientSize$, isMouseOver$]).pipe(
|
||||
const gradientPosition$: Observable<[number, number]> = combineLatest([
|
||||
mousePosition$,
|
||||
gradientSize$,
|
||||
isMouseOver$
|
||||
]).pipe(
|
||||
filter(([_, __, isMouseOver]) => isMouseOver),
|
||||
map(([{ clientX, clientY }, gradientSize]) => {
|
||||
const { x, y } = wrapperElement?.getBoundingClientRect() ?? { x: 0, y: 0 }
|
||||
return [clientX - x - gradientSize * 0.5, clientY - y - gradientSize * 0.5]
|
||||
return [clientX - x - gradientSize * 0.5, clientY - y - gradientSize * 0.5] as [
|
||||
number,
|
||||
number
|
||||
]
|
||||
}),
|
||||
startWith(startPosition)
|
||||
)
|
||||
const gradientWiggle = spring(startPosition, { damping: 0.95, stiffness: 0.1 })
|
||||
const gradientWiggle = new Spring(startPosition, { damping: 0.95, stiffness: 0.1 })
|
||||
const subscription = gradientPosition$.subscribe((data) => gradientWiggle.set(data))
|
||||
|
||||
onDestroy(() => {
|
||||
|
|
@ -60,16 +69,16 @@
|
|||
hasJustMounted = false
|
||||
})
|
||||
|
||||
$: {
|
||||
$effect(() => {
|
||||
if (!$isMouseOver$) {
|
||||
globalThis.document?.removeEventListener('mousemove', track)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function resizeGradient() {
|
||||
if (hasJustMounted || !isMouseOver$) return
|
||||
|
||||
gradientSize$.next()
|
||||
gradientSize$.next(0)
|
||||
}
|
||||
|
||||
function startTrackingMouse() {
|
||||
|
|
@ -78,7 +87,7 @@
|
|||
globalThis.document?.addEventListener('mousemove', track)
|
||||
}
|
||||
|
||||
function track({ clientX, clientY }) {
|
||||
function track({ clientX, clientY }: { clientX: number; clientY: number }) {
|
||||
mousePosition$.next({ clientX, clientY })
|
||||
}
|
||||
|
||||
|
|
@ -90,9 +99,9 @@
|
|||
<svelte:window on:resize={resizeGradient} />
|
||||
|
||||
<div
|
||||
class={$$props.class + ' wrapper'}
|
||||
on:mouseenter={startTrackingMouse}
|
||||
on:mouseleave={({ clientX, clientY, currentTarget }) => {
|
||||
class={className + ' wrapper'}
|
||||
onmouseenter={startTrackingMouse}
|
||||
onmouseleave={({ clientX, clientY, currentTarget }) => {
|
||||
const { x, width, y, height } = currentTarget.getBoundingClientRect()
|
||||
const isMouseStillOver =
|
||||
x <= clientX && y <= clientY && x + width > clientX && y + height > clientY
|
||||
|
|
@ -103,8 +112,8 @@
|
|||
>
|
||||
<div
|
||||
class="gradient"
|
||||
style:--x={$gradientWiggle.at(0) + 'px'}
|
||||
style:--y={$gradientWiggle.at(1) + 'px'}
|
||||
style:--x={gradientWiggle.current.at(0) + 'px'}
|
||||
style:--y={gradientWiggle.current.at(1) + 'px'}
|
||||
style:--size={$gradientSize$ + 'px'}
|
||||
></div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,37 @@
|
|||
<script lang="ts">
|
||||
import { cn } from '$lib/Helper'
|
||||
|
||||
export let size: 'md' | 'lg' | 'xl' = 'md'
|
||||
export let type: 'primary' | 'outline' | 'fancyOutline' = 'primary'
|
||||
interface Props {
|
||||
size?: 'md' | 'lg' | 'xl'
|
||||
type?: 'primary' | 'outline' | 'fancyOutline'
|
||||
href?: string | undefined
|
||||
newTab?: boolean
|
||||
children?: import('svelte').Snippet
|
||||
onClick?: () => void
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export let href: string | undefined = undefined
|
||||
export let newTab = false
|
||||
let {
|
||||
size = 'md',
|
||||
type = 'primary',
|
||||
href = undefined,
|
||||
newTab = false,
|
||||
children,
|
||||
onClick,
|
||||
...rest
|
||||
}: Props = $props()
|
||||
|
||||
$: classes = cn(
|
||||
'animate rounded text-sm font-bold hover:scale-[1.01] active:scale-100',
|
||||
'primary' == type && 'bg-slate-200 text-black',
|
||||
'outline' == type && 'bg-transparent text-white outline outline-2 outline-slate-200',
|
||||
'fancyOutline' == type && 'fancy',
|
||||
'md' == size && 'min-w-[5.5rem] px-4 py-2.5',
|
||||
'lg' == size && 'min-w-[5.5rem] px-6 py-3',
|
||||
'xl' == size && 'min-w-[5.5rem] px-8 py-3.5',
|
||||
$$restProps.class
|
||||
let classes = $derived(
|
||||
cn(
|
||||
'animate rounded text-sm font-bold hover:scale-[1.01] active:scale-100',
|
||||
'primary' == type && 'bg-slate-200 text-black',
|
||||
'outline' == type && 'bg-transparent text-white outline outline-2 outline-slate-200',
|
||||
'fancyOutline' == type && 'fancy',
|
||||
'md' == size && 'min-w-[5.5rem] px-4 py-2.5',
|
||||
'lg' == size && 'min-w-[5.5rem] px-6 py-3',
|
||||
'xl' == size && 'min-w-[5.5rem] px-8 py-3.5',
|
||||
rest.class
|
||||
)
|
||||
)
|
||||
</script>
|
||||
|
||||
|
|
@ -23,38 +39,39 @@
|
|||
<div class="relative max-w-max">
|
||||
<svelte:element
|
||||
this={href ? 'a' : 'button'}
|
||||
{...$$restProps}
|
||||
{...rest}
|
||||
{href}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
target={newTab ? '_blank' : undefined}
|
||||
class={classes}
|
||||
on:click
|
||||
onclick={onClick}
|
||||
>
|
||||
<slot>NO LABEL PROVIDED</slot>
|
||||
{#if children}{@render children()}{:else}NO LABEL PROVIDED{/if}
|
||||
</svelte:element>
|
||||
<span
|
||||
class="fancy-bg absolute inset-0 -z-10 h-full w-[110%] min-w-[5rem] scale-y-75 bg-cyan-500/90 px-4 py-2 blur-xl"
|
||||
style="--easing: x; --duration: 8s;"
|
||||
/>
|
||||
></span>
|
||||
<span
|
||||
class="fancy-bg absolute inset-0 -z-10 h-full w-[110%] min-w-[5rem] scale-y-75 bg-secondary/90 px-4 py-2 blur-xl"
|
||||
style="--easing: y; --duration: 8s;"
|
||||
/>
|
||||
></span>
|
||||
<span
|
||||
class="fancy-bg absolute inset-0 -z-10 h-full w-[110%] min-w-[5rem] scale-y-75 bg-purple-500/90 px-4 py-2 blur-xl"
|
||||
style="--easing: z;--duration: 8s;"
|
||||
/>
|
||||
></span>
|
||||
</div>
|
||||
{:else}
|
||||
<svelte:element
|
||||
this={href ? 'a' : 'button'}
|
||||
{...$$restProps}
|
||||
{...rest}
|
||||
{href}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
class={classes}
|
||||
on:click><slot>NO LABEL PROVIDED</slot></svelte:element
|
||||
onclick={onClick}
|
||||
>{#if children}{@render children()}{:else}NO LABEL PROVIDED{/if}</svelte:element
|
||||
>
|
||||
{/if}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,28 @@
|
|||
<script>
|
||||
import { run, createBubbler, handlers } from 'svelte/legacy';
|
||||
|
||||
const bubble = createBubbler();
|
||||
import clsx from 'clsx'
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import { cardsContext } from '$lib/components/CardsContainer.svelte'
|
||||
import { spring } from 'svelte/motion'
|
||||
import { getIsMobile } from '$lib/Helper.ts'
|
||||
/** @type {'cyan' | 'purple'}*/
|
||||
export let color = 'cyan'
|
||||
/** @type {number | number}*/
|
||||
export let gradientOpacity = undefined
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {'cyan' | 'purple'} [color]
|
||||
* @property {number | number} [gradientOpacity]
|
||||
* @property {import('svelte').Snippet} [children]
|
||||
*/
|
||||
|
||||
/** @type {Props & { [key: string]: any }} */
|
||||
let { color = 'cyan', gradientOpacity = undefined, children, ...rest } = $props();
|
||||
|
||||
const { mouseCoordinates$, isHoverCards, enableBorders = true } = getContext(cardsContext)
|
||||
|
||||
/** @type HTMLDivElement */
|
||||
let container
|
||||
let container = $state()
|
||||
let isMobile = false
|
||||
|
||||
const damping = 0.2
|
||||
|
|
@ -25,15 +35,10 @@
|
|||
const bounceBack = 2
|
||||
const soft = 0.8
|
||||
|
||||
let isMouseOver = false
|
||||
let isMouseOver = $state(false)
|
||||
/** Has the mouse entered and not left*/
|
||||
let hasMouseEntered = false
|
||||
|
||||
$: {
|
||||
if (container && $mouseCoordinates$?.x !== undefined) {
|
||||
updateGradient()
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
isMobile = getIsMobile()
|
||||
|
|
@ -75,35 +80,38 @@
|
|||
if ($mouseCoordinates$.y > rectY + height) fillX.set(rectY - height - bounceBack, { soft })
|
||||
else fillY.set(normY)
|
||||
}
|
||||
run(() => {
|
||||
if (container && $mouseCoordinates$?.x !== undefined) {
|
||||
updateGradient()
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={clsx('card group ', $$restProps.class)}
|
||||
class={clsx('card group ', rest.class)}
|
||||
style:--x={$fillX}
|
||||
style:--y={$fillY}
|
||||
style:--borderX={enableBorders && $borderX}
|
||||
style:--borderY={enableBorders && $borderY}
|
||||
class:isHoverCards={$isHoverCards}
|
||||
bind:this={container}
|
||||
on:mouseenter={() => (isMouseOver = true)}
|
||||
on:mouseleave={() => {
|
||||
onmouseenter={handlers(() => (isMouseOver = true), bubble('mouseenter'))}
|
||||
onmouseleave={handlers(() => {
|
||||
isMouseOver = false
|
||||
updateGradient()
|
||||
}}
|
||||
}, bubble('mouseleave'))}
|
||||
class:purpleGradient={color === 'purple'}
|
||||
role="contentinfo"
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
>
|
||||
<div
|
||||
class="absolute inset-0 z-10 m-0.5 size-full min-h-0 min-w-0 grow overflow-hidden rounded-3xl"
|
||||
>
|
||||
<slot>Nothing in the slot here</slot>
|
||||
{#if children}{@render children()}{:else}Nothing in the slot here{/if}
|
||||
</div>
|
||||
<div class="gradient max-sm:hidden" style:opacity={gradientOpacity} />
|
||||
<div class="gradient_black max-sm:hidden" />
|
||||
<div class="gradient max-sm:hidden" style:opacity={gradientOpacity}></div>
|
||||
<div class="gradient_black max-sm:hidden"></div>
|
||||
{#if enableBorders}
|
||||
<div class="border-gradient max-sm:hidden" />
|
||||
<div class="border-gradient max-sm:hidden"></div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<script context="module">
|
||||
<script module>
|
||||
export const cardsContext = Symbol('mouseContext')
|
||||
</script>
|
||||
|
||||
|
|
@ -9,13 +9,20 @@
|
|||
import { onMount, setContext } from 'svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
export let enableBorders = true
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {boolean} [enableBorders]
|
||||
* @property {import('svelte').Snippet} [children]
|
||||
*/
|
||||
|
||||
/** @type {Props & { [key: string]: any }} */
|
||||
let { enableBorders = true, children, ...rest } = $props();
|
||||
|
||||
const fps = 1000 / 60
|
||||
|
||||
/** @type {HTMLElement}*/
|
||||
let containerElement
|
||||
let isMobile = false
|
||||
let containerElement = $state()
|
||||
let isMobile = $state(false)
|
||||
|
||||
const context = setContext(cardsContext, {
|
||||
mouseCoordinates$: new BehaviorSubject({ x: 0, y: 0 }).pipe(throttleTime(fps)),
|
||||
|
|
@ -43,11 +50,11 @@
|
|||
</script>
|
||||
|
||||
<div
|
||||
class={$$restProps.class}
|
||||
class={rest.class}
|
||||
bind:this={containerElement}
|
||||
role="contentinfo"
|
||||
on:mouseenter={!isMobile && onMouseEnter}
|
||||
on:mouseleave={!isMobile && onMouseLeave}
|
||||
onmouseenter={!isMobile && onMouseEnter}
|
||||
onmouseleave={!isMobile && onMouseLeave}
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,22 @@
|
|||
<script lang="ts">
|
||||
export let href: string | undefined = undefined
|
||||
export let isExternal: boolean = true
|
||||
interface Props {
|
||||
href?: string | undefined;
|
||||
isExternal?: boolean;
|
||||
children?: import('svelte').Snippet;
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
let { href = undefined, isExternal = true, children, ...rest }: Props = $props();
|
||||
|
||||
let isExternalProp = isExternal ? { target: '_blank', rel: 'noopener noreferrer' } : {}
|
||||
</script>
|
||||
|
||||
{#if href}
|
||||
<a {href} {...isExternalProp} class="cursor-pointer {$$restProps.class}">
|
||||
<slot />
|
||||
<a {href} {...isExternalProp} class="cursor-pointer {rest.class}">
|
||||
{@render children?.()}
|
||||
</a>
|
||||
{:else}
|
||||
<div class={$$restProps.class}>
|
||||
<slot />
|
||||
<div class={rest.class}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
<!-- @migration-task Error while migrating Svelte code: Cannot split a chunk that has already been edited (28:12 – "on:click={$$slots.default ? undefined : copyCommand}") -->
|
||||
<script>
|
||||
import { onDestroy } from 'svelte'
|
||||
import ClipboardIcon from '~icons/mingcute/copy-2-line'
|
||||
|
|
|
|||
|
|
@ -1,56 +1,86 @@
|
|||
<script lang="ts">
|
||||
import clsx from 'clsx'
|
||||
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import { spring, type Spring } from 'svelte/motion'
|
||||
import { convertStoreToObservable, lerp } from '$lib/Helper'
|
||||
import { inview } from 'svelte-inview'
|
||||
import { throttleTime } from 'rxjs'
|
||||
import type { Interactable } from '@interactjs/types/index'
|
||||
import { lerp } from '$lib/Helper'
|
||||
|
||||
export let image: string
|
||||
export let containerClass: string = ''
|
||||
export let size: number
|
||||
export let coordinates: readonly [number, number]
|
||||
type Props = {
|
||||
image: string
|
||||
containerClass?: string
|
||||
size: number
|
||||
coordinates: readonly [number, number]
|
||||
class?: string
|
||||
|
||||
export let quote: string | undefined = undefined
|
||||
quote?: string | undefined
|
||||
|
||||
export let isAnimating = true
|
||||
isAnimating?: boolean
|
||||
|
||||
export let element: HTMLElement | undefined = undefined
|
||||
export let imageWrapper: HTMLDivElement | undefined = undefined
|
||||
export let imageElement: HTMLImageElement | undefined = undefined
|
||||
export let style: string | undefined = undefined
|
||||
export let spawnDelay = 0
|
||||
export let spawnInstanly = false
|
||||
/**
|
||||
* Usually just the size / biggestSize
|
||||
* Goes from 0 - 1
|
||||
*/
|
||||
export let weight: number
|
||||
export let getRestrictionElement: (() => HTMLElement) | undefined = undefined
|
||||
element?: HTMLElement | undefined
|
||||
imageWrapper?: HTMLDivElement | undefined
|
||||
imageElement?: HTMLImageElement | undefined
|
||||
style?: string | undefined
|
||||
spawnDelay?: number
|
||||
spawnInstanly?: boolean
|
||||
/**
|
||||
* Usually just the size / biggestSize
|
||||
* Goes from 0 - 1
|
||||
*/
|
||||
weight: number
|
||||
getRestrictionElement?: (() => HTMLElement) | undefined
|
||||
onClick?: () => void
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
enteredView: {
|
||||
enteredView?: (data: {
|
||||
dragCoordinates: Spring<readonly [number, number]>
|
||||
imageElement: HTMLImageElement
|
||||
element: HTMLElement
|
||||
delay: number
|
||||
}
|
||||
dragged: readonly [number, number]
|
||||
dragStart: any
|
||||
dragEnd: any
|
||||
hover: any
|
||||
}>()
|
||||
}) => void
|
||||
dragged?: (data: readonly [number, number]) => void
|
||||
dragStart?: (data: any) => void
|
||||
dragEnd?: (data: any) => void
|
||||
hover?: (data: any) => void
|
||||
|
||||
children?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
let {
|
||||
image,
|
||||
class: className,
|
||||
containerClass = '',
|
||||
size,
|
||||
coordinates,
|
||||
quote = undefined,
|
||||
isAnimating = true,
|
||||
element = undefined,
|
||||
imageWrapper = undefined,
|
||||
imageElement = undefined,
|
||||
style = undefined,
|
||||
spawnDelay = 0,
|
||||
spawnInstanly = false,
|
||||
/**
|
||||
* Usually just the size / biggestSize
|
||||
* Goes from 0 - 1
|
||||
*/
|
||||
weight,
|
||||
getRestrictionElement = undefined,
|
||||
onClick,
|
||||
dragEnd,
|
||||
dragStart,
|
||||
enteredView,
|
||||
hover,
|
||||
children
|
||||
}: Props = $props()
|
||||
|
||||
const dragCoordinates = spring([0, 0] as readonly [number, number], {
|
||||
damping: lerp(0.2, 0.03, weight),
|
||||
stiffness: lerp(0.2, 0.01, weight),
|
||||
// stiffness: lerp(0.81, 0.9, relativeSize),
|
||||
precision: 0.001
|
||||
})
|
||||
|
||||
let hasEnteredView = false
|
||||
let hasImageLoaded = false
|
||||
let hasEnteredView = $state(false)
|
||||
let hasImageLoaded = $state(false)
|
||||
|
||||
let interactionjs: Interactable
|
||||
|
||||
|
|
@ -63,7 +93,7 @@
|
|||
() => {
|
||||
hasEnteredView = true
|
||||
|
||||
dispatch('enteredView', {
|
||||
enteredView?.({
|
||||
dragCoordinates,
|
||||
imageElement: imageElement!,
|
||||
element: element!,
|
||||
|
|
@ -79,18 +109,14 @@
|
|||
inertia: { resistance: lerp(5, 200, weight) },
|
||||
listeners: {
|
||||
move({ dx, dy }) {
|
||||
dragCoordinates.update(([x, y]) => {
|
||||
x += dx
|
||||
y += dy
|
||||
return [x, y]
|
||||
})
|
||||
dragCoordinates.update(([x, y]) => [x + dx, y + dy])
|
||||
},
|
||||
|
||||
start(event) {
|
||||
dispatch('dragStart', event)
|
||||
dragStart?.(event)
|
||||
},
|
||||
end(event) {
|
||||
dispatch('dragEnd', event)
|
||||
dragEnd?.(event)
|
||||
}
|
||||
},
|
||||
modifiers: getRestrictionElement
|
||||
|
|
@ -105,29 +131,21 @@
|
|||
})
|
||||
}
|
||||
|
||||
const draggedSubscription = convertStoreToObservable(dragCoordinates)
|
||||
.pipe(throttleTime(80))
|
||||
.subscribe((drag) => {
|
||||
const displayedPosition = getDisplayedPosition(coordinates, drag)
|
||||
dispatch('dragged', displayedPosition)
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
// Nesecarry as the load image event might not get fired when its already loaded ( for example after a page reload )
|
||||
hasImageLoaded = hasImageLoaded || !!imageElement?.complete
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
draggedSubscription.unsubscribe()
|
||||
interactionjs?.unset()
|
||||
})
|
||||
|
||||
function getDisplayedPosition(
|
||||
origin: readonly [x: number, y: number],
|
||||
dragCoordinates: readonly [x: number, y: number]
|
||||
) {
|
||||
return [origin[0] + dragCoordinates[0], origin[1] + dragCoordinates[1]] as const
|
||||
}
|
||||
// function getDisplayedPosition(
|
||||
// origin: readonly [x: number, y: number],
|
||||
// dragCoordinates: readonly [x: number, y: number]
|
||||
// ) {
|
||||
// return [origin[0] + dragCoordinates[0], origin[1] + dragCoordinates[1]] as const
|
||||
// }
|
||||
</script>
|
||||
|
||||
<div
|
||||
|
|
@ -149,18 +167,18 @@
|
|||
style:translate={`calc( ${$dragCoordinates[0]}px ) ${$dragCoordinates[1]}px`}
|
||||
use:inview={{ unobserveOnEnter: true, threshold: 0.2 }}
|
||||
class:_animate={hasImageLoaded && isAnimating && hasEnteredView}
|
||||
on:inview_enter={onViewEnter}
|
||||
on:click
|
||||
oninview_enter={onViewEnter}
|
||||
onclick={onClick}
|
||||
>
|
||||
<div class="" bind:this={imageWrapper}>
|
||||
<img
|
||||
class="group aspect-square h-full w-full touch-none select-none rounded-[50%] object-cover outline outline-4 {$$restProps.class}"
|
||||
class="group aspect-square h-full w-full touch-none select-none rounded-[50%] object-cover outline outline-4 {className}"
|
||||
bind:this={imageElement}
|
||||
on:load={() => (hasImageLoaded = true)}
|
||||
onload={() => (hasImageLoaded = true)}
|
||||
src={image}
|
||||
alt="community profile picture"
|
||||
aria-hidden="true"
|
||||
on:mouseenter={(event) => dispatch('hover', event)}
|
||||
onmouseenter={(event) => hover?.(event)}
|
||||
class:hover:scale-125={!!quote}
|
||||
loading="lazy"
|
||||
referrerpolicy="no-referrer"
|
||||
|
|
@ -170,10 +188,12 @@
|
|||
{...{
|
||||
/* @ts-ignore */
|
||||
}}
|
||||
onerror="this.__error = true"
|
||||
onerror={() => (this.__error = true)}
|
||||
{style}
|
||||
/>
|
||||
<slot />
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if quote}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gradient" aria-hidden="true" />
|
||||
<div class="gradient" aria-hidden="true"></div>
|
||||
</footer>
|
||||
|
||||
<style lang="postcss">
|
||||
|
|
|
|||
|
|
@ -1,25 +1,29 @@
|
|||
<script>
|
||||
import { lerp } from '$lib/Helper.ts'
|
||||
<script lang="ts">
|
||||
import { lerp } from '$lib/Helper'
|
||||
import { createNoise2D } from 'simplex-noise'
|
||||
import { onMount } from 'svelte'
|
||||
import { expoIn } from 'svelte/easing'
|
||||
|
||||
/** Lifespan in milliseconds */
|
||||
export let lifeSpan = 1500
|
||||
export let maxSpeed = 20
|
||||
export let minSpeed = 4
|
||||
export let maxOpacity = 1
|
||||
export let scale = 1
|
||||
type Props = {
|
||||
/** Lifespan in milliseconds */
|
||||
lifeSpan: number
|
||||
maxSpeed: number
|
||||
minSpeed: number
|
||||
maxOpacity: number
|
||||
scale: number
|
||||
}
|
||||
|
||||
let { lifeSpan = 1500, maxSpeed = 20, minSpeed = 4, maxOpacity = 1, scale = 1 }: Props = $props()
|
||||
|
||||
const isDescending = Math.random() > 0.8
|
||||
const wobbliness = lerp(0.0001, 0.004, Math.random())
|
||||
|
||||
const speed = Math.random() * (maxSpeed - minSpeed) + minSpeed
|
||||
|
||||
let x = Math.random() * 30 - 30
|
||||
let y = Math.random() * 2
|
||||
let lifeRemaining = lifeSpan
|
||||
$: lifePercentage = lifeRemaining / lifeSpan
|
||||
let x = $state(Math.random() * 30 - 30)
|
||||
let y = $state(Math.random() * 2)
|
||||
let lifeRemaining = $state(lifeSpan)
|
||||
let lifePercentage = $derived(lifeRemaining / lifeSpan)
|
||||
let timestamp = Date.now()
|
||||
|
||||
const noiseY = createNoise2D()
|
||||
|
|
@ -29,13 +33,14 @@
|
|||
const color = colors.at(Math.ceil(colors.length * Math.random()))
|
||||
|
||||
onMount(() => {
|
||||
let animationId
|
||||
let animationId: number
|
||||
let i = 0
|
||||
|
||||
animate()
|
||||
|
||||
async function animate() {
|
||||
const deltaTime = (timestamp - Date.now()) / 17 // One frame should last roughly 17ms for 60fps
|
||||
// One frame should last roughly 17ms for 60fps
|
||||
const deltaTime = (timestamp - Date.now()) / 17
|
||||
|
||||
x += noiseX(i, 1) * speed * deltaTime * expoIn(lifePercentage)
|
||||
y += noiseY(i, 1) * speed * deltaTime * expoIn(lifePercentage)
|
||||
|
|
|
|||
|
|
@ -3,10 +3,21 @@
|
|||
import type { Sponsor } from '../../routes/api/sponsors/+server'
|
||||
import Clickable from './Clickable.svelte'
|
||||
|
||||
export let sponsor: Sponsor
|
||||
export let showImage = false
|
||||
export let showSlogan = false
|
||||
export let sloganClass = ''
|
||||
interface Props {
|
||||
sponsor: Sponsor;
|
||||
showImage?: boolean;
|
||||
showSlogan?: boolean;
|
||||
sloganClass?: string;
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
let {
|
||||
sponsor,
|
||||
showImage = false,
|
||||
showSlogan = false,
|
||||
sloganClass = '',
|
||||
...rest
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
<Clickable href={sponsor.link} class="flex flex-col items-center gap-4">
|
||||
|
|
@ -15,7 +26,7 @@
|
|||
title={sponsor.name}
|
||||
class={cn(
|
||||
'size-full self-center justify-self-start rounded-md object-contain',
|
||||
$$restProps.class
|
||||
rest.class
|
||||
)}
|
||||
src={sponsor.image}
|
||||
alt={sponsor.name}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,15 @@
|
|||
default: 'text-5xl md:text-7xl lg:text-8xl',
|
||||
small: 'text-3xl md:text-5xl lg:text-6xl'
|
||||
}
|
||||
export let size: keyof typeof sizes = 'default'
|
||||
interface Props {
|
||||
size?: keyof typeof sizes;
|
||||
children?: import('svelte').Snippet;
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
let { size = 'default', children, ...rest }: Props = $props();
|
||||
</script>
|
||||
|
||||
<h1 class={cn('mb-12 font-bold', sizes[size], $$restProps.class)}>
|
||||
<slot>No title given!!!</slot>
|
||||
<h1 class={cn('mb-12 font-bold', sizes[size], rest.class)}>
|
||||
{#if children}{@render children()}{:else}No title given!!!{/if}
|
||||
</h1>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,13 @@
|
|||
<script>
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {import('svelte').Snippet} [children]
|
||||
*/
|
||||
|
||||
/** @type {Props} */
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<p class="mb-3 font-extrabold text-slate-300">
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,13 @@
|
|||
<p class="-mt-4 mb-7 font-extrabold text-slate-300 sm:text-lg {$$restProps.class}">
|
||||
<slot />
|
||||
<script>
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {import('svelte').Snippet} [children]
|
||||
*/
|
||||
|
||||
/** @type {Props & { [key: string]: any }} */
|
||||
let { children, ...rest } = $props();
|
||||
</script>
|
||||
|
||||
<p class="-mt-4 mb-7 font-extrabold text-slate-300 sm:text-lg {rest.class}">
|
||||
{@render children?.()}
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -1,26 +1,43 @@
|
|||
<script>
|
||||
import { animateIn, cn } from '$lib/Helper.ts'
|
||||
|
||||
/** @type { 'left' | 'right' | 'center'} */
|
||||
export let align = 'center'
|
||||
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property { 'left' | 'right' | 'center'} [align]
|
||||
* @property {import('svelte').Snippet} [pre]
|
||||
* @property {import('svelte').Snippet} [title]
|
||||
* @property {import('svelte').Snippet} [subtitle]
|
||||
* @property {import('svelte').Snippet} [children]
|
||||
*/
|
||||
|
||||
$: alignClass =
|
||||
align === 'center'
|
||||
/** @type {Props & { [key: string]: any }} */
|
||||
let {
|
||||
align = 'center',
|
||||
pre,
|
||||
title,
|
||||
subtitle,
|
||||
children,
|
||||
...rest
|
||||
} = $props();
|
||||
|
||||
let alignClass =
|
||||
$derived(align === 'center'
|
||||
? 'text-center items-center'
|
||||
: align === 'left'
|
||||
? 'text-left items-start'
|
||||
: 'text-right items-end'
|
||||
: 'text-right items-end')
|
||||
</script>
|
||||
|
||||
<hgroup
|
||||
use:animateIn={{ slide: 24, fade: 0 }}
|
||||
class={cn('z-10 flex flex-col px-3 ', alignClass, $$restProps.class)}
|
||||
class={cn('z-10 flex flex-col px-3 ', alignClass, rest.class)}
|
||||
>
|
||||
<slot name="pre" />
|
||||
{@render pre?.()}
|
||||
|
||||
<slot name="title">No title given!!!</slot>
|
||||
{#if title}{@render title()}{:else}No title given!!!{/if}
|
||||
|
||||
<slot name="subtitle" />
|
||||
{@render subtitle?.()}
|
||||
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</hgroup>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
<!-- @migration-task Error while migrating Svelte code: $$props is used together with named props in a way that cannot be automatically migrated. -->
|
||||
<script>
|
||||
import clsx from 'clsx'
|
||||
import PlayIcon from '~icons/mingcute/play-circle-line'
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
import ArrowRight from '~icons/mingcute/arrow-right-circle-line'
|
||||
|
||||
import { animateIn, formatDate } from '$lib/Helper.ts'
|
||||
export let entry
|
||||
let { entry } = $props();
|
||||
|
||||
$: link = `/news/${entry.slug}`
|
||||
let link = $derived(`/news/${entry.slug}`)
|
||||
</script>
|
||||
|
||||
<li class="flex" use:animateIn={{ fade: 0, slide: 24 }}>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
<svg viewBox="0 330 1006.49 347.4685344827586" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" style="width: 6rem; height: 2rem;" class={$$props.class} width="1006.49" height="347.4685344827586">
|
||||
<script>
|
||||
/** @type {{ [key: string]: any }} */
|
||||
let { ...props } = $props();
|
||||
</script>
|
||||
|
||||
<svg viewBox="0 330 1006.49 347.4685344827586" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" style="width: 6rem; height: 2rem;" class={props.class} width="1006.49" height="347.4685344827586">
|
||||
<defs>
|
||||
<style>
|
||||
.st0 {
|
||||
|
|
@ -36,7 +41,7 @@
|
|||
.st8 {
|
||||
fill: url(#i);
|
||||
}
|
||||
</style><style class="darkreader darkreader--sync" media="screen"/>
|
||||
</style><style class="darkreader darkreader--sync" media="screen"></style>
|
||||
<linearGradient gradientUnits="userSpaceOnUse" y2="491.29" x2="561.93" y1="593.85" x1="561.93" id="i">
|
||||
<stop stop-color="#00a8f4" offset="0" style="--darkreader-inline-stopcolor: var(--darkreader-background-00a8f4, #0086c3);" data-darkreader-inline-stopcolor=""/>
|
||||
<stop stop-color="#00e5d0" offset="1" style="--darkreader-inline-stopcolor: var(--darkreader-background-00e5d0, #00b7a6);" data-darkreader-inline-stopcolor=""/>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
|
|
@ -4,12 +4,19 @@
|
|||
import './styles.css'
|
||||
import '@fontsource-variable/inter'
|
||||
import '@fontsource/ibm-plex-mono/500.css'
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {import('svelte').Snippet} [children]
|
||||
*/
|
||||
|
||||
/** @type {Props} */
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<Navbar />
|
||||
|
||||
<main class="mx-auto flex min-h-screen w-full flex-col">
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
import Sponsors from './home-slices/SponsorsSlice.svelte'
|
||||
import CommunitySlice from './home-slices/CommunitySlice.svelte'
|
||||
|
||||
export let data
|
||||
let { data } = $props()
|
||||
</script>
|
||||
|
||||
<div class="overflow-hidden">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import { navigating, page } from '$app/stores'
|
||||
import GithubIcon from '~icons/ri/github-fill'
|
||||
import DiscordIcon from '~icons/prime/discord'
|
||||
|
|
@ -9,9 +11,11 @@
|
|||
import { discordLink } from '$lib/constants'
|
||||
import { forgejoLink } from '$lib/constants'
|
||||
|
||||
let isExpanded = false
|
||||
let isExpanded = $state(false)
|
||||
|
||||
$: if ($navigating) isExpanded = false
|
||||
run(() => {
|
||||
if ($navigating) isExpanded = false
|
||||
});
|
||||
|
||||
function toggleExpanded() {
|
||||
isExpanded = !isExpanded
|
||||
|
|
@ -30,7 +34,7 @@
|
|||
|
||||
<button
|
||||
class="z-50 rounded-full bg-black/50 p-2 md:backdrop-blur nav:hidden"
|
||||
on:click={toggleExpanded}
|
||||
onclick={toggleExpanded}
|
||||
aria-label="Open Navigation"
|
||||
>
|
||||
{#if isExpanded}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@
|
|||
import Contest from './Contest.svelte'
|
||||
import FamedRice from './FamedRice.svelte'
|
||||
|
||||
export let data
|
||||
let { data } = $props();
|
||||
</script>
|
||||
|
||||
<div class="fancy-top-gradient" />
|
||||
<div class="fancy-top-gradient"></div>
|
||||
|
||||
<section>
|
||||
<div class="hero-wrapper">
|
||||
|
|
@ -19,10 +19,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<Title>
|
||||
<TitleHeading slot="title" class="">Hall of Fame</TitleHeading>
|
||||
<TitleSubtile slot="subtitle" class="class-w-[40ch]">
|
||||
The chronicles of the triumphant from bygone rice contests held within our Discord
|
||||
</TitleSubtile>
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Hall of Fame</TitleHeading>
|
||||
{/snippet}
|
||||
{#snippet subtitle()}
|
||||
<TitleSubtile class="class-w-[40ch]">
|
||||
The chronicles of the triumphant from bygone rice contests held within our Discord
|
||||
</TitleSubtile>
|
||||
{/snippet}
|
||||
</Title>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,27 @@
|
|||
<script>
|
||||
import { inview } from 'svelte-inview'
|
||||
|
||||
/** @type {string} */
|
||||
export let name
|
||||
/** @type {number} */
|
||||
export let number
|
||||
/** @type {string} */
|
||||
export let date
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {string} name
|
||||
* @property {number} number
|
||||
* @property {string} date
|
||||
* @property {import('svelte').Snippet} [children]
|
||||
*/
|
||||
|
||||
/** @type {Props} */
|
||||
let {
|
||||
name,
|
||||
number,
|
||||
date,
|
||||
children
|
||||
} = $props();
|
||||
|
||||
/** Used to show the background gradient. Show if user scrolls, but do not disable when the user scrolls past from the top. */
|
||||
let enabled = false
|
||||
let enabled = $state(false)
|
||||
|
||||
function setEnabled({ detail }) {
|
||||
// Show the effect when the user scrolls in and keep it enabled until the user scrolls up from it again
|
||||
|
|
@ -31,7 +43,7 @@
|
|||
<header
|
||||
class="flex flex-col items-center gap-2 p-6 mix-blend-color-dodge"
|
||||
use:inview={{ threshold: 0.45 }}
|
||||
on:inview_change={setEnabled}
|
||||
oninview_change={setEnabled}
|
||||
>
|
||||
<div class="text-xl font-bold text-neutral-300/80 sm:text-2xl">Contest #{number}</div>
|
||||
<h2 class="text-center text-6xl font-bold text-neutral-200/80 sm:text-9xl">{name}</h2>
|
||||
|
|
@ -48,7 +60,7 @@
|
|||
</div>
|
||||
|
||||
<div class="flex flex-col gap-40">
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
|
|||
|
|
@ -4,25 +4,41 @@
|
|||
import PlayIconFill from '~icons/mingcute/play-fill'
|
||||
import PlayIconOutline from '~icons/mingcute/play-line'
|
||||
|
||||
export let name: string
|
||||
export let creator: string
|
||||
export let dotfilesLink: string
|
||||
export let creatorProfilePicture: string
|
||||
export let thumbnail: string
|
||||
export let video: string | undefined = undefined
|
||||
/**
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
creator: string;
|
||||
dotfilesLink: string;
|
||||
creatorProfilePicture: string;
|
||||
thumbnail: string;
|
||||
video?: string | undefined;
|
||||
/**
|
||||
* Specify the blurred background image to be used.
|
||||
* Defaults to `"generated_<thumbnail>"` * */
|
||||
export let blurredThumbnail: string | undefined = undefined
|
||||
export let pretitel: string
|
||||
blurredThumbnail?: string | undefined;
|
||||
pretitel: string;
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
let toShow: 'thumbnail' | 'video' = 'thumbnail'
|
||||
let {
|
||||
name,
|
||||
creator,
|
||||
dotfilesLink,
|
||||
creatorProfilePicture,
|
||||
thumbnail,
|
||||
video = undefined,
|
||||
blurredThumbnail = undefined,
|
||||
pretitel,
|
||||
...rest
|
||||
}: Props = $props();
|
||||
|
||||
let toShow: 'thumbnail' | 'video' = $state('thumbnail')
|
||||
|
||||
let background = blurredThumbnail ?? getGeneratedPath(thumbnail)
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="flex flex-col items-center gap-10 px-4 {$$restProps.class}"
|
||||
class="flex flex-col items-center gap-10 px-4 {rest.class}"
|
||||
style:--bg="url('{background}')"
|
||||
>
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
|
|
@ -57,24 +73,24 @@
|
|||
<div class="rice group relative" class:hasVideo={video}>
|
||||
{#if toShow === 'thumbnail'}
|
||||
{#if video}
|
||||
<button on:click={() => (toShow = 'video')}>
|
||||
<button onclick={() => (toShow = 'video')}>
|
||||
<img src={thumbnail} alt={`${name} by ${creator} thumbnail`} class="" loading="lazy" />
|
||||
</button>
|
||||
<button
|
||||
class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-gray-900/60 p-4 text-white/80 opacity-0 outline outline-gray-100/10 duration-300 hover:scale-105 hover:bg-gray-900/80 hover:text-white group-hover:opacity-100"
|
||||
on:click={() => (toShow = 'video')}><PlayIconFill class="size-8 " /></button
|
||||
onclick={() => (toShow = 'video')}><PlayIconFill class="size-8 " /></button
|
||||
>
|
||||
|
||||
<button
|
||||
class="absolute bottom-6 left-6 rounded-full bg-gray-900/60 p-2 text-white/80 outline outline-gray-100/10 duration-300 hover:scale-105 hover:bg-gray-900/80 hover:text-white"
|
||||
on:click={() => (toShow = 'video')}><PlayIconOutline class="size-5 " /></button
|
||||
onclick={() => (toShow = 'video')}><PlayIconOutline class="size-5 " /></button
|
||||
>
|
||||
{:else}
|
||||
<img src={thumbnail} alt={`${name} by ${creator} thumbnail`} class="" loading="lazy" />
|
||||
{/if}
|
||||
{:else if toShow === 'video'}
|
||||
<!-- svelte-ignore a11y-media-has-caption -->
|
||||
<video autoplay controls poster={thumbnail} src={video} />
|
||||
<!-- svelte-ignore a11y_media_has_caption -->
|
||||
<video autoplay controls poster={thumbnail} src={video}></video>
|
||||
{/if}
|
||||
</div>
|
||||
<!-- blur background -->
|
||||
|
|
@ -85,7 +101,7 @@
|
|||
alt={`${name} by ${creator} thumbnail`}
|
||||
loading="lazy"
|
||||
/>
|
||||
<div class="grain_" />
|
||||
<div class="grain_"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,14 +10,18 @@
|
|||
import Chan from './community/Chan.svelte'
|
||||
import type { CommunityProfile } from '$lib/Types'
|
||||
|
||||
export let communityProfiles: readonly CommunityProfile[]
|
||||
interface Props {
|
||||
communityProfiles: readonly CommunityProfile[];
|
||||
}
|
||||
|
||||
let { communityProfiles }: Props = $props();
|
||||
|
||||
const biggestSize = communityProfiles.reduce(
|
||||
(previousSize, { size }) => (size > previousSize ? size : previousSize),
|
||||
0
|
||||
)
|
||||
|
||||
let restrictionElement: HTMLElement
|
||||
let restrictionElement: HTMLElement | undefined = $state()
|
||||
</script>
|
||||
|
||||
<section
|
||||
|
|
@ -25,10 +29,14 @@
|
|||
bind:this={restrictionElement}
|
||||
>
|
||||
<Title>
|
||||
<TitleHeading slot="title" class="">Join a great<br />community</TitleHeading>
|
||||
<TitleSubtile slot="subtitle" class="class-w-[40ch]">
|
||||
Get help from Distro Hoppers, Haiku writers,<br />Hydrohomies, and human_(probably)
|
||||
</TitleSubtile>
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Join a great<br />community</TitleHeading>
|
||||
{/snippet}
|
||||
{#snippet subtitle()}
|
||||
<TitleSubtile class="class-w-[40ch]">
|
||||
Get help from Distro Hoppers, Haiku writers,<br />Hydrohomies, and human_(probably)
|
||||
</TitleSubtile>
|
||||
{/snippet}
|
||||
</Title>
|
||||
|
||||
<div class="group mt-16 flex flex-col items-center">
|
||||
|
|
@ -55,11 +63,11 @@
|
|||
{...props}
|
||||
weight={relativeSize}
|
||||
spawnDelay={Math.pow(1 - props.size / biggestSize, 4) * 4654}
|
||||
getRestrictionElement={() => restrictionElement}
|
||||
getRestrictionElement={() => restrictionElement!}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<Chan {biggestSize} getRestrictionElement={() => restrictionElement} />
|
||||
<Chan {biggestSize} getRestrictionElement={() => restrictionElement!} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,22 @@
|
|||
<script>
|
||||
/** @type {string | undefined} */
|
||||
export let image = undefined
|
||||
/** @type {string | undefined} */
|
||||
export let name = undefined
|
||||
|
||||
|
||||
/**
|
||||
* @type {string | undefined }
|
||||
* Classes for the command text
|
||||
* @typedef {Object} Props
|
||||
* @property {string | undefined} [image]
|
||||
* @property {string | undefined} [name]
|
||||
* @property {import('svelte').Snippet} [imageExtra]
|
||||
* @property {import('svelte').Snippet} [children]
|
||||
*/
|
||||
|
||||
/** @type {Props} */
|
||||
let {
|
||||
image = undefined,
|
||||
name = undefined,
|
||||
imageExtra,
|
||||
children
|
||||
} = $props();
|
||||
|
||||
</script>
|
||||
|
||||
<div class="group relative flex flex-col items-center gap-2 md:flex-row md:gap-4">
|
||||
|
|
@ -16,12 +26,12 @@
|
|||
>
|
||||
<img src={image} class="h-20 w-32 object-contain" alt="{name} Logo" loading="lazy" />{name}
|
||||
|
||||
<slot name="imageExtra" />
|
||||
{@render imageExtra?.()}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Command button slot -->
|
||||
<div class="mb-2 w-full">
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,25 @@
|
|||
<script>
|
||||
import { animateIn, getGeneratedPath } from '$lib/Helper.ts'
|
||||
|
||||
/** @type {string}
|
||||
* The path to the image. Usually the file within `static`, but can also be an URL
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {string} image
|
||||
* @property {string | undefined} [imageClass]
|
||||
* @property {string | undefined} [containerClass]
|
||||
* @property {string} [blurredBackground]
|
||||
*/
|
||||
export let image
|
||||
/** @type {string | undefined} */
|
||||
export let imageClass = undefined
|
||||
/** @type {string | undefined} */
|
||||
export let containerClass = undefined
|
||||
/** @type {string}
|
||||
* The path to the image. Usually the file within `static`, but can also be an URL. Defaults to `generated_<image>`
|
||||
*/
|
||||
export let blurredBackground = undefined
|
||||
|
||||
/** @type {Props} */
|
||||
let {
|
||||
image,
|
||||
imageClass = undefined,
|
||||
containerClass = undefined,
|
||||
blurredBackground = undefined
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<div class="rice {containerClass} group">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<script>
|
||||
<script lang='ts'>
|
||||
import Card from '$lib/components/Card.svelte'
|
||||
import GameIcon from '~icons/gg/games'
|
||||
import SpecialWorkspaceIcon from '~icons/gg/shutterstock'
|
||||
|
|
@ -21,8 +21,12 @@
|
|||
|
||||
<section class="relative flex max-w-screen-xl flex-col items-center px-3 md:px-8">
|
||||
<Title>
|
||||
<TitlePre slot="pre">TLDR</TitlePre>
|
||||
<TitleHeading slot="title" class="">Features</TitleHeading>
|
||||
{#snippet pre()}
|
||||
<TitlePre >TLDR</TitlePre>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Features</TitleHeading>
|
||||
{/snippet}
|
||||
</Title>
|
||||
|
||||
<CardsContainer
|
||||
|
|
@ -162,6 +166,8 @@
|
|||
</section>
|
||||
|
||||
<style lang="postcss">
|
||||
|
||||
|
||||
.icon-feature {
|
||||
@apply flex items-center justify-center gap-3 font-bold text-slate-400;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,12 +10,16 @@
|
|||
<section class="" use:animateIn={{ fade: 0, slide: 24, duration: 3000, threshold: 0.1 }}>
|
||||
<div class="z-20 -mb-40 px-4">
|
||||
<Title>
|
||||
<TitlePre slot="pre">Memorials of the ricing legends</TitlePre>
|
||||
<TitleHeading slot="title" class="">Hall of Fame</TitleHeading>
|
||||
{#snippet pre()}
|
||||
<TitlePre >Memorials of the ricing legends</TitlePre>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Hall of Fame</TitleHeading>
|
||||
{/snippet}
|
||||
</Title>
|
||||
</div>
|
||||
|
||||
<div class="atmosphere" />
|
||||
<div class="atmosphere"></div>
|
||||
|
||||
<div
|
||||
class="relative -mt-8 flex w-full max-w-[1100px] flex-col items-center justify-end gap-16 overflow-hidden [perspective:100px] md:px-16 lg:gap-24"
|
||||
|
|
@ -41,7 +45,7 @@
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div class="glow" />
|
||||
<div class="glow"></div>
|
||||
</section>
|
||||
|
||||
<style lang="postcss">
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@
|
|||
import HeroBackground from './HeroBackground.svelte'
|
||||
import HyprlandLogo from '$lib/images/logos/HyprlandLogo.svelte'
|
||||
|
||||
export let backgroundData
|
||||
let { backgroundData } = $props();
|
||||
|
||||
let isVisible = true
|
||||
let isVisible = $state(true)
|
||||
let isMobile = false
|
||||
|
||||
onMount(() => {
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<section
|
||||
class="relative flex h-[100svh] min-h-[36rem] w-full flex-col items-center justify-center overflow-x-hidden"
|
||||
use:inview
|
||||
on:inview_change={({ detail: { inView } }) => {
|
||||
oninview_change={({ detail: { inView } }) => {
|
||||
isVisible = inView
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
export let backgroundData
|
||||
let { backgroundData } = $props();
|
||||
|
||||
const { workspacesPerRow, gapLength, workspaceHeight, height, leftColumns, rightColumns } =
|
||||
backgroundData
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
style={`--amount: ${workspacesPerRow}; --workspace-gap: ${gapLength}px;--workspace-height: ${workspaceHeight}px; --length: ${height}px;`}
|
||||
>
|
||||
<!-- Gradient background -->
|
||||
<div class="top-light" />
|
||||
<div class="top-light"></div>
|
||||
|
||||
<div class="columns left" aria-hidden="true">
|
||||
{#each leftColumns as column}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,15 @@
|
|||
|
||||
<section class="relative w-full">
|
||||
<div class="mb-16 md:mb-32">
|
||||
<div class="h-[2px] w-full bg-gradient-to-l from-transparent via-cyan-400/50 to-transparent" />
|
||||
<div class="h-[2px] w-full bg-gradient-to-l from-transparent via-cyan-400/50 to-transparent"></div>
|
||||
</div>
|
||||
<div class="top-gradient"></div>
|
||||
|
||||
<div class="relative mx-auto mb-12 max-w-5xl px-8 md:mb-20">
|
||||
<Title class="mb-10">
|
||||
<TitleHeading slot="title" class="">Hyprperks</TitleHeading>
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Hyprperks</TitleHeading>
|
||||
{/snippet}
|
||||
<TitleSubtile class="max-w-[55ch]">
|
||||
<a href={hyprperksLink} class=" text-primary hover:underline" target="_blank"
|
||||
>Subscribe for first-party configurations, priority support</a
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<script>
|
||||
<script lang="ts">
|
||||
import ActiveGitIcon from '~icons/gg/git-branch'
|
||||
import VaxryImage from '$lib/images/vaxry-github.webp'
|
||||
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
take
|
||||
} from 'rxjs'
|
||||
import GitTile from '$lib/components/GitTile.svelte'
|
||||
import { lerp } from '$lib/Helper.ts'
|
||||
import { lerp } from '$lib/Helper'
|
||||
import { cubicInOut, expoInOut } from 'svelte/easing'
|
||||
import DiscordProfilePicture from '$lib/components/DiscordProfilePicture.svelte'
|
||||
import { setContext } from 'svelte'
|
||||
|
|
@ -76,18 +76,18 @@
|
|||
),
|
||||
scan(
|
||||
(acc, value) => (value === 0 ? [] : [...acc, ...Array.from({ length: value }, () => 1)]),
|
||||
[]
|
||||
[] as number[]
|
||||
),
|
||||
startWith([])
|
||||
startWith([] as number[])
|
||||
)
|
||||
|
||||
$: hue = lerp(200, 130, $cubicRelativeLevel$)
|
||||
$: scale = lerp(0.9, 2, $cubicRelativeLevel$)
|
||||
$: translateY = lerp(0, 10, $cubicRelativeLevel$)
|
||||
let hue = $derived(lerp(200, 130, $cubicRelativeLevel$))
|
||||
let scale = $derived(lerp(0.9, 2, $cubicRelativeLevel$))
|
||||
let translateY = $derived(lerp(0, 10, $cubicRelativeLevel$))
|
||||
|
||||
/** @type {HTMLDivElement} */
|
||||
let containerElement
|
||||
let isAnimationComplete = false
|
||||
let containerElement = $state()
|
||||
let isAnimationComplete = $state(false)
|
||||
|
||||
const vaxrySize = 220
|
||||
const contextId = Symbol('hypractive context')
|
||||
|
|
@ -108,7 +108,7 @@
|
|||
<div class="relative overflow-visible">
|
||||
<button
|
||||
class="flex items-center gap-3 font-bold text-slate-400 shadow-black drop-shadow-lg transition-colors hover:underline active:scale-95"
|
||||
on:click={isAnimationComplete ? onClickUnlocked : onClick}
|
||||
onclick={isAnimationComplete ? onClickUnlocked : onClick}
|
||||
style:color={$relativeLevel$ > 0 ? `hsl(${hue} 64% 53%)` : undefined}
|
||||
style:scale={$relativeLevel$ > 0 ? scale : undefined}
|
||||
style:translate={$relativeLevel$ > 0 ? `0px -${translateY}px` : undefined}
|
||||
|
|
@ -132,14 +132,14 @@
|
|||
<div
|
||||
class="vaxx-wrapper absolute bottom-[240px] left-1/2 z-50 aspect-square -translate-x-[100px] rounded-full animate-in fade-in-0 zoom-in-95 slide-in-from-bottom-[500px] slide-in-from-left-20 [animation-duration:2.5s]"
|
||||
style:width={vaxrySize + 'px'}
|
||||
on:animationend={() => (isAnimationComplete = true)}
|
||||
onanimationend={() => (isAnimationComplete = true)}
|
||||
>
|
||||
<DiscordProfilePicture
|
||||
image={VaxryImage}
|
||||
size={vaxrySize}
|
||||
weight={10}
|
||||
coordinates={[0, 0]}
|
||||
class="outline-orange-300"
|
||||
{contextId}
|
||||
isAnimating={false}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -150,7 +150,7 @@
|
|||
class="bg-gradient"
|
||||
style:opacity={$hasAscended$ ? 1 : $relativeLevel$}
|
||||
style="--relativeLevel: {$hasAscended$ ? 1 : $expoRelativeLevel$ - 0.2}"
|
||||
/>
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,12 @@
|
|||
|
||||
<section class="pb-6">
|
||||
<Title>
|
||||
<TitleHeading slot="title" class="">Install now</TitleHeading>
|
||||
<TitleSubtile slot="subtitle" class="">For your favourite distro</TitleSubtile>
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Install now</TitleHeading>
|
||||
{/snippet}
|
||||
{#snippet subtitle()}
|
||||
<TitleSubtile class="">For your favourite distro</TitleSubtile>
|
||||
{/snippet}
|
||||
</Title>
|
||||
|
||||
<div class="flex flex-col items-center gap-12 md:gap-6" use:animateIn={{ slide: 24, fade: 0 }}>
|
||||
|
|
@ -25,23 +29,27 @@
|
|||
>
|
||||
<DistroOption name="Arch" image={archLogo}>
|
||||
<CommandButton command="pacman -S hyprland">
|
||||
<div slot="extra" class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2">
|
||||
AUR git version: <a
|
||||
class=" "
|
||||
target="_blank"
|
||||
href="https://aur.archlinux.org/packages/hyprland-git/">hyprland-git</a
|
||||
>
|
||||
</div>
|
||||
{#snippet extra()}
|
||||
<div class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2">
|
||||
AUR git version: <a
|
||||
class=" "
|
||||
target="_blank"
|
||||
href="https://aur.archlinux.org/packages/hyprland-git/">hyprland-git</a
|
||||
>
|
||||
</div>
|
||||
{/snippet}
|
||||
</CommandButton>
|
||||
</DistroOption>
|
||||
|
||||
<DistroOption name="NixOS" image={nixLogo}>
|
||||
<CommandButton command="programs.hyprland.enable">
|
||||
<div slot="extra" class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2">
|
||||
<a href="https://wiki.hypr.land/Nix/" target="_blank"
|
||||
>See more details and git version ↗</a
|
||||
>
|
||||
</div>
|
||||
{#snippet extra()}
|
||||
<div class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2">
|
||||
<a href="https://wiki.hypr.land/Nix/" target="_blank"
|
||||
>See more details and git version ↗</a
|
||||
>
|
||||
</div>
|
||||
{/snippet}
|
||||
</CommandButton>
|
||||
</DistroOption>
|
||||
|
||||
|
|
@ -51,17 +59,21 @@
|
|||
|
||||
<DistroOption name="openSUSE" image={suseLogo}>
|
||||
<CommandButton command="zypper in hyprland">
|
||||
<div slot="extra" class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2">
|
||||
or install “hyprland” via YaST2 Software.
|
||||
</div>
|
||||
{#snippet extra()}
|
||||
<div class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2">
|
||||
or install “hyprland” via YaST2 Software.
|
||||
</div>
|
||||
{/snippet}
|
||||
</CommandButton>
|
||||
<img
|
||||
class=" absolute inset-0 -z-10 translate-y-1 rotate-0 scale-90 opacity-0 transition-all duration-700 [transition-delay:2s] group-hover:-translate-x-3 group-hover:-translate-y-0 group-hover:-rotate-12 group-hover:scale-100 group-hover:opacity-90"
|
||||
src={amongus}
|
||||
slot="imageExtra"
|
||||
alt=""
|
||||
srcset=""
|
||||
/>
|
||||
{#snippet imageExtra()}
|
||||
<img
|
||||
class=" absolute inset-0 -z-10 translate-y-1 rotate-0 scale-90 opacity-0 transition-all duration-700 [transition-delay:2s] group-hover:-translate-x-3 group-hover:-translate-y-0 group-hover:-rotate-12 group-hover:scale-100 group-hover:opacity-90"
|
||||
src={amongus}
|
||||
|
||||
alt=""
|
||||
srcset=""
|
||||
/>
|
||||
{/snippet}
|
||||
</DistroOption>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,17 @@
|
|||
import TitlePre from '$lib/components/Title/TitlePre.svelte'
|
||||
import Title from '$lib/components/Title/TitleWrapper.svelte'
|
||||
|
||||
export let news
|
||||
let { news } = $props();
|
||||
</script>
|
||||
|
||||
<section class="relative mb-12 max-w-5xl px-8 md:mb-20">
|
||||
<Title>
|
||||
<TitlePre slot="pre">Read while it's fresh!</TitlePre>
|
||||
<TitleHeading slot="title" class="">Latest news</TitleHeading>
|
||||
{#snippet pre()}
|
||||
<TitlePre >Read while it's fresh!</TitlePre>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Latest news</TitleHeading>
|
||||
{/snippet}
|
||||
</Title>
|
||||
|
||||
<ul class="mt-8 flex flex-col gap-10">
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@
|
|||
import Button from '$lib/components/Button.svelte'
|
||||
|
||||
/** @type {HTMLVideoElement[]}*/
|
||||
const videos = []
|
||||
let activeIndex = 0
|
||||
let isHoveringVideo = false
|
||||
const videos = $state([])
|
||||
let activeIndex = $state(0)
|
||||
let isHoveringVideo = $state(false)
|
||||
const isVideoCroppedInput$ = new Subject()
|
||||
/** @type {import('rxjs').Subject<boolean>}*/
|
||||
const isVideoCropped$ = isVideoCroppedInput$.pipe(
|
||||
|
|
@ -78,7 +78,7 @@
|
|||
})
|
||||
</script>
|
||||
|
||||
<svelte:window on:resize={() => isVideoCroppedInput$.next(0)} />
|
||||
<svelte:window onresize={() => isVideoCroppedInput$.next(0)} />
|
||||
|
||||
<section class="relative z-0 flex min-h-max w-full flex-col items-center py-20">
|
||||
<div
|
||||
|
|
@ -102,14 +102,15 @@
|
|||
<div class="flex h-full flex-col gap-4">
|
||||
{#each items as { icon, title, description }, index}
|
||||
{@const isActive = index === activeIndex}
|
||||
{@const SvelteComponent = icon}
|
||||
<button
|
||||
class={clsx(
|
||||
'flex gap-3 rounded-xl px-4 py-4 outline-0 outline-cyan-400/50 transition-all sm:-ml-4',
|
||||
isActive && 'bg-blue-300/5 shadow-md outline outline-1 backdrop-blur-sm '
|
||||
)}
|
||||
on:mouseenter={() => setActiveItem(index)}
|
||||
onmouseenter={() => setActiveItem(index)}
|
||||
>
|
||||
<svelte:component this={icon} class="h-8 w-8 shrink-0 text-primary" />
|
||||
<SvelteComponent class="h-8 w-8 shrink-0 text-primary" />
|
||||
<p
|
||||
class={clsx(
|
||||
'txt-shadow_ text-left text-lg font-medium transition-colors ',
|
||||
|
|
@ -153,7 +154,7 @@
|
|||
>
|
||||
{#if $isVideoCropped$}
|
||||
<button
|
||||
on:click={toggleVideoSlide}
|
||||
onclick={toggleVideoSlide}
|
||||
class:rotate-180={isHoveringVideo}
|
||||
class="group absolute -left-6 top-1/2 z-50 rounded-full bg-blue-400/5 p-2 outline outline-white/10 backdrop-blur-sm transition-transform"
|
||||
out:fade
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@
|
|||
import { inview } from 'svelte-inview'
|
||||
import Video from '$lib/components/Video.svelte'
|
||||
import { onMount } from 'svelte'
|
||||
/** @type {{ [key: string]: any }} */
|
||||
let { ...rest } = $props();
|
||||
|
||||
/** @type HTMLVideoElement */
|
||||
let videoElement
|
||||
let videoElement = $state()
|
||||
|
||||
let isVisible = false
|
||||
let isManuallyPaused = false
|
||||
let isChrome = false
|
||||
let isVisible = $state(false)
|
||||
let isManuallyPaused = $state(false)
|
||||
let isChrome = $state(false)
|
||||
|
||||
onMount(() => {
|
||||
isChrome = navigator.userAgent.toLowerCase().includes('chrome')
|
||||
|
|
@ -21,18 +23,18 @@
|
|||
})
|
||||
</script>
|
||||
|
||||
<section class={$$restProps.class} class:isVisible>
|
||||
<section class={rest.class} class:isVisible>
|
||||
<div
|
||||
class="wrapper"
|
||||
role="banner"
|
||||
use:inview={{ threshold: 0.5 }}
|
||||
on:inview_enter={() => {
|
||||
oninview_enter={() => {
|
||||
isVisible = true
|
||||
if (!isManuallyPaused && isChrome) {
|
||||
videoElement.play().catch(() => {})
|
||||
}
|
||||
}}
|
||||
on:inview_leave={() => {
|
||||
oninview_leave={() => {
|
||||
isVisible = false
|
||||
isManuallyPaused = videoElement.paused
|
||||
videoElement.pause()
|
||||
|
|
@ -56,7 +58,7 @@
|
|||
>
|
||||
</div>
|
||||
|
||||
<div class="preview-rice-bg" aria="hidden" />
|
||||
<div class="preview-rice-bg" aria="hidden"></div>
|
||||
</section>
|
||||
|
||||
<style lang="postcss">
|
||||
|
|
|
|||
|
|
@ -7,7 +7,11 @@
|
|||
import Button from '$lib/components/Button.svelte'
|
||||
import PatternBackground from '$lib/PatternBackground.svelte'
|
||||
|
||||
export let sponsors: SponsorsRanked
|
||||
interface Props {
|
||||
sponsors: SponsorsRanked
|
||||
}
|
||||
|
||||
let { sponsors }: Props = $props()
|
||||
const hasSponsors = Object.values(sponsors)
|
||||
.filter((value) => !Array.isArray(value))
|
||||
.every((array) => array.length === 0)
|
||||
|
|
@ -18,13 +22,17 @@
|
|||
{#if hasSponsors}
|
||||
<div class="relative flex w-full flex-col items-center justify-center">
|
||||
<PatternBackground
|
||||
class="absolute -inset-12 inset-x-0 h-[100%] w-full text-slate-800 opacity-40"
|
||||
class="absolute -inset-24 inset-x-0 h-[120%] w-full text-slate-800 opacity-40"
|
||||
/>
|
||||
|
||||
<section class="relative mb-12 flex max-w-5xl flex-col gap-2 px-8 md:mb-20">
|
||||
<Title class="px-0">
|
||||
<TitlePre slot="pre">A huge thank you!</TitlePre>
|
||||
<TitleHeading size="small" slot="title">Our sponsors</TitleHeading>
|
||||
{#snippet pre()}
|
||||
<TitlePre>A huge thank you!</TitlePre>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<TitleHeading size="small">Our sponsors</TitleHeading>
|
||||
{/snippet}
|
||||
</Title>
|
||||
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
<script lang="ts">
|
||||
import DiscordProfilePicture from '$lib/components/DiscordProfilePicture.svelte'
|
||||
|
||||
export let biggestSize: number
|
||||
export let getRestrictionElement: (() => HTMLElement) | undefined = undefined
|
||||
interface Props {
|
||||
biggestSize: number
|
||||
getRestrictionElement?: (() => HTMLElement) | undefined
|
||||
}
|
||||
|
||||
let { biggestSize, getRestrictionElement = undefined }: Props = $props()
|
||||
const size = 90
|
||||
|
||||
let isDragging = false
|
||||
let isHover = false
|
||||
let image: string
|
||||
$: image = isDragging
|
||||
? 'imgs/chan/surprise.svg'
|
||||
: isHover
|
||||
? 'imgs/chan/wink.svg'
|
||||
: 'imgs/chan/joy.svg'
|
||||
let isDragging = $state(false)
|
||||
let isHover = $state(false)
|
||||
let image: string = $derived(
|
||||
isDragging ? 'imgs/chan/surprise.svg' : isHover ? 'imgs/chan/wink.svg' : 'imgs/chan/joy.svg'
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class="absolute z-20">
|
||||
|
|
@ -23,8 +24,8 @@
|
|||
weight={size / biggestSize}
|
||||
{getRestrictionElement}
|
||||
class={'bg-blue-300 outline-cyan-500'}
|
||||
on:dragStart={() => (isDragging = true)}
|
||||
on:dragEnd={() => (isDragging = false)}
|
||||
on:hover={() => (isHover = true)}
|
||||
ondragStart={() => (isDragging = true)}
|
||||
ondragEnd={() => (isDragging = false)}
|
||||
onhover={() => (isHover = true)}
|
||||
></DiscordProfilePicture>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,8 +4,12 @@
|
|||
import { Subject, filter, first, map, merge, of, startWith, switchMap, timer } from 'rxjs'
|
||||
import { onDestroy } from 'svelte'
|
||||
|
||||
export let biggestSize: number
|
||||
export let getRestrictionElement: (() => HTMLElement) | undefined = undefined
|
||||
interface Props {
|
||||
biggestSize: number
|
||||
getRestrictionElement?: (() => HTMLElement) | undefined
|
||||
}
|
||||
|
||||
let { biggestSize, getRestrictionElement = undefined }: Props = $props()
|
||||
|
||||
const thePozArmy = Object.values(import.meta.glob('$lib/images/poz/*', { eager: true })).map(
|
||||
(x) => x.default as string
|
||||
|
|
@ -13,7 +17,7 @@
|
|||
|
||||
const size = 90
|
||||
const origin = [710, 615] as const
|
||||
let newPosition: readonly [number, number]
|
||||
let newPosition: readonly [number, number] = $state()
|
||||
const clicksTarget = 9
|
||||
const shakeMax = 24
|
||||
const clicksInput$ = new Subject()
|
||||
|
|
@ -74,7 +78,7 @@
|
|||
isAnimating={false}
|
||||
tag="poz"
|
||||
{getRestrictionElement}
|
||||
on:enteredView={({ detail: { dragCoordinates } }) => {
|
||||
onenteredView={({ detail: { dragCoordinates } }) => {
|
||||
dragCoordinates.update(([x, y]) => {
|
||||
x += lerp(400, 0, (size / maxSize) * (1 - Math.random())) * (Math.random() > 0.5 ? 1 : -1)
|
||||
y += lerp(400, 0, (size / maxSize) * (1 - Math.random())) * (Math.random() > 0.5 ? 1 : -1)
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@
|
|||
import TitleSubtile from '$lib/components/Title/TitleSubtile.svelte'
|
||||
import TitleHeading from '$lib/components/Title/TitleHeading.svelte'
|
||||
|
||||
export let data
|
||||
let { data } = $props();
|
||||
|
||||
/** @type {HTMLElement} */
|
||||
let asciiElement
|
||||
let asciiElement = $state()
|
||||
|
||||
const { posts } = data
|
||||
|
||||
|
|
@ -68,14 +68,16 @@
|
|||
<title>Hyprland News</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="fancy-top-gradient" />
|
||||
<div class="fancy-top-gradient"></div>
|
||||
|
||||
<section>
|
||||
<header class="header">
|
||||
<pre class="spinner-wrapper" bind:this={asciiElement} />
|
||||
<pre class="spinner-wrapper" bind:this={asciiElement}></pre>
|
||||
|
||||
<Title>
|
||||
<TitleHeading slot="title" class="">News</TitleHeading>
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">News</TitleHeading>
|
||||
{/snippet}
|
||||
<TitleSubtile>Fresh updates straight from the oven</TitleSubtile>
|
||||
</Title>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
import NewsThumb from '$lib/components/news-thumb.svelte'
|
||||
import clsx from 'clsx'
|
||||
|
||||
export let data
|
||||
let { data } = $props();
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
<meta property="og:title" content={data.meta.title} />
|
||||
</svelte:head>
|
||||
|
||||
<div class="fancy-top-gradient fad animate-in" />
|
||||
<div class="fancy-top-gradient fad animate-in"></div>
|
||||
|
||||
<article
|
||||
class="mx-auto mt-navbar flex max-w-4xl flex-col gap-4 px-6 pt-20 md:gap-8 md:px-8 lg:mt-32 lg:gap-14"
|
||||
|
|
@ -59,14 +59,16 @@
|
|||
<div
|
||||
class="prose prose-slate prose-invert transition-none delay-500 animate-in fade-in-0 fill-mode-backwards [animation-duration:2500ms] lg:prose-xl prose-a:text-cyan-400 prose-img:rounded-lg"
|
||||
>
|
||||
<svelte:component this={data.content} />
|
||||
<data.content />
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{#if data.other.length > 0}
|
||||
<section class="mx-auto mt-72 max-w-screen-lg">
|
||||
<Title class="mb-6">
|
||||
<TitleHeading slot="title" class="">More news</TitleHeading>
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">More news</TitleHeading>
|
||||
{/snippet}
|
||||
</Title>
|
||||
|
||||
<ul class="grid grid-cols-2 gap-x-7 gap-y-16">
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
import TitleHeading from '$lib/components/Title/TitleHeading.svelte'
|
||||
import Button from '$lib/components/Button.svelte'
|
||||
|
||||
export let data
|
||||
let { data } = $props();
|
||||
|
||||
const { plugins } = data
|
||||
const featuredPlugins = plugins.filter(({ featured }) => featured).slice(0, 4)
|
||||
|
|
@ -50,8 +50,12 @@
|
|||
|
||||
<header class="mt-24 flex flex-col items-center justify-center md:mt-32">
|
||||
<Title>
|
||||
<TitlePre slot="pre">Plugins</TitlePre>
|
||||
<TitleHeading slot="title" class="">Unlock full power</TitleHeading>
|
||||
{#snippet pre()}
|
||||
<TitlePre >Plugins</TitlePre>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Unlock full power</TitleHeading>
|
||||
{/snippet}
|
||||
<TitleSubtile>Easily load up plugins and customize everything</TitleSubtile>
|
||||
</Title>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,22 +6,34 @@
|
|||
|
||||
// Dont forget to put this component inside of CardsContainer.svelte
|
||||
// Also pass a hight class to the element, as otherwise the banner might not align properly,
|
||||
// due to the parent container being unable to use height: full as there wont be a reference
|
||||
|
||||
|
||||
export let plugin
|
||||
export let color = undefined
|
||||
export let showCategory = false
|
||||
export let taglineMaxLength = 0
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {any} plugin - due to the parent container being unable to use height: full as there wont be a reference
|
||||
* @property {any} [color]
|
||||
* @property {boolean} [showCategory]
|
||||
* @property {number} [taglineMaxLength]
|
||||
*/
|
||||
|
||||
/** @type {Props & { [key: string]: any }} */
|
||||
let {
|
||||
plugin,
|
||||
color = undefined,
|
||||
showCategory = false,
|
||||
taglineMaxLength = 0,
|
||||
...rest
|
||||
} = $props();
|
||||
|
||||
/** @type {HTMLVideoElement}*/
|
||||
let videoElement
|
||||
let videoElement = $state()
|
||||
</script>
|
||||
|
||||
<Card
|
||||
on:mouseenter={() => videoElement && videoElement.play().catch(console.error)}
|
||||
on:mouseleave={() => videoElement && videoElement.pause()}
|
||||
{color}
|
||||
class={$$restProps.class}
|
||||
class={rest.class}
|
||||
gradientOpacity={0.5}
|
||||
><a
|
||||
href={plugin.url}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
<script>
|
||||
/** @type {string}*/
|
||||
export let tag
|
||||
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {string} tag
|
||||
*/
|
||||
|
||||
/** @type {Props} */
|
||||
let { tag } = $props();
|
||||
|
||||
const colors = {
|
||||
Design: 'text-pink-200 bg-pink-500/15',
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
import Button from '$lib/components/Button.svelte'
|
||||
import CloseIcon from '~icons/mingcute/close-line'
|
||||
|
||||
let kofiModal: HTMLDialogElement | undefined
|
||||
let kofiModal: HTMLDialogElement | undefined = $state()
|
||||
|
||||
function lockScroll() {
|
||||
document.querySelector('html')?.classList.add('lock-scroll')
|
||||
|
|
@ -26,8 +26,12 @@
|
|||
|
||||
<section class="min-h-screen w-full md:mt-32">
|
||||
<Title class="mb-4 md:mb-8">
|
||||
<TitlePre slot="pre">Help continue Hyprland development</TitlePre>
|
||||
<TitleHeading slot="title" class="">Support Hyprland</TitleHeading>
|
||||
{#snippet pre()}
|
||||
<TitlePre >Help continue Hyprland development</TitlePre>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Support Hyprland</TitleHeading>
|
||||
{/snippet}
|
||||
</Title>
|
||||
|
||||
<div
|
||||
|
|
@ -101,7 +105,7 @@
|
|||
<div class="relative h-[712px] min-h-[712px] shadow-2xl">
|
||||
<form class="absolute -right-2 -top-4 z-10">
|
||||
<button
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
unlockScroll()
|
||||
}}
|
||||
formmethod="dialog"
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ const mdsvexOptions = {
|
|||
const config = {
|
||||
extensions: ['.svelte', '.md'],
|
||||
kit: { adapter: adapter() },
|
||||
preprocess: [vitePreprocess(), mdsvex(mdsvexOptions)]
|
||||
preprocess: [vitePreprocess({ script: true }), mdsvex(mdsvexOptions)]
|
||||
}
|
||||
|
||||
export default config
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue