From 2ad0ac34910693988ec72b27889a2ca7df86d364 Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Fri, 2 Jun 2023 09:41:00 -0500 Subject: [PATCH] add toggler to boolean menu items --- sites/shared/components/workbench/index.mjs | 7 +- .../workbench/menus/core-settings/index.mjs | 98 ++++++++----------- .../workbench/menus/core-settings/inputs.mjs | 82 +++++----------- .../workbench/menus/core-settings/values.mjs | 26 ++--- .../workbench/menus/design-options/index.mjs | 67 ++----------- .../workbench/menus/design-options/inputs.mjs | 36 +++++++ .../workbench/menus/design-options/values.mjs | 14 ++- .../workbench/menus/shared/inputs.mjs | 77 ++++++++------- .../workbench/menus/shared/menu-item.mjs | 18 ++-- .../components/workbench/views/draft/menu.mjs | 3 +- 10 files changed, 197 insertions(+), 231 deletions(-) create mode 100644 sites/shared/components/workbench/menus/design-options/inputs.mjs diff --git a/sites/shared/components/workbench/index.mjs b/sites/shared/components/workbench/index.mjs index 3a9703ccbbb..d52fbeb3d02 100644 --- a/sites/shared/components/workbench/index.mjs +++ b/sites/shared/components/workbench/index.mjs @@ -23,7 +23,7 @@ const defaultUi = { const draftViews = ['draft', 'test'] -export const Workbench = ({ design, Design, baseSettings, DynamicDocs, from }) => { +export const Workbench = ({ design, Design, baseSettings, DynamicDocs, from, set }) => { // Hooks const { t, i18n } = useTranslation(ns) const { language } = i18n @@ -38,8 +38,9 @@ export const Workbench = ({ design, Design, baseSettings, DynamicDocs, from }) = // Effect useEffect(() => { // Force re-render when baseSettings changes. Required when they are loaded async. - setSettings({ ...baseSettings, embed: true }) - }, [baseSettings]) + console.log('in effect') + setSettings({ ...baseSettings, embed: true, measurements: set.measies }) + }, [baseSettings, set]) // Helper methods for settings/ui updates const update = { diff --git a/sites/shared/components/workbench/menus/core-settings/index.mjs b/sites/shared/components/workbench/menus/core-settings/index.mjs index 7d25b98db45..96814e47f98 100644 --- a/sites/shared/components/workbench/menus/core-settings/index.mjs +++ b/sites/shared/components/workbench/menus/core-settings/index.mjs @@ -2,62 +2,45 @@ import { loadSettingsConfig, defaultSamm } from './config.mjs' // Components import { SettingsIcon } from 'shared/components/icons.mjs' -import { - CompleteSettingInput, - LocaleSettingInput, - MarginSettingInput, - OnlySettingInput, - PaperlessSettingInput, - RendererSettingInput, - SaBoolSettingInput, - SaMmSettingInput, - ScaleSettingInput, - UnitsSettingInput, -} from './inputs.mjs' -import { - CompleteSettingValue, - LocaleSettingValue, - MarginSettingValue, - OnlySettingValue, - PaperlessSettingValue, - RendererSettingValue, - SaBoolSettingValue, - SaMmSettingValue, - ScaleSettingValue, - UnitsSettingValue, -} from './values.mjs' import { WorkbenchMenu } from '../shared/index.mjs' - -// Facilitate lookup of the value component -const values = { - complete: CompleteSettingValue, - locale: LocaleSettingValue, - margin: MarginSettingValue, - only: OnlySettingValue, - paperless: PaperlessSettingValue, - renderer: RendererSettingValue, - sabool: SaBoolSettingValue, - samm: SaMmSettingValue, - scale: ScaleSettingValue, - units: UnitsSettingValue, -} - -// Facilitate lookup of the input component -const inputs = { - complete: CompleteSettingInput, - locale: LocaleSettingInput, - margin: MarginSettingInput, - only: OnlySettingInput, - paperless: PaperlessSettingInput, - renderer: RendererSettingInput, - sabool: SaBoolSettingInput, - samm: SaMmSettingInput, - scale: ScaleSettingInput, - units: UnitsSettingInput, -} +import { MenuItem } from '../shared/menu-item.mjs' +// input components and event handlers +import { inputs, handlers } from './inputs.mjs' +// values +import { values } from './values.mjs' export const ns = ['core-settings', 'modal'] +/** A wrapper for {@see MenuItem} to handle core settings-specific business */ +const CoreSetting = ({ name, config, control, updateFunc, current, passProps, ...rest }) => { + // is toggling allowed? + const allowToggle = control > 3 && config.list?.length === 2 + + const handlerArgs = { + updateFunc, + current, + config, + ...passProps, + } + // get the appropriate event handler if there is one + const handler = handlers[name] ? handlers[name](handlerArgs) : updateFunc + + return ( + + ) +} + /** * The core settings menu * @param {Object} options.update settings and ui update functions @@ -82,6 +65,11 @@ export const CoreSettings = ({ parts: patternConfig.draftOrder, }) + const passProps = { + samm: typeof settings.samm === 'undefined' ? defaultSamm(settings.units) : settings.samm, + units: settings.units, + } + return ( ) diff --git a/sites/shared/components/workbench/menus/core-settings/inputs.mjs b/sites/shared/components/workbench/menus/core-settings/inputs.mjs index bce7ed9f503..e19fd5fcee5 100644 --- a/sites/shared/components/workbench/menus/core-settings/inputs.mjs +++ b/sites/shared/components/workbench/menus/core-settings/inputs.mjs @@ -1,26 +1,33 @@ -import { useCallback } from 'react' import { measurementAsMm } from 'shared/utils.mjs' import { ListInput, SliderInput, BoolInput, MmInput } from '../shared/inputs.mjs' -export const LocaleSettingInput = ListInput -export const UnitsSettingInput = ListInput -export const RendererSettingInput = ListInput -export const CompleteSettingInput = ListInput -export const PaperlessSettingInput = ListInput - -export const MarginSettingInput = MmInput -export const ScaleSettingInput = SliderInput - /** an input for the 'only' setting. toggles individual parts*/ -export const OnlySettingInput = (props) => { - const { config, updateFunc, current } = props +const OnlySettingInput = (props) => { + const { config } = props // set up choice titles config.choiceTitles = {} config.list.forEach((p) => (config.choiceTitles[p] = p)) - // make an update function that toggles the parts - const onlyUpdateFunc = useCallback( + return +} + +export const inputs = { + complete: ListInput, + locale: ListInput, + margin: MmInput, + only: OnlySettingInput, + paperless: BoolInput, + sabool: BoolInput, + samm: MmInput, + scale: SliderInput, + units: BoolInput, +} + +/** custom event handlers for inputs that need them */ +export const handlers = { + only: + ({ updateFunc, current }) => (path, part) => { // if there's no part being set, it's a reset if (part === undefined) return updateFunc(path, part) @@ -37,18 +44,8 @@ export const OnlySettingInput = (props) => { updateFunc(path, newParts) }, - [updateFunc, current] - ) - - return -} - -/** An input for the samm setting */ -export const SaMmSettingInput = (props) => { - const { updateFunc, units, config } = props - - // the update function to switch the 'sa' setting along with samm - const mmUpdateFunc = useCallback( + samm: + ({ updateFunc, config, units }) => (_path, newCurrent) => { // convert to millimeters if there's a value newCurrent = newCurrent === undefined ? measurementAsMm(config.dflt, units) : newCurrent @@ -58,25 +55,8 @@ export const SaMmSettingInput = (props) => { [['sa'], newCurrent], ]) }, - [updateFunc, units, config.dflt] - ) - - return ( - - ) -} - -/** An input for the sabool setting */ -export const SaBoolSettingInput = (props) => { - const { updateFunc, samm } = props - - // the update function to toggle the 'sa' setting based on 'sabool' - const saUpdateFunc = useCallback( + sabool: + ({ updateFunc, samm }) => (_path, newCurrent) => { updateFunc([ // update sabool to the new current @@ -85,16 +65,4 @@ export const SaBoolSettingInput = (props) => { [['sa'], newCurrent ? samm : undefined], ]) }, - [updateFunc, samm] - ) - - return ( - - ) } diff --git a/sites/shared/components/workbench/menus/core-settings/values.mjs b/sites/shared/components/workbench/menus/core-settings/values.mjs index 2cb358b0f44..075957bf321 100644 --- a/sites/shared/components/workbench/menus/core-settings/values.mjs +++ b/sites/shared/components/workbench/menus/core-settings/values.mjs @@ -1,23 +1,25 @@ import { ListValue, MmValue, PlainValue } from '../shared/values' -export const RendererSettingValue = ListValue -export const LocaleSettingValue = ListValue -export const CompleteSettingValue = ListValue -export const PaperlessSettingValue = ListValue -export const SaBoolSettingValue = ListValue -export const UnitsSettingValue = ListValue - -export const MarginSettingValue = MmValue -export const SaMmSettingValue = MmValue - -export const ScaleSettingValue = ({ current, config, changed }) => ( +const ScaleSettingValue = ({ current, config, changed }) => ( ) -export const OnlySettingValue = ({ current, config }) => ( +const OnlySettingValue = ({ current, config }) => ( ) + +export const values = { + complete: ListValue, + locale: ListValue, + margin: MmValue, + only: OnlySettingValue, + paperless: ListValue, + sabool: ListValue, + samm: MmValue, + scale: ScaleSettingValue, + units: ListValue, +} diff --git a/sites/shared/components/workbench/menus/design-options/index.mjs b/sites/shared/components/workbench/menus/design-options/index.mjs index 02b5a620b62..a8c5e444691 100644 --- a/sites/shared/components/workbench/menus/design-options/index.mjs +++ b/sites/shared/components/workbench/menus/design-options/index.mjs @@ -1,67 +1,14 @@ // Components import { OptionsIcon } from 'shared/components/icons.mjs' -import { optionsMenuStructure } from 'shared/utils.mjs' -import { optionType, formatMm } from 'shared/utils.mjs' -import { - BoolInput, - ConstantInput, - SliderInput, - DegInput, - ListInput, - PctInput, -} from '../shared/inputs.mjs' -import { - BoolOptionValue, - ConstantOptionValue, - CountOptionValue, - DegOptionValue, - ListOptionValue, - MmOptionValue, - PctOptionValue, -} from './values.mjs' +import { optionsMenuStructure, optionType } from 'shared/utils.mjs' + +import { values } from './values.mjs' +import { inputs } from './inputs.mjs' import { WorkbenchMenu } from '../shared/index.mjs' import { MenuItem } from '../shared/menu-item.mjs' export const ns = ['design-options'] -const PctOptionInput = (props) => { - const { config, settings, changed } = props - const currentOrDefault = changed ? props.current : config.dflt / 100 - return ( - -
- - {config.toAbs && settings.measurements - ? formatMm(config.toAbs(currentOrDefault, settings)) - : ' '} - -
-
- ) -} - -// Facilitate lookup of the input component -const inputs = { - bool: BoolInput, - constant: ConstantInput, - count: SliderInput, - deg: DegInput, - list: ListInput, - mm: () => FIXME: Mm options are deprecated. Please report this , - pct: PctOptionInput, -} - -// Facilitate lookup of the value component -const values = { - bool: BoolOptionValue, - constant: ConstantOptionValue, - count: CountOptionValue, - deg: DegOptionValue, - list: ListOptionValue, - mm: MmOptionValue, - pct: PctOptionValue, -} - // Emojis for option groups :) const emojis = { advanced: '🤓', @@ -77,11 +24,13 @@ const emojis = { * @param {Object} options.settings core settings * @param {Object} options.rest the rest of the props */ -export const DesignOption = ({ config, settings, ...rest }) => { +const DesignOption = ({ config, settings, control, ...rest }) => { const type = optionType(config) const Input = inputs[type] const Value = values[type] const allowOverride = ['pct', 'count', 'deg'].includes(type) + const allowToggle = + (control > 3 && type === 'bool') || (type == 'list' && config.list.length === 2) // Hide option? if (config?.hide || (typeof config?.hide === 'function' && config.hide(settings))) return null @@ -90,10 +39,12 @@ export const DesignOption = ({ config, settings, ...rest }) => { ) diff --git a/sites/shared/components/workbench/menus/design-options/inputs.mjs b/sites/shared/components/workbench/menus/design-options/inputs.mjs new file mode 100644 index 00000000000..fc8c244f8b4 --- /dev/null +++ b/sites/shared/components/workbench/menus/design-options/inputs.mjs @@ -0,0 +1,36 @@ +import { formatMm } from 'shared/utils.mjs' +import { + BoolInput, + ConstantInput, + SliderInput, + DegInput, + ListInput, + PctInput, +} from '../shared/inputs.mjs' + +const PctOptionInput = (props) => { + const { config, settings, changed } = props + const currentOrDefault = changed ? props.current : config.dflt / 100 + return ( + +
+ + {config.toAbs && settings.measurements + ? formatMm(config.toAbs(currentOrDefault, settings)) + : ' '} + +
+
+ ) +} + +// Facilitate lookup of the input component +export const inputs = { + bool: BoolInput, + constant: ConstantInput, + count: SliderInput, + deg: DegInput, + list: ListInput, + mm: () => FIXME: Mm options are deprecated. Please report this , + pct: PctOptionInput, +} diff --git a/sites/shared/components/workbench/menus/design-options/values.mjs b/sites/shared/components/workbench/menus/design-options/values.mjs index 0275187a0e2..dd935ea3867 100644 --- a/sites/shared/components/workbench/menus/design-options/values.mjs +++ b/sites/shared/components/workbench/menus/design-options/values.mjs @@ -13,9 +13,6 @@ export const PctOptionValue = ({ config, current, settings, changed }) => { ) } -/** Displays a boolean value */ -export const BoolOptionValue = BoolValue - /** Displays a count value*/ export const CountOptionValue = ({ config, current, changed }) => ( @@ -40,3 +37,14 @@ export const MmOptionValue = () => ( export const ConstantOptionValue = () => ( FIXME: No ConstantOptionvalue implemented ) + +// Facilitate lookup of the value component +export const values = { + bool: BoolValue, + constant: ConstantOptionValue, + count: CountOptionValue, + deg: DegOptionValue, + list: ListOptionValue, + mm: MmOptionValue, + pct: PctOptionValue, +} diff --git a/sites/shared/components/workbench/menus/shared/inputs.mjs b/sites/shared/components/workbench/menus/shared/inputs.mjs index 6b92ff1e720..8f1cb9425d4 100644 --- a/sites/shared/components/workbench/menus/shared/inputs.mjs +++ b/sites/shared/components/workbench/menus/shared/inputs.mjs @@ -1,4 +1,4 @@ -import { useEffect, useCallback } from 'react' +import { useCallback, useMemo } from 'react' import { round, measurementAsMm, measurementAsUnits, formatFraction128 } from 'shared/utils.mjs' import { ChoiceButton } from 'shared/components/choice-button.mjs' @@ -35,31 +35,55 @@ const EditCount = (props) => ( /** * A hook to get the change handler for an input. - * Also sets the reset function on a parent component * @param {Number|String|Boolean} options.dflt the default value for the input * @param {Function} options.updateFunc the onChange * @param {string} options.name the name of the property being changed - * @param {Function} options.setReset the setReset function passed from the parent component - * @return {ret.handleChange} the change handler for the input + * @return the change handler for the input */ -const useSharedHandlers = ({ dflt, updateFunc, name, setReset }) => { - const reset = useCallback(() => updateFunc(name), [updateFunc, name]) - - const handleChange = useCallback( +const useSharedHandlers = ({ dflt, updateFunc, name }) => { + return useCallback( (newCurrent) => { - if (newCurrent === dflt) reset() + if (newCurrent === dflt) newCurrent = undefined else { updateFunc(name, newCurrent) } }, - [dflt, updateFunc, name, reset] + [dflt, updateFunc, name] ) +} - useEffect(() => { - if (typeof setReset === 'function') setReset(() => reset) - }, [reset, setReset]) +/** get the configuration that allows a boolean value to use the list input */ +const useBoolConfig = (name, config) => { + return useMemo( + () => ({ + list: [false, true], + choiceTitles: { + false: `${name}No`, + true: `${name}Yes`, + }, + valueTitles: { + false: 'no', + true: 'yes', + }, + ...config, + }), + [name, config] + ) +} - return { handleChange, reset } +/** a toggle input for list/boolean values */ +export const ListToggle = ({ config, changed, updateFunc, name }) => { + const boolConfig = useBoolConfig(name, config) + const handleChange = useSharedHandlers({ dflt: boolConfig.dflt, updateFunc, name }) + + const dfltIndex = boolConfig.list.indexOf(boolConfig.dflt) + + const doToggle = () => + handleChange(boolConfig.list[changed ? dfltIndex : Math.abs(dfltIndex - 1)]) + + const checked = boolConfig.dflt == false ? changed : !changed + + return } /** @@ -70,14 +94,12 @@ const useSharedHandlers = ({ dflt, updateFunc, name, setReset }) => { * @param {Function} options.updateFunc the function called by the event handler to update the value * @param {Boolean} options.compact include descriptions with the list items? * @param {Function} options.t translation function - * @param {Function} options.setReset a setter for the reset function on the parent component */ -export const ListInput = ({ name, config, current, updateFunc, compact = false, t, setReset }) => { - const { handleChange } = useSharedHandlers({ +export const ListInput = ({ name, config, current, updateFunc, compact = false, t, changed }) => { + const handleChange = useSharedHandlers({ dflt: config.dflt, updateFunc, name, - setReset, }) return ( @@ -90,7 +112,7 @@ export const ListInput = ({ name, config, current, updateFunc, compact = false, key={entry} title={t(`${titleKey}${compact ? '' : '.t'}`)} color={entry === config.dflt ? 'primary' : 'accent'} - active={current === entry} + active={changed ? current === entry : entry === config.dflt} onClick={() => handleChange(entry)} > {compact ? null : t(`${titleKey}.d`)} @@ -103,19 +125,8 @@ export const ListInput = ({ name, config, current, updateFunc, compact = false, /** A boolean version of {@see ListInput} that sets up the necessary configuration */ 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, - } + const { name, config } = props + const boolConfig = useBoolConfig(name, config) return } @@ -146,7 +157,7 @@ export const SliderInput = ({ changed, }) => { const { max, min } = config - const { handleChange } = useSharedHandlers({ + const handleChange = useSharedHandlers({ current, dflt: config.dflt, updateFunc, diff --git a/sites/shared/components/workbench/menus/shared/menu-item.mjs b/sites/shared/components/workbench/menus/shared/menu-item.mjs index 81c4284c26d..45974649c68 100644 --- a/sites/shared/components/workbench/menus/shared/menu-item.mjs +++ b/sites/shared/components/workbench/menus/shared/menu-item.mjs @@ -1,6 +1,7 @@ import { ClearIcon, HelpIcon, EditIcon } from 'shared/components/icons.mjs' import { Collapse } from 'shared/components/collapse.mjs' import { useState, useMemo } from 'react' +import { ListToggle } from './inputs.mjs' /** * Check to see if a value is different from its default @@ -10,7 +11,7 @@ import { useState, useMemo } from 'react' */ export const wasChanged = (current, config) => { if (typeof current === 'undefined') return false - if (current === config.dflt) return false + if (current == config.dflt) return false return true } @@ -65,27 +66,26 @@ export const MenuItem = ({ Input, Value, allowOverride = false, + allowToggle = false, control = Infinity, }) => { // state for knowing whether the override input should be shown const [override, setOverride] = useState(false) - // store the reset function in state because the Input may set a custom one - const [reset, setReset] = useState(() => () => updateFunc(name)) // generate properties to pass to the Input const drillProps = useMemo( () => ({ name, config, + control, current, updateFunc, t, changed, override, - setReset, // allow setting of the reset function ...passProps, }), - [name, config, current, updateFunc, t, changed, override, setReset, passProps] + [name, config, current, updateFunc, t, changed, override, passProps, control] ) // don't render if this item is more advanced than the user has chosen to see @@ -113,13 +113,13 @@ export const MenuItem = ({ ) - if (changed) { + if (changed && !allowToggle) { const ResetButton = ({ open }) => (