Skip to content

Commit

Permalink
display mods and loaders properly
Browse files Browse the repository at this point in the history
  • Loading branch information
naterfute committed Nov 26, 2024
1 parent 873b0fa commit 9f896b1
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 12 deletions.
37 changes: 37 additions & 0 deletions resources/scripts/components/elements/CheckBoxArrow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import * as React from 'react';

import { cn } from '@/lib/utils';

const CheckboxArrow = React.forwardRef<
React.ElementRef<'div'>,
React.ComponentPropsWithoutRef<'div'> & { label?: string; onChange?: () => void }
>(({ className, label, onChange, ...props }, ref) => {
const [checked, setChecked] = React.useState(false);

const toggleChecked = () => {
setChecked((prev) => {
const newCheckedState = !prev;
if (onChange) onChange(); // Call the onChange handler when the checkbox is toggled
return newCheckedState;
});
};

return (
<div className='flex items-center gap-2 select-none'>
{label && (
<span
onClick={toggleChecked}
className={'inline-block rounded-lg w-full px-2 py-1 cursor-pointer transition-colors duration-200'}
{...props}
ref={ref}
>
{label}
</span>
)}
</div>
);
});

CheckboxArrow.displayName = 'CheckboxArrow';

export { CheckboxArrow };
15 changes: 9 additions & 6 deletions resources/scripts/components/elements/ScrollMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,24 @@ import { Checkbox } from '@/components/elements/CheckboxLabel';

import { cn } from '@/lib/utils';

interface Props {
appVersion;
baseUrl: string;
}

const ScrollMenu = React.forwardRef<
React.ElementRef<'div'>,
React.ComponentPropsWithoutRef<'div'> & { items: string[] }
>(({ className, items, ...props }, ref) => {
const [checkedItems, setCheckedItems] = React.useState<string[]>([]);

// Handle checkbox change
const handleCheckboxChange = (item: string) => {
// Update the checked state
setCheckedItems((prev) => {
const updatedItems = prev.includes(item) ? prev.filter((i) => i !== item) : [...prev, item];

// Log the name of the item that was selected/deselected
console.log(`${item} is now ${updatedItems.includes(item) ? 'selected' : 'deselected'}`);
console.log(updatedItems);
// console.log(`${item} is now ${updatedItems.includes(item) ? 'selected' : 'deselected'}`);
// console.log(updatedItems);

return updatedItems;
});
Expand All @@ -32,8 +35,8 @@ const ScrollMenu = React.forwardRef<
<li key={item}>
<Checkbox
label={item}
// checked={checkedItems.includes(item)} // Set checked state
onChange={() => handleCheckboxChange(item)} // Handle change
// checked={checkedItems.includes(item)}
onChange={() => handleCheckboxChange(item)}
/>
</li>
))}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//? https://api.modrinth.com/v2/search?facets=[[%22categories:forge%22],[%22versions:1.17.1%22,%20%22versions:1.21.3%22],[%22project_type:mod%22],[%22license:mit%22]]&index=relevance
//? ?offset=40 Offset is where to start in the index. assuming we show 20 results per page. it should be 0, 20, 40, 60, 80, 100, So on and so forth
import { useEffect, useState } from 'react';
import { toast } from 'sonner';

Expand Down
41 changes: 41 additions & 0 deletions resources/scripts/components/server/modrinth/ItemSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as React from 'react';

import { CheckboxArrow } from '@/components/elements/CheckBoxArrow';
import { Checkbox } from '@/components/elements/CheckboxLabel';
import ArrowDownIcon from '@/components/elements/hugeicons/ArrowDown';

import { cn } from '@/lib/utils';

interface Props {
items: string[];
}

