fix: change colors based on which color scheme

This commit is contained in:
trafficlunar 2024-12-26 23:30:52 +00:00
parent 2c852694ea
commit a5bea3b585
9 changed files with 48 additions and 43 deletions

View file

@ -8,6 +8,7 @@ import { ImageContext } from "@/context/Image";
import { LoadingContext } from "@/context/Loading"; import { LoadingContext } from "@/context/Loading";
import { SettingsContext } from "@/context/Settings"; import { SettingsContext } from "@/context/Settings";
import { TexturesContext } from "@/context/Textures"; import { TexturesContext } from "@/context/Textures";
import { ThemeContext } from "@/context/Theme";
import { ToolContext } from "@/context/Tool"; import { ToolContext } from "@/context/Tool";
import Blocks from "./Blocks"; import Blocks from "./Blocks";
@ -29,6 +30,7 @@ function Canvas() {
const { setLoading } = useContext(LoadingContext); const { setLoading } = useContext(LoadingContext);
const { settings } = useContext(SettingsContext); const { settings } = useContext(SettingsContext);
const { missingTexture, textures, solidTextures } = useContext(TexturesContext); const { missingTexture, textures, solidTextures } = useContext(TexturesContext);
const { isDark } = useContext(ThemeContext);
const { tool, radius, selectedBlock, cssCursor, setTool, setSelectedBlock, setCssCursor } = useContext(ToolContext); const { tool, radius, selectedBlock, cssCursor, setTool, setSelectedBlock, setCssCursor } = useContext(ToolContext);
const stageContainerRef = useRef<HTMLDivElement>(null); const stageContainerRef = useRef<HTMLDivElement>(null);
@ -278,7 +280,7 @@ function Canvas() {
}, []); }, []);
return ( return (
<div ref={stageContainerRef} className="relative w-full h-full" style={{ cursor: cssCursor }}> <div ref={stageContainerRef} className="relative w-full h-full bg-zinc-200 dark:bg-black" style={{ cursor: cssCursor }}>
<Stage <Stage
width={stageSize.width} width={stageSize.width}
height={stageSize.height} height={stageSize.height}
@ -287,6 +289,7 @@ function Canvas() {
onMouseUp={onMouseUp} onMouseUp={onMouseUp}
onWheel={onWheel} onWheel={onWheel}
onClick={onClick} onClick={onClick}
options={{ backgroundAlpha: 0 }}
> >
<Blocks <Blocks
blocks={visibleBlocks} blocks={visibleBlocks}
@ -302,13 +305,13 @@ function Canvas() {
/> />
<Container x={coords.x} y={coords.y} scale={scale}> <Container x={coords.x} y={coords.y} scale={scale}>
{settings.canvasBorder && <CanvasBorder canvasSize={canvasSize} />} {settings.canvasBorder && <CanvasBorder canvasSize={canvasSize} isDark={isDark} />}
<Cursor mouseCoords={mouseCoords} radius={radius} /> <Cursor mouseCoords={mouseCoords} radius={radius} isDark={isDark} />
</Container> </Container>
{settings.grid && ( {settings.grid && (
<Container> <Container>
<Grid stageSize={stageSize} coords={coords} scale={scale} /> <Grid stageSize={stageSize} coords={coords} scale={scale} isDark={isDark} />
</Container> </Container>
)} )}
</Stage> </Stage>

View file

@ -2,14 +2,15 @@ import { Graphics } from "@pixi/react";
interface Props { interface Props {
canvasSize: CanvasSize; canvasSize: CanvasSize;
isDark: boolean;
} }
function CanvasBorder({ canvasSize }: Props) { function CanvasBorder({ canvasSize, isDark }: Props) {
return ( return (
<Graphics <Graphics
draw={(g) => { draw={(g) => {
g.clear(); g.clear();
g.lineStyle(2, 0xffffff, 0.25, 1); g.lineStyle(2, isDark ? 0xffffff : 0x000000, 0.25, 1);
g.drawRect(canvasSize.minX * 16, canvasSize.minY * 16, (canvasSize.maxX - canvasSize.minX) * 16, (canvasSize.maxY - canvasSize.minY) * 16); g.drawRect(canvasSize.minX * 16, canvasSize.minY * 16, (canvasSize.maxX - canvasSize.minX) * 16, (canvasSize.maxY - canvasSize.minY) * 16);
}} }}
/> />

View file

@ -3,9 +3,10 @@ import { Graphics } from "@pixi/react";
interface Props { interface Props {
mouseCoords: Position; mouseCoords: Position;
radius: number; radius: number;
isDark: boolean;
} }
function Cursor({ mouseCoords, radius }: Props) { function Cursor({ mouseCoords, radius, isDark }: Props) {
const isOddRadius = radius % 2 !== 0; const isOddRadius = radius % 2 !== 0;
const halfSize = Math.floor(radius / 2); const halfSize = Math.floor(radius / 2);
@ -18,7 +19,7 @@ function Cursor({ mouseCoords, radius }: Props) {
y={(mouseCoords.y + offset) * 16} y={(mouseCoords.y + offset) * 16}
draw={(g) => { draw={(g) => {
g.clear(); g.clear();
g.lineStyle(1, 0xffffff, 1); g.lineStyle(1, isDark ? 0xffffff : 0x000000, 1);
g.drawRect(0, 0, size, size); g.drawRect(0, 0, size, size);
}} }}
/> />

View file

@ -4,14 +4,15 @@ interface Props {
stageSize: Dimension; stageSize: Dimension;
coords: Position; coords: Position;
scale: number; scale: number;
isDark: boolean;
} }
function Grid({ stageSize, coords, scale }: Props) { function Grid({ stageSize, coords, scale, isDark }: Props) {
return ( return (
<Graphics <Graphics
draw={(g) => { draw={(g) => {
g.clear(); g.clear();
g.lineStyle(1, 0xffffff, 0.1); g.lineStyle(1, isDark ? 0xffffff : 0x000000, 0.1);
const tileSize = 16 * scale; const tileSize = 16 * scale;

View file

@ -1,8 +1,9 @@
import { useContext } from "react";
import { ThemeContext } from "@/context/Theme";
import { MenubarRadioGroup, MenubarRadioItem, MenubarSub, MenubarSubContent, MenubarSubTrigger } from "@/components/ui/menubar"; import { MenubarRadioGroup, MenubarRadioItem, MenubarSub, MenubarSubContent, MenubarSubTrigger } from "@/components/ui/menubar";
import { useTheme } from "@/context/Theme";
function ThemeChanger() { function ThemeChanger() {
const { setTheme, theme } = useTheme(); const { setTheme, theme } = useContext(ThemeContext);
return ( return (
<MenubarSub> <MenubarSub>

View file

@ -1,7 +1,10 @@
import { useContext } from "react";
import { Link } from "react-router"; import { Link } from "react-router";
import { Menubar as UIMenubar } from "@/components/ui/menubar";
import { DialogProvider } from "@/context/Dialog"; import { DialogProvider } from "@/context/Dialog";
import { ThemeContext } from "@/context/Theme";
import { Menubar as UIMenubar } from "@/components/ui/menubar";
import FileMenu from "./FileMenu"; import FileMenu from "./FileMenu";
import ViewMenu from "./ViewMenu"; import ViewMenu from "./ViewMenu";
@ -11,11 +14,13 @@ import BlockmaticText from "@/assets/blockmatic-text.svg?react";
import GithubIcon from "@/assets/github.svg?react"; import GithubIcon from "@/assets/github.svg?react";
function Menubar() { function Menubar() {
const { isDark } = useContext(ThemeContext);
return ( return (
<DialogProvider> <DialogProvider>
<UIMenubar className="rounded-none border-t-0 border-x-0 col-span-3"> <UIMenubar className="rounded-none border-t-0 border-x-0 col-span-3">
<Link to={{ pathname: "/" }} className="px-4 w-32"> <Link to={{ pathname: "/" }} className="px-4 w-32">
<BlockmaticText className="h-full w-full" fill="white" /> <BlockmaticText className="h-full w-full" fill={isDark ? "white" : "black"} />
</Link> </Link>
<FileMenu /> <FileMenu />
@ -23,7 +28,7 @@ function Menubar() {
<MoreMenu /> <MoreMenu />
<a href="https://github.com/trafficlunar/blockmatic" className="w-5 absolute right-2"> <a href="https://github.com/trafficlunar/blockmatic" className="w-5 absolute right-2">
<GithubIcon fill="white" /> <GithubIcon fill={isDark ? "white" : "black"} />
</a> </a>
</UIMenubar> </UIMenubar>
</DialogProvider> </DialogProvider>

View file

@ -3,6 +3,7 @@ import { Container, Graphics, Sprite, Stage } from "@pixi/react";
import { BlocksIcon } from "lucide-react"; import { BlocksIcon } from "lucide-react";
import { TexturesContext } from "@/context/Textures"; import { TexturesContext } from "@/context/Textures";
import { ThemeContext } from "@/context/Theme";
import { ToolContext } from "@/context/Tool"; import { ToolContext } from "@/context/Tool";
import _blockData from "@/data/blocks/programmer-art/data.json"; import _blockData from "@/data/blocks/programmer-art/data.json";
@ -15,6 +16,7 @@ interface Props {
function SelectorBlocks({ stageWidth, searchInput }: Props) { function SelectorBlocks({ stageWidth, searchInput }: Props) {
const { missingTexture, textures } = useContext(TexturesContext); const { missingTexture, textures } = useContext(TexturesContext);
const { isDark } = useContext(ThemeContext);
const { selectedBlock, setSelectedBlock } = useContext(ToolContext); const { selectedBlock, setSelectedBlock } = useContext(ToolContext);
const [hoverPosition, setHoverPosition] = useState<Position | null>(null); const [hoverPosition, setHoverPosition] = useState<Position | null>(null);
@ -82,8 +84,8 @@ function SelectorBlocks({ stageWidth, searchInput }: Props) {
y={hoverPosition.y} y={hoverPosition.y}
draw={(g) => { draw={(g) => {
g.clear(); g.clear();
g.beginFill("#0000004D"); g.beginFill(0x000000, 0.5);
g.lineStyle(2, 0xffffff, 1, 1); g.lineStyle(2, isDark ? 0xffffff : 0x000000, 1, 1);
g.drawRect(0, 0, 32, 32); g.drawRect(0, 0, 32, 32);
}} }}
/> />
@ -95,7 +97,7 @@ function SelectorBlocks({ stageWidth, searchInput }: Props) {
y={selectedBlockPosition.y} y={selectedBlockPosition.y}
draw={(g) => { draw={(g) => {
g.clear(); g.clear();
g.lineStyle(2, 0xffffff, 1, 1); g.lineStyle(2, isDark ? 0xffffff : 0x000000, 1, 1);
g.drawRect(0, 0, 32, 32); g.drawRect(0, 0, 32, 32);
}} }}
/> />

View file

@ -2,7 +2,7 @@ import React, { createContext, ReactNode, useMemo, useState } from "react";
interface Context { interface Context {
stageSize: Dimension; stageSize: Dimension;
canvasSize: Dimension; canvasSize: CanvasSize;
blocks: Block[]; blocks: Block[];
coords: Position; coords: Position;
scale: number; scale: number;

View file

@ -1,25 +1,22 @@
import { createContext, useContext, useEffect, useState } from "react"; import { createContext, useEffect, useState } from "react";
type ThemeProviderProps = { interface Props {
children: React.ReactNode; children: React.ReactNode;
defaultTheme?: Theme; defaultTheme?: Theme;
storageKey?: string; storageKey?: string;
}; }
type ThemeProviderState = { interface Context {
theme: Theme; theme: Theme;
isDark: boolean;
setTheme: (theme: Theme) => void; setTheme: (theme: Theme) => void;
}; }
const initialState: ThemeProviderState = { export const ThemeContext = createContext<Context>({} as Context);
theme: "system",
setTheme: () => null,
};
const ThemeProviderContext = createContext<ThemeProviderState>(initialState); export function ThemeProvider({ children, defaultTheme = "system", storageKey = "vite-ui-theme", ...props }: Props) {
export function ThemeProvider({ children, defaultTheme = "system", storageKey = "vite-ui-theme", ...props }: ThemeProviderProps) {
const [theme, setTheme] = useState<Theme>(() => (localStorage.getItem(storageKey) as Theme) || defaultTheme); const [theme, setTheme] = useState<Theme>(() => (localStorage.getItem(storageKey) as Theme) || defaultTheme);
const [isDark, setIsDark] = useState(true);
useEffect(() => { useEffect(() => {
const root = window.document.documentElement; const root = window.document.documentElement;
@ -27,17 +24,19 @@ export function ThemeProvider({ children, defaultTheme = "system", storageKey =
root.classList.remove("light", "dark"); root.classList.remove("light", "dark");
if (theme === "system") { if (theme === "system") {
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches;
root.classList.add(systemTheme); root.classList.add(systemTheme ? "dark" : "light");
return; return;
} }
root.classList.add(theme); root.classList.add(theme);
setIsDark(theme == "dark");
}, [theme]); }, [theme]);
const value = { const value = {
theme, theme,
isDark,
setTheme: (theme: Theme) => { setTheme: (theme: Theme) => {
localStorage.setItem(storageKey, theme); localStorage.setItem(storageKey, theme);
setTheme(theme); setTheme(theme);
@ -45,16 +44,8 @@ export function ThemeProvider({ children, defaultTheme = "system", storageKey =
}; };
return ( return (
<ThemeProviderContext.Provider {...props} value={value}> <ThemeContext.Provider {...props} value={value}>
{children} {children}
</ThemeProviderContext.Provider> </ThemeContext.Provider>
); );
} }
export const useTheme = () => {
const context = useContext(ThemeProviderContext);
if (context === undefined) throw new Error("useTheme must be used within a ThemeProvider");
return context;
};