basic theme editor
This commit is contained in:
		
							parent
							
								
									4960df370c
								
							
						
					
					
						commit
						bacd1777be
					
				
					 9 changed files with 138 additions and 38 deletions
				
			
		|  | @ -3,7 +3,7 @@ import {useMutation} from '@tanstack/react-query'; | |||
| 
 | ||||
| import {makeApiUrl} from './serverCalls'; | ||||
| import Button from './components/Button.tsx'; | ||||
| import TextInput from './components/TextInput.tsx'; | ||||
| import {TextInput} from './components/Input.tsx'; | ||||
| import Dialog from './components/Dialog.tsx'; | ||||
| import './JoinForm.css'; | ||||
| 
 | ||||
|  | @ -30,8 +30,7 @@ export default function JoinForm({onDone}: { | |||
| 
 | ||||
|     const confirm = () => postPlayer.mutate({name}); | ||||
| 
 | ||||
|     return <Dialog className="JoinForm"> | ||||
|         <h3> Enter your name to play </h3> | ||||
|     return <Dialog className="JoinForm" title="Enter your name to play"> | ||||
|         <TextInput | ||||
|             value={name} | ||||
|             placeholder="player name" | ||||
|  |  | |||
|  | @ -57,8 +57,7 @@ function MapChooserDialog({mapNames, onClose, onConfirm}: { | |||
|     readonly onClose: () => void; | ||||
| }) { | ||||
|     const [chosenMap, setChosenMap] = useState<string>(); | ||||
|     return <Dialog> | ||||
|         <h3>Choose a map</h3> | ||||
|     return <Dialog title='Choose a map' onClose={onClose}> | ||||
|         <Row className="MapChooser-Row overflow-scroll"> | ||||
|             {mapNames.map(name => <MapPreview | ||||
|                 key={name} | ||||
|  |  | |||
|  | @ -1,11 +1,79 @@ | |||
| import Button from './components/Button.tsx'; | ||||
| import {getRandomTheme, useHslTheme} from './theme.tsx'; | ||||
| import {getRandomTheme, HSL, hslToString, useHslTheme} from './theme.tsx'; | ||||
| import Dialog from './components/Dialog.tsx'; | ||||
| import {useState} from 'react'; | ||||
| import {NumberInput} from './components/Input.tsx'; | ||||
| import Row from './components/Row.tsx'; | ||||
| import Column from './components/Column.tsx'; | ||||
| 
 | ||||
| function HslEditor({name, value, setValue}: { | ||||
|     name: string; | ||||
|     value: HSL; | ||||
|     setValue: (value: HSL) => void | ||||
| }) { | ||||
|     return <Column> | ||||
|         <Row> | ||||
|             <div className="" style={{background: hslToString(value), border: '1px solid white', aspectRatio: '1'}}/> | ||||
|             <p>{name}</p> | ||||
|         </Row> | ||||
|         <div style={{ | ||||
|             display: 'grid', | ||||
|             columnGap: 'var(--padding-normal)', | ||||
|             gridTemplateColumns: 'auto auto', | ||||
|             gridTemplateRows: 'auto' | ||||
|         }}> | ||||
|             <p>Hue</p> | ||||
|             <NumberInput value={Math.round(value.h)} placeholder="Hue" onChange={event => { | ||||
|                 setValue({...value, h: parseInt(event.target.value)}); | ||||
|             }}/> | ||||
| 
 | ||||
|             <p>Saturation</p> | ||||
|             <NumberInput value={Math.round(value.s)} placeholder="Saturation" onChange={event => { | ||||
|                 setValue({...value, s: parseInt(event.target.value)}); | ||||
|             }}/> | ||||
| 
 | ||||
|             <p>Lightness</p> | ||||
|             <NumberInput value={Math.round(value.l)} placeholder="Lightness" onChange={event => { | ||||
|                 setValue({...value, l: parseInt(event.target.value)}); | ||||
|             }}/> | ||||
|         </div> | ||||
|     </Column>; | ||||
| } | ||||
| 
 | ||||
| function ThemeChooserDialog({onClose}: { | ||||
|     onClose: () => void; | ||||
| }) { | ||||
|     const {hslTheme, setHslTheme} = useHslTheme(); | ||||
|     return <Dialog title="Theme editor" onClose={onClose}> | ||||
|         <Button | ||||
|             text="surprise me" | ||||
|             onClick={() => setHslTheme(_ => getRandomTheme())}/> | ||||
|         <HslEditor | ||||
|             name="background" | ||||
|             value={hslTheme.background} | ||||
|             setValue={value => setHslTheme(old => ({...old, background: value}))}/> | ||||
|         <HslEditor | ||||
|             name="primary" | ||||
|             value={hslTheme.primary} | ||||
|             setValue={value => setHslTheme(old => ({...old, primary: value}))}/> | ||||
|         <HslEditor | ||||
|             name="secondary" | ||||
|             value={hslTheme.secondary} | ||||
|             setValue={value => setHslTheme(old => ({...old, secondary: value}))}/> | ||||
|         <HslEditor | ||||
|             name="background" | ||||
|             value={hslTheme.tertiary} | ||||
|             setValue={value => setHslTheme(old => ({...old, tertiary: value}))}/> | ||||
|     </Dialog>; | ||||
| } | ||||
| 
 | ||||
| export default function ThemeChooser({}: {}) { | ||||
|     const {setHslTheme} = useHslTheme(); | ||||
|     const [open, setOpen] = useState(false); | ||||
| 
 | ||||
|     return <> | ||||
|         <Button | ||||
|             text="☼ change colors" | ||||
|             onClick={() => setHslTheme(_ => getRandomTheme())}/> | ||||
|             onClick={() => setOpen(true)}/> | ||||
|         {open && <ThemeChooserDialog onClose={() => setOpen(false)}/>} | ||||
|     </>; | ||||
| } | ||||
|  |  | |||
|  | @ -1,12 +1,20 @@ | |||
| import {ReactNode} from 'react'; | ||||
| import Column from './Column.tsx'; | ||||
| import Row from './Row.tsx'; | ||||
| import Button from './Button.tsx'; | ||||
| import './Dialog.css'; | ||||
| 
 | ||||
| export default function Dialog({children, className}: { | ||||
|     children: ReactNode; | ||||
| export default function Dialog({children, className, title, onClose}: { | ||||
|     title?: string; | ||||
|     children?: ReactNode; | ||||
|     className?: string; | ||||
|     onClose?: () => void; | ||||
| }) { | ||||
|     return <Column className={'Dialog overflow-scroll ' + (className ?? '')}> | ||||
|         <Row> | ||||
|             <h3 className='flex-grow'>{title}</h3> | ||||
|             {onClose && <Button text='x' onClick={onClose} />} | ||||
|         </Row> | ||||
|         {children} | ||||
|     </Column> | ||||
| } | ||||
|  |  | |||
|  | @ -1,3 +1,3 @@ | |||
| .TextInput { | ||||
| .Input { | ||||
|     padding: var(--padding-normal); | ||||
| } | ||||
							
								
								
									
										45
									
								
								tank-frontend/src/components/Input.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								tank-frontend/src/components/Input.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | |||
| import {ChangeEventHandler} from "react"; | ||||
| import './Input.css'; | ||||
| 
 | ||||
| export function TextInput( {onChange, className, value, placeholder, onEnter }: { | ||||
|     onChange?: ChangeEventHandler<HTMLInputElement> | undefined; | ||||
|     className?: string; | ||||
|     value: string; | ||||
|     placeholder: string; | ||||
|     onEnter?: () => void; | ||||
| }) { | ||||
|     return <input | ||||
|         type="text" | ||||
|         className={'Input ' + (className?? '')} | ||||
|         value={value} | ||||
|         placeholder={placeholder} | ||||
|         onChange={onChange} | ||||
|         onKeyUp={event => { | ||||
|             if (onEnter && event.key === 'Enter') | ||||
|                 onEnter(); | ||||
|         }} | ||||
|     />; | ||||
| } | ||||
| 
 | ||||
| export function NumberInput( {onChange, className, value, placeholder, onEnter }: { | ||||
|     onChange?: ChangeEventHandler<HTMLInputElement> | undefined; | ||||
|     className?: string; | ||||
|     value: number; | ||||
|     placeholder: string; | ||||
|     onEnter?: () => void; | ||||
| }) { | ||||
|     return <input | ||||
|         type="number" | ||||
|         className={'Input ' + (className?? '')} | ||||
|         value={value} | ||||
|         placeholder={placeholder} | ||||
|         onChange={onChange} | ||||
|         onKeyUp={event => { | ||||
|             if (onEnter && event.key === 'Enter') | ||||
|                 onEnter(); | ||||
|         }} | ||||
|     />; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -1,9 +1,12 @@ | |||
| import {ReactNode} from "react"; | ||||
| import {ReactNode} from 'react'; | ||||
| 
 | ||||
| import './Row.css'; | ||||
| 
 | ||||
| export default function Row({children, className}: { children: ReactNode, className?: string }) { | ||||
| export default function Row({children, className}: { | ||||
|     children?: ReactNode; | ||||
|     className?: string; | ||||
| }) { | ||||
|     return <div className={'Row flex-row ' + (className ?? '')}> | ||||
|         {children} | ||||
|     </div> | ||||
|     </div>; | ||||
| } | ||||
|  |  | |||
|  | @ -1,22 +0,0 @@ | |||
| import {ChangeEventHandler} from "react"; | ||||
| import './TextInput.css'; | ||||
| 
 | ||||
| export default function TextInput( {onChange, className, value, placeholder, onEnter }: { | ||||
|     onChange?: ChangeEventHandler<HTMLInputElement> | undefined; | ||||
|     className?: string; | ||||
|     value: string; | ||||
|     placeholder: string; | ||||
|     onEnter?: () => void; | ||||
| }) { | ||||
|     return <input | ||||
|         type="text" | ||||
|         className={'TextInput ' + (className?? '')} | ||||
|         value={value} | ||||
|         placeholder={placeholder} | ||||
|         onChange={onChange} | ||||
|         onKeyUp={event => { | ||||
|             if (onEnter && event.key === 'Enter') | ||||
|                 onEnter(); | ||||
|         }} | ||||
|     />; | ||||
| } | ||||
|  | @ -1,7 +1,7 @@ | |||
| import {useStoredObjectState} from './useStoredState.ts'; | ||||
| import {createContext, ReactNode, useContext, useMemo, useRef, useState} from 'react'; | ||||
| 
 | ||||
| type HSL = { | ||||
| export type HSL = { | ||||
|     h: number; | ||||
|     s: number; | ||||
|     l: number; | ||||
|  | @ -53,7 +53,7 @@ function getRandomHsl(params: { | |||
|     return {h, s, l}; | ||||
| } | ||||
| 
 | ||||
| function hslToString({h, s, l}: HSL) { | ||||
| export function hslToString({h, s, l}: HSL) { | ||||
|     return `hsl(${h},${s}%,${l}%)`; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vinzenz Schroeter
						Vinzenz Schroeter