const EnvironmentSelector: React.FC<Props> = ({ items }) => {
const [checked, setChecked] = React.useState(false);

const [selectedItems, setSelectedItems] = React.useState<string[]>([]);
const [showAll, setShowAll] = React.useState(false);

const handleToggle = (item: string) => {
setSelectedItems((prev) => (prev.includes(item) ? prev.filter((i) => i !== item) : [...prev, item]));
};

return (
<div>
{items.slice(0, showAll ? items.length : 5).map((item) => (
<Checkbox key={item} label={item} onChange={() => handleToggle(item)} />
))}
{items.length > 5 && (
<div className='flex items-center gap-2 cursor-pointer mt-2' onClick={() => setShowAll(!showAll)}>
<CheckboxArrow label={showAll ? 'Show Less' : 'Show More'} />
<ArrowDownIcon
className={`transform transition-transform ${showAll ? 'rotate-180' : ''}`}
fill='currentColor'
/>
</div>
)}
</div>
);
};

export default EnvironmentSelector;
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { toast } from 'sonner';

import { ScrollMenu } from '@/components/elements/ScrollMenu';

import EnvironmentSelector from './ItemSelector';

interface GameLoaders {
icon: string; // SVG data(I probably wont use this)
name: string;
Expand Down Expand Up @@ -43,7 +45,7 @@ const LoaderSelector: React.FC<Props> = ({ appVersion, baseUrl }) => {

if (appVersion) {
fetchLoaders();
console.log();
// console.log();
}
}, [appVersion]);

