1
0
Fork 0

both menus using same structure and components

This commit is contained in:
Enoch Riese 2023-05-29 22:34:33 -05:00
parent 75bc302dd4
commit cc14c562d2
12 changed files with 164 additions and 264 deletions

View file

@ -99,6 +99,7 @@ export const loadSettingsConfig = ({
only: {
control: 4, // Show when control > 3
dflt: false,
list: parts,
parts,
emoji: '🛍️',
},

View file

@ -82,28 +82,28 @@ export const CoreSettings = ({
sabool: settings.sabool,
parts: patternConfig.draftOrder,
})
// Default control level is 2 (in case people are not logged in)
const control = account.control || 5
const control = account.control
return (
<WorkbenchMenu
{...{
updateFunc: update.settings,
ns,
Icon: SettingsIcon,
name: 'coreSettings',
config: settingsConfig,
control,
inputs,
values,
currentValues: settings,
DynamicDocs,
getDocsPath: (setting) => `site/draft/core-settings${setting ? `/${setting}` : ''}`,
Icon: SettingsIcon,
inputs,
language,
name: 'coreSettings',
ns,
passProps: {
samm: typeof settings.samm === 'undefined' ? defaultSamm(settings.units) : settings.samm,
units: settings.units,
},
language,
DynamicDocs,
getDocsPath: (setting) => `site/draft/core-settings${setting ? `/${setting}` : ''}`,
updateFunc: update.settings,
values,
}}
/>
)

View file

@ -12,46 +12,26 @@ export const PaperlessSettingInput = ListInput
export const MarginSettingInput = MmInput
export const ScaleSettingInput = SliderInput
export const OnlySettingInput = ({ name, config, current, updateFunc, t, draftOrder, design }) => {
const partNames = config.parts.map((part) => ({
id: part,
t: t(`${design}:${part}.t`),
d: t(`${design}:${part}.d`),
}))
export const OnlySettingInput = (props) => {
props.config.choiceTitles = {}
props.config.list.forEach((p) => (props.config.choiceTitles[p] = p))
const togglePart = (part) => {
const parts = current || []
const newParts = new Set(parts)
if (newParts.has(part)) newParts.delete(part)
else newParts.add(part)
if (newParts.size < 1) reset()
else updateFunc(['only'], [...newParts])
}
const onlyUpdateFunc = useCallback(
(path, part) => {
if (part === undefined) return props.updateFunc(path, part)
const reset = () => {
updateFunc(['only'])
}
let newParts = new Set(props.current || [])
if (newParts.has(part)) newParts.delete(part)
else newParts.add(part)
if (newParts.size < 1) newParts = undefined
else newParts = [...newParts]
return (
<>
<p>{t(`core-settings:only.d`)}</p>
{orderBy(partNames, ['name'], ['asc']).map((part) => {
const included = Array.isArray(current) ? (current.includes(part.id) ? true : false) : true
return (
<ChoiceButton
key={part.id}
title={part.t}
color={included ? 'secondary' : 'accent'}
active={included}
onClick={() => togglePart(part.id)}
>
{part.d}
</ChoiceButton>
)
})}
</>
props.updateFunc(path, newParts)
},
[props.updateFunc, props.current]
)
return <ListInput {...props} updateFunc={onlyUpdateFunc} />
}
export const SaMmSettingInput = (props) => {

View file

@ -1,4 +1,4 @@
import { ListValue, MmValue, PlainValue, HighlightedValue } from '../shared/values'
import { ListValue, MmValue, PlainValue } from '../shared/values'
export const RendererSettingValue = ListValue
export const LocaleSettingValue = ListValue
@ -10,11 +10,14 @@ export const UnitsSettingValue = ListValue
export const MarginSettingValue = MmValue
export const SaMmSettingValue = MmValue
export const ScaleSettingValue = PlainValue
export const ScaleSettingValue = ({ current, config, changed }) => (
<PlainValue current={current} dflt={config.dflt} changed={changed} />
)
export const OnlySettingValue = ({ current, config }) => (
<HighlightedValue changed={current !== undefined}>
{' '}
{current ? current.length : config.parts.length}{' '}
</HighlightedValue>
<PlainValue
current={current?.length}
dflt={config.parts.length}
changed={current !== undefined}
/>
)

View file

@ -39,7 +39,7 @@ const inputs = {
count: SliderInput,
deg: DegInput,
list: ListInput,
mm: MmInput,
mm: () => <span>FIXME: Mm options are deprecated. Please report this </span>,
pct: PctInput,
}
@ -68,7 +68,7 @@ export const DesignOption = ({
current,
config,
settings,
update,
updateFunc,
t,
loadDocs,
changed = false,
@ -81,30 +81,13 @@ export const DesignOption = ({
// Hide option?
if (config?.hide || (typeof config?.hide === 'function' && config.hide(settings))) return null
if (type === 'bool') {
config = {
...config,
list: [0, 1],
choiceTitles: {
0: `${name}No`,
1: `${name}Yes`,
},
valueTitles: {
0: 'no',
1: 'yes',
},
dflt: config.dflt ? 1 : 0,
}
}
return (
<MenuItem
{...{
name,
config,
current,
updateFunc: update.settings,
updatePath: ['options'],
updateFunc,
t,
changed,
loadDocs,
@ -117,53 +100,6 @@ export const DesignOption = ({
)
}
export const DesignOptionGroup = ({
design,
patternConfig,
settings,
update,
group,
options,
t,
loadDocs,
}) => (
<Collapse
bottom
color="secondary"
title={
<ItemTitle
{...{
name: group,
t,
emoji: emojis[group] ? emojis[group] : emojis.groupDflt,
}}
/>
}
openTitle={t(group)}
>
{Object.entries(options).map(([option, type]) =>
typeof type === 'string' ? (
<DesignOption
{...{ t, design, update, settings, loadDocs }}
key={option}
name={option}
settings={settings}
current={settings.options?.[option]}
config={patternConfig.options[option]}
changed={wasChanged(settings.options?.[option], option, patternConfig.options)}
/>
) : (
<DesignOptionGroup
{...{ design, patternConfig, settings, update, Option, t, loadDocs }}
group={option}
options={type}
key={option}
/>
)
)}
</Collapse>
)
export const DesignOptions = ({
design,
patternConfig,
@ -183,31 +119,19 @@ export const DesignOptions = ({
return (
<WorkbenchMenu
{...{
name: 'design-options:designOptions',
updateFunc: update.settings,
ns: menuNs,
Icon: OptionsIcon,
inputs,
values,
config: optionsMenu,
currentValues: settings.options,
language,
DynamicDocs,
emojis,
getDocsPath,
Icon: OptionsIcon,
Item: DesignOption,
name: 'design-options:designOptions',
language,
ns: menuNs,
passProps: { settings },
updateFunc: (name, value) => update.settings(['options', name], value),
}}
>
<MenuItemGroup
{...{
collapsible: false,
groupConfig: patternConfig.options,
currents: settings.options,
items: optionsMenu,
Item: DesignOption,
loadDocs,
itemProps: { design, update, settings },
emojis,
t,
}}
/>
</WorkbenchMenu>
/>
)
}

View file

@ -1,7 +1,7 @@
import { formatMm, formatPercentage } from 'shared/utils.mjs'
import { ListValue, HighlightedValue, PlainValue } from '../shared/values'
export const PctOptionValue = ({ name, config, current, settings, changed }) => {
const val = typeof current === 'undefined' ? config.pct / 100 : current
const val = changed ? current : config.pct / 100
return (
<HighlightedValue changed={changed}>
@ -23,7 +23,7 @@ export const BoolOptionValue = ({ name, config, current, t, changed }) => (
)
export const CountOptionValue = ({ config, current, changed }) => (
<PlainValue {...{ current, changed, config: { ...config, dflt: config.count } }} />
<PlainValue {...{ current, changed, dflt: config.count }} />
)
export const ListOptionValue = ({ name, config, current, t, changed }) => {

View file

@ -1,6 +1,6 @@
import { useContext } from 'react'
import { Collapse } from 'shared/components/collapse.mjs'
import { MenuItem, wasChanged } from './menu-item.mjs'
import { MenuItemGroup, wasChanged } from './menu-item.mjs'
import { useTranslation } from 'next-i18next'
import { HelpIcon } from 'shared/components/icons.mjs'
import { ModalWrapper } from 'shared/components/wrappers/modal.mjs'
@ -25,7 +25,6 @@ export const useDocsLoader = (DynamicDocs, getDocsPath, language) => {
export const WorkbenchMenu = ({
updateFunc,
updatePath = [],
ns,
Icon,
name,
@ -38,6 +37,8 @@ export const WorkbenchMenu = ({
DynamicDocs = false,
getDocsPath = () => {},
language,
emojis,
Item,
children,
}) => {
const { t, i18n } = useTranslation(ns)
@ -69,29 +70,24 @@ export const WorkbenchMenu = ({
openTitle={t(`${name}.t`)}
openButtons={openButtons}
>
<p>{t('core-settings:coreSettings.d')}</p>
{children ||
Object.keys(config)
.filter((name) => config[name].control <= control)
.map((name) => (
<MenuItem
key={name}
{...{
name,
config: config[name],
current: currentValues[name],
updateFunc,
updatePath,
t,
passProps,
changed: wasChanged(currentValues[name], name, config),
loadDocs,
Input: inputs[name],
Value: values[name],
i18n: i18n,
}}
/>
))}
{children || (
<MenuItemGroup
{...{
collapsible: false,
control,
currentValues,
structure: config,
Item,
values,
inputs,
loadDocs,
passProps,
updateFunc,
emojis,
t,
}}
/>
)}
</Collapse>
)
}

View file

@ -29,17 +29,17 @@ const EditCount = (props) => (
</div>
)
const useSharedHandlers = ({ dflt, updateFunc, updatePath, name, setReset }) => {
const reset = useCallback(() => updateFunc([...updatePath, name]), [updatePath, updateFunc, name])
const useSharedHandlers = ({ dflt, updateFunc, name, setReset }) => {
const reset = useCallback(() => updateFunc(name), [updateFunc, name])
const handleChange = useCallback(
(newCurrent) => {
if (newCurrent === dflt) reset()
else {
updateFunc([...updatePath, name], newCurrent)
updateFunc(name, newCurrent)
}
},
[dflt, updateFunc, updatePath, name]
[dflt, updateFunc, name]
)
useEffect(() => setReset(() => reset), [reset, setReset])
@ -47,20 +47,10 @@ const useSharedHandlers = ({ dflt, updateFunc, updatePath, name, setReset }) =>
return { handleChange, reset }
}
export const ListInput = ({
name,
config,
current,
updateFunc,
updatePath = [],
compact = false,
t,
setReset,
}) => {
export const ListInput = ({ name, config, current, updateFunc, compact = false, t, setReset }) => {
const { handleChange, reset, set } = useSharedHandlers({
dflt: config.dflt,
updateFunc,
updatePath,
name,
setReset,
})
@ -86,7 +76,23 @@ export const ListInput = ({
)
}
export const BoolInput = ListInput
export const BoolInput = (props) => {
const boolConfig = {
list: [0, 1],
choiceTitles: {
0: `${props.name}No`,
1: `${props.name}Yes`,
},
valueTitles: {
0: 'no',
1: 'yes',
},
dflt: props.config.dflt ? 1 : 0,
...props.config,
}
return <ListInput {...props} config={boolConfig} />
}
const EditInputValue = (props) => (
<div className="form-control mb-2 w-full">
@ -123,7 +129,6 @@ export const SliderInput = ({
config,
current,
updateFunc,
updatePath = [],
t,
override,
suffix = '',
@ -136,7 +141,6 @@ export const SliderInput = ({
current,
dflt: config.dflt,
updateFunc,
updatePath,
name,
setReset,
})
@ -192,8 +196,7 @@ export const SliderInput = ({
export const PctInput = ({ config, settings, current, updateFunc, type = 'pct', ...rest }) => {
const suffix = type === 'deg' ? '°' : '%'
const factor = type === 'deg' ? 1 : 100
const dflt = config[type]
let pctCurrent = typeof current === 'undefined' ? dflt : current * factor
let pctCurrent = typeof current === 'undefined' ? config.dflt : current * factor
const valFormatter = (val) => round(val)
const pctUpdateFunc = useCallback(
@ -208,7 +211,6 @@ export const PctInput = ({ config, settings, current, updateFunc, type = 'pct',
config: {
...config,
step: 0.1,
dflt,
},
current: pctCurrent,
updateFunc: pctUpdateFunc,
@ -217,7 +219,7 @@ export const PctInput = ({ config, settings, current, updateFunc, type = 'pct',
}}
>
<div className="flex flex-row justify-around">
<span className={current === dflt ? 'text-secondary' : 'text-accent'}>
<span className={current === config.dflt ? 'text-secondary' : 'text-accent'}>
{config.toAbs && settings.measurements
? formatMm(config.toAbs(current / factor, settings))
: ' '}
@ -238,6 +240,7 @@ export const MmInput = (props) => {
},
[props.updateFunc, props.units]
)
return (
<SliderInput
{...{

View file

@ -2,9 +2,9 @@ import { ClearIcon, HelpIcon, EditIcon } from 'shared/components/icons.mjs'
import { Collapse } from 'shared/components/collapse.mjs'
import { useState, useMemo } from 'react'
export const wasChanged = (current, name, settingsConfig) => {
export const wasChanged = (current, config) => {
if (typeof current === 'undefined') return false
if (current === settingsConfig[name].dflt) return false
if (current === config.dflt) return false
return true
}
@ -16,18 +16,17 @@ export const ItemTitle = ({ name, t, changed, current = null, open = false, emoj
{emoji}
</span>
{t(`${name}.t`)}
{open ? ':' : ''}
</span>
<span className="font-bold">{current}</span>
</div>
)
const openButtonClass = 'btn btn-xs btn-ghost px-0'
export const MenuItem = ({
name,
config,
current,
updateFunc,
updatePath = [],
t,
passProps = {},
changed,
@ -35,9 +34,10 @@ export const MenuItem = ({
Input,
Value,
allowOverride = false,
control = Infinity,
}) => {
const [override, setOverride] = useState(false)
const [reset, setReset] = useState(() => () => updateFunc([...updatePath, name]))
const [reset, setReset] = useState(() => () => updateFunc(name))
const drillProps = useMemo(
() => ({
@ -47,23 +47,20 @@ export const MenuItem = ({
updateFunc,
t,
changed,
updatePath,
override,
setReset,
...passProps,
}),
[name, config, current, updateFunc, t, changed, updatePath, override, setReset, passProps]
[name, config, current, updateFunc, t, changed, override, setReset, passProps]
)
if (config.control && config.control > control) return null
const buttons = []
const openButtons = []
if (loadDocs)
openButtons.push(
<button
className="btn btn-xs btn-ghost px-0"
key="help"
onClick={(evt) => loadDocs(evt, name)}
>
<button className={openButtonClass} key="help" onClick={(evt) => loadDocs(evt, name)}>
<HelpIcon className="w-4 h-4" />
</button>
)
@ -71,7 +68,7 @@ export const MenuItem = ({
openButtons.push(
<button
key="edit"
className="btn btn-xs btn-ghost px-0"
className={openButtonClass}
onClick={(evt) => {
evt.stopPropagation()
setOverride(!override)
@ -81,22 +78,9 @@ export const MenuItem = ({
</button>
)
if (changed) {
buttons.push(
const ResetButton = ({ open }) => (
<button
className="btn btn-accent"
key="clear"
onClick={(evt) => {
evt.stopPropagation()
reset()
}}
>
<ClearIcon />
</button>
)
openButtons.push(
<button
className="btn btn-ghost btn-xs px-0"
key="clear"
className={open ? openButtonClass : 'btn btn-accent'}
onClick={(evt) => {
evt.stopPropagation()
reset()
@ -105,6 +89,8 @@ export const MenuItem = ({
<ClearIcon />
</button>
)
buttons.push(<ResetButton key="clear" />)
openButtons.push(<ResetButton open key="clear" />)
}
const titleProps = { name, t, current: <Value {...drillProps} />, emoji: config.emoji }
@ -124,29 +110,37 @@ export const MenuItem = ({
export const MenuItemGroup = ({
collapsible = true,
control,
name,
groupConfig,
currents = {},
items,
currentValues = {},
structure,
Item = MenuItem,
values = {},
inputs = {},
loadDocs,
itemProps = {},
passProps = {},
emojis = {},
updateFunc,
t,
}) => {
const content = Object.entries(items).map(([itemName, item]) => {
if (typeof item === 'string')
const content = Object.entries(structure).map(([itemName, item]) => {
if (itemName === 'isMenu' || item === false) return null
if (!item.isMenu)
return (
<Item
key={itemName}
{...{
name: itemName,
current: currents[itemName],
config: groupConfig[itemName],
changed: wasChanged(currents[itemName], itemName, groupConfig),
current: currentValues[itemName],
config: item,
control,
changed: wasChanged(currentValues[itemName], item),
Value: values[itemName],
Input: inputs[itemName],
t,
loadDocs,
...itemProps,
updateFunc,
passProps,
}}
/>
)
@ -156,43 +150,34 @@ export const MenuItemGroup = ({
key={itemName}
{...{
collapsible: true,
control,
name: itemName,
groupConfig,
currents,
items: item,
currentValues,
structure: item,
Item,
values,
inputs,
loadDocs,
itemProps,
passProps,
emojis,
updateFunc,
t,
}}
/>
)
})
const titleProps = {
name,
t,
emoji: emojis[name] || emojis.dflt,
}
return collapsible ? (
<Collapse
bottom
color="secondary"
title={
<ItemTitle
{...{
name,
t,
emoji: emojis[name] || emojis.dflt,
}}
/>
}
openTitle={
<ItemTitle
open
{...{
name,
t,
emoji: emojis[name] || emojis.dflt,
}}
/>
}
title={<ItemTitle {...titleProps} />}
openTitle={<ItemTitle open {...titleProps} />}
>
{content}
</Collapse>

View file

@ -1,18 +1,23 @@
import { formatMm, formatFraction128 } from 'shared/utils.mjs'
export const HighlightedValue = ({ changed, children }) => (
<span className={changed ? 'text-accent' : 'text-secondary-focus'}> {children} </span>
<span className={changed ? 'text-info' : ''}> {children} </span>
)
export const PlainValue = ({ current, config, changed }) => (
<HighlightedValue changed={changed}> {changed ? current : config.dflt} </HighlightedValue>
export const PlainValue = ({ current, dflt, changed }) => (
<HighlightedValue changed={changed}> {changed ? current : dflt} </HighlightedValue>
)
export const ListValue = ({ current, t, config, changed }) => (
<HighlightedValue changed={changed}>
{changed ? t(`${config.valueTitles[current]}`) : t(`${config.valueTitles[config.dflt]}`)}
</HighlightedValue>
)
export const ListValue = ({ current, t, config, changed }) => {
const val = changed ? current : config.dflt
let key
if (config.valueTitles) key = config.valueTitles[val]
else if (typeof val === 'string') key = val
else if (val) key = 'yes'
else key = 'no'
return <HighlightedValue changed={changed}>{t(key)}</HighlightedValue>
}
export const MmValue = ({ current, t, config, units, changed }) => (
<HighlightedValue changed={changed}>

View file

@ -8,9 +8,9 @@ const usePersistedToken = createPersistedState('fs-token')
const usePersistedSeenUser = createPersistedState('fs-seen-user')
/*
* Make it possible to always check for account.username
* Make it possible to always check for account.username and account.control
*/
const noAccount = { username: false }
const noAccount = { username: false, control: 2 }
/*
* The useAccount hook

View file

@ -197,8 +197,11 @@ export const optionsMenuStructure = (options) => {
// Fixme: One day we should sort this based on the translation
for (const option of orderBy(sorted, ['menu', 'name'], ['asc'])) {
if (typeof option === 'object') {
if (option.menu) set(menu, `${option.menu}.${option.name}`, optionType(option))
else if (typeof option.menu === 'undefined') {
option.dflt = option.dflt || option[optionType(option)]
if (option.menu) {
set(menu, `${option.menu}.isMenu`, true)
set(menu, `${option.menu}.${option.name}`, option)
} else if (typeof option.menu === 'undefined') {
console.log(
`Warning: Option ${option.name} does not have a menu config. ` +
'Either configure it, or set it to false to hide this option.'