Skip to content

Commit

Permalink
Automaticly save settings to localStorage
Browse files Browse the repository at this point in the history
  • Loading branch information
kuchta committed Oct 24, 2023
1 parent 6e744fb commit 74fa32d
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 102 deletions.
1 change: 1 addition & 0 deletions .eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ rules:
"@typescript-eslint/no-non-null-assertion": off
"@typescript-eslint/no-unused-vars": warn
"import/no-anonymous-default-export": off
"@typescript-eslint/no-explicit-any": warn
79 changes: 73 additions & 6 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,90 @@ import Show from './Show'
import Add from './Add'
import Multiply from './Multiply'
import Convert from './Convert'
import { Radix, createRadixes } from '../utils'
import { Radix, defaultChars, defaultCharsArray, createRadixes, createRadix, getRadixesLS, setCharsLS, setRadixesLS, s2a } from '../utils'


type Who = 'all' | 'odd' | 'even' | 'standard' | 'bijective' | 'balanced'

export type SetRadixes = (command: {
who: Who, what: 'toggle', enabled: boolean } | {
who: Radix, what: 'toggle' } | {
who: Who, what: 'set-chars', chars?: string } | {
who: Radix, what: 'set-chars', chars?: string }) => void

export default function App() {
const [ activeTab, setActiveTab ] = useState(0)
const [ radixes, setRadixes ] = useState(createRadixes())
const [ radixes, _setRadixes ] = useState(getRadixesLS() ?? createRadixes())
const [ enabledRadixes, setEnabledRadixes ] = useState(radixes.filter(v => v.enabled))

const setAndFilterRadixes = (radixes: Radix[]) => {
setRadixes(radixes)
setEnabledRadixes(radixes.filter(v => v.enabled))
const setRadixes: SetRadixes = (command) => {
// console.log('setRadixes:', command)

let newRadixes = radixes

switch (command.who) {
case 'all': {
if (command.what === 'toggle') {
radixes.forEach(r => r.enabled = command.enabled)
} else if (command.what === 'set-chars') {
const chars = s2a(command.chars)
if (chars && chars.length !== defaultCharsArray.length) {
throw new Error(`Invalid number of chars provided: ${chars.length}, expected: ${defaultCharsArray.length}`)
}
setCharsLS(command.chars && command.chars !== defaultChars ? command.chars : undefined)
newRadixes = createRadixes(chars)
}
} break
case 'odd': if (command.what === 'toggle') {
radixes.forEach(r => { if (r.radix % 2n === 1n) r.enabled = command.enabled })
} break
case 'even': {
if (command.what === 'toggle') {
radixes.forEach(r => { if (r.radix % 2n === 0n) r.enabled = command.enabled })
}
} break
case 'standard': {
if (command.what === 'toggle') {
radixes.forEach(r => { if (r.system === 'standard') r.enabled = command.enabled })
}
} break
case 'bijective': {
if (command.what === 'toggle') {
radixes.forEach(r => { if (r.system === 'bijective') r.enabled = command.enabled })
}
} break
case 'balanced': {
if (command.what === 'toggle') {
radixes.forEach(r => { if (r.system === 'balanced') r.enabled = command.enabled })
}
} break
default: {
if (command.what === 'toggle') {
command.who.enabled = !command.who.enabled
} else if (command.what === 'set-chars') {
const i = radixes.findIndex(r => r.name === command.who.name)
const oldRadix = radixes[i]
const chars = s2a(command.chars)
if (chars != undefined) {
if (chars.length !== oldRadix.chars.length) {
throw new Error(`Invalid number of chars provided: ${chars.length}, expected: ${oldRadix.chars.length}`)
}
}
const newRadix = createRadix(Number(oldRadix.radix), oldRadix.system, chars, oldRadix.enabled)
radixes[i] = newRadix
}
}
}
// console.log('setRadixes: newRadixes:', newRadixes)
setRadixesLS(newRadixes)
_setRadixes(newRadixes)
setEnabledRadixes(newRadixes.filter(v => v.enabled))
}

// console.log('App: ', { enabledRadixes })

return <div className="font-mono">
<Header radixes={radixes} setRadixes={setAndFilterRadixes}/>
<Header radixes={radixes} setRadixes={setRadixes}/>
<div className="tabs justify-center mb-4">
<button className={`tab tab-bordered ${activeTab === 0 ? 'tab-active' : ''}`} onClick={() => setActiveTab(0)}>
Show
Expand Down
134 changes: 65 additions & 69 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,78 +1,68 @@
import { FormEventHandler, useState } from 'react'
import themes from 'daisyui/src/theming/themes'

import { Radix, baseChars, createRadixes, createRadix, s2a } from '../utils'
import { SetRadixes } from './App'
import { Radix, defaultChars, getCharsLS, getThemeLS, setThemeLS } from '../utils'


let allChars = getCharsLS() ?? defaultChars
const themeNames = Object.keys(themes).sort().map(t => t.split('=')[1].slice(0, -1))
let allChars = baseChars

export default function Header({ radixes, setRadixes }: {
radixes: Radix[],
setRadixes: (radixes: Radix[]) => void,
setRadixes: SetRadixes,
}) {
const [ theme, _setTheme ] = useState(getThemeLS)
const [ expanded, setExpanded ] = useState(false)
const [ chars, setChars ] = useState(allChars)
const [ inputChars, setInputChars ] = useState(allChars)

const setBaseChars: FormEventHandler<HTMLFormElement> = (e) => {
// console.log('Header: ', { theme })

const setTheme = (theme: string) => {
_setTheme(theme)
setThemeLS(theme)
}

const setRadixChars: FormEventHandler<HTMLFormElement> = (e) => {
e.preventDefault()

const data = new FormData(e.currentTarget)
const radix = data.get('radix')
const chars = s2a(data.get('chars') as string)
const radix = data.get('radix') as string
const chars = data.get('chars') as string

if (radix === 'all') {
if (e.type === 'reset') {
allChars = baseChars
setChars(allChars)
} else if (chars.length !== allChars.length) {
return alert('Invalid number of chars provided')
} else {
allChars = chars
}
setRadixes(createRadixes(allChars))
} else {
const i = radixes.findIndex(r => r.name === radix)
const r = radixes[i]
if (e.type === 'reset') {
const rb = createRadix(Number(r.radix), r.system, allChars, r.enabled)
setChars(rb.chars)
radixes[i] = rb
} else if (chars.length !== r.chars.length) {
return alert('Invalid number of chars provided')
try {
if (radix ==='all') {
if (e.type === 'submit') {
allChars = chars
setRadixes({ what: 'set-chars', who: 'all', chars })
} else {
if (allChars !== defaultChars) {
allChars = defaultChars
setRadixes({ what: 'set-chars', who: 'all' })
}
setInputChars(allChars)
}
} else {
radixes[i] = createRadix(Number(r.radix), r.system, chars, r.enabled)
const r = radixes.find(r => r.name === radix)!
setRadixes({ what: 'set-chars', who: r, chars: e.type === 'submit' ? chars : undefined })
if (e.type === 'reset') {
setInputChars(r?.chars.join(''))
}
}
setRadixes(radixes)
} catch (error) {
alert(error)
}
}

const toggle = (command: { what: 'all' | 'odd' | 'even' | 'standard' | 'bijective' | 'balanced', enabled: boolean } | { what: 'one', radix: Radix }) => {
// console.log({ action, value })

switch (command.what) {
case 'all': radixes.forEach(r => r.enabled = command.enabled); break
case 'odd': radixes.forEach(r => { if (r.radix % 2n === 1n) r.enabled = command.enabled }); break
case 'even': radixes.forEach(r => { if (r.radix % 2n === 0n) r.enabled = command.enabled }); break
case 'standard': radixes.forEach(r => { if (r.system === 'standard') r.enabled = command.enabled }); break
case 'bijective': radixes.forEach(r => { if (r.system === 'bijective') r.enabled = command.enabled }); break
case 'balanced': radixes.forEach(r => { if (r.system === 'balanced') r.enabled = command.enabled }); break
default: command.radix.enabled = !command.radix.enabled
}
setRadixes(radixes)
}

const button = (radix: Radix) =>
<button
className={`btn btn-xs btn-outline ${radix.enabled ? 'btn-active' : ''} w-12 m-1`}
key={radix.name}
onClick={() => toggle({ what: 'one', radix })}
onClick={() => setRadixes({ what: 'toggle', who: radix })}
>
{ String(radix.radix) }
</button>

// console.log('Header: ', { chars })

return (
<header>
<nav className="navbar bg-base-100">
Expand All @@ -92,12 +82,12 @@ export default function Header({ radixes, setRadixes }: {
</div>
<div className="flex flex-row justify-between z-50">
<ul className="menu menu-horizontal px-1">
<li><a onClick={() => setExpanded(!expanded)}>Settings</a></li>
<li><a className={`menu-dropdown-toggle ${expanded ? 'menu-dropdown-show' : ''}`} onClick={() => setExpanded(!expanded)}>Settings</a></li>
<li>
<details>
<summary>Themes</summary>
<ul className="p-2 bg-base-100">
{themeNames.map(t => <li key={t}><a onClick={() => document.documentElement.setAttribute('data-theme', t)}>{t[0].toUpperCase() + t.slice(1)}</a></li>)}
{themeNames.map(t => <li key={t}><a className={t === theme ? 'active': ''} onClick={() => setTheme(t)}>{t[0].toUpperCase() + t.slice(1)}</a></li>)}
</ul>
</details>
</li>
Expand All @@ -108,12 +98,18 @@ export default function Header({ radixes, setRadixes }: {
<div className="collapse-content">
<div className="card card-bordered">
<div className="card-actions justify-center items-center p-2">
<form className="card card-bordered flex-row justify-items-center items-center gap-1 p-2" onReset={setBaseChars} onSubmit={setBaseChars}>
<select className="text-xs rounded-md bg-base-100" name="radix" defaultValue="all" onChange={e => setChars(e.target.value === 'all' ? allChars : radixes.find(r => r.name === e.target.value)?.chars ?? ['']) }>
<form className="card card-bordered flex-row justify-items-center items-center gap-1 p-2" onReset={setRadixChars} onSubmit={setRadixChars}>
<select className="text-sm rounded-md bg-base-100" name="radix" defaultValue="all"
onChange={e => setInputChars(e.target.value === 'all' ? allChars : radixes.find(r => r.name === e.target.value)!.chars.join('')) }
>
<option value="all">All</option>
{ radixes.map(radix => <option key={radix.name}>{radix.name}</option>) }
</select>
<textarea className="resize-none bg-base-100 leading-4 h-[5em] w-[12rem] md:h-[2em] md:w-[29em] xl:h-[1em] xl:w-[57em] p-0" name="chars" value={chars.join('')} onChange={e => setChars(s2a(e.target.value))}/>
<textarea className="resize-none bg-base-100 leading-4 h-[5em] w-[12rem] md:h-[2em] md:w-[29em] xl:h-[1em] xl:w-[57em] p-0"
name="chars"
value={inputChars}
onChange={e => setInputChars(e.target.value)}
/>
<span className="flex flex-col gap-1 lg:gap-0 lg:flex-row lg:join justify-center">
<button className="btn btn-xs btn-outline btn-success join-item" type="reset" >Reset</button>
<button className="btn btn-xs btn-outline btn-error join-item" type="submit" >Set</button>
Expand All @@ -122,34 +118,34 @@ export default function Header({ radixes, setRadixes }: {
</div>
<div className="flex flex-row flex-wrap justify-center items-center gap-2">
<span className="join">
<button className="btn btn-xs btn-outline btn-success join-item" onClick={() => toggle({ what: 'all', enabled: true })}>Add</button>
<span className="btn btn-xs btn-outline join-item cursor-default">All</span>
<button className="btn btn-xs btn-outline btn-error join-item" onClick={() => toggle({ what: 'all', enabled: false })}>Remove</button>
<button className="btn btn-xs btn-outline btn-success join-item" onClick={() => setRadixes({ what: 'toggle', who: 'all', enabled: true })}>Add</button>
<span className="btn btn-xs btn-outline pointer-events-none join-item cursor-default">All</span>
<button className="btn btn-xs btn-outline btn-error join-item" onClick={() => setRadixes({ what: 'toggle', who: 'all', enabled: false })}>Remove</button>
</span>
<span className="join">
<button className="btn btn-xs btn-outline btn-success join-item" onClick={() => toggle({ what: 'odd', enabled: true })}>Add</button>
<span className="btn btn-xs btn-outline join-item cursor-default">Odd</span>
<button className="btn btn-xs btn-outline btn-error join-item" onClick={() => toggle({ what: 'odd', enabled: false })}>Remove</button>
<button className="btn btn-xs btn-outline btn-success join-item" onClick={() => setRadixes({ what: 'toggle', who: 'odd', enabled: true })}>Add</button>
<span className="btn btn-xs btn-outline pointer-events-none join-item cursor-default">Odd</span>
<button className="btn btn-xs btn-outline btn-error join-item" onClick={() => setRadixes({ what: 'toggle', who: 'odd', enabled: false })}>Remove</button>
</span>
<span className="join">
<button className="btn btn-xs btn-outline btn-success join-item" onClick={() => toggle({ what: 'even', enabled: true })}>Add</button>
<span className="btn btn-xs btn-outline join-item cursor-default">Even</span>
<button className="btn btn-xs btn-outline btn-error join-item" onClick={() => toggle({ what: 'even', enabled: false })}>Remove</button>
<button className="btn btn-xs btn-outline btn-success join-item" onClick={() => setRadixes({ what: 'toggle', who: 'even', enabled: true })}>Add</button>
<span className="btn btn-xs btn-outline pointer-events-none join-item cursor-default">Even</span>
<button className="btn btn-xs btn-outline btn-error join-item" onClick={() => setRadixes({ what: 'toggle', who: 'even', enabled: false })}>Remove</button>
</span>
<span className="join">
<button className="btn btn-xs btn-outline btn-success join-item" onClick={() => toggle({ what: 'standard', enabled: true })}>Add</button>
<span className="btn btn-xs btn-outline join-item cursor-default">Standard</span>
<button className="btn btn-xs btn-outline btn-error join-item" onClick={() => toggle({ what: 'standard', enabled: false })}>Remove</button>
<button className="btn btn-xs btn-outline btn-success join-item" onClick={() => setRadixes({ what: 'toggle', who: 'standard', enabled: true })}>Add</button>
<span className="btn btn-xs btn-outline pointer-events-none join-item cursor-default">Standard</span>
<button className="btn btn-xs btn-outline btn-error join-item" onClick={() => setRadixes({ what: 'toggle', who: 'standard', enabled: false })}>Remove</button>
</span>
<span className="join">
<button className="btn btn-xs btn-outline btn-success join-item" onClick={() => toggle({ what: 'bijective', enabled: true })}>Add</button>
<span className="btn btn-xs btn-outline join-item cursor-default">Bijective</span>
<button className="btn btn-xs btn-outline btn-error join-item" onClick={() => toggle({ what: 'bijective', enabled: false })}>Remove</button>
<button className="btn btn-xs btn-outline btn-success join-item" onClick={() => setRadixes({ what: 'toggle', who: 'bijective', enabled: true })}>Add</button>
<span className="btn btn-xs btn-outline pointer-events-none join-item cursor-default">Bijective</span>
<button className="btn btn-xs btn-outline btn-error join-item" onClick={() => setRadixes({ what: 'toggle', who: 'bijective', enabled: false })}>Remove</button>
</span>
<span className="join">
<button className="btn btn-xs btn-outline btn-success join-item" onClick={() => toggle({ what: 'balanced', enabled: true })}>Add</button>
<span className="btn btn-xs btn-outline join-item cursor-default">Balanced</span>
<button className="btn btn-xs btn-outline btn-error join-item" onClick={() => toggle({ what: 'balanced', enabled: false })}>Remove</button>
<button className="btn btn-xs btn-outline btn-success join-item" onClick={() => setRadixes({ what: 'toggle', who: 'balanced', enabled: true })}>Add</button>
<span className="btn btn-xs btn-outline pointer-events-none join-item cursor-default">Balanced</span>
<button className="btn btn-xs btn-outline btn-error join-item" onClick={() => setRadixes({ what: 'toggle', who: 'balanced', enabled: false })}>Remove</button>
</span>
</div>
<div className="card md:flex-row p-1">
Expand Down
Loading

0 comments on commit 74fa32d

Please sign in to comment.