feat(preact): migrate react to lightweight preact

This commit is contained in:
Vrezh Fedora 2025-02-23 20:10:11 +01:00
parent 53efb5d87d
commit fc07f4989a
9 changed files with 2924 additions and 1705 deletions

View file

@ -2,7 +2,7 @@
import { defineConfig } from 'astro/config' import { defineConfig } from 'astro/config'
import tailwind from '@astrojs/tailwind' import tailwind from '@astrojs/tailwind'
import react from '@astrojs/react' import preact from '@astrojs/preact'
import sitemap from '@astrojs/sitemap' import sitemap from '@astrojs/sitemap'
@ -10,7 +10,7 @@ import sitemap from '@astrojs/sitemap'
export default defineConfig({ export default defineConfig({
integrations: [ integrations: [
tailwind(), tailwind(),
react(), preact({ compat: true }),
sitemap({ sitemap({
// TODO: Maybe? Maybe not? // TODO: Maybe? Maybe not?
// filter: (page) => !page.includes('mods/'), // filter: (page) => !page.includes('mods/'),

4558
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -10,10 +10,14 @@
"wrangler": "wrangler", "wrangler": "wrangler",
"astro": "astro" "astro": "astro"
}, },
"overrides": {
"react": "npm:@preact/compat@latest",
"react-dom": "npm:@preact/compat@latest"
},
"dependencies": { "dependencies": {
"@astrojs/check": "^0.9.4", "@astrojs/check": "^0.9.4",
"@astrojs/cloudflare": "^12.2.1", "@astrojs/cloudflare": "^12.2.1",
"@astrojs/react": "^4.2.0", "@astrojs/preact": "^4.0.4",
"@astrojs/rss": "^4.0.11", "@astrojs/rss": "^4.0.11",
"@astrojs/sitemap": "^3.2.1", "@astrojs/sitemap": "^3.2.1",
"@astrojs/tailwind": "^6.0.0", "@astrojs/tailwind": "^6.0.0",
@ -21,8 +25,6 @@
"@fortawesome/fontawesome-svg-core": "^6.7.1", "@fortawesome/fontawesome-svg-core": "^6.7.1",
"@fortawesome/free-brands-svg-icons": "^6.7.1", "@fortawesome/free-brands-svg-icons": "^6.7.1",
"@fortawesome/free-solid-svg-icons": "^6.7.1", "@fortawesome/free-solid-svg-icons": "^6.7.1",
"@types/react": "^19.0.1",
"@types/react-dom": "^19.0.2",
"astro": "^5.3.0", "astro": "^5.3.0",
"astro-navbar": "^2.3.7", "astro-navbar": "^2.3.7",
"autoprefixer": "10.4.14", "autoprefixer": "10.4.14",
@ -32,11 +34,10 @@
"lucide-react": "^0.475.0", "lucide-react": "^0.475.0",
"motion": "^11.13.5", "motion": "^11.13.5",
"postcss": "^8.5.1", "postcss": "^8.5.1",
"preact": "^10.26.2",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"prettier-plugin-astro": "^0.14.1", "prettier-plugin-astro": "^0.14.1",
"prettier-plugin-tailwindcss": "^0.6.6", "prettier-plugin-tailwindcss": "^0.6.6",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"sharp": "^0.33.5", "sharp": "^0.33.5",
"tailwindcss": "^3.4.15", "tailwindcss": "^3.4.15",
"typescript": "^5.6.3" "typescript": "^5.6.3"

View file

@ -1,9 +1,9 @@
import React, { useState } from 'react' import { useState } from 'preact/hooks'
import MyLogo from './Mylogo' import MyLogo from './Mylogo'
import { Menu } from 'lucide-react' import { Menu } from 'lucide-react'
import ThemeSwitch from './ThemeSwitch/ThemeSwitch' import ThemeSwitch from './ThemeSwitch/ThemeSwitch'
const MobileNavbar: React.FC = () => { const MobileNavbar = () => {
const [isMenuOpen, setMenuOpen] = useState(false) const [isMenuOpen, setMenuOpen] = useState(false)
const menuTransformClass = isMenuOpen ? 'translate-x-0' : 'translate-x-full' const menuTransformClass = isMenuOpen ? 'translate-x-0' : 'translate-x-full'

View file

@ -1,5 +1,4 @@
import type React from 'react' import { useState, useEffect } from 'preact/hooks'
import { useState, useEffect } from 'react'
import type { ZenTheme } from '../mods' import type { ZenTheme } from '../mods'
import { library, icon } from '@fortawesome/fontawesome-svg-core' import { library, icon } from '@fortawesome/fontawesome-svg-core'
import { faSort, faSortUp, faSortDown } from '@fortawesome/free-solid-svg-icons' import { faSort, faSortUp, faSortDown } from '@fortawesome/free-solid-svg-icons'
@ -48,15 +47,17 @@ export default function ModsList({ mods }: ModsListProps) {
return defaultSortIcon return defaultSortIcon
} }
function handleSearch(e: React.ChangeEvent<HTMLInputElement>) { function handleSearch(e: Event) {
setSearch(e.target.value) const target = e.target as HTMLInputElement
setSearch(target.value)
} }
function handleLimitChange(e: React.ChangeEvent<HTMLSelectElement>) { function handleLimitChange(e: Event) {
setLimit(Number.parseInt(e.target.value, 10)) const target = e.target as HTMLSelectElement
setLimit(Number.parseInt(target.value, 10))
} }
function handlePageSubmit(e: React.FormEvent) { function handlePageSubmit(e: Event) {
e.preventDefault() e.preventDefault()
const newPage = Number.parseInt(pageInput, 10) const newPage = Number.parseInt(pageInput, 10)
if (!Number.isNaN(newPage) && newPage >= 1 && newPage <= totalPages) { if (!Number.isNaN(newPage) && newPage >= 1 && newPage <= totalPages) {
@ -67,27 +68,24 @@ export default function ModsList({ mods }: ModsListProps) {
} }
} }
function handlePageInputChange(e: React.ChangeEvent<HTMLInputElement>) { function handlePageInputChange(e: Event) {
setPageInput(e.target.value) const target = e.target as HTMLInputElement
setPageInput(target.value)
} }
function getPageUrl(pageNum: number) { function getPageUrl(pageNum: number) {
const params = new URLSearchParams(searchParams) const params = new URLSearchParams(searchParams)
if (pageNum > 1) { if (pageNum > 1) {
params.set('page', pageNum.toString()) params.set('page', pageNum.toString())
} else { } else {
params.delete('page') params.delete('page')
} }
const queryString = params.toString() const queryString = params.toString()
return `/mods${queryString ? `?${queryString}` : ''}` return `/mods${queryString ? `?${queryString}` : ''}`
} }
function renderPagination() { function renderPagination() {
if (totalPages <= 1) return null if (totalPages <= 1) return null
// Render page input for larger page counts
return ( return (
<div className="mx-auto mb-12 flex items-center justify-center gap-4 px-8"> <div className="mx-auto mb-12 flex items-center justify-center gap-4 px-8">
<a <a
@ -105,7 +103,7 @@ export default function ModsList({ mods }: ModsListProps) {
<input <input
type="text" type="text"
value={pageInput} value={pageInput}
onChange={handlePageInputChange} onInput={handlePageInputChange}
className="w-16 rounded border border-dark bg-transparent px-2 py-1 text-center text-sm" className="w-16 rounded border border-dark bg-transparent px-2 py-1 text-center text-sm"
aria-label="Page number" aria-label="Page number"
/> />
@ -137,7 +135,7 @@ export default function ModsList({ mods }: ModsListProps) {
className="w-full rounded-full border-2 border-dark bg-transparent px-6 py-2 text-lg outline-none" className="w-full rounded-full border-2 border-dark bg-transparent px-6 py-2 text-lg outline-none"
placeholder="Type to search..." placeholder="Type to search..."
value={search} value={search}
onChange={handleSearch} onInput={handleSearch}
/> />
</div> </div>
@ -179,7 +177,7 @@ export default function ModsList({ mods }: ModsListProps) {
<select <select
id="limit" id="limit"
value={limit} value={limit}
onChange={handleLimitChange} onInput={handleLimitChange}
className="rounded border border-dark bg-transparent px-2 py-1 text-sm [&>option]:text-black" className="rounded border border-dark bg-transparent px-2 py-1 text-sm [&>option]:text-black"
> >
<option value="12">12</option> <option value="12">12</option>

View file

@ -1,10 +1,8 @@
import React from 'react'
interface MyLogoProps { interface MyLogoProps {
className?: string className?: string
} }
const MyLogo: React.FC<MyLogoProps> = ({ className }) => ( const MyLogo = ({ className }: MyLogoProps) => (
<svg <svg
className={className} className={className}
width="32" width="32"

View file

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react' import { useEffect, useState } from 'preact/hooks'
import './index.css' import './index.css'
interface Props { interface Props {
@ -6,8 +6,8 @@ interface Props {
className?: string className?: string
} }
const ThemeSwitch: React.FC<Props> = ({ label, className }) => { const ThemeSwitch = ({ label, className = '' }: Props) => {
const [isLight, setIsLight] = useState<boolean>(() => { const [isLight, setIsLight] = useState(() => {
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
return localStorage.getItem('theme') === 'light' return localStorage.getItem('theme') === 'light'
} }
@ -43,7 +43,7 @@ const ThemeSwitch: React.FC<Props> = ({ label, className }) => {
<path <path
d="M12 9c1.654 0 3 1.346 3 3s-1.346 3-3 3-3-1.346-3-3 1.346-3 3-3zm0-2c-2.762 0-5 2.238-5 5s2.238 5 5 5 5-2.238 5-5-2.238-5-5-5zm-4.184-.599l-3.593-3.594-1.415 1.414 3.595 3.595c.401-.537.876-1.013 1.413-1.415zm4.184-1.401c.34 0 .672.033 1 .08v-5.08h-2v5.08c.328-.047.66-.08 1-.08zm5.598 2.815l3.595-3.595-1.414-1.414-3.595 3.595c.537.402 1.012.878 1.414 1.414zm-12.598 4.185c0-.34.033-.672.08-1h-5.08v2h5.08c-.047-.328-.08-.66-.08-1zm11.185 5.598l3.594 3.593 1.415-1.414-3.594-3.593c-.403.536-.879 1.012-1.415 1.414zm-9.784-1.414l-3.593 3.593 1.414 1.414 3.593-3.593c-.536-.402-1.011-.877-1.414-1.414zm12.519-5.184c.047.328.08.66.08 1s-.033.672-.08 1h5.08v-2h-5.08zm-6.92 8c-.34 0-.672-.033-1-.08v5.08h2v-5.08c-.328.047-.66.08-1 .08z" d="M12 9c1.654 0 3 1.346 3 3s-1.346 3-3 3-3-1.346-3-3 1.346-3 3-3zm0-2c-2.762 0-5 2.238-5 5s2.238 5 5 5 5-2.238 5-5-2.238-5-5-5zm-4.184-.599l-3.593-3.594-1.415 1.414 3.595 3.595c.401-.537.876-1.013 1.413-1.415zm4.184-1.401c.34 0 .672.033 1 .08v-5.08h-2v5.08c.328-.047.66-.08 1-.08zm5.598 2.815l3.595-3.595-1.414-1.414-3.595 3.595c.537.402 1.012.878 1.414 1.414zm-12.598 4.185c0-.34.033-.672.08-1h-5.08v2h5.08c-.047-.328-.08-.66-.08-1zm11.185 5.598l3.594 3.593 1.415-1.414-3.594-3.593c-.403.536-.879 1.012-1.415 1.414zm-9.784-1.414l-3.593 3.593 1.414 1.414 3.593-3.593c-.536-.402-1.011-.877-1.414-1.414zm12.519-5.184c.047.328.08.66.08 1s-.033.672-.08 1h5.08v-2h-5.08zm-6.92 8c-.34 0-.672-.033-1-.08v5.08h2v-5.08c-.328.047-.66.08-1 .08z"
fill="currentColor" fill="currentColor"
></path> />
</svg> </svg>
<svg <svg
className="ac-theme-switch-icon ac-theme-switch-icon--light" className="ac-theme-switch-icon ac-theme-switch-icon--light"
@ -52,7 +52,7 @@ const ThemeSwitch: React.FC<Props> = ({ label, className }) => {
<path <path
d="M10.719 2.082c-2.572 2.028-4.719 5.212-4.719 9.918 0 4.569 1.938 7.798 4.548 9.895-4.829-.705-8.548-4.874-8.548-9.895 0-5.08 3.808-9.288 8.719-9.918zm1.281-2.082c-6.617 0-12 5.383-12 12s5.383 12 12 12c1.894 0 3.87-.333 5.37-1.179-3.453-.613-9.37-3.367-9.37-10.821 0-7.555 6.422-10.317 9.37-10.821-1.74-.682-3.476-1.179-5.37-1.179zm0 10.999c1.437.438 2.562 1.564 2.999 3.001.44-1.437 1.565-2.562 3.001-3-1.436-.439-2.561-1.563-3.001-3-.437 1.436-1.562 2.561-2.999 2.999zm8.001.001c.958.293 1.707 1.042 2 2.001.291-.959 1.042-1.709 1.999-2.001-.957-.292-1.707-1.042-2-2-.293.958-1.042 1.708-1.999 2zm-1-9c-.437 1.437-1.563 2.562-2.998 3.001 1.438.44 2.561 1.564 3.001 3.002.437-1.438 1.563-2.563 2.996-3.002-1.433-.437-2.559-1.564-2.999-3.001z" d="M10.719 2.082c-2.572 2.028-4.719 5.212-4.719 9.918 0 4.569 1.938 7.798 4.548 9.895-4.829-.705-8.548-4.874-8.548-9.895 0-5.08 3.808-9.288 8.719-9.918zm1.281-2.082c-6.617 0-12 5.383-12 12s5.383 12 12 12c1.894 0 3.87-.333 5.37-1.179-3.453-.613-9.37-3.367-9.37-10.821 0-7.555 6.422-10.317 9.37-10.821-1.74-.682-3.476-1.179-5.37-1.179zm0 10.999c1.437.438 2.562 1.564 2.999 3.001.44-1.437 1.565-2.562 3.001-3-1.436-.439-2.561-1.563-3.001-3-.437 1.436-1.562 2.561-2.999 2.999zm8.001.001c.958.293 1.707 1.042 2 2.001.291-.959 1.042-1.709 1.999-2.001-.957-.292-1.707-1.042-2-2-.293.958-1.042 1.708-1.999 2zm-1-9c-.437 1.437-1.563 2.562-2.998 3.001 1.438.44 2.561 1.564 3.001 3.002.437-1.438 1.563-2.563 2.996-3.002-1.433-.437-2.559-1.564-2.999-3.001z"
fill="currentColor" fill="currentColor"
></path> />
</svg> </svg>
{label ? ( {label ? (
<span className="ac-theme-switch-label">{label}</span> <span className="ac-theme-switch-label">{label}</span>

View file

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'preact/hooks'
import type { ZenTheme } from '../mods' import type { ZenTheme } from '../mods'
type SortOrder = 'default' | 'asc' | 'desc' type SortOrder = 'default' | 'asc' | 'desc'
@ -70,7 +70,9 @@ export function useModsSearch(mods: ZenTheme[]) {
searchParams.delete('limit') searchParams.delete('limit')
} }
const newUrl = `${window.location.pathname}${searchParams.toString() ? `?${searchParams.toString()}` : ''}` const newUrl = `${window.location.pathname}${
searchParams.toString() ? `?${searchParams.toString()}` : ''
}`
window.history.replaceState({}, '', newUrl) window.history.replaceState({}, '', newUrl)
}, [state, searchParams]) }, [state, searchParams])

View file

@ -2,6 +2,6 @@
"extends": "astro/tsconfigs/strict", "extends": "astro/tsconfigs/strict",
"compilerOptions": { "compilerOptions": {
"jsx": "react-jsx", "jsx": "react-jsx",
"jsxImportSource": "react" "jsxImportSource": "preact"
} }
} }