feat: Banner background inherits cluster icon

This commit is contained in:
LynithDev 2024-08-02 13:04:59 +02:00
parent 32d27bc9fb
commit a0d64eaffa
3 changed files with 65 additions and 17 deletions

View file

@ -0,0 +1,15 @@
.linearBlur::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
-webkit-backdrop-filter: blur(var(--blur));
backdrop-filter: blur(var(--blur));
-webkit-mask: linear-gradient(var(--degrees), transparent, black 50%);
mask: linear-gradient(var(--degrees), transparent, black 50%);
background: linear-gradient(var(--degrees), transparent, rgba(0, 0, 0, 0.4) 50%);
}

View file

@ -1,21 +1,28 @@
import { convertFileSrc } from '@tauri-apps/api/core'; import { convertFileSrc } from '@tauri-apps/api/core';
import { type JSX, splitProps } from 'solid-js'; import { type JSX, type ParentProps, Show, splitProps } from 'solid-js';
import styles from './ClusterCover.module.scss';
import defaultCover from '~assets/images/default_instance_cover.jpg'; import defaultCover from '~assets/images/default_instance_cover.jpg';
import type { Cluster } from '~bindings'; import type { Cluster } from '~bindings';
type ClusterCoverProps = JSX.HTMLAttributes<HTMLImageElement> & { type ClusterCoverProps = JSX.HTMLAttributes<HTMLImageElement> & {
cluster: Cluster | undefined; cluster: Cluster | undefined;
override?: string; override?: string;
fallback?: string;
linearBlur?: {
degrees?: number;
blur?: number;
class?: string;
};
}; };
function ClusterCover(props: ClusterCoverProps) { function ClusterCover(props: ClusterCoverProps) {
const [split, rest] = splitProps(props, ['cluster', 'override', 'onError']); const [split, rest] = splitProps(props, ['cluster', 'override', 'onError', 'fallback', 'linearBlur', 'class']);
const image = () => { const image = () => {
const url = split.override || split.cluster?.meta.icon_url || split.cluster?.meta.icon; const url = split.override || split.cluster?.meta.icon_url || split.cluster?.meta.icon;
if (url === undefined || url === null) if (url === undefined || url === null)
return defaultCover; return split.fallback || defaultCover;
if (url.startsWith('/')) if (url.startsWith('/'))
return convertFileSrc(url); return convertFileSrc(url);
@ -23,17 +30,33 @@ function ClusterCover(props: ClusterCoverProps) {
return url; return url;
}; };
const Wrapper = (props: ParentProps) => (
<Show when={split.linearBlur !== undefined} fallback={<>{props.children}</>}>
<div
class={`${styles.linearBlur} ${split.linearBlur?.class || ''}`}
style={{
'--degrees': `${split.linearBlur?.degrees ?? 0}deg`,
'--blur': `${split.linearBlur?.blur ?? 0}px`,
}}
children={props.children}
/>
</Show>
);
return ( return (
<img <Wrapper>
{...rest} <img
src={image()} {...rest}
onError={(e) => { class={`${styles.cover} ${split.class || ''}`}
e.currentTarget.src = split.override ? convertFileSrc(split.override) : defaultCover; src={image()}
if (split.onError) onError={(e) => {
e.currentTarget.src = split.override ? convertFileSrc(split.override) : defaultCover;
if (split.onError)
// @ts-expect-error -- jsx stuff // @ts-expect-error -- jsx stuff
split.onError!(e); split.onError!(e);
}} }}
/> />
</Wrapper>
); );
} }

View file

@ -1,5 +1,5 @@
import { import {
ChevronRightIcon, DotsVerticalIcon,
PlayIcon, PlayIcon,
PlusIcon, PlusIcon,
SearchMdIcon, SearchMdIcon,
@ -8,7 +8,7 @@ import { For, Show, createEffect, createSignal } from 'solid-js';
import { useNavigate } from '@solidjs/router'; import { useNavigate } from '@solidjs/router';
import { mergeRefs } from '@solid-primitives/refs'; import { mergeRefs } from '@solid-primitives/refs';
import image from '../../assets/images/header.png'; import BannerBackground from '../../assets/images/header.png';
import Button from '../components/base/Button'; import Button from '../components/base/Button';
import TextField from '../components/base/TextField'; import TextField from '../components/base/TextField';
import ClusterRoot from './cluster/ClusterRoot'; import ClusterRoot from './cluster/ClusterRoot';
@ -217,8 +217,18 @@ function Banner(props: BannerProps) {
}); });
return ( return (
<div class="relative w-full h-52"> <div class="relative w-full h-52 min-h-52 overflow-hidden rounded-xl">
<img src={image} class="absolute rounded-xl w-full h-52 object-cover" /> <ClusterCover
class="absolute rounded-xl w-full h-52 object-cover"
linearBlur={{
degrees: 270,
blur: 30,
class: 'after:right-1/3!',
}}
cluster={cluster()}
fallback={BannerBackground}
/>
<div class="relative z-10 h-full px-8 py-6 text-fg-primary flex flex-col justify-between items-start"> <div class="relative z-10 h-full px-8 py-6 text-fg-primary flex flex-col justify-between items-start">
<div class="flex flex-col gap-y-2"> <div class="flex flex-col gap-y-2">
<h1>{cluster()?.meta.name || 'Create a cluster'}</h1> <h1>{cluster()?.meta.name || 'Create a cluster'}</h1>
@ -254,7 +264,7 @@ function Banner(props: BannerProps) {
<Button <Button
buttonStyle="iconSecondary" buttonStyle="iconSecondary"
class="bg-op-10!" class="bg-op-10!"
children={<ChevronRightIcon />} children={<DotsVerticalIcon />}
onClick={() => ClusterRoot.open(navigate, cluster()!.uuid)} onClick={() => ClusterRoot.open(navigate, cluster()!.uuid)}
/> />
</> </>