Expand All @@ -54,7 +56,7 @@ const LoaderSelector: React.FC<Props> = ({ appVersion, baseUrl }) => {
return (
<div>
{filterLoaders.length > 0 ? (
<ScrollMenu items={filterLoaders.map((loaders) => loaders.name)} />
<EnvironmentSelector items={filterLoaders.map((loaders) => loaders.name)} />
) : (
<p>No Loaders available...</p>
)}
Expand Down
14 changes: 10 additions & 4 deletions resources/scripts/components/server/modrinth/ModrinthContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ import { useEffect, useState } from 'react';
import isEqual from 'react-fast-compare';
import { toast } from 'sonner';

import Button from '@/components/elements/ButtonV2';
import { Checkbox } from '@/components/elements/CheckboxLabel';
import ContentBox from '@/components/elements/ContentBox';
import { ModBox } from '@/components/elements/ModBox';
import PageContentBlock from '@/components/elements/PageContentBlock';
import { ScrollMenu } from '@/components/elements/ScrollMenu';

import ProjectSelector from './DisplayMods';
import GameVersionSelector from './GameVersionSelector';
import EnvironmentSelector from './ItemSelector';
import LoaderSelector from './LoaderSelector';

export default () => {
const environment = ['Client', 'Server'];
const headers: Headers = new Headers();
const url = 'https://staging-api.modrinth.com/v2';
const nonApiUrl = 'https://staging.modrinth.com';
Expand All @@ -30,6 +30,13 @@ export default () => {

return (
<PageContentBlock title={'Mods/Plugins'}>
<div className='flex justify-center items-center w-full'>
<ModBox>
<ContentBox className='p-8 bg-[#ffffff09] border-[1px] border-[#ffffff11] shadow-sm rounded-xl text-center'>
test
</ContentBox>
</ModBox>
</div>
<div className='flex flex-wrap gap-4'>
<ContentBox
className='p-8 bg-[#ffffff09] border-[1px] border-[#ffffff11] shadow-sm rounded-xl md:w-1/6'
Expand All @@ -48,8 +55,7 @@ export default () => {
</ModBox>
<ModBox>
<ContentBox title='Environment' className=''>
<Checkbox label='Client'>Client</Checkbox>
<Checkbox label='Server'>Server</Checkbox>
<EnvironmentSelector items={environment}></EnvironmentSelector>
</ContentBox>
</ModBox>
</ContentBox>
Expand Down
139 changes: 139 additions & 0 deletions resources/scripts/components/server/modrinth/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
//? https://api.modrinth.com/v2/search?facets=[[%22categories:forge%22],[%22versions:1.17.1%22,%20%22versions:1.21.3%22],[%22project_type:mod%22],[%22license:mit%22]]&index=relevance
//? ?offset=40 Offset is where to start in the index. assuming we show 20 results per page. it should be 0, 20, 40, 60, 80, 100, So on and so forth
import { useEffect, useState } from 'react';
import { toast } from 'sonner';

import ContentBox from '@/components/elements/ContentBox';
import { ScrollMenu } from '@/components/elements/ScrollMenu';

interface Project {
project_id: string;
project_type: string;
slug: string;
author: string;
title: string;
description: string;
categories: string[];
versions: string[];
downloads: number;
follows: number;
icon_url: string;
}

interface Props {
appVersion;
baseUrl: string;
nonApiUrl: string;
}

const ProjectSelector: React.FC<Props> = ({ appVersion, baseUrl, nonApiUrl }) => {
const [projects, setProjects] = useState<Project[]>([]);
const apiUrl = `${baseUrl}/search`;

useEffect(() => {
async function fetchProjects() {
try {
const response = await fetch(apiUrl, {
headers: {
'Content-Type': 'application/json',
'User-Agent': `pyrohost/pyrodactyl/${appVersion} (pyro.host)`,
},
});

if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}

const data = await response.json();
for (const x of data.hits) {
if (x['icon_url'] == '') {
x['icon_url'] = 'N/A';
}
}

setProjects(data.hits || []); // Safely access hits
} catch (error) {
toast.error('Failed to fetch projects.');
console.error(error);
}
}

if (appVersion) {
fetchProjects();
}
}, [appVersion]);

const filteredProjects = projects.filter((project) => {
return ['forge', 'fabric'].some((category) => project.categories.includes(category));
});

return (
<div>
{filteredProjects.length > 0 ? (
<div>
{filteredProjects.map((project) => (
// <ContentBox className='p-8 bg-[#ffffff09] border-[1px] border-[#ffffff11] shadow-sm rounded-xl w-full mb-4'>
<ContentBox
key={project.project_id}
className='p-8 bg-[#ffffff09] border-[1px] border-[#ffffff11] shadow-sm rounded-xl w-full mb-4'
>
<div className='flex items-center'>
<ContentBox className='p-3 rounded-xl mr-4'>
{project.icon_url && project.icon_url !== 'N/A' ? (
<img src={project.icon_url} className='mt-4 w-16 h-16 object-cover rounded' />
) : (
<svg
fillRule='evenodd'
strokeLinecap='round'
strokeLinejoin='round'
strokeMiterlimit='1.5'
clipRule='evenodd'
height='70'
width='70'
viewBox='0 0 104 104'
aria-hidden='true'
>
<path fill='none' d='M0 0h103.4v103.4H0z'></path>{' '}
<path
fill='none'
stroke='#9a9a9a'
strokeWidth='5'
d='M51.7 92.5V51.7L16.4 31.3l35.3 20.4L87 31.3 51.7 11 16.4 31.3v40.8l35.3 20.4L87 72V31.3L51.7 11'
></path>
</svg>
)}
</ContentBox>
<div className=''>
<a href={`${nonApiUrl}/mod/${project.project_id}`} target='_blank' rel='noreferrer'>
<h2 className='text-lg font-semibold text-white'>{project.title}</h2>
</a>
<p className='text-sm text-gray-300 mb-2'>
Author: <span className='font-medium'>{project.author}</span>
</p>
<p className='text-sm text-gray-400'>{project.description}</p>
<p className='text-sm text-gray-500 mt-2'>
Downloads: {project.downloads} | Follows: {project.follows}
</p>
<p>Loader: {project.categories}</p>
</div>
</div>
</ContentBox>
))}
</div>
) : (
<p className='text-white'>No projects available...</p>
)}
</div>
);
};

export default ProjectSelector;
// <path data-v-78e239b4="" fill="none" stroke="#9a9a9a" stroke-width="5" d="M51.7 92.5V51.7L16.4 31.3l35.3 20.4L87 31.3 51.7 11 16.4 31.3v40.8l35.3 20.4L87 72V31.3L51.7 11"></path>
// <ScrollMenu
// items={filteredProjects.map((project) => ({
// id: project.project_id,
// name: project.title,
// description: project.description,
// icon: project.icon_url,
// }))}
// />

0 comments on commit 9f896b1

Please sign in to comment.