feat: Added mod cards component

This commit is contained in:
LynithDev 2024-07-08 12:15:49 +02:00
parent 4754374476
commit 09b73dc55e
6 changed files with 135 additions and 76 deletions

View file

@ -42,12 +42,12 @@ h4 { @apply text-lg font-medium; }
}
.page-animation-enter-active, .page-animation-leave-active {
transition: opacity 160ms, transform 175ms;
transition: opacity 130ms, transform 155ms;
transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.page-animation-enter, .page-animation-leave-to {
opacity: 0;
.page-animation-enter {
opacity: 0.1;
transform: translateX(-75px);
}
@ -56,6 +56,11 @@ h4 { @apply text-lg font-medium; }
transform: translateX(0);
}
.page-animation-leave-to {
opacity: 0;
transform: translateX(75px);
}
.noti-animation-enter-active, .noti-animation-leave-active {
transition: opacity 160ms, transform 175ms;
transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);

View file

@ -1,4 +1,17 @@
declare namespace globalThis {
export type WithIndex<T> = T & { index: number }
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
}
// TODO remove in place for specta-generated types
interface Mod {
id: string;
name: string;
description: string;
author: string;
icon_url: string;
provider: Provider;
page_url: string;
downloads: number;
ratings: number;
}

View file

@ -0,0 +1,62 @@
import { Download01Icon, HeartIcon } from '@untitled-theme/icons-solid';
import { useNavigate } from '@solidjs/router';
import { abbreviateNumber } from '../../../utils/primitives';
import BrowserMod from '~ui/pages/browser/BrowserMod';
// TODO: Remove in place for specta generated types
export enum Provider {
Curseforge,
Modrinth,
Polyfrost,
Skyclient,
}
function ModCard(props: Mod) {
const navigate = useNavigate();
function redirect() {
navigate(BrowserMod.getUrl({
id: props.id,
provider: Provider[props.provider] || 'unknown',
}));
}
return (
<div onClick={redirect} class="flex flex-col overflow-hidden rounded-lg bg-component-bg max-w-53 min-w-53 w-full max-h-68 min-h-68 h-full">
<div class="relative h-28 overflow-hidden flex justify-center items-center">
<img class="absolute filter-blur-xl z-0 max-w-none w-7/6" src={props.icon_url} alt={`Icon for ${props.name}`} />
<img class="relative w-2/5 aspect-ratio-square z-1 rounded-2xl" src={props.icon_url} alt={`Icon for ${props.name}`} />
</div>
<div class="flex flex-col flex-1 p-3 gap-4">
<div class="flex flex-col gap-2">
<h5 class="font-medium text-fg-primary">{props.name}</h5>
<p class="text-fg-secondary text-xs">
By
{' '}
<span class="text-fg-primary">{props.author}</span>
{' '}
on
{' '}
{Provider[props.provider]}
</p>
</div>
<p class="text-fg-secondary text-sm flex-1">{props.description}</p>
<div class="flex flex-row gap-4 text-xs">
<div class="flex flex-row items-center gap-2">
<Download01Icon class="w-4 h-4" />
{abbreviateNumber(props.downloads)}
</div>
<div class="flex flex-row items-center gap-2">
<HeartIcon class="w-4 h-4" />
{abbreviateNumber(props.ratings)}
</div>
</div>
</div>
</div>
);
}
export default ModCard;

View file

@ -1,10 +1,9 @@
import { Download01Icon, FileCode01Icon, FilterLinesIcon, HeartIcon, SearchMdIcon } from '@untitled-theme/icons-solid';
import { type Accessor, For, type Setter, createEffect, createSignal } from 'solid-js';
import { Download01Icon, FileCode01Icon, FilterLinesIcon, SearchMdIcon } from '@untitled-theme/icons-solid';
import { For, createSignal } from 'solid-js';
import Button from '~ui/components/base/Button';
import Dropdown from '~ui/components/base/Dropdown';
import TextField from '~ui/components/base/TextField';
import ScrollableContainer from '~ui/components/ScrollableContainer';
import { abbreviateNumber } from '~utils/primitives';
import ModCard, { Provider } from '~ui/components/content/ModCard';
import createSortable from '~utils/sorting';
interface CardProps {
@ -15,13 +14,6 @@ interface CardProps {
provider: Provider;
};
enum Provider {
Curseforge,
Modrinth,
Polyfrost,
Skyclient,
}
interface BrowserFilters {
provider: Provider[]; // TODO: Change with Provider types from Rust
}
@ -90,22 +82,25 @@ function BrowserMain() {
);
}
export const modStore: Mod[] = [
{
id: '1yIQcc2b',
name: 'EvergreenHUD',
description: 'Improves your heads up display.',
author: 'Polyfrost',
icon_url: 'https://cdn.modrinth.com/data/1yIQcc2b/icon.png',
page_url: 'https://modrinth.com/mod/evergreenhud',
provider: Provider.Modrinth,
downloads: 281700,
ratings: 220,
},
];
const _modRowData: ModsRowProps[] = [
{
header: 'Highly Endorsed',
category: 'endorsed',
mods: [
{
name: 'EvergreenHUD',
description: 'Improves your heads up display.',
author: 'Polyfrost',
icon_url: 'https://cdn.modrinth.com/data/1yIQcc2b/icon.png',
page_url: 'https://modrinth.com/mod/evergreenhud',
provider: Provider.Modrinth,
downloads: 281700,
ratings: 220,
},
],
mods: modStore,
},
];
@ -115,17 +110,6 @@ interface ModsRowProps {
mods: Mod[];
};
interface Mod {
name: string;
description: string;
author: string;
icon_url: string;
provider: Provider;
page_url: string;
downloads: number;
ratings: number;
}
function ModsRow(props: ModsRowProps) {
return (
<div class="flex flex-col flex-1 gap-3">
@ -144,43 +128,4 @@ function ModsRow(props: ModsRowProps) {
);
}
function ModCard(props: Mod) {
return (
<div class="flex flex-col overflow-hidden rounded-lg bg-component-bg max-w-53 min-w-53 w-full max-h-68 min-h-68 h-full">
<div class="relative h-28 overflow-hidden flex justify-center items-center">
<img class="absolute filter-blur-xl z-0 max-w-none w-7/6" src={props.icon_url} alt={`Icon for ${props.name}`} />
<img class="relative w-2/5 aspect-ratio-square z-1 rounded-2xl" src={props.icon_url} alt={`Icon for ${props.name}`} />
</div>
<div class="flex flex-col flex-1 p-3 gap-4">
<div class="flex flex-col gap-2">
<h5 class="font-medium text-fg-primary">{props.name}</h5>
<p class="text-fg-secondary text-xs">
By
{' '}
<span class="text-fg-primary">{props.author}</span>
{' '}
on
{' '}
{Provider[props.provider]}
</p>
</div>
<p class="text-fg-secondary text-sm flex-1">{props.description}</p>
<div class="flex flex-row gap-4 text-xs">
<div class="flex flex-row items-center gap-2">
<Download01Icon class="w-4 h-4" />
{abbreviateNumber(props.downloads)}
</div>
<div class="flex flex-row items-center gap-2">
<HeartIcon class="w-4 h-4" />
{abbreviateNumber(props.ratings)}
</div>
</div>
</div>
</div>
);
}
export default BrowserMain;

View file

@ -0,0 +1,32 @@
import { type Params, useSearchParams } from '@solidjs/router';
import { Match, Switch } from 'solid-js';
interface BrowserModParams extends Params {
id: string;
provider: string;
}
function BrowserMod() {
const [params] = useSearchParams<BrowserModParams>();
const isInvalid = !params.id || !params.provider;
return (
<Switch>
<Match when={isInvalid === true}>
{/* TODO */}
<div>uh oh this doesnt exist</div>
</Match>
<Match when={isInvalid === false}>
<div>
{JSON.stringify(params)}
</div>
</Match>
</Switch>
);
}
BrowserMod.getUrl = function (params: BrowserModParams): string {
return `/browser/mod?id=${params.id}&fprovider=${params.provider}`;
};
export default BrowserMod;

View file

@ -2,12 +2,14 @@ import { Route } from '@solidjs/router';
import type { ParentProps } from 'solid-js';
import BrowserMain from './BrowserMain';
import BrowserCategory from './BrowserCategory';
import BrowserMod from './BrowserMod';
function BrowserRoutes() {
return (
<>
<Route path="/" component={BrowserMain} />
<Route path="/category" component={BrowserCategory} />
<Route path="/mod" component={BrowserMod} />
</>
);
}