import/export theme

This commit is contained in:
Vinzenz Schroeter 2024-05-07 14:47:48 +02:00
parent e854f77bdc
commit d4d2b6397c
5 changed files with 56 additions and 17 deletions

View file

@ -34,7 +34,7 @@ export default function JoinForm({onDone}: {
<TextInput <TextInput
value={name} value={name}
placeholder="player name" placeholder="player name"
onChange={e => setName(e.target.value)} onChange={n => setName(n)}
onEnter={confirm} onEnter={confirm}
/> />
<Button <Button

View file

@ -7,6 +7,7 @@ import Column from './components/Column.tsx';
import Button from './components/Button.tsx'; import Button from './components/Button.tsx';
import Row from './components/Row.tsx'; import Row from './components/Row.tsx';
import './MapChooser.css'; import './MapChooser.css';
import Spacer from './components/Spacer.tsx';
function base64ToArrayBuffer(base64: string) { function base64ToArrayBuffer(base64: string) {
const binaryString = atob(base64); const binaryString = atob(base64);
@ -57,7 +58,7 @@ function MapChooserDialog({mapNames, onClose, onConfirm}: {
readonly onClose: () => void; readonly onClose: () => void;
}) { }) {
const [chosenMap, setChosenMap] = useState<string>(); const [chosenMap, setChosenMap] = useState<string>();
return <Dialog title='Choose a map' onClose={onClose}> return <Dialog title="Choose a map" onClose={onClose}>
<Row className="MapChooser-Row overflow-scroll"> <Row className="MapChooser-Row overflow-scroll">
{mapNames.map(name => <MapPreview {mapNames.map(name => <MapPreview
key={name} key={name}
@ -67,7 +68,7 @@ function MapChooserDialog({mapNames, onClose, onConfirm}: {
/>)} />)}
</Row> </Row>
<Row> <Row>
<div className="flex-grow"/> <Spacer/>
<Button text="« cancel" onClick={onClose}/> <Button text="« cancel" onClick={onClose}/>
<Button text="√ confirm" disabled={!chosenMap} onClick={() => chosenMap && onConfirm(chosenMap)}/> <Button text="√ confirm" disabled={!chosenMap} onClick={() => chosenMap && onConfirm(chosenMap)}/>
</Row> </Row>

View file

@ -1,27 +1,28 @@
import Button from './components/Button.tsx'; import Button from './components/Button.tsx';
import {getRandomTheme, HSL, hslToString, useHslTheme} from './theme.tsx'; import {getRandomTheme, HSL, hslToString, useHslTheme} from './theme.tsx';
import Dialog from './components/Dialog.tsx'; import Dialog from './components/Dialog.tsx';
import {useState} from 'react'; import {useMemo, useState} from 'react';
import {NumberInput, RangeInput} from './components/Input.tsx'; import {NumberInput, RangeInput, TextInput} from './components/Input.tsx';
import Row from './components/Row.tsx'; import Row from './components/Row.tsx';
import Column from './components/Column.tsx'; import Column from './components/Column.tsx';
import './ThemeChooser.css'; import './ThemeChooser.css';
import Spacer from './components/Spacer.tsx';
function HslEditor({name, value, setValue}: { function HslEditor({name, value, setValue}: {
name: string; name: string;
value: HSL; value: HSL;
setValue: (value: HSL) => void setValue: (value: HSL) => void
}) { }) {
const setH = (h: number) => setValue({...value, h}); const setH = (h: number) => setValue({...value, h});
const setS = (s: number) => setValue({...value, s}); const setS = (s: number) => setValue({...value, s});
const setL = (l: number) => setValue({...value, l}); const setL = (l: number) => setValue({...value, l});
return <Column> return <Column>
<Row> <Row>
<div className="" style={{background: hslToString(value), border: '1px solid white', aspectRatio: '1'}}/> <div className="" style={{background: hslToString(value), border: '1px solid white', aspectRatio: '1'}}/>
<p>{name}</p> <p>{name}</p>
</Row> </Row>
<div className='HslEditor-Inputs'> <div className="HslEditor-Inputs">
<p>Hue</p> <p>Hue</p>
<NumberInput value={Math.round(value.h)} onChange={setH}/> <NumberInput value={Math.round(value.h)} onChange={setH}/>
<RangeInput value={Math.round(value.h)} min={0} max={360} onChange={setH}/> <RangeInput value={Math.round(value.h)} min={0} max={360} onChange={setH}/>
@ -41,14 +42,43 @@ function ThemeChooserDialog({onClose}: {
onClose: () => void; onClose: () => void;
}) { }) {
const {hslTheme, setHslTheme} = useHslTheme(); const {hslTheme, setHslTheme} = useHslTheme();
const [themeString, setThemeString] = useState<string>(JSON.stringify(hslTheme));
const [errorMsg, setErrorMsg] = useState<string>();
useMemo(() => {
setThemeString(JSON.stringify(hslTheme));
}, [hslTheme]);
return <Dialog title="Theme editor" onClose={onClose}> return <Dialog title="Theme editor" onClose={onClose}>
<Row> <Row>
<Button <Button
text="surprise me" text="? randomize"
className='flex-grow'
onClick={() => setHslTheme(_ => getRandomTheme())}/> onClick={() => setHslTheme(_ => getRandomTheme())}/>
<Spacer/>
</Row> </Row>
<Column className='overflow-scroll'> <Row>
<TextInput
value={themeString}
onChange={setThemeString}
className="flex-grow"/>
<Button text="» import" onClick={() => {
try {
const theme = JSON.parse(themeString);
setHslTheme(old => ({...old, theme}));
} catch (e: any) {
setErrorMsg(e.message);
}
}}/>
{errorMsg &&
<Dialog
title="Error"
onClose={() => setErrorMsg(undefined)}
>
<p>{errorMsg}</p>
</Dialog>
}
</Row>
<Column className="overflow-scroll">
<HslEditor <HslEditor
name="background" name="background"
value={hslTheme.background} value={hslTheme.background}

View file

@ -1,8 +1,7 @@
import {ChangeEventHandler} from 'react';
import './Input.css'; import './Input.css';
export function TextInput({onChange, className, value, placeholder, onEnter}: { export function TextInput({onChange, className, value, placeholder, onEnter}: {
onChange?: ChangeEventHandler<HTMLInputElement> | undefined; onChange?: (value: string) => void;
className?: string; className?: string;
value: string; value: string;
placeholder?: string; placeholder?: string;
@ -13,10 +12,15 @@ export function TextInput({onChange, className, value, placeholder, onEnter}: {
className={'Input ' + (className ?? '')} className={'Input ' + (className ?? '')}
value={value} value={value}
placeholder={placeholder} placeholder={placeholder}
onChange={onChange} onChange={event => {
if (!onChange)
return;
onChange(event.target.value);
}}
onKeyUp={event => { onKeyUp={event => {
if (onEnter && event.key === 'Enter') if (!onEnter || event.key !== 'Enter')
onEnter(); return;
onEnter();
}} }}
/>; />;
} }

View file

@ -0,0 +1,4 @@
export default function Spacer() {
return <div className='flex-grow' />;
}