mirror of
https://github.com/Snigdha-OS/package-browser.git
synced 2025-09-11 07:05:02 +02:00
feat: use JSX.Element as component type
This commit is contained in:
@@ -1,4 +1,9 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import {
|
||||||
|
useState,
|
||||||
|
useEffect,
|
||||||
|
JSX
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import { Header } from './components/Header';
|
import { Header } from './components/Header';
|
||||||
import { SearchBar } from './components/SearchBar';
|
import { SearchBar } from './components/SearchBar';
|
||||||
import { PackageList } from './components/PackageList';
|
import { PackageList } from './components/PackageList';
|
||||||
@@ -8,7 +13,7 @@ import {
|
|||||||
Repository
|
Repository
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
export default function App() {
|
export default function App(): JSX.Element {
|
||||||
const { packages, loading, error } = usePackages();
|
const { packages, loading, error } = usePackages();
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const [selectedRepository, setSelectedRepository] = useState('all');
|
const [selectedRepository, setSelectedRepository] = useState('all');
|
||||||
|
@@ -1,3 +1,7 @@
|
|||||||
|
import {
|
||||||
|
JSX
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import { ThemeToggle } from './ThemeToggle';
|
import { ThemeToggle } from './ThemeToggle';
|
||||||
import { Logo } from './Logo';
|
import { Logo } from './Logo';
|
||||||
|
|
||||||
|
@@ -1,12 +1,16 @@
|
|||||||
import React from 'react';
|
import {
|
||||||
|
useState,
|
||||||
|
JSX
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import { Terminal, Copy, Check } from 'lucide-react';
|
import { Terminal, Copy, Check } from 'lucide-react';
|
||||||
|
|
||||||
interface InstallGuideProps {
|
interface InstallGuideProps {
|
||||||
packageName: string;
|
packageName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function InstallGuide({ packageName }: InstallGuideProps) {
|
export function InstallGuide({ packageName }: InstallGuideProps): JSX.Element {
|
||||||
const [copied, setCopied] = React.useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
||||||
const command = `sudo pacman -S ${packageName}`;
|
const command = `sudo pacman -S ${packageName}`;
|
||||||
|
|
||||||
|
@@ -1,4 +1,8 @@
|
|||||||
export function Logo() {
|
import {
|
||||||
|
JSX
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
|
export function Logo(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
{/* Replace this SVG with your custom logo */}
|
{/* Replace this SVG with your custom logo */}
|
||||||
|
@@ -1,4 +1,8 @@
|
|||||||
import { useState } from 'react';
|
import {
|
||||||
|
useState,
|
||||||
|
JSX
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import { Box, ChevronDown, ChevronUp } from 'lucide-react';
|
import { Box, ChevronDown, ChevronUp } from 'lucide-react';
|
||||||
import { Package } from '../types';
|
import { Package } from '../types';
|
||||||
import { InstallGuide } from './InstallGuide';
|
import { InstallGuide } from './InstallGuide';
|
||||||
@@ -7,7 +11,7 @@ interface PackageCardProps {
|
|||||||
package: Package;
|
package: Package;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PackageCard({ package: pkg }: PackageCardProps) {
|
export function PackageCard({ package: pkg }: PackageCardProps): JSX.Element {
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
import React from 'react';
|
import React, {
|
||||||
|
JSX
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
interface BadgeProps {
|
interface BadgeProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -20,7 +22,7 @@ const sizeClasses = {
|
|||||||
large: 'text-base px-4 py-2',
|
large: 'text-base px-4 py-2',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Badge({ children, color = 'default', size = 'medium', ariaLabel }: BadgeProps) {
|
export function Badge({ children, color = 'default', size = 'medium', ariaLabel }: BadgeProps): JSX.Element {
|
||||||
const badgeColorClass = colorClasses[color] || colorClasses.default;
|
const badgeColorClass = colorClasses[color] || colorClasses.default;
|
||||||
const badgeSizeClass = sizeClasses[size] || sizeClasses.medium;
|
const badgeSizeClass = sizeClasses[size] || sizeClasses.medium;
|
||||||
|
|
||||||
|
@@ -1,3 +1,7 @@
|
|||||||
|
import {
|
||||||
|
JSX
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import { ChevronDown, ChevronUp } from 'lucide-react';
|
import { ChevronDown, ChevronUp } from 'lucide-react';
|
||||||
|
|
||||||
interface ExpandButtonProps {
|
interface ExpandButtonProps {
|
||||||
@@ -5,7 +9,7 @@ interface ExpandButtonProps {
|
|||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ExpandButton({ expanded, onClick }: ExpandButtonProps) {
|
export function ExpandButton({ expanded, onClick }: ExpandButtonProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
@@ -1,4 +1,8 @@
|
|||||||
import { useState } from 'react';
|
import {
|
||||||
|
useState,
|
||||||
|
JSX
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import { Boxes } from 'lucide-react';
|
import { Boxes } from 'lucide-react';
|
||||||
import { Package } from '../../types';
|
import { Package } from '../../types';
|
||||||
import { InstallGuide } from '../InstallGuide';
|
import { InstallGuide } from '../InstallGuide';
|
||||||
@@ -9,7 +13,7 @@ interface PackageCardProps {
|
|||||||
package: Package;
|
package: Package;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PackageCard({ package: pkg }: PackageCardProps) {
|
export function PackageCard({ package: pkg }: PackageCardProps): JSX.Element {
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -1,3 +1,7 @@
|
|||||||
|
import {
|
||||||
|
JSX
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import { Package } from '../types';
|
import { Package } from '../types';
|
||||||
import { PackageCard } from './PackageCard';
|
import { PackageCard } from './PackageCard';
|
||||||
|
|
||||||
@@ -6,7 +10,7 @@ interface PackageListProps {
|
|||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PackageList({ packages, loading }: PackageListProps) {
|
export function PackageList({ packages, loading }: PackageListProps): JSX.Element {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center h-64">
|
<div className="flex items-center justify-center h-64">
|
||||||
|
@@ -1,31 +0,0 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
|
|
||||||
type RepositorySelectorProps = {
|
|
||||||
onSelectRepository: (repo: 'core' | 'extra' | 'all') => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function RepositorySelector({ onSelectRepository }: RepositorySelectorProps) {
|
|
||||||
const [selectedRepository, setSelectedRepository] = useState<'core' | 'extra' | 'all'>('all');
|
|
||||||
|
|
||||||
const handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
|
||||||
const selected = event.target.value as 'core' | 'extra' | 'all';
|
|
||||||
setSelectedRepository(selected);
|
|
||||||
onSelectRepository(selected); // Pass the selection back to parent
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<label htmlFor="repository-selector" className="mr-2 text-nord-6">Select Repository:</label>
|
|
||||||
<select
|
|
||||||
id="repository-selector"
|
|
||||||
value={selectedRepository}
|
|
||||||
onChange={handleChange}
|
|
||||||
className="bg-nord-1 text-nord-6 border border-nord-4 rounded-md p-2"
|
|
||||||
>
|
|
||||||
<option value="all">All</option>
|
|
||||||
<option value="core">Core</option>
|
|
||||||
<option value="extra">Extra</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@@ -1,10 +1,14 @@
|
|||||||
|
import {
|
||||||
|
JSX
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import { Search } from 'lucide-react';
|
import { Search } from 'lucide-react';
|
||||||
|
|
||||||
interface SearchBarProps {
|
interface SearchBarProps {
|
||||||
value: string;
|
value: string;
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
selectedRepository: 'core' | 'extra' | 'all';
|
//selectedRepository: 'core' | 'extra' | 'all';
|
||||||
onFilterChange: (repo: 'core' | 'extra' | 'all') => void;
|
//onFilterChange: (repo: 'core' | 'extra' | 'all') => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SearchBar({
|
export function SearchBar({
|
||||||
@@ -12,7 +16,7 @@ export function SearchBar({
|
|||||||
onChange,
|
onChange,
|
||||||
// selectedRepository,
|
// selectedRepository,
|
||||||
// onFilterChange,
|
// onFilterChange,
|
||||||
}: SearchBarProps) {
|
}: SearchBarProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full max-w-md mx-auto">
|
<div className="relative w-full max-w-md mx-auto">
|
||||||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||||
|
@@ -1,7 +1,11 @@
|
|||||||
|
import {
|
||||||
|
JSX
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import { Moon, Sun } from 'lucide-react';
|
import { Moon, Sun } from 'lucide-react';
|
||||||
import { useTheme } from '../hooks/useTheme';
|
import { useTheme } from '../hooks/useTheme';
|
||||||
|
|
||||||
export function ThemeToggle() {
|
export function ThemeToggle(): JSX.Element {
|
||||||
const { theme, toggleTheme } = useTheme();
|
const { theme, toggleTheme } = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -1,55 +0,0 @@
|
|||||||
export interface Package {
|
|
||||||
name: string;
|
|
||||||
version: string;
|
|
||||||
description: string;
|
|
||||||
repository: 'core' | 'extra'; // This can be 'core', 'extra', or 'all'
|
|
||||||
}
|
|
||||||
|
|
||||||
const MIRRORS = [
|
|
||||||
'https://raw.githubusercontent.com/Snigdha-OS/snigdhaos-core/refs/heads/master/packages.txt',
|
|
||||||
'https://raw.githubusercontent.com/Snigdha-OS/snigdhaos-extra/refs/heads/master/packages.txt'
|
|
||||||
];
|
|
||||||
|
|
||||||
async function fetchFromMirror(url: string): Promise<Package[]> {
|
|
||||||
const response = await fetch(url);
|
|
||||||
const text = await response.text();
|
|
||||||
|
|
||||||
// Determine the repository based on the URL
|
|
||||||
const repository = url.includes('snigdhaos-core') ? 'core' : 'extra';
|
|
||||||
|
|
||||||
return text
|
|
||||||
.split('\n')
|
|
||||||
.filter(Boolean) // Remove empty lines
|
|
||||||
.map((line) => {
|
|
||||||
const [name, version, ...descParts] = line.split(' ');
|
|
||||||
return {
|
|
||||||
name,
|
|
||||||
version,
|
|
||||||
description: descParts.join(' '),
|
|
||||||
repository, // Set the repository name dynamically
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function fetchPackages(): Promise<Package[]> {
|
|
||||||
let packages: Package[] = [];
|
|
||||||
|
|
||||||
// Try fetching from each mirror
|
|
||||||
for (const mirror of MIRRORS) {
|
|
||||||
try {
|
|
||||||
const result = await fetchFromMirror(mirror);
|
|
||||||
packages = packages.concat(result); // Append fetched packages
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(`Failed to fetch from mirror ${mirror}:`, error);
|
|
||||||
continue; // Continue to next mirror if one fails
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no packages were fetched, throw an error
|
|
||||||
if (packages.length === 0) {
|
|
||||||
throw new Error('All mirrors failed to respond');
|
|
||||||
}
|
|
||||||
|
|
||||||
return packages;
|
|
||||||
}
|
|
||||||
|
|
@@ -2,7 +2,12 @@ import { useState, useEffect, useCallback } from 'react';
|
|||||||
import { Package } from '../types';
|
import { Package } from '../types';
|
||||||
import { fetchPackages } from '../services/api';
|
import { fetchPackages } from '../services/api';
|
||||||
|
|
||||||
export function usePackages() {
|
export function usePackages(): {
|
||||||
|
packages: Package[];
|
||||||
|
loading: boolean;
|
||||||
|
error: string | null;
|
||||||
|
retryLoading: () => void;
|
||||||
|
} {
|
||||||
const [packages, setPackages] = useState<Package[]>([]);
|
const [packages, setPackages] = useState<Package[]>([]);
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
@@ -1,8 +1,13 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
|
|
||||||
type Theme = 'light' | 'dark';
|
import {
|
||||||
|
Theme
|
||||||
|
} from '../types';
|
||||||
|
|
||||||
export function useTheme() {
|
export function useTheme(): {
|
||||||
|
theme: Theme;
|
||||||
|
toggleTheme: () => void;
|
||||||
|
} {
|
||||||
const getInitialTheme = (): Theme => {
|
const getInitialTheme = (): Theme => {
|
||||||
const savedTheme = localStorage.getItem('theme') as Theme;
|
const savedTheme = localStorage.getItem('theme') as Theme;
|
||||||
if (savedTheme === 'dark' || savedTheme === 'light') return savedTheme;
|
if (savedTheme === 'dark' || savedTheme === 'light') return savedTheme;
|
||||||
|
@@ -7,6 +7,9 @@ export enum Repository {
|
|||||||
Multilib = 'multilib'
|
Multilib = 'multilib'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type alias for UI themes
|
||||||
|
export type Theme = ('light' | 'dark');
|
||||||
|
|
||||||
// Interface representing a single package
|
// Interface representing a single package
|
||||||
export interface Package {
|
export interface Package {
|
||||||
/** The name of the package */
|
/** The name of the package */
|
||||||
|
Reference in New Issue
Block a user