feat: Banner background inherits cluster icon
This commit is contained in:
parent
32d27bc9fb
commit
a0d64eaffa
3 changed files with 65 additions and 17 deletions
|
|
@ -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%);
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue