Add unit setting (#183)

* Add Toggle component

* Add default units to settings

* Add defaultBaseUnit, shorten settings names

* Make debug panel use Toggle, fix colors

* add eslint-plugin-css-modules
This commit is contained in:
Frank Noirot
2023-07-21 12:48:23 -04:00
committed by GitHub
parent 87aecf7f50
commit 48f1d5e623
9 changed files with 170 additions and 15 deletions

View File

@ -1,7 +1,11 @@
{
"plugins": [
"css-modules"
],
"extends": [
"react-app",
"react-app/jest"
"react-app/jest",
"plugin:css-modules/recommended"
],
"rules": {
"semi": [

View File

@ -85,6 +85,7 @@
"babel-jest": "^29.6.1",
"eslint": "^8.44.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-css-modules": "^2.11.0",
"isomorphic-fetch": "^3.0.0",
"jest": "^29.6.1",
"jest-environment-jsdom": "^29.6.1",

View File

@ -0,0 +1,32 @@
.toggle {
@apply flex items-center gap-2 w-fit;
--toggle-size: 1.25rem;
--padding: 0.25rem;
}
.toggle:focus-within > span {
@apply outline-none ring-2;
}
.toggle input {
@apply sr-only;
}
.toggle > span {
@apply relative rounded border border-chalkboard-110;
width: calc(2 * (var(--toggle-size) + var(--padding)));
height: calc(var(--toggle-size) + var(--padding));
}
.toggle > span::after {
content: '';
@apply absolute w-4 h-4 rounded-sm bg-chalkboard-110;
top: 50%;
left: 50%;
translate: calc(-100% - var(--padding)) -50%;
transition: translate 0.08s ease-out;
}
.toggle input:checked + span::after {
translate: calc(50% - var(--padding)) -50%;
}

View File

@ -0,0 +1,34 @@
import styles from './Toggle.module.css'
interface ToggleProps {
className?: string
offLabel?: string
onLabel?: string
name: string
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
checked: boolean
}
export const Toggle = ({
className = '',
offLabel = 'Off',
onLabel = 'On',
name = '',
onChange,
checked,
}: ToggleProps) => {
return (
<label className={`${styles.toggle} ${className}`}>
{offLabel}
<input
type="checkbox"
name={name}
id={name}
checked={checked}
onChange={onChange}
/>
<span></span>
{onLabel}
</label>
)
}

View File

@ -1,4 +1,5 @@
@import '../node_modules/allotment/dist/style.css';
@import './colors.css';
@tailwind base;
@tailwind components;

View File

@ -2,16 +2,21 @@ import { faCheck, faFolder, faXmark } from '@fortawesome/free-solid-svg-icons'
import { ActionButton } from '../components/ActionButton'
import { AppHeader } from '../components/AppHeader'
import { open } from '@tauri-apps/api/dialog'
import { useStore } from '../useStore'
import { baseUnits, useStore } from '../useStore'
import { useState } from 'react'
import { toast } from 'react-hot-toast'
import { Toggle } from '../components/Toggle/Toggle'
export const Settings = () => {
const {
defaultDir: originalDefaultDir,
defaultDir: ogDefaultDir,
setDefaultDir: saveDefaultDir,
defaultProjectName: originalDefaultProjectName,
defaultProjectName: ogDefaultProjectName,
setDefaultProjectName: saveDefaultProjectName,
defaultUnitSystem: ogDefaultUnitSystem,
setDefaultUnitSystem: saveDefaultUnitSystem,
defaultBaseUnit: ogDefaultBaseUnit,
setDefaultBaseUnit: saveDefaultBaseUnit,
saveDebugPanel,
originalDebugPanel,
} = useStore((s) => ({
@ -19,13 +24,19 @@ export const Settings = () => {
setDefaultDir: s.setDefaultDir,
defaultProjectName: s.defaultProjectName,
setDefaultProjectName: s.setDefaultProjectName,
defaultUnitSystem: s.defaultUnitSystem,
setDefaultUnitSystem: s.setDefaultUnitSystem,
defaultBaseUnit: s.defaultBaseUnit,
setDefaultBaseUnit: s.setDefaultBaseUnit,
saveDebugPanel: s.setDebugPanel,
originalDebugPanel: s.debugPanel,
}))
const [defaultDir, setDefaultDir] = useState(originalDefaultDir)
const [defaultProjectName, setDefaultProjectName] = useState(
originalDefaultProjectName
)
const [defaultDir, setDefaultDir] = useState(ogDefaultDir)
const [defaultProjectName, setDefaultProjectName] =
useState(ogDefaultProjectName)
const [defaultUnitSystem, setDefaultUnitSystem] =
useState(ogDefaultUnitSystem)
const [defaultBaseUnit, setDefaultBaseUnit] = useState(ogDefaultBaseUnit)
const [debugPanel, setDebugPanel] = useState(originalDebugPanel)
async function handleDirectorySelection() {
@ -43,6 +54,8 @@ export const Settings = () => {
const handleSaveClick = () => {
saveDefaultDir(defaultDir)
saveDefaultProjectName(defaultProjectName)
saveDefaultUnitSystem(defaultUnitSystem)
saveDefaultBaseUnit(defaultBaseUnit)
saveDebugPanel(debugPanel)
toast.success('Settings saved!')
}
@ -77,7 +90,7 @@ export const Settings = () => {
value={defaultDir.dir}
onChange={(e) =>
setDefaultDir({
base: originalDefaultDir.base,
base: ogDefaultDir.base,
dir: e.target.value,
})
}
@ -109,12 +122,43 @@ export const Settings = () => {
onChange={(e) => setDefaultProjectName(e.target.value)}
/>
</SettingsSection>
<SettingsSection
title="Unit System"
description="Which unit system to use by default"
>
<Toggle
offLabel="Imperial"
onLabel="Metric"
name="settings-units"
checked={defaultUnitSystem === 'metric'}
onChange={(e) =>
setDefaultUnitSystem(e.target.checked ? 'metric' : 'imperial')
}
/>
</SettingsSection>
<SettingsSection
title="Base Unit"
description="Which base unit to use in dimensions by default"
>
<select
id="base-unit"
className="block w-full px-3 py-1 border border-chalkboard-30 bg-transparent"
value={defaultBaseUnit}
onChange={(e) => setDefaultBaseUnit(e.target.value)}
>
{baseUnits[defaultUnitSystem].map((unit) => (
<option key={unit} value={unit}>
{unit}
</option>
))}
</select>
</SettingsSection>
<SettingsSection
title="Debug Panel"
description="Show the debug panel in the editor"
>
<input
type="checkbox"
<Toggle
name="settings-debug-panel"
checked={debugPanel}
onChange={(e) => setDebugPanel(e.target.checked)}
/>
@ -153,7 +197,7 @@ function SettingsSection({
<h2 className="text-2xl">{title}</h2>
<p className="mt-2 text-sm">{description}</p>
</div>
{children}
<div>{children}</div>
</section>
)
}

View File

@ -91,6 +91,13 @@ export type GuiModes =
position: Position
}
type UnitSystem = 'imperial' | 'metric'
export const baseUnits: Record<UnitSystem, string[]> = {
imperial: ['in', 'ft'],
metric: ['mm', 'cm', 'm'],
}
interface DefaultDir {
base?: BaseDirectory
dir: string
@ -152,6 +159,10 @@ export interface StoreState {
setDefaultDir: (dir: DefaultDir) => void
defaultProjectName: string
setDefaultProjectName: (defaultProjectName: string) => void
defaultUnitSystem: UnitSystem,
setDefaultUnitSystem: (defaultUnitSystem: UnitSystem) => void
defaultBaseUnit: string,
setDefaultBaseUnit: (defaultBaseUnit: string) => void
showHomeMenu: boolean
setHomeShowMenu: (showMenu: boolean) => void
homeMenuItems: {
@ -314,6 +325,10 @@ export const useStore = create<StoreState>()(
setDefaultDir: (dir) => set({ defaultDir: dir }),
defaultProjectName: 'new-project-$n',
setDefaultProjectName: (defaultProjectName) => set({ defaultProjectName }),
defaultUnitSystem: 'imperial',
setDefaultUnitSystem: (defaultUnitSystem) => set({ defaultUnitSystem }),
defaultBaseUnit: 'in',
setDefaultBaseUnit: (defaultBaseUnit) => set({ defaultBaseUnit }),
showHomeMenu: true,
setHomeShowMenu: (showHomeMenu) => set({ showHomeMenu }),
homeMenuItems: [],
@ -331,6 +346,8 @@ export const useStore = create<StoreState>()(
'code',
'defaultDir',
'defaultProjectName',
'defaultUnitSystem',
'defaultBaseUnit',
'token',
'debugPanel'
].includes(key))

View File

@ -2,7 +2,7 @@ import react from '@vitejs/plugin-react'
import viteTsconfigPaths from 'vite-tsconfig-paths'
import eslint from 'vite-plugin-eslint'
export default {
const config = {
server: {
open: true,
port: 3000,
@ -16,3 +16,5 @@ export default {
eslint(),
],
}
export default config

View File

@ -3519,6 +3519,14 @@ eslint-module-utils@^2.7.4:
dependencies:
debug "^3.2.7"
eslint-plugin-css-modules@^2.11.0:
version "2.11.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-css-modules/-/eslint-plugin-css-modules-2.11.0.tgz#8de4d01d523a2d51c03043fa8004aab6b6cf3b1a"
integrity sha512-CLvQvJOMlCywZzaI4HVu7QH/ltgNXvCg7giJGiE+sA9wh5zQ+AqTgftAzrERV22wHe1p688wrU/Zwxt1Ry922w==
dependencies:
gonzales-pe "^4.0.3"
lodash "^4.17.2"
eslint-plugin-flowtype@^8.0.3:
version "8.0.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz#e1557e37118f24734aa3122e7536a038d34a4912"
@ -4095,6 +4103,13 @@ globrex@^0.1.2:
resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098"
integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==
gonzales-pe@^4.0.3:
version "4.3.0"
resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3"
integrity sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==
dependencies:
minimist "^1.2.5"
goober@^2.1.10:
version "2.1.13"
resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.13.tgz#e3c06d5578486212a76c9eba860cbc3232ff6d7c"
@ -5181,7 +5196,7 @@ lodash.merge@^4.6.2:
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.21:
lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.2, lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@ -5295,6 +5310,11 @@ minimist@^1.2.0:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
minimist@^1.2.5:
version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
minimist@^1.2.6:
version "1.2.7"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18"