import/export theme
This commit is contained in:
parent
e854f77bdc
commit
d4d2b6397c
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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();
|
||||||
}}
|
}}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
4
tank-frontend/src/components/Spacer.tsx
Normal file
4
tank-frontend/src/components/Spacer.tsx
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
export default function Spacer() {
|
||||||
|
return <div className='flex-grow' />;
|
||||||
|
}
|
Loading…
Reference in a new issue