From b603732dd6cce71df122cf9cae05d2d871a1094b Mon Sep 17 00:00:00 2001 From: anibus Date: Tue, 21 Jul 2026 02:27:22 +0300 Subject: [PATCH] =?UTF-8?q?Vibe-code=20=D0=B4=D0=B8=D0=B7=D0=B0=D0=B9?= =?UTF-8?q?=D0=BD=20dark=20theme=20re-vibecode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/images/dowdelogo.png | Bin 0 -> 33458 bytes src/App.tsx | 132 +++++++++++-------- src/Routes.tsx | 6 +- src/classes/Ability.tsx | 8 +- src/classes/AbilityFull.tsx | 28 +--- src/classes/AbilitySpawnedObject.tsx | 19 +-- src/classes/DeathExplosion.tsx | 25 +--- src/classes/DpsTable.tsx | 6 +- src/classes/ModifiersProvideTable.tsx | 22 +--- src/classes/Required.tsx | 30 ++--- src/classes/Sergeant.tsx | 19 +-- src/classes/UnitsTable.tsx | 22 +--- src/classes/Weapon.tsx | 10 +- src/classes/WeaponFull.tsx | 34 ++--- src/classes/building/BuildingAddon.tsx | 25 +--- src/classes/building/Research.tsx | 14 +- src/classes/building/ResearchFull.tsx | 16 +-- src/commons/BackButton.tsx | 8 +- src/commons/DescriptionBox.tsx | 6 +- src/commons/StyledAccordion.tsx | 6 +- src/commons/StyledLink.tsx | 4 +- src/commons/StyledPaper.tsx | 19 +++ src/context/ThemeContext.tsx | 48 +++++++ src/core/api.js | 11 ++ src/index.css | 44 ++++++- src/pages/BuildingPage.tsx | 96 ++++++++------ src/pages/ModPage.tsx | 32 +++-- src/pages/ModsPage.tsx | 169 ++++++++++++++----------- src/pages/RacePageFast.tsx | 136 ++++++++++++-------- src/pages/UnitPage.tsx | 87 ++++++++----- src/themes/theme.ts | 83 ++++++++++++ 31 files changed, 689 insertions(+), 476 deletions(-) create mode 100644 public/images/dowdelogo.png create mode 100644 src/commons/StyledPaper.tsx create mode 100644 src/context/ThemeContext.tsx create mode 100644 src/themes/theme.ts diff --git a/public/images/dowdelogo.png b/public/images/dowdelogo.png new file mode 100644 index 0000000000000000000000000000000000000000..483980f58ed7c3bfc9cd00a7252d661f78784ae9 GIT binary patch literal 33458 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 ({ @@ -20,12 +22,20 @@ const StyledAppBar = styled(AppBar)(({ theme }) => ({ borderBottom: '1px solid rgba(255, 255, 255, 0.1)', })); +const StyledAppBarLight = styled(AppBar)(({ theme }) => ({ + background: 'linear-gradient(135deg, #ffffff 0%, #f0f0f0 50%, #e0e0e0 100%)', + boxShadow: '0 4px 20px rgba(0, 0, 0, 0.1)', + borderBottom: '1px solid rgba(0, 0, 0, 0.1)', +})); + const LogoText = styled(Typography)(({ theme }) => ({ fontSize: '1.5rem', fontWeight: 800, - background: 'linear-gradient(135deg, #e94560 0%, #ff6b6b 100%)', + background: theme.palette.mode === 'dark' + ? 'linear-gradient(135deg, #dee2e6 0%, #cccccc 100%)' + : 'linear-gradient(135deg, #1a1a2e 0%, #333333 100%)', WebkitBackgroundClip: 'text', - WebkitTextFillColor: 'transparent', + WebkitTextFillColor: theme.palette.mode === 'dark' ? '#dee2e6' : '#1a1a2e', letterSpacing: '-0.5px', cursor: 'pointer', '&:hover': { @@ -34,69 +44,79 @@ const LogoText = styled(Typography)(({ theme }) => ({ }, })); -function App() { - const theme = useTheme(); +const ThemeToggle = styled(IconButton)(({ theme }) => ({ + color: 'inherit', + '&:hover': { + backgroundColor: 'rgba(255, 255, 255, 0.1)', + }, +})); - const themeConfig = createTheme({ - breakpoints: { - values: { - xs: 0, - sm: 600, - md: 900, - lg: 1320, - xl: 1536, - }, - }, - palette: { - primary: { - main: "#1a1a2e", - }, - background: { - default: '#0a0a0a', - paper: '#1a1a2e', - }, - }, - typography: { - fontFamily: '"Inter", "Roboto", "Helvetica", sans-serif', - }, - shape: { - borderRadius: 12, - }, - components: { - MuiTableCell: { - styleOverrides: { - root: { - color: 'rgba(255, 255, 255, 0.85)', - }, - }, - }, - }, - }); +const ThemeToggleLight = styled(IconButton)(({ theme }) => ({ + color: 'inherit', + '&:hover': { + backgroundColor: 'rgba(0, 0, 0, 0.05)', + }, +})); + +function AppBarContent() { + const { mode, toggleTheme } = useThemeContext(); + const navigate = useNavigate(); + const isDark = mode === 'dark'; + + const handleLogoClick = () => navigate('/'); return ( - - - + <> + {isDark ? ( - {/* Left side - Logo */} - - 🎮 Autogenerated wiki + logo + + Autogenerated wiki + + + - - - - - - - + ) : ( + + + + + logo + + Autogenerated wiki + + + + + + + + + )} + ); } -export default App; \ No newline at end of file +function App() { + return ( + + + + + + + + + + + ); +} + +export default App; diff --git a/src/Routes.tsx b/src/Routes.tsx index 4a25525..4e60614 100644 --- a/src/Routes.tsx +++ b/src/Routes.tsx @@ -1,4 +1,4 @@ -import {Routes, Route, BrowserRouter} from "react-router-dom"; +import {Routes, Route} from "react-router-dom"; import ModsPage from "./pages/ModsPage"; import ModPage from "./pages/ModPage"; import RacePageFast from "./pages/RacePageFast"; @@ -8,14 +8,12 @@ import BuildingPage from "./pages/BuildingPage"; export const MyRoutes = () => { return( - - + }/> }/> }/> }/> }/> - ) } \ No newline at end of file diff --git a/src/classes/Ability.tsx b/src/classes/Ability.tsx index 47c0aca..231dddf 100644 --- a/src/classes/Ability.tsx +++ b/src/classes/Ability.tsx @@ -20,18 +20,18 @@ function Ability(props: IAbilityProps){ return
} + sx={{color: '#dee2e6', '& .MuiAccordionSummary-content': { margin: '12px 0' }, '& .MuiAccordionSummary-content.Mui-expanded': { margin: '12px 0' }}} + expandIcon={} aria-controls="panel1-content" - sx={{color: '#ffffff'}} > - + {ability.activationType === "Passive ability" ? : }   {ability.name ? ability.name : ability.fileName.charAt(0).toUpperCase() + ability.fileName.slice(1).replaceAll('_', ' ').replace('.rgd', '')} ({ability.activationType}) - +
diff --git a/src/classes/AbilityFull.tsx b/src/classes/AbilityFull.tsx index 4bf0e73..e37eee0 100644 --- a/src/classes/AbilityFull.tsx +++ b/src/classes/AbilityFull.tsx @@ -5,7 +5,6 @@ import ArmorTypeNames from "../types/ArmorTypeValues"; import { AccordionDetails, Grid2, - Paper, styled, Table, TableBody, @@ -28,6 +27,7 @@ import ability from "./Ability"; import AbilitySpawnedObject from "./AbilitySpawnedObject"; import DpsTable from "./DpsTable"; import {DescriptionBox} from "../commons/DescriptionBox"; +import {StyledPaper} from "../commons/StyledPaper"; interface IAbilityFullState { ability?: IAbilityFill; @@ -42,36 +42,22 @@ export default function AbilityFull(props: { abilityId?: number, mod: IMod, race const StyledTableCell = styled(TableCell)(({theme}) => ({ [`&.${tableCellClasses.head}`]: { - backgroundColor: 'rgba(233, 69, 96, 0.15)', + backgroundColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(233, 69, 96, 0.15)', fontWeight: 700, marginRight: 'auto', marginLeft: 'auto', paddingLeft: 10, - borderBottom: '2px solid rgba(233, 69, 96, 0.3)', + borderBottom: `2px solid ${theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.3)' : 'rgba(233, 69, 96, 0.3)'}`, }, [`&.${tableCellClasses.body}`]: { fontSize: 12, textAlign: 'center', - color: 'rgba(255, 255, 255, 0.85)', + color: '#dee2e6', paddingRight: 18, paddingLeft: 10, }, })); - const StyledPaper = styled(Paper)(({ theme }) => ({ - background: 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)', - border: '1px solid rgba(255, 255, 255, 0.1)', - borderRadius: '12px', - overflow: 'hidden', - '& .MuiTableBody .MuiTableRow-root .MuiTableCell-root': { - color: 'rgba(255, 255, 255, 0.9)', - }, - '& .MuiTableBody .MuiTableRow-root .MuiTableCell-head': { - color: '#e94560', - fontWeight: 600, - }, - })); - useEffect(() => { if(props.abilityFull !== undefined){ @@ -108,11 +94,11 @@ export default function AbilityFull(props: { abilityId?: number, mod: IMod, race const showTargetTable = !showDpsTable && ability.abilityEnvironment == null && (ability.targetFilter.length > 0 || ability.radius !== 0) return ( -
+
{ability.initialDelayTime !== undefined && props.isChild === true && -
 After {ability.initialDelayTime.toFixed(1)}s  +
 After {ability.initialDelayTime.toFixed(1)}s 
} @@ -215,7 +201,7 @@ export default function AbilityFull(props: { abilityId?: number, mod: IMod, race {showDpsTable ? - {(ability.refreshTime !== undefined && ability.durationTime !== undefined && ability.refreshTime * 2 < ability.durationTime) && Every {ability.refreshTime.toFixed(1)}s deal damege:} + {(ability.refreshTime !== undefined && ability.durationTime !== undefined && ability.refreshTime * 2 < ability.durationTime) && Every {ability.refreshTime.toFixed(1)}s deal damege:} : showTargetTable ? diff --git a/src/classes/AbilitySpawnedObject.tsx b/src/classes/AbilitySpawnedObject.tsx index 93ecbda..46eb817 100644 --- a/src/classes/AbilitySpawnedObject.tsx +++ b/src/classes/AbilitySpawnedObject.tsx @@ -1,7 +1,7 @@ import { Accordion, AccordionDetails, - AccordionSummary, Grid2, Paper, + AccordionSummary, Grid2, Table, TableBody, TableCell, TableContainer, @@ -24,6 +24,7 @@ import Ability from "./Ability"; import DeathExplosion from "./DeathExplosion"; import {styled} from '@mui/material/styles'; import {DescriptionBox} from "../commons/DescriptionBox"; +import {StyledPaper} from "../commons/StyledPaper"; const SectionTitle = styled(Typography)(({ theme }) => ({ fontWeight: 700, @@ -31,20 +32,6 @@ const SectionTitle = styled(Typography)(({ theme }) => ({ fontSize: '1.25rem', })); -const StyledPaper = styled(Paper)(({ theme }) => ({ - background: 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)', - border: '1px solid rgba(255, 255, 255, 0.1)', - borderRadius: '12px', - overflow: 'hidden', - '& .MuiTableBody .MuiTableRow-root .MuiTableCell-root': { - color: 'rgba(255, 255, 255, 0.9)', - }, - '& .MuiTableBody .MuiTableRow-root .MuiTableCell-head': { - color: '#e94560', - fontWeight: 600, - }, -})); - interface AbilityEnvironmentProps { abilityEnvironment: IAbilityEnvironment, @@ -56,7 +43,7 @@ function AbilitySpawnedObject(props: AbilityEnvironmentProps) { const abilityEnvironment = props.abilityEnvironment - return
+ return
{props.abilityEnvironment.name ?? props.abilityEnvironment.filename.charAt(0).toUpperCase() + props.abilityEnvironment.filename.slice(1).replaceAll('_', ' ').replace('.rgd', '')} (Spawned object) diff --git a/src/classes/DeathExplosion.tsx b/src/classes/DeathExplosion.tsx index 4ffa2b0..69a29ea 100644 --- a/src/classes/DeathExplosion.tsx +++ b/src/classes/DeathExplosion.tsx @@ -1,6 +1,6 @@ import { AccordionDetails, - AccordionSummary, Grid2, Paper, + AccordionSummary, Grid2, Table, TableBody, TableCell, TableContainer, TableHead, @@ -24,22 +24,9 @@ import Required from "./Required"; import ArmorTypeNames from "../types/ArmorTypeValues"; import DpsTable from "./DpsTable"; import {StyledAccordion} from "../commons/StyledAccordion"; +import {StyledPaper} from "../commons/StyledPaper"; import {styled} from "@mui/material/styles"; -const StyledPaper = styled(Paper)(({ theme }) => ({ - background: 'rgba(255, 255, 255, 0.03)', - border: '1px solid rgba(255, 255, 255, 0.05)', - borderRadius: '12px', - overflow: 'hidden', - '& .MuiTableBody .MuiTableRow-root .MuiTableCell-root': { - color: 'rgba(255, 255, 255, 0.85)', - }, - '& .MuiTableBody .MuiTableRow-root .MuiTableCell-head': { - color: '#e94560', - fontWeight: 600, - }, -})); - interface DeathExpProps { deathExplosion: IDeathExplosion, @@ -54,15 +41,15 @@ function DeathExplosion(props: DeathExpProps) { return chanceTotalNotZero ?
} + sx={{color: '#dee2e6', '& .MuiAccordionSummary-content': { margin: '12px 0' }, '& .MuiAccordionSummary-content.Mui-expanded': { margin: '12px 0' }}} + expandIcon={} aria-controls="panel1-content" - sx={{color: '#ffffff'}} > - + Death Explosion - + diff --git a/src/classes/DpsTable.tsx b/src/classes/DpsTable.tsx index d872f02..275844a 100644 --- a/src/classes/DpsTable.tsx +++ b/src/classes/DpsTable.tsx @@ -19,17 +19,17 @@ export default function DpsTable(props: { mod: IMod, minDamageValue?: number, mi const StyledTableCell = styled(TableCell)(({theme}) => ({ [`&.${tableCellClasses.head}`]: { - backgroundColor: 'rgba(233, 69, 96, 0.15)', + backgroundColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(233, 69, 96, 0.15)', fontWeight: 700, marginRight: 'auto', marginLeft: 'auto', paddingLeft: 10, - borderBottom: '2px solid rgba(233, 69, 96, 0.3)', + borderBottom: `2px solid ${theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.3)' : 'rgba(233, 69, 96, 0.3)'}`, }, [`&.${tableCellClasses.body}`]: { fontSize: 12, textAlign: 'center', - color: 'rgba(255, 255, 255, 0.85)', + color: '#dee2e6', paddingRight: 18, paddingLeft: 10, }, diff --git a/src/classes/ModifiersProvideTable.tsx b/src/classes/ModifiersProvideTable.tsx index b73e353..374c859 100644 --- a/src/classes/ModifiersProvideTable.tsx +++ b/src/classes/ModifiersProvideTable.tsx @@ -8,17 +8,7 @@ import {IMod} from "../types/Imod"; import AvTimerOutlinedIcon from "@mui/icons-material/AvTimer"; import {styled} from '@mui/material/styles'; import {StyledLink} from "../commons/StyledLink"; - -// Styled components for dark theme -const StyledPaper = styled(Paper)(({ theme }) => ({ - background: 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)', - border: '1px solid rgba(255, 255, 255, 0.1)', - borderRadius: '12px', - overflow: 'hidden', - '& .MuiTableBody .MuiTableRow-root .MuiTableCell-root': { - color: 'rgba(255, 255, 255, 0.85)', - }, -})); +import {StyledPaper} from "../commons/StyledPaper"; interface IModifiersProvidesTable { @@ -162,8 +152,8 @@ export function ModifiersProvidesAddonTable(props: IModifiersProvidesTable) { - {getModName(m.reference)} - {getModIcon(m.reference)} {getChangeDescription(m.usageType, m.value)} + {getModName(m.reference)} + {getModIcon(m.reference)} {getChangeDescription(m.usageType, m.value)} )} @@ -247,9 +237,9 @@ export function ModifiersProvidesTable(props: IModifiersProvidesResearchTable) { - {getTarget(m.target)} - {getModName(m.reference)} - {getModIcon(m.reference)} {getChangeDescription(m.usageType, m.value)} + {getTarget(m.target)} + {getModName(m.reference)} + {getModIcon(m.reference)} {getChangeDescription(m.usageType, m.value)} {m.maxLifeTime !== undefined && m.maxLifeTime > 0 && {m.maxLifeTime}s } diff --git a/src/classes/Required.tsx b/src/classes/Required.tsx index ea29d11..0a0663e 100644 --- a/src/classes/Required.tsx +++ b/src/classes/Required.tsx @@ -115,20 +115,20 @@ function Required (props: IRequiredProps) { return ( -
-

Required:

+
+

Required:

{requirement.requiredTotalPop !== undefined && requirement.requiredTotalPop !== null && -
  Population:   Population:   {requirement.requiredTotalPop}
} {requirement.mobBonus !== null && requirement.mobBonus !== undefined && -
  Mob requirement:   Mob requirement:   {requirement.mobBonus.mobvalueRequired} in radius {requirement.mobBonus.proximityRequired}
} {requirement.requireAddon !== null && -
  Addon:   Addon:   {requirement.requireAddon.name} @@ -137,26 +137,26 @@ function Required (props: IRequiredProps) {
} {requirement.requirementAddonExclusive != null && -
{renderRequiremenAddonExclusive(requirement.requirementAddonExclusive,)}
} +
{renderRequiremenAddonExclusive(requirement.requirementAddonExclusive,)}
} {requirement.requirementBuildings.map(b => -
  Building:   Building:   {b.name}
) } {requirement.requirementResearches.length !== 0 && -
{renderRequirementResearches(requirement.requirementResearches,)}
} +
{renderRequirementResearches(requirement.requirementResearches,)}
} {requirement.requirementSquads.length !== 0 && -
{renderRequirementSquads(requirement.requirementSquads,)}
} +
{renderRequirementSquads(requirement.requirementSquads,)}
} {requirement.requirementResearchesEither.length !== 0 && -
{renderRequirementResearchesEither(requirement.requirementResearchesEither,)}
} +
{renderRequirementResearchesEither(requirement.requirementResearchesEither,)}
} {requirement.requirementBuildingsEither.length !== 0 && -
{renderRequirementBuildingEither(requirement.requirementBuildingsEither,)}
} +
{renderRequirementBuildingEither(requirement.requirementBuildingsEither,)}
} {requirement.requirementStructureExclusive != null && -
{renderRequiremenStructureExclusive(requirement.requirementStructureExclusive,)}
} - {requirement.limitByBuilding !== undefined && requirement.limitByBuilding !== null &&
{renderLimitPerBuilding(requirement.limitByBuilding)}
} +
{renderRequiremenStructureExclusive(requirement.requirementStructureExclusive,)}
} + {requirement.limitByBuilding !== undefined && requirement.limitByBuilding !== null &&
{renderLimitPerBuilding(requirement.limitByBuilding)}
} {requirement.requirementsGlobalAddons.length !== 0 && -
{renderRequirementGlobalAddons(requirement.requirementsGlobalAddons,)}
} -
{renderRequirementOwnership(requirement.requiredOwnership)}
+
{renderRequirementGlobalAddons(requirement.requirementsGlobalAddons,)}
} +
{renderRequirementOwnership(requirement.requiredOwnership)}
) } diff --git a/src/classes/Sergeant.tsx b/src/classes/Sergeant.tsx index b22f22a..f9a221b 100644 --- a/src/classes/Sergeant.tsx +++ b/src/classes/Sergeant.tsx @@ -1,6 +1,6 @@ import React from "react"; import {ISergeant, IShortWeapon} from "../types/IUnit"; -import {Grid2, Paper, Table, TableBody, TableCell, TableContainer, TableRow, Tooltip, Typography} from "@mui/material"; +import {Grid2, Table, TableBody, TableCell, TableContainer, TableRow, Tooltip, Typography} from "@mui/material"; import AvTimerOutlinedIcon from "@mui/icons-material/AvTimer"; import ArmorType from "./ArmorType"; import WeaponSlot from "./WeaponSlot"; @@ -14,20 +14,7 @@ import Ability from "./Ability"; import DeathExplosion from "./DeathExplosion"; import {styled} from '@mui/material/styles'; import {DescriptionBox} from "../commons/DescriptionBox"; - -const StyledPaper = styled(Paper)(({ theme }) => ({ - background: 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)', - border: '1px solid rgba(255, 255, 255, 0.1)', - borderRadius: '12px', - overflow: 'hidden', - '& .MuiTableBody .MuiTableRow-root .MuiTableCell-root': { - color: 'rgba(255, 255, 255, 0.9)', - }, - '& .MuiTableBody .MuiTableRow-root .MuiTableCell-head': { - color: '#e94560', - fontWeight: 600, - }, -})); +import {StyledPaper} from "../commons/StyledPaper"; const SectionTitle = styled(Typography)(({ theme }) => ({ fontWeight: 700, @@ -60,7 +47,7 @@ const Sergeant = (props: SergeantProps) => { }) return ( -
+
diff --git a/src/classes/UnitsTable.tsx b/src/classes/UnitsTable.tsx index 26cb8c5..74af101 100644 --- a/src/classes/UnitsTable.tsx +++ b/src/classes/UnitsTable.tsx @@ -5,30 +5,22 @@ import {styled} from '@mui/material/styles'; import {IRaceUnits, IUnitShort} from "../types/IUnitShort"; import {IBuildingShort, IRaceBuildings} from "../types/IBuildingShort"; import {StyledLink} from "../commons/StyledLink"; - - -// Styled components for dark theme -const StyledPaper = styled(Paper)(({ theme }) => ({ - background: 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)', - border: '1px solid rgba(255, 255, 255, 0.1)', - borderRadius: '12px', - overflow: 'hidden', -})); +import {StyledPaper} from "../commons/StyledPaper"; const StyledTable = styled(Table)(({ theme }) => ({ '& .MuiTableCell-head': { - backgroundColor: 'rgba(233, 69, 96, 0.15)', + backgroundColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(233, 69, 96, 0.15)', fontWeight: 700, fontSize: '1rem', - borderBottom: '2px solid rgba(233, 69, 96, 0.3)', + borderBottom: `2px solid ${theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.3)' : 'rgba(233, 69, 96, 0.3)'}`, }, '& .MuiTableCell-body': { - color: 'rgba(255, 255, 255, 0.85)', - borderBottom: '1px solid rgba(255, 255, 255, 0.05)', + color: theme.palette.text.primary, + borderBottom: `1px solid ${theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.05)' : 'rgba(0, 0, 0, 0.05)'}`, fontSize: '0.9rem', }, '& .MuiTableRow-hover:hover': { - backgroundColor: 'rgba(233, 69, 96, 0.08)', + backgroundColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.05)' : 'rgba(233, 69, 96, 0.08)', }, })); @@ -36,7 +28,7 @@ const StyledHeaderLink = styled(StyledLink)(({ theme }) => ({ fontWeight: 700, fontSize: '1.1rem', '&:hover': { - color: '#e94560', + color: theme.palette.mode === 'dark' ? '#dee2e6' : '#e94560', }, })); diff --git a/src/classes/Weapon.tsx b/src/classes/Weapon.tsx index 6cd014f..b1bef73 100644 --- a/src/classes/Weapon.tsx +++ b/src/classes/Weapon.tsx @@ -37,21 +37,21 @@ class Weapon extends React.Component { return (
} + expandIcon={} aria-controls="panel1-content" id="panel1-header" - sx={{color: '#ffffff'}} + sx={{color: '#dee2e6', '& .MuiAccordionSummary-content': { margin: '12px 0' }, '& .MuiAccordionSummary-content.Mui-expanded': { margin: '12px 0' }}} > - {!this.props.isDefault && weapon.icon && !weapon.icon.endsWith("upgrade.png") ? + {!this.props.isDefault && weapon.icon && !weapon.icon.endsWith("upgrade.png") ? : ( weapon.isMeleeWeapon ? : - )} {weapon.name ? weapon.name : this.humanReadableName(weapon.filename)} + )}   {weapon.name ? weapon.name : this.humanReadableName(weapon.filename)} {this.props.isDefault && (default)} - +
diff --git a/src/classes/WeaponFull.tsx b/src/classes/WeaponFull.tsx index a5b257b..25d3243 100644 --- a/src/classes/WeaponFull.tsx +++ b/src/classes/WeaponFull.tsx @@ -5,7 +5,6 @@ import ArmorTypeNames from "../types/ArmorTypeValues"; import { AccordionDetails, Grid2, - Paper, styled, Table, TableBody, @@ -23,6 +22,7 @@ import {IRaceUnits} from "../types/IUnitShort"; import {IRaceBuildings} from "../types/IBuildingShort"; import {ModifiersProvidesTable} from "./ModifiersProvideTable"; import {DescriptionBox} from "../commons/DescriptionBox"; +import {StyledPaper} from "../commons/StyledPaper"; import AvTimerOutlinedIcon from "@mui/icons-material/AvTimer"; interface IWeaponFull { @@ -39,35 +39,21 @@ export default function WeaponFull(props: {weaponId: number, isDefault: Boolean, const StyledTableCell = styled(TableCell)(({theme}) => ({ [`&.${tableCellClasses.head}`]: { - backgroundColor: 'rgba(233, 69, 96, 0.15)', + backgroundColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(233, 69, 96, 0.15)', marginRight: 'auto', marginLeft: 'auto', paddingLeft: 10, - borderBottom: '2px solid rgba(233, 69, 96, 0.3)', + borderBottom: `2px solid ${theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.3)' : 'rgba(233, 69, 96, 0.3)'}`, }, [`&.${tableCellClasses.body}`]: { fontSize: 12, textAlign: 'center', - color: 'rgba(255, 255, 255, 0.85)', + color: '#dee2e6', paddingRight: 18, paddingLeft: 10, }, })); - const StyledPaper = styled(Paper)(({ theme }) => ({ - background: 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)', - border: '1px solid rgba(255, 255, 255, 0.1)', - borderRadius: '12px', - overflow: 'hidden', - '& .MuiTableBody .MuiTableRow-root .MuiTableCell-root': { - color: 'rgba(255, 255, 255, 0.9)', - }, - '& .MuiTableBody .MuiTableRow-root .MuiTableCell-head': { - color: '#e94560', - fontWeight: 600, - }, - })); - useEffect(() => { fetch(WeaponUrl + "/" + props.mod.id + "/" + props.weaponId) @@ -151,7 +137,7 @@ export default function WeaponFull(props: {weaponId: number, isDefault: Boolean, return ( -
+
{!props.isDefault && @@ -262,8 +248,8 @@ export default function WeaponFull(props: {weaponId: number, isDefault: Boolean, onChange={handleChange} sx={{ mb: 2 }} > - Dps - One hit average damage + Dps + One hit average damage { props.mod !== undefined && props.mod.technicalName === 'UltimateApocalypse' ? @@ -327,7 +313,7 @@ export default function WeaponFull(props: {weaponId: number, isDefault: Boolean, name={ArmorTypeNames.BuildingSuper}/> -
+
Morale
@@ -381,7 +367,7 @@ export default function WeaponFull(props: {weaponId: number, isDefault: Boolean, name={ArmorTypeNames.BuildingHigh}/> -
+
Morale
@@ -413,7 +399,7 @@ export default function WeaponFull(props: {weaponId: number, isDefault: Boolean, {weapon.modifiers.length > 0 && -

Modifiers

+

Modifiers

} {weapon.requirements !== null && !props.isDefault && props.haveReinforceMenu && diff --git a/src/classes/building/BuildingAddon.tsx b/src/classes/building/BuildingAddon.tsx index fccf102..09f0edb 100644 --- a/src/classes/building/BuildingAddon.tsx +++ b/src/classes/building/BuildingAddon.tsx @@ -3,7 +3,6 @@ import { AccordionDetails, AccordionSummary, Grid2, - Paper, Table, TableBody, TableCell, @@ -19,24 +18,10 @@ import {getIcon, ModifiersProvidesTable,} from "../ModifiersProvideTable"; import Required from "../Required"; import {StyledAccordion} from "../../commons/StyledAccordion"; import {DescriptionBox} from "../../commons/DescriptionBox"; +import {StyledPaper} from "../../commons/StyledPaper"; import {styled} from "@mui/material/styles"; - -const StyledPaper = styled(Paper)(({ theme }) => ({ - background: 'rgba(255, 255, 255, 0.03)', - border: '1px solid rgba(255, 255, 255, 0.05)', - borderRadius: '12px', - overflow: 'hidden', - '& .MuiTableBody .MuiTableRow-root .MuiTableCell-root': { - color: 'rgba(255, 255, 255, 0.85)', - }, - '& .MuiTableBody .MuiTableRow-root .MuiTableCell-head': { - color: '#e94560', - fontWeight: 600, - }, -})); - interface IBuildingAddonProps { addon: IBuildingAddon, building: IBuilding, @@ -52,16 +37,16 @@ function BuildingAddon(props: IBuildingAddonProps){ return
} + sx={{color: '#dee2e6', '& .MuiAccordionSummary-content': { margin: '12px 0' }, '& .MuiAccordionSummary-content.Mui-expanded': { margin: '12px 0' }}} + expandIcon={} aria-controls="panel1-content" - sx={{color: '#ffffff'}} > - +   {addon.name} - + diff --git a/src/classes/building/Research.tsx b/src/classes/building/Research.tsx index caa1483..761e151 100644 --- a/src/classes/building/Research.tsx +++ b/src/classes/building/Research.tsx @@ -23,10 +23,10 @@ import {StyledAccordion} from "../../commons/StyledAccordion"; import {styled} from "@mui/material/styles"; const StyledLink = styled('a')(({ theme }) => ({ - color: '#e94560', + color: theme.palette.mode === 'dark' ? '#dee2e6' : '#e94560', textDecoration: 'none', '&:hover': { - color: '#ff6b6b', + color: theme.palette.mode === 'dark' ? '#cccccc' : '#ff6b6b', textDecoration: 'underline', }, })); @@ -45,16 +45,16 @@ function Research(props: IResearchProps){ return
} + sx={{color: '#dee2e6', '& .MuiAccordionSummary-content': { margin: '12px 0' }, '& .MuiAccordionSummary-content.Mui-expanded': { margin: '12px 0' }}} + expandIcon={} aria-controls="panel1-content" - sx={{color: '#ffffff'}} > - +   {research.name} - +
@@ -71,7 +71,7 @@ export function renderAffectedResearches(researches: IResearchShort[], modId: nu } - return
{researches.map(rs => + return
{researches.map(rs => rs.buildingId == null ? : Research affect: {researchLink(rs)}
)}
diff --git a/src/classes/building/ResearchFull.tsx b/src/classes/building/ResearchFull.tsx index d4b8272..1c4c813 100644 --- a/src/classes/building/ResearchFull.tsx +++ b/src/classes/building/ResearchFull.tsx @@ -5,7 +5,6 @@ import { AccordionDetails, AccordionSummary, Grid2, - Paper, Table, TableBody, TableCell, @@ -24,20 +23,7 @@ import {ResearchUrl, WeaponUrl} from "../../core/api"; import {IWeapon} from "../../types/IUnit"; import {styled} from '@mui/material/styles'; import {DescriptionBox} from "../../commons/DescriptionBox"; - -const StyledPaper = styled(Paper)(({ theme }) => ({ - background: 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)', - border: '1px solid rgba(255, 255, 255, 0.1)', - borderRadius: '12px', - overflow: 'hidden', - '& .MuiTableBody .MuiTableRow-root .MuiTableCell-root': { - color: 'rgba(255, 255, 255, 0.9)', - }, - '& .MuiTableBody .MuiTableRow-root .MuiTableCell-head': { - color: '#e94560', - fontWeight: 600, - }, -})); +import {StyledPaper} from "../../commons/StyledPaper"; interface IResearchFull { diff --git a/src/commons/BackButton.tsx b/src/commons/BackButton.tsx index 8d99c39..c09cb11 100644 --- a/src/commons/BackButton.tsx +++ b/src/commons/BackButton.tsx @@ -3,12 +3,12 @@ import {styled} from '@mui/material/styles'; export const BackButton = styled(Button)(({ theme }) => ({ marginBottom: theme.spacing(3), - color: '#e94560BB', - borderColor: 'rgba(233, 69, 96, 0.5)', + color: '#FFD700', + borderColor: 'rgba(255, 215, 0, 0.5)', textTransform: 'none', fontWeight: 600, '&:hover': { - borderColor: '#e94560', - backgroundColor: 'rgba(233, 69, 96, 0.1)', + borderColor: '#FFD700', + backgroundColor: 'rgba(255, 215, 0, 0.1)', }, })); \ No newline at end of file diff --git a/src/commons/DescriptionBox.tsx b/src/commons/DescriptionBox.tsx index 0ad4309..c6316d4 100644 --- a/src/commons/DescriptionBox.tsx +++ b/src/commons/DescriptionBox.tsx @@ -1,12 +1,12 @@ import {styled} from '@mui/material/styles'; export const DescriptionBox = styled('div')(({ theme }) => ({ - color: 'rgba(255, 255, 255, 0.85)', + color: theme.palette.text.primary, lineHeight: 1.7, fontSize: '0.95rem', padding: theme.spacing(3), - background: 'rgba(255, 255, 255, 0.03)', + background: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.03)' : 'rgba(0, 0, 0, 0.03)', borderRadius: '12px', - border: '1px solid rgba(255, 255, 255, 0.05)', + border: `1px solid ${theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.05)' : 'rgba(0, 0, 0, 0.05)'}`, whiteSpace: 'pre-wrap', })); \ No newline at end of file diff --git a/src/commons/StyledAccordion.tsx b/src/commons/StyledAccordion.tsx index 2f7b592..86f497d 100644 --- a/src/commons/StyledAccordion.tsx +++ b/src/commons/StyledAccordion.tsx @@ -2,8 +2,10 @@ import {Accordion} from '@mui/material'; import {styled} from '@mui/material/styles'; export const StyledAccordion = styled(Accordion)(({ theme }) => ({ - background: 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)', - border: '1px solid rgba(255, 255, 255, 0.1)', + background: theme.palette.mode === 'dark' + ? 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)' + : 'linear-gradient(145deg, #ffffff 0%, #f5f5f5 100%)', + border: `1px solid ${theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'}`, borderRadius: '12px', '&:before': { display: 'none' }, '&.Mui-expanded': { margin: '0 0 8px 0' }, diff --git a/src/commons/StyledLink.tsx b/src/commons/StyledLink.tsx index 8343828..68da5c3 100644 --- a/src/commons/StyledLink.tsx +++ b/src/commons/StyledLink.tsx @@ -2,11 +2,11 @@ import {Link} from "@mui/material"; import {styled} from '@mui/material/styles'; export const StyledLink = styled(Link)(({ theme }) => ({ - color: '#e94560BB', + color: theme.palette.mode === 'dark' ? '#abc3ff' : '#e94560BB', textDecoration: 'none', transition: 'all 0.2s ease', '&:hover': { - color: '#ff6b6bBB', + color: theme.palette.mode === 'dark' ? '#ffffff' : '#ff6b6bBB', textDecoration: 'underline', }, })); diff --git a/src/commons/StyledPaper.tsx b/src/commons/StyledPaper.tsx new file mode 100644 index 0000000..a593891 --- /dev/null +++ b/src/commons/StyledPaper.tsx @@ -0,0 +1,19 @@ +import {Paper} from '@mui/material'; +import {styled} from '@mui/material/styles'; + +export const StyledPaper = styled(Paper)(({ theme }) => ({ + background: theme.palette.mode === 'dark' + ? 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)' + : 'linear-gradient(145deg, #ffffff 0%, #f5f5f5 100%)', + border: `1px solid ${theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'}`, + borderRadius: '12px', + overflow: 'hidden', + '& .MuiTableBody .MuiTableRow-root .MuiTableCell-root': { + color: theme.palette.text.primary, + }, + '& .MuiTableBody .MuiTableRow-root .MuiTableCell-head': { + color: theme.palette.mode === 'dark' ? '#dee2e6' : '#e94560', + fontWeight: 600, + }, +})); + diff --git a/src/context/ThemeContext.tsx b/src/context/ThemeContext.tsx new file mode 100644 index 0000000..a75774a --- /dev/null +++ b/src/context/ThemeContext.tsx @@ -0,0 +1,48 @@ +import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'; +import { ThemeProvider as MuiThemeProvider } from '@mui/material/styles'; +import CssBaseline from '@mui/material/CssBaseline'; +import { darkTheme, lightTheme } from '../themes/theme'; + +type ThemeMode = 'dark' | 'light'; + +interface ThemeContextType { + mode: ThemeMode; + toggleTheme: () => void; +} + +const ThemeContext = createContext(undefined); + + +export const ThemeProvider = ({ children }: { children: ReactNode }) => { + const [mode, setMode] = useState(() => { + const saved = localStorage.getItem('theme'); + return (saved === 'light' ? 'light' : 'dark') as ThemeMode; + }); + + useEffect(() => { + localStorage.setItem('theme', mode); + }, [mode]); + + const toggleTheme = () => { + setMode(prev => prev === 'dark' ? 'light' : 'dark'); + }; + + const theme = mode === 'dark' ? darkTheme : lightTheme; + + return ( + + + + {children} + + + ); +}; + +export const useThemeContext = () => { + const context = useContext(ThemeContext); + if (!context) { + throw new Error('useThemeContext must be used within a ThemeProvider'); + } + return context; +}; diff --git a/src/core/api.js b/src/core/api.js index 292f2cf..17b4afe 100644 --- a/src/core/api.js +++ b/src/core/api.js @@ -1,4 +1,7 @@ +import React from 'react'; +import { useTheme } from '@mui/material/styles'; + export const UserUrl = process.env.REACT_APP_HOST_URL + '/api/v1/user'; export const WeaponUrl = process.env.REACT_APP_HOST_URL + '/api/v1/weapon'; export const AbilityUrl = process.env.REACT_APP_HOST_URL + '/api/v1/ability'; @@ -8,3 +11,11 @@ export const AvailableRacesPart = process.env.REACT_APP_HOST_URL + '/api/v1/race export const AvailableUnits = process.env.REACT_APP_HOST_URL + '/api/v1/units'; export const AvailableBuildings = process.env.REACT_APP_HOST_URL + '/api/v1/buildings'; export const IconUrl = process.env.REACT_APP_HOST_URL + '/api/v1/grapics/icon/'; + +export function withTheme(Component) { + return function WithTheme(props) { + const theme = useTheme(); + return ; + }; +} + diff --git a/src/index.css b/src/index.css index 02d3cde..ecc94a3 100644 --- a/src/index.css +++ b/src/index.css @@ -1,5 +1,35 @@ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap'); +:root { + --bg-primary: #0a0a0a; + --bg-secondary: #1a1a2e; + --bg-tertiary: #16213e; + --text-primary: #dee2e6; + --text-secondary: #dee2e6; + --text-muted: rgba(255, 255, 255, 0.5); + --border-color: rgba(255, 255, 255, 0.1); + --border-light: rgba(255, 255, 255, 0.05); + --card-bg: rgba(255, 255, 255, 0.03); + --accent: #dee2e6; + --accent-light: #cccccc; + --scrollbar-track: #1a1a2e; +} + +body.light-theme { + --bg-primary: #f5f5f5; + --bg-secondary: #ffffff; + --bg-tertiary: #e8e8e8; + --text-primary: rgba(0, 0, 0, 0.85); + --text-secondary: rgba(0, 0, 0, 0.6); + --text-muted: rgba(0, 0, 0, 0.5); + --border-color: rgba(0, 0, 0, 0.1); + --border-light: rgba(0, 0, 0, 0.05); + --card-bg: rgba(0, 0, 0, 0.03); + --accent: #e94560; + --accent-light: #ff6b6b; + --scrollbar-track: #e8e8e8; +} + body { margin: 0; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', @@ -7,8 +37,12 @@ body { sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - background-color: #0a0a0a; - color: rgba(255, 255, 255, 0.85); + background-color: var(--bg-primary); + color: var(--text-primary); +} + +body.light-theme code { + background: rgba(0, 0, 0, 0.1); } code { @@ -21,7 +55,7 @@ code { * { scrollbar-width: thin; - scrollbar-color: #e94560 #1a1a2e; + scrollbar-color: var(--accent) var(--scrollbar-track); } *::-webkit-scrollbar { @@ -29,11 +63,11 @@ code { } *::-webkit-scrollbar-track { - background: #1a1a2e; + background: var(--scrollbar-track); } *::-webkit-scrollbar-thumb { - background-color: #e94560; + background-color: var(--accent); border-radius: 4px; } diff --git a/src/pages/BuildingPage.tsx b/src/pages/BuildingPage.tsx index 5a65273..a18bfbf 100644 --- a/src/pages/BuildingPage.tsx +++ b/src/pages/BuildingPage.tsx @@ -17,7 +17,7 @@ import { TableCell, TableRow, Tooltip, - Typography, LinearProgress + Typography, LinearProgress, Theme, } from "@mui/material"; import ArmorType from "../classes/ArmorType"; import AvTimerOutlinedIcon from '@mui/icons-material/AvTimer'; @@ -34,6 +34,7 @@ import {ModifiersProvidesTable} from "../classes/ModifiersProvideTable"; import Ability from "../classes/Ability"; import DeathExplosion from "../classes/DeathExplosion"; import {styled} from '@mui/material/styles'; +import {withTheme} from "../core/api"; import {DescriptionBox} from "../commons/DescriptionBox"; import {ArrowBack} from "@mui/icons-material"; import {BackButton} from "../commons/BackButton"; @@ -46,51 +47,80 @@ const SectionTitle = styled(Typography)(({ theme }) => ({ })); const StatsPaper = styled(Paper)(({ theme }) => ({ - background: 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)', - border: '1px solid rgba(255, 255, 255, 0.1)', + background: theme.palette.mode === 'dark' + ? 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)' + : 'linear-gradient(145deg, #ffffff 0%, #f5f5f5 100%)', + border: `1px solid ${theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'}`, borderRadius: '12px', overflow: 'hidden', '& .MuiTableBody .MuiTableRow-root .MuiTableCell-root': { - color: 'rgba(255, 255, 255, 0.9)', + color: theme.palette.text.primary, }, '& .MuiTableBody .MuiTableRow-root .MuiTableCell-head': { - color: '#e94560', + color: theme.palette.mode === 'dark' ? '#dee2e6' : '#e94560', fontWeight: 600, }, })); +const StyledDivider = styled(Divider)(({ theme }) => ({ + my: 3, + borderColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)', +})); + +const BuildingTitle = styled(Typography)(({ theme }) => ({ + fontWeight: 800, + mb: 1, + display: 'flex', + alignItems: 'center', + gap: 2, +})); + +const BuildingSubtitle = styled(Typography)(({ theme }) => ({ + color: theme.palette.text.secondary, + fontWeight: 500, +})); + +const UnitLink = styled(Link)(({ theme }) => ({ + color: theme.palette.text.primary, + textDecoration: 'none', + '&:hover': { color: theme.palette.mode === 'dark' ? '#dee2e6' : '#e94560' }, +})); + +const UnitListItem = styled(ListItem)(({ theme }) => ({ + color: theme.palette.text.primary, + padding: '8px 12px', + background: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.03)' : 'rgba(0,0,0,0.03)', + borderRadius: '8px', + border: `1px solid ${theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)'}`, + transition: 'all 0.2s ease', + '&:hover': { background: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.1)' : 'rgba(233, 69, 96, 0.1)', borderColor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.3)' : 'rgba(233, 69, 96, 0.3)' }, +})); + interface UintPageState { building: IBuilding, mod: IMod, } -function Unit (unit: IUnitShort, modId: number, raceId: String) { +function Unit (unit: IUnitShort, modId: number, raceId: String, theme: Theme) { return ( - - + + {unit.icon && } {unit.name} {unit.canDetect &&  } - - + + ) } -function Building(building: IBuilding, mod: IMod) { +function Building(building: IBuilding, mod: IMod, theme: Theme) { document.title = building.name + " — " + mod.name + const isDark = theme.palette.mode === 'dark'; let mapBuildingWeapons: Map> = new Map(); @@ -113,23 +143,14 @@ function Building(building: IBuilding, mod: IMod) { return ( - + {building.icon && } {buildingName} - - + + {mod.name} ({mod.version}) - + @@ -232,7 +253,7 @@ function Building(building: IBuilding, mod: IMod) { Unit production {building.units.map(unit => - Unit(unit, mod.id, building.race.id) + Unit(unit, mod.id, building.race.id, theme) )} } @@ -271,7 +292,7 @@ function Building(building: IBuilding, mod: IMod) { Hotkey: {building.hotkey} - + ) } @@ -298,9 +319,10 @@ class BuildingPage extends React.Component { if (this.state != null && this.state.building != null && this.state.mod != null) { const backRef = "/mod/" + this.props.match.params.modId + "/race/" + this.props.match.params.raceId + const { theme } = this.props; return ( - + } @@ -308,7 +330,7 @@ class BuildingPage extends React.Component { > Back to race - {Building(this.state.building, this.state.mod)} + {Building(this.state.building, this.state.mod, theme)} ); } else { @@ -321,4 +343,4 @@ class BuildingPage extends React.Component { } } -export default withRouter(BuildingPage); +export default withRouter(withTheme(BuildingPage)); diff --git a/src/pages/ModPage.tsx b/src/pages/ModPage.tsx index 8fdf927..3b96c43 100644 --- a/src/pages/ModPage.tsx +++ b/src/pages/ModPage.tsx @@ -1,6 +1,7 @@ import {AvailableMods, AvailableRacesPart, AvailableUnits, IconUrl} from "../core/api"; import React, { useState } from "react"; import {withRouter} from "../core/withrouter"; +import {withTheme} from "../core/api"; import {IMod} from "../types/Imod"; import { ArrowBack, @@ -19,6 +20,12 @@ import UnitsTable from "../classes/UnitsTable"; import {BackButton} from "../commons/BackButton"; + +const ModNotFound = styled(Typography)(({ theme }) => ({ + color: theme.palette.text.secondary, +})); + + interface ModPageState { mod: IMod | null, loading: boolean, @@ -60,6 +67,7 @@ class ModPage extends React.Component { if (this.state != null && this.state.mod != null) { document.title = this.state.mod.name + " — ModWiki"; + const isDark = this.props.theme.palette.mode === 'dark'; return ( @@ -71,12 +79,20 @@ class ModPage extends React.Component { Back to mods list - - {this.state.mod.name} ({this.state.mod.version}) + {this.state.mod.name} + + + ({this.state.mod.version}) @@ -87,9 +103,9 @@ class ModPage extends React.Component { } else { return ( - + Mod not found - + ); } @@ -98,4 +114,4 @@ class ModPage extends React.Component { -export default withRouter(ModPage); \ No newline at end of file +export default withRouter(withTheme(ModPage)); diff --git a/src/pages/ModsPage.tsx b/src/pages/ModsPage.tsx index 4eedae5..47fd8e7 100644 --- a/src/pages/ModsPage.tsx +++ b/src/pages/ModsPage.tsx @@ -23,7 +23,9 @@ import { styled } from '@mui/material/styles'; const PageHeader = styled(Paper)(({ theme }) => ({ padding: theme.spacing(6, 4), marginBottom: theme.spacing(6), - background: 'linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%)', + background: theme.palette.mode === 'dark' + ? 'linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%)' + : 'linear-gradient(135deg, #ffffff 0%, #f0f0f0 50%, #e0e0e0 100%)', borderRadius: '16px', position: 'relative', overflow: 'hidden', @@ -34,7 +36,7 @@ const PageHeader = styled(Paper)(({ theme }) => ({ left: 0, right: 0, bottom: 0, - background: 'radial-gradient(circle at 30% 50%, rgba(233, 69, 96, 0.1) 0%, transparent 50%)', + background: 'radial-gradient(circle at 30% 50%, rgba(255, 255, 255, 0.05) 0%, #dee2e6 50%)', pointerEvents: 'none', }, })); @@ -43,8 +45,10 @@ const ModCard = styled(Card)(({ theme }) => ({ height: '100%', display: 'flex', flexDirection: 'column', - background: 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)', - border: '1px solid rgba(255, 255, 255, 0.1)', + background: theme.palette.mode === 'dark' + ? 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)' + : 'linear-gradient(145deg, #ffffff 0%, #f5f5f5 100%)', + border: `1px solid ${theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'}`, borderRadius: '16px', transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', position: 'relative', @@ -56,14 +60,15 @@ const ModCard = styled(Card)(({ theme }) => ({ left: 0, right: 0, height: '3px', - background: 'linear-gradient(90deg, #e94560 0%, #ff6b6b 100%)', + background: theme.palette.mode === 'dark' + ? 'linear-gradient(90deg, #dee2e6 0%, #cccccc 100%)' + : 'linear-gradient(90deg, #e94560 0%, #ff6b6b 100%)', transform: 'scaleX(0)', transition: 'transform 0.3s ease', }, '&:hover': { - transform: 'translateY(-8px)', - boxShadow: '0 20px 40px rgba(233, 69, 96, 0.2)', - borderColor: 'rgba(233, 69, 96, 0.3)', + boxShadow: theme.palette.mode === 'dark' ? '0 20px 40px rgba(255, 255, 255, 0.1)' : '0 20px 40px rgba(233, 69, 96, 0.2)', + borderColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.3)' : 'rgba(233, 69, 96, 0.3)', '&::before': { transform: 'scaleX(1)', }, @@ -75,16 +80,79 @@ const VersionLink = styled(NavLink)(({ theme }) => ({ alignItems: 'center', padding: theme.spacing(1.5, 2), marginBottom: theme.spacing(1), - background: 'rgba(255, 255, 255, 0.05)', + background: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.05)' : 'rgba(0, 0, 0, 0.05)', borderRadius: '10px', textDecoration: 'none', color: 'inherit', transition: 'all 0.2s ease', - border: '1px solid rgba(255, 255, 255, 0.1)', + border: `1px solid ${theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'}`, '&:hover': { - background: 'rgba(233, 69, 96, 0.15)', - borderColor: 'rgba(233, 69, 96, 0.3)', - transform: 'translateX(4px)', + background: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(233, 69, 96, 0.15)', + borderColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.3)' : 'rgba(233, 69, 96, 0.3)', + }, +})); + +const ModTitle = styled(Typography)(({ theme }) => ({ + fontWeight: 700, + mb: 1, + background: theme.palette.mode === 'dark' + ? 'linear-gradient(135deg, #dee2e6 0%, #e0e0e0 100%)' + : 'linear-gradient(135deg, #000000 0%, #333333 100%)', + WebkitBackgroundClip: theme.palette.mode === 'dark' ? 'text' : 'initial', + WebkitTextFillColor: theme.palette.mode === 'dark' ? '#dee2e6' : 'initial', + color: theme.palette.mode === 'dark' ? '#dee2e6' : '#000000', +})); + +const ModDescription = styled(Typography)(({ theme }) => ({ + color: theme.palette.text.secondary, + lineHeight: 1.6, +})); + +const VersionsChip = styled(Chip)(({ theme }) => ({ + backgroundColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.15)' : 'rgba(233, 69, 96, 0.2)', + color: theme.palette.mode === 'dark' ? '#dee2e6' : '#ff6b6b', + fontWeight: 600, +})); + +const StyledDivider = styled(Divider)(({ theme }) => ({ + my: 2, + borderColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)', +})); + +const MainVersionsLabel = styled(Typography)(({ theme }) => ({ + color: theme.palette.text.primary, + fontWeight: 600, + mb: 1.5, +})); + +const VersionText = styled(Typography)(({ theme }) => ({ + fontWeight: 600, + color: theme.palette.text.secondary, +})); + +const VersionArrow = styled(Typography)(({ theme }) => ({ + color: theme.palette.text.secondary, +})); + +const BetaVersionsLabel = styled(Typography)(({ theme }) => ({ + color: 'rgba(255, 193, 7, 0.9)', + fontWeight: 600, + mb: 1, +})); + +const OpenLatestButton = styled(Button)(({ theme }) => ({ + width: '100%', + color: '#FFD700', + borderColor: 'rgba(255, 215, 0, 0.5)', + fontWeight: 600, + borderRadius: '10px', + textTransform: 'none', + fontSize: '1rem', + py: 1.5, + '&:hover': { + borderColor: '#FFD700', + backgroundColor: 'rgba(255, 215, 0, 0.1)', + boxShadow: '0 8px 20px rgba(255, 215, 0, 0.2)', }, })); @@ -128,88 +196,48 @@ function Mods({ mods }: ModsProps) { const { latest, hasBeta, betaVersion } = getLatestVersion(modName); return ( - + - + {modName} - - - + - - {hasBeta && ( - - )} - - - - Main versions: - - {sameMods.filter(m => !m.isBeta).map(mod => ( - + Version {mod.version} - - + + → - + ))} {hasBeta && ( - + Beta versions: - + {betaVersion && ( Version {betaVersion.version} (Beta) - + → - + )} @@ -226,18 +254,17 @@ function Mods({ mods }: ModsProps) { state={latest?.id} sx={{ width: '100%', - color: '#e94560', - borderColor: 'rgba(233, 69, 96, 0.5)', + color: '#FFD700', + borderColor: 'rgba(255, 215, 0, 0.5)', fontWeight: 600, borderRadius: '10px', textTransform: 'none', fontSize: '1rem', py: 1.5, '&:hover': { - borderColor: '#e94560', - backgroundColor: 'rgba(233, 69, 96, 0.1)', - transform: 'translateY(-2px)', - boxShadow: '0 8px 20px rgba(233, 69, 96, 0.2)', + borderColor: '#FFD700', + backgroundColor: 'rgba(255, 215, 0, 0.1)', + boxShadow: '0 8px 20px rgba(255, 215, 0, 0.2)', }, }} > diff --git a/src/pages/RacePageFast.tsx b/src/pages/RacePageFast.tsx index 48d81d0..c422fd8 100644 --- a/src/pages/RacePageFast.tsx +++ b/src/pages/RacePageFast.tsx @@ -1,5 +1,5 @@ import {AvailableBuildings, AvailableMods, AvailableRacesPart, AvailableUnits, IconUrl} from "../core/api"; -import React, {useState} from "react"; +import React from "react"; import {withRouter} from "../core/withrouter"; import {IMod} from "../types/Imod"; import {Irace} from "../types/Irace"; @@ -18,11 +18,13 @@ import { ListItem, Paper, Typography, + Theme, } from "@mui/material"; import {ArrowBack, ExpandMore} from "@mui/icons-material"; import {IRaceUnits, IUnitShort} from "../types/IUnitShort"; import {IBuildingShort, IRaceBuildings} from "../types/IBuildingShort"; import {styled} from '@mui/material/styles'; +import {withTheme} from "../core/api"; import {StyledLink} from "../commons/StyledLink"; import {BackButton} from "../commons/BackButton"; @@ -33,19 +35,30 @@ const SectionTitle = styled(Typography)(({ theme }) => ({ })); const UnitCard = styled(Paper)(({ theme }) => ({ - background: 'rgba(255, 255, 255, 0.03)', - border: '1px solid rgba(255, 255, 255, 0.05)', + background: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.03)' : 'rgba(0, 0, 0, 0.03)', + border: `1px solid ${theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.05)' : 'rgba(0, 0, 0, 0.05)'}`, borderRadius: '10px', padding: theme.spacing(1.5, 2), transition: 'all 0.2s ease', '&:hover': { - background: 'rgba(233, 69, 96, 0.1)', - borderColor: 'rgba(233, 69, 96, 0.3)', + background: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(233, 69, 96, 0.1)', + borderColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.3)' : 'rgba(233, 69, 96, 0.3)', }, })); const BuildingCard = styled(UnitCard)(({ theme }) => ({})); +const UnitLink = styled(Link)(({ theme }) => ({ + color: theme.palette.text.primary, + textDecoration: 'none', + display: 'block', + '&:hover': { color: theme.palette.mode === 'dark' ? '#dee2e6' : '#e94560' } +})); + +const LoadingText = styled(Typography)(({ theme }) => ({ + color: theme.palette.text.secondary, +})); + interface RacePageState { mod: IMod, race: Irace, @@ -53,29 +66,29 @@ interface RacePageState { } -function Unit(unit: IUnitShort, modId: number, raceId: String) { +function Unit(unit: IUnitShort, modId: number, raceId: String, theme: Theme) { + const isDark = theme.palette.mode === 'dark'; + const borderColor = isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)'; + const bgColor = isDark ? 'rgba(255,255,255,0.02)' : 'rgba(0,0,0,0.02)'; - return ( - - {unit.icon && } - {unit.name} - {unit.canDetect &&  } - - ) + return ( + + + {unit.icon && } + {unit.name} + {unit.canDetect &&  } + + + ) } function UnitSmall(unit: IUnitShort, modId: number, raceId: String) { @@ -94,13 +107,14 @@ function UnitSmall(unit: IUnitShort, modId: number, raceId: String) { src="/images/DETECT_YES.webp"/>}
) } -function Building(building: IBuildingShort, modId: number, raceId: String) { +function Building(building: IBuildingShort, modId: number, raceId: String, theme: Theme) { + const isDark = theme.palette.mode === 'dark'; return ( { +class Units extends React.Component { constructor(props: any) { @@ -157,7 +171,8 @@ class Units extends React.Component { } render() { - + const { theme } = this.props; + const isDark = theme.palette.mode === 'dark'; if (this.state && this.state.units) { @@ -165,65 +180,73 @@ class Units extends React.Component { document.title = this.state.buildings?.race.name } - const accordionSx = { + const accordionSx = isDark ? { background: 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)', border: '1px solid rgba(255, 255, 255, 0.1)', borderRadius: '12px', '&:before': { display: 'none' }, '&.Mui-expanded': { margin: '0 0 8px 0' }, + } : { + background: 'linear-gradient(145deg, #ffffff 0%, #f5f5f5 100%)', + border: '1px solid rgba(0, 0, 0, 0.1)', + borderRadius: '12px', + '&:before': { display: 'none' }, + '&.Mui-expanded': { margin: '0 0 8px 0' }, }; return (this.state.buildings != null ? - {this.state.buildings.buildings.map(building => Building(building, this.props.modId, this.props.raceId))} + {this.state.buildings.buildings.map(building => Building(building, this.props.modId, this.props.raceId, theme))} {this.state.buildings.buildingsAdvanced.length > 0 && - + Advanced buildings - {this.state.buildings.buildingsAdvanced.map(building => Building(building, this.props.modId, this.props.raceId))} + {this.state.buildings.buildingsAdvanced.map(building => Building(building, this.props.modId, this.props.raceId, theme))}
} } + expandIcon={} aria-controls="units-accordion" id="units-accordion" - sx={{color: '#ffffff'}} + sx={{color: isDark ? '#dee2e6' : '#000000', '& .MuiAccordionSummary-content': { margin: '12px 0' }, '& .MuiAccordionSummary-content.Mui-expanded': { margin: '12px 0' }}} > - All units + All units - Infantry - {this.state.units.infantry.map(unit => Unit(unit, this.props.modId, this.props.raceId))} + Infantry + {this.state.units.infantry.map(unit => Unit(unit, this.props.modId, this.props.raceId, theme))} - Tech + Tech - {this.state.units.tech.map(unit => Unit(unit, this.props.modId, this.props.raceId))} + {this.state.units.tech.map(unit => Unit(unit, this.props.modId, this.props.raceId, theme))} - Support + Support - {this.state.units.support.map(unit => Unit(unit, this.props.modId, this.props.raceId))} + {this.state.units.support.map(unit => Unit(unit, this.props.modId, this.props.raceId, theme))} -
: Loading +
: Loading ) } else { - return Loading...; + return Loading...; } } } +const UnitsWithTheme = withTheme(Units) as React.ComponentType; + class RacePageFast extends React.Component { constructor(props: any) { @@ -252,9 +275,11 @@ class RacePageFast extends React.Component { if (this.state != null && this.state.mod != null && this.state.race != null) { const backRef = "/mod/" + this.state.mod.id + const theme = this.props.theme; + const isDark = theme.palette.mode === 'dark'; return ( - + } @@ -267,26 +292,29 @@ class RacePageFast extends React.Component { {this.state.race.name} {this.state.mod.name} ({this.state.mod.version})
- +
); } else { return ( - + ); @@ -294,4 +322,4 @@ class RacePageFast extends React.Component { } } -export default withRouter(RacePageFast); \ No newline at end of file +export default withRouter(withTheme(RacePageFast)); diff --git a/src/pages/UnitPage.tsx b/src/pages/UnitPage.tsx index 0dbe11c..077b00d 100644 --- a/src/pages/UnitPage.tsx +++ b/src/pages/UnitPage.tsx @@ -18,7 +18,7 @@ import { TableCell, TableRow, Tooltip, - Typography, + Typography, Theme, } from "@mui/material"; import {ArrowBack, ExpandMore} from "@mui/icons-material"; import ArmorType from "../classes/ArmorType"; @@ -34,6 +34,7 @@ import {ModifiersProvidesTable} from "../classes/ModifiersProvideTable"; import Ability from "../classes/Ability"; import DeathExplosion from "../classes/DeathExplosion"; import {styled} from '@mui/material/styles'; +import {withTheme} from "../core/api"; import {DescriptionBox} from "../commons/DescriptionBox"; import {BackButton} from "../commons/BackButton"; @@ -44,19 +45,50 @@ const SectionTitle = styled(Typography)(({ theme }) => ({ })); const StatsPaper = styled(Paper)(({ theme }) => ({ - background: 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)', - border: '1px solid rgba(255, 255, 255, 0.1)', + background: theme.palette.mode === 'dark' + ? 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)' + : 'linear-gradient(145deg, #ffffff 0%, #f5f5f5 100%)', + border: `1px solid ${theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'}`, borderRadius: '12px', overflow: 'hidden', '& .MuiTableBody .MuiTableRow-root': { '&:last-child td, &:last-child th': { border: 0 }, }, '& .MuiTableBody .MuiTableRow-root .MuiTableCell-head': { - color: '#e94560', + color: theme.palette.mode === 'dark' ? '#dee2e6' : '#e94560', fontWeight: 600, }, })); +const SergeantAccordion = styled(Accordion)(({ theme }) => ({ + background: theme.palette.mode === 'dark' + ? 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)' + : 'linear-gradient(145deg, #ffffff 0%, #f5f5f5 100%)', + border: `1px solid ${theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'}`, + borderRadius: '12px', + mb: 1, + '&:before': { display: 'none' }, + '&.Mui-expanded': { margin: '0 0 8px 0' }, +})); + +const StyledDivider = styled(Divider)(({ theme }) => ({ + my: 3, + borderColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)', +})); + +const UnitTitle = styled(Typography)(({ theme }) => ({ + fontWeight: 800, + mb: 1, + display: 'flex', + alignItems: 'center', + gap: 2, +})); + +const UnitSubtitle = styled(Typography)(({ theme }) => ({ + color: theme.palette.text.secondary, + fontWeight: 500, +})); + interface UintPageState { unit: IUnit, @@ -64,10 +96,12 @@ interface UintPageState { } -function Unit(unit: IUnit, mod: IMod) { +function Unit(unit: IUnit, mod: IMod, theme: Theme) { document.title = unit.name + " — " + mod.name + const isDark = theme.palette.mode === 'dark'; + const morale = (unit.moraleMax !== null) ?  {unit.moraleMax} +{unit.moraleRegeneration}/s @@ -98,7 +132,7 @@ function Unit(unit: IUnit, mod: IMod) { const SergeantShort = (props: sergeantProps) => { return ( - + {props.icon && }   {props.name} {props.canDetect &&   - + {unit.icon && } {unit.name} - - + + {mod.name} ({mod.version}) - + @@ -297,22 +322,15 @@ function Unit(unit: IUnit, mod: IMod) { } {unit.sergeants.map(s => - + + sx={{color: isDark ? '#dee2e6' : '#000000', '& .MuiAccordionSummary-content': { margin: '12px 0' }, '& .MuiAccordionSummary-content.Mui-expanded': { margin: '12px 0' }}}> 0}/> - + - )} + )} {unit.moraleMax !== null && !(unit.moraleBrakeModifiers.find(m => m.reference.includes("accuracy_weapon_modifier") && m.value === 0.2) !== undefined && @@ -355,7 +373,7 @@ function Unit(unit: IUnit, mod: IMod) { Hotkey: {unit.hotkey} - + ) @@ -387,9 +405,10 @@ class UnitPage extends React.Component { if (this.state != null && this.state.unit != null && this.state.mod != null) { const backRef = "/mod/" + this.props.match.params.modId + "/race/" + this.props.match.params.raceId + const { theme } = this.props; return ( - + } @@ -397,7 +416,7 @@ class UnitPage extends React.Component { > Back to race - {Unit(this.state.unit, this.state.mod)} + {Unit(this.state.unit, this.state.mod, theme)} ); } else { @@ -410,4 +429,4 @@ class UnitPage extends React.Component { } } -export default withRouter(UnitPage); \ No newline at end of file +export default withRouter(withTheme(UnitPage)); diff --git a/src/themes/theme.ts b/src/themes/theme.ts new file mode 100644 index 0000000..202411d --- /dev/null +++ b/src/themes/theme.ts @@ -0,0 +1,83 @@ +import { createTheme } from '@mui/material/styles'; + +export const darkTheme = createTheme({ + breakpoints: { + values: { + xs: 0, + sm: 600, + md: 900, + lg: 1320, + xl: 1536, + }, + }, + palette: { + mode: 'dark', + primary: { + main: "#1a1a2e", + }, + background: { + default: '#0a0a0a', + paper: '#1a1a2e', + }, + text: { + primary: '#dee2e6', + secondary: '#dee2e6', + }, + }, + typography: { + fontFamily: '"Inter", "Roboto", "Helvetica", sans-serif', + }, + shape: { + borderRadius: 12, + }, + components: { + MuiTableCell: { + styleOverrides: { + root: { + color: '#dee2e6', + }, + }, + }, + }, +}); + +export const lightTheme = createTheme({ + breakpoints: { + values: { + xs: 0, + sm: 600, + md: 900, + lg: 1320, + xl: 1536, + }, + }, + palette: { + mode: 'light', + primary: { + main: "#1a1a2e", + }, + background: { + default: '#f5f5f5', + paper: '#ffffff', + }, + text: { + primary: 'rgba(0, 0, 0, 0.85)', + secondary: 'rgba(0, 0, 0, 0.6)', + }, + }, + typography: { + fontFamily: '"Inter", "Roboto", "Helvetica", sans-serif', + }, + shape: { + borderRadius: 12, + }, + components: { + MuiTableCell: { + styleOverrides: { + root: { + color: 'rgba(0, 0, 0, 0.85)', + }, + }, + }, + }, +});