1
0
Fork 0

chore(shared): Adapted (part of) workbench for UI consistency

This commit is contained in:
joostdecock 2023-08-27 16:24:18 +02:00
parent 084e6e534c
commit 5849e6d931
22 changed files with 490 additions and 230 deletions

View file

@ -0,0 +1,58 @@
import { useState } from 'react'
/*
* DaisyUI's accordion seems rather unreliable.
* So instead, we handle this in React state
*/
export const Accordion = ({
items, // Items in the accordion
}) => {
const [active, setActive] = useState()
return (
<nav>
{items.map((item, i) => (
<button
className={`
p-2 px-4 rounded-lg bg-transparent shadow
w-full mt-2 py-4 h-auto content-start text-left bg-opacity-20
${active === i ? 'hover:bg-transparent' : 'hover:bg-secondary hover:bg-opacity-10'}
`}
onClick={() => setActive(i)}
>
{item[0]}
{active === i ? item[1] : null}
</button>
))}
</nav>
)
}
export const SubAccordion = ({
items, // Items in the accordion
}) => {
const [active, setActive] = useState()
return (
<nav>
{items.map((item, i) => (
<button
className={`
p-2 px-4 rounded
bg-transparent
w-full mt-2 py-4 h-auto content-start bg-secondary
text-left bg-opacity-20
${
active === i
? 'bg-opacity-100 hover:bg-transparent shadow'
: 'hover:bg-opacity-10 hover:bg-secondary '
}
`}
onClick={() => setActive(i)}
>
{item[0]}
{active === i ? item[1] : null}
</button>
))}
</nav>
)
}

View file

@ -109,14 +109,17 @@ export const ButtonFrame = ({
children, // Children of the button children, // Children of the button
onClick, // onClick handler onClick, // onClick handler
active, // Whether or not to render the button as active/selected active, // Whether or not to render the button as active/selected
accordion = false, // Set this to true to not set a background color when active
}) => ( }) => (
<button <button
className={` className={`
btn btn-ghost btn-secondary btn btn-ghost btn-secondary
w-full mt-2 py-4 h-auto content-start w-full mt-2 py-4 h-auto content-start
border-2 border-secondary text-left bg-opacity-20 border-2 border-secondary text-left bg-opacity-20
hover:bg-secondary hover:text-secondary-content hover:border-secondary hover:border-solid hover:border-2 ${accordion ? 'hover:bg-transparent' : 'hover:bg-secondary hover:bg-opacity-10'}
${active ? 'bg-secondary border-solid' : 'bg-transparent border-dotted'} hover:border-secondary hover:border-solid hover:border-2
${active ? 'border-solid' : 'border-dotted'}
${active && !accordion ? 'bg-secondary' : 'bg-transparent'}
`} `}
onClick={onClick} onClick={onClick}
> >
@ -398,9 +401,11 @@ export const ListInput = ({
<ButtonFrame key={i} active={item.val === current} onClick={() => update(item.val)}> <ButtonFrame key={i} active={item.val === current} onClick={() => update(item.val)}>
<div className="w-full flex flex-col gap-2"> <div className="w-full flex flex-col gap-2">
<div className="w-full text-lg leading-5">{item.label}</div> <div className="w-full text-lg leading-5">{item.label}</div>
{item.desc ? (
<div className="w-full text-normal font-normal normal-case pt-1 leading-5"> <div className="w-full text-normal font-normal normal-case pt-1 leading-5">
{item.desc} {item.desc}
</div> </div>
) : null}
</div> </div>
</ButtonFrame> </ButtonFrame>
))} ))}

View file

@ -57,4 +57,11 @@ yamlEditViewTitleThing: 'Edit Pattern Configuration for {thing}'
yamlEditViewError: Issues with YAML yamlEditViewError: Issues with YAML
yamlEditViewErrorDesc: We saved your input, but it might not work for the following reasons yamlEditViewErrorDesc: We saved your input, but it might not work for the following reasons
saveSettings: Save Settings saveSettings: Save Settings
youUseDefaultValue: You are using a custom value
youUseCustomValue: You are using the default value
testOptions: Test design options
testOptionsDesc: Test how the design adapts to changes in a specific desing option
testMeasurements: Test measurements
testMeasurementsDesc: Test how the design adapts to changes to a specific measurement
testSets: Test measurements sets
testSetsDesc: Test how the design adapts across different measurements sets

View file

@ -16,6 +16,9 @@ import {
DocsIcon, DocsIcon,
SearchIcon, SearchIcon,
MeasieIcon, MeasieIcon,
XrayIcon,
EditIcon,
ExportIcon,
} from 'shared/components/icons.mjs' } from 'shared/components/icons.mjs'
import Link from 'next/link' import Link from 'next/link'
import { MenuWrapper } from 'shared/components/workbench/menus/shared/menu-wrapper.mjs' import { MenuWrapper } from 'shared/components/workbench/menus/shared/menu-wrapper.mjs'
@ -24,14 +27,14 @@ export const ns = ['workbench', 'sections']
const icons = { const icons = {
test: BeakerIcon, test: BeakerIcon,
export: BriefcaseIcon, export: ExportIcon,
edit: CodeIcon, Edit: EditIcon,
cut: CutIcon, cut: CutIcon,
draft: OptionsIcon, draft: OptionsIcon,
print: PrintIcon, print: PrintIcon,
save: UploadIcon, save: UploadIcon,
logs: DocsIcon, logs: CodeIcon,
inspect: SearchIcon, inspect: XrayIcon,
measies: MeasieIcon, measies: MeasieIcon,
} }
@ -128,31 +131,31 @@ const NavIcons = ({ setView, setDense, dense, view }) => {
label={t('workbench:exportPattern')} label={t('workbench:exportPattern')}
active={view === 'export'} active={view === 'export'}
> >
<BriefcaseIcon className={iconSize} /> <ExportIcon className={iconSize} />
</NavButton> </NavButton>
<NavButton <NavButton
onClick={() => setView('edit')} onClick={() => setView('edit')}
label={t('workbench:editSettings')} label={t('workbench:editSettings')}
active={view === 'edit'} active={view === 'edit'}
> >
<CodeIcon className={iconSize} /> <EditIcon className={iconSize} />
</NavButton> </NavButton>
<NavButton <NavButton
onClick={() => setView('logs')} onClick={() => setView('logs')}
label={t('workbench:patternLogs')} label={t('workbench:patternLogs')}
active={view === 'logs'} active={view === 'logs'}
> >
<DocsIcon className={iconSize} /> <CodeIcon className={iconSize} />
</NavButton> </NavButton>
<NavButton <NavButton
onClick={() => setView('inspect')} onClick={() => setView('inspect')}
label={t('workbench:patternInspector')} label={t('workbench:patternInspector')}
active={view === 'inspect'} active={view === 'inspect'}
> >
<SearchIcon className={iconSize} /> <XrayIcon className={iconSize} />
</NavButton> </NavButton>
<NavButton label={t('workbench:docs')} href="/docs/site/draft"> <NavButton label={t('workbench:docs')} href="/docs/site/draft">
<HelpIcon className={iconSize} /> <DocsIcon className={iconSize} />
</NavButton> </NavButton>
</> </>
) )

View file

@ -49,4 +49,5 @@ saNo.t: Do not include seam allowance
saNo.d: This generates a pattern which does not include any seam allowance. The size of the seam allowance does not matter as no seam allowancce will be included. saNo.d: This generates a pattern which does not include any seam allowance. The size of the seam allowance does not matter as no seam allowancce will be included.
saYes.t: Include seam allowance saYes.t: Include seam allowance
saYes.d: This generates a pattern that will include seam allowance. The size of the seam allowance is set individually. saYes.d: This generates a pattern that will include seam allowance. The size of the seam allowance is set individually.
clearSettingsNotMeasurements: Clear settings, but keep measurements
clearSettingsAndMeasurements: Clear settings & Clear measurements

View file

@ -71,7 +71,7 @@ export const DesignOptions = ({
isFirst = true, isFirst = true,
DynamicDocs = false, DynamicDocs = false,
}) => { }) => {
const menuNs = [`o_${design}`, ...ns] const menuNs = [design, ...ns]
const optionsMenu = useMemo(() => optionsMenuStructure(patternConfig.options), [patternConfig]) const optionsMenu = useMemo(() => optionsMenuStructure(patternConfig.options), [patternConfig])
const updateFunc = useCallback( const updateFunc = useCallback(
(name, value) => update.settings(['options', ...name], value), (name, value) => update.settings(['options', ...name], value),
@ -101,6 +101,8 @@ export const DesignOptions = ({
ns: menuNs, ns: menuNs,
passProps: { settings, patternConfig }, passProps: { settings, patternConfig },
updateFunc, updateFunc,
values,
isDesignOptionsGroup: true,
}} }}
/> />
) )

View file

@ -35,7 +35,7 @@ const PctOptionInput = (props) => {
export const inputs = { export const inputs = {
bool: BoolInput, bool: BoolInput,
constant: ConstantInput, constant: ConstantInput,
count: SliderInput, count: (props) => <SliderInput {...props} config={{ ...props.config, step: 1 }} />,
deg: DegInput, deg: DegInput,
list: ListInput, list: ListInput,
mm: () => <span>FIXME: Mm options are deprecated. Please report this </span>, mm: () => <span>FIXME: Mm options are deprecated. Please report this </span>,

View file

@ -2,7 +2,7 @@ import { formatMm, formatPercentage } from 'shared/utils.mjs'
import { ListValue, HighlightedValue, PlainValue, BoolValue } from '../shared/values' import { ListValue, HighlightedValue, PlainValue, BoolValue } from '../shared/values'
import { mergeOptions } from '@freesewing/core' import { mergeOptions } from '@freesewing/core'
/** Displays the current percentatge value, and the absolute value if configured */ /** Displays the current percentage value, and the absolute value if configured */
export const PctOptionValue = ({ config, current, settings, changed, patternConfig }) => { export const PctOptionValue = ({ config, current, settings, changed, patternConfig }) => {
const val = changed ? current : config.pct / 100 const val = changed ? current : config.pct / 100

View file

@ -1,8 +1,8 @@
import { useContext } from 'react' //import { useContext } from 'react'
import { MenuItemGroup } from './menu-item.mjs' import { MenuItemGroup } from './menu-item.mjs'
import { useTranslation } from 'next-i18next' import { useTranslation } from 'next-i18next'
import { ModalWrapper } from 'shared/components/wrappers/modal.mjs' //import { ModalWrapper } from 'shared/components/wrappers/modal.mjs'
import { ModalContext } from 'shared/context/modal-context.mjs' //import { ModalContext } from 'shared/context/modal-context.mjs'
/** /**
* get a loadDocs method for a menu * get a loadDocs method for a menu
@ -11,6 +11,7 @@ import { ModalContext } from 'shared/context/modal-context.mjs'
* @param {string} language the language to get documentation in * @param {string} language the language to get documentation in
* @return {Function | false} an event handler that loads does into a modal * @return {Function | false} an event handler that loads does into a modal
*/ */
/*
export const useDocsLoader = (DynamicDocs, getDocsPath, language) => { export const useDocsLoader = (DynamicDocs, getDocsPath, language) => {
const { setModal } = useContext(ModalContext) const { setModal } = useContext(ModalContext)
return DynamicDocs return DynamicDocs
@ -27,6 +28,7 @@ export const useDocsLoader = (DynamicDocs, getDocsPath, language) => {
} }
: false : false
} }
*/
/** /**
* A component for a collapsible sidebar menu in workbench views * A component for a collapsible sidebar menu in workbench views
@ -65,33 +67,18 @@ export const WorkbenchMenu = ({
Item, Item,
isFirst, isFirst,
children, children,
docsPath,
isDesignOptionsGroup,
}) => { }) => {
// get translation for the menu // get translation for the menu
const { t } = useTranslation(ns) const { t } = useTranslation(ns)
// get a documentation loader // get a documentation loader
const loadDocs = useDocsLoader(DynamicDocs, getDocsPath, language) //const loadDocs = useDocsLoader(DynamicDocs, getDocsPath, language)
return ( return children ? (
<> children
<div className="px-2" key="header">
{control > 4 ? (
isFirst ? (
''
) : ( ) : (
<div className="border-t border-solid border-base-300 mx-36"></div>
)
) : (
<>
<h5 className="flex flex-row gap-2 items-center">
<Icon />
<span>{t(`${name}`)}</span>
</h5>
<p>{t(`${name}.d`)}</p>
</>
)}
</div>
{children || (
<MenuItemGroup <MenuItemGroup
{...{ {...{
collapsible: false, collapsible: false,
@ -103,14 +90,16 @@ export const WorkbenchMenu = ({
Icon, Icon,
values, values,
inputs, inputs,
loadDocs, //loadDocs,
passProps, passProps,
updateFunc, updateFunc,
emojis, emojis,
t, t,
DynamicDocs,
getDocsPath,
language,
isDesignOptionsGroup,
}} }}
/> />
)}
</>
) )
} }

View file

@ -9,6 +9,8 @@ import {
import { ChoiceButton } from 'shared/components/choice-button.mjs' import { ChoiceButton } from 'shared/components/choice-button.mjs'
import debounce from 'lodash.debounce' import debounce from 'lodash.debounce'
import { ButtonFrame } from 'shared/components/inputs.mjs'
/******************************************************************************************* /*******************************************************************************************
* This file contains the base components to be used by inputs in menus in the workbench * This file contains the base components to be used by inputs in menus in the workbench
* For the purposes of our menus, we have two main types: * For the purposes of our menus, we have two main types:
@ -229,25 +231,29 @@ export const ListInput = ({ name, config, current, updateFunc, compact = false,
name, name,
}) })
return ( return config.list.map((entry) => {
<>
<p>{t(`${name}.d`)}</p>
{config.list.map((entry) => {
const titleKey = config.choiceTitles ? config.choiceTitles[entry] : `${name}.o.${entry}` const titleKey = config.choiceTitles ? config.choiceTitles[entry] : `${name}.o.${entry}`
const title = t(`${titleKey}.t`)
const desc = t(`${titleKey}.d`)
const sideBySide = desc.length + title.length < 70
return ( return (
<ChoiceButton <ButtonFrame
key={entry} key={entry}
title={t(`${titleKey}.t`)}
color={entry === config.dflt ? 'primary' : 'secondary'}
active={changed ? current === entry : entry === config.dflt} active={changed ? current === entry : entry === config.dflt}
onClick={() => handleChange(entry)} onClick={() => handleChange(entry)}
> >
{compact ? null : t(`${titleKey}.d`)} <div
</ChoiceButton> className={`w-full flex items-start ${
) sideBySide ? 'flex-row justify-between gap-2' : 'flex-col'
})} }`}
</> >
<div className="font-bold text-lg shrink-0">{title}</div>
{compact ? null : <div className="text-base font-normal">{desc}</div>}
</div>
</ButtonFrame>
) )
})
} }
/** A boolean version of {@see ListInput} that sets up the necessary configuration */ /** A boolean version of {@see ListInput} that sets up the necessary configuration */
@ -328,7 +334,6 @@ export const SliderInput = ({
return ( return (
<> <>
<p>{t(`${name}.d`)}</p>
<div className="flex flex-row justify-between"> <div className="flex flex-row justify-between">
{override ? ( {override ? (
<EditCount <EditCount
@ -475,7 +480,6 @@ export const ConstantInput = ({
config, config,
}) => ( }) => (
<> <>
<p>{t(`${name}.d`)}</p>
<input <input
type={type} type={type}
className={` className={`

View file

@ -1,7 +1,11 @@
import { ClearIcon, HelpIcon, EditIcon } from 'shared/components/icons.mjs' import { ResetIcon, HelpIcon, EditIcon } from 'shared/components/icons.mjs'
import { Collapse } from 'shared/components/collapse.mjs' import { Collapse } from 'shared/components/collapse.mjs'
import { useState, useMemo } from 'react' import { useState, useMemo } from 'react'
import { ListToggle } from './inputs.mjs' import { ListToggle } from './inputs.mjs'
import { SubAccordion } from 'shared/components/accordion.mjs'
import { FormControl } from 'shared/components/inputs.mjs'
import { BoxIcon as GroupIcon, OptionsIcon } from 'shared/components/icons.mjs'
import { optionType } from 'shared/utils.mjs'
/** /**
* Check to see if a value is different from its default * Check to see if a value is different from its default
@ -34,7 +38,7 @@ export const ItemTitle = ({ name, t, current = null, open = false, emoji = '', I
) )
/** @type {String} class to apply to buttons on open menu items */ /** @type {String} class to apply to buttons on open menu items */
const openButtonClass = 'btn btn-xs btn-ghost px-0' const iconButtonClass = 'btn btn-xs btn-ghost px-0 text-accent'
/** /**
* A generic component for handling a menu item. * A generic component for handling a menu item.
@ -66,6 +70,9 @@ export const MenuItem = ({
allowOverride = false, allowOverride = false,
allowToggle = false, allowToggle = false,
control = Infinity, control = Infinity,
DynamicDocs,
docsPath,
language,
}) => { }) => {
// state for knowing whether the override input should be shown // state for knowing whether the override input should be shown
const [override, setOverride] = useState(false) const [override, setOverride] = useState(false)
@ -91,68 +98,54 @@ export const MenuItem = ({
// get buttons for open and closed states // get buttons for open and closed states
const buttons = [] const buttons = []
const openButtons = []
if (loadDocs)
openButtons.push(
<button className={openButtonClass} key="help" onClick={(evt) => loadDocs(evt, name)}>
<HelpIcon className="w-6 h-6" />
</button>
)
if (allowOverride) if (allowOverride)
openButtons.push( buttons.push(
<button <button
key="edit" key="edit"
className={openButtonClass} className={iconButtonClass}
onClick={(evt) => { onClick={(evt) => {
evt.stopPropagation() evt.stopPropagation()
setOverride(!override) setOverride(!override)
}} }}
> >
<EditIcon className={`w-6 h-6 ${override ? 'bg-base-100 text-accent rounded' : ''}`} /> <EditIcon
className={`w-6 h-6 ${
override ? 'bg-secondary text-secondary-content rounded' : 'text-secondary'
}`}
/>
</button> </button>
) )
const ResetButton = ({ open, disabled = false }) => ( const ResetButton = ({ open, disabled = false }) => (
<button <button
className={`${open ? openButtonClass : 'btn btn-accent'} disabled:bg-opacity-0`} className={`${iconButtonClass} disabled:bg-opacity-0`}
disabled={disabled} disabled={disabled}
onClick={(evt) => { onClick={(evt) => {
evt.stopPropagation() evt.stopPropagation()
updateFunc([name]) updateFunc([name])
}} }}
> >
<ClearIcon /> <ResetIcon />
</button> </button>
) )
if (changed && !allowToggle) { buttons.push(<ResetButton open disabled={!changed} key="clear" />)
buttons.push(<ResetButton key="clear" />)
}
if (allowToggle) {
buttons.push(<ListToggle key="toggle" {...{ config, changed, updateFunc, name }} />)
} else {
openButtons.push(<ResetButton open disabled={!changed} key="clear" />)
}
// props to pass to the ItemTitle
const titleProps = {
name,
t,
current: <Value {...drillProps} />,
emoji: config.emoji,
Icon: config.icon,
}
return ( return (
<Collapse <FormControl
color={changed ? 'accent' : 'secondary'} label={<span className="text-base font-normal">{t([`${name}.d`, name])}</span>}
openTitle={<ItemTitle open {...titleProps} />} docs={<DynamicDocs path={docsPath} language={language} />}
title={<ItemTitle {...titleProps} />} id={config.name}
buttons={buttons} labelBR={<div className="flex flex-row items-center gap-2">{buttons}</div>}
openButtons={openButtons} labelBL={
<span
className={`text-base font-medium -mt-2 block ${changed ? 'text-accent' : 'opacity-50'}`}
>
{t(`workbench:youUse${changed ? 'Default' : 'Custom'}Value`)}
</span>
}
> >
<Input {...drillProps} /> <Input {...drillProps} />
</Collapse> </FormControl>
) )
} }
@ -175,6 +168,7 @@ export const MenuItem = ({
* @param {Function} updateFunc the function called by change handlers on inputs within menu items * @param {Function} updateFunc the function called by change handlers on inputs within menu items
* @param {Boolean} topLevel is this group the top level group? false for nested * @param {Boolean} topLevel is this group the top level group? false for nested
* @param {Function} t translation function * @param {Function} t translation function
* @param {Function} getDocsPath returns the path to the docs for the current item
*/ */
export const MenuItemGroup = ({ export const MenuItemGroup = ({
collapsible = true, collapsible = true,
@ -192,35 +186,53 @@ export const MenuItemGroup = ({
updateFunc, updateFunc,
topLevel = false, topLevel = false,
t, t,
DynamicDocs,
language,
getDocsPath,
isDesignOptionsGroup = false,
}) => { }) => {
// map the entries in the structure // map the entries in the structure
const content = Object.entries(structure).map(([itemName, item]) => { const content = Object.entries(structure).map(([itemName, item]) => {
// if it's the isGroup property, or it is false, it shouldn't be shown // if it's the isGroup property, or it is false, it shouldn't be shown
if (itemName === 'isGroup' || item === false) return null if (itemName === 'isGroup' || item === false) return null
if (!item) return null
// if the item is not a menu, it's an Item const ItemIcon = item.icon
if (!item.isGroup) ? item.icon
return ( : item.isGroup
<Item ? GroupIcon
key={itemName} : Icon
{...{ ? Icon
name: itemName, : () => <span role="img">{emoji}</span>
current: currentValues[itemName], const Value = item.isGroup
config: item, ? () => (
control, <div className="flex flex-row gap-2 items-center font-medium">
changed: wasChanged(currentValues[itemName], item), {Object.keys(item).filter((i) => i !== 'isGroup').length}
Value: values[itemName], <OptionsIcon className="w-5 h-5" />
Input: inputs[itemName], </div>
t,
loadDocs,
updateFunc,
passProps,
}}
/>
) )
: isDesignOptionsGroup
? values[optionType(item)]
: values[itemName]
? values[itemName]
: () => <span>¯\_()_/¯</span>
// otherwise, it's a group return [
return ( <div className="flex flex-row items-center justify-between" key="a">
<div className="flex flex-row items-center gap-4">
<ItemIcon />
<h6>{t([`${itemName}.t`, itemName])}</h6>
</div>
<div className="font-bold">
<Value
current={currentValues[itemName]}
config={item}
t={t}
changed={wasChanged(currentValues[itemName], item)}
/>
</div>
</div>,
item.isGroup ? (
<MenuItemGroup <MenuItemGroup
key={itemName} key={itemName}
{...{ {...{
@ -240,12 +252,38 @@ export const MenuItemGroup = ({
emojis, emojis,
updateFunc, updateFunc,
t, t,
DynamicDocs,
language,
getDocsPath,
isDesignOptionsGroup,
}} }}
/> />
) ) : (
<Item
key={itemName}
{...{
name: itemName,
current: currentValues[itemName],
config: item,
control,
changed: wasChanged(currentValues[itemName], item),
Value: values[itemName],
Input: inputs[itemName],
t,
loadDocs,
updateFunc,
passProps,
DynamicDocs,
docsPath: getDocsPath(itemName),
language,
}}
/>
),
]
}) })
// if it should be wrapped in a collapsible // if it should be wrapped in a collapsible
/*
if (collapsible) { if (collapsible) {
// props to give to the group title // props to give to the group title
const titleProps = { const titleProps = {
@ -269,7 +307,8 @@ export const MenuItemGroup = ({
</Collapse> </Collapse>
) )
} }
*/
//otherwise just return the content //otherwise just return the content
return content return <SubAccordion items={content.filter((item) => item !== null)} />
} }

View file

@ -7,7 +7,7 @@ import { formatMm } from 'shared/utils.mjs'
/** The basis of it all. Handles the changed/unchanged styling for the wrapped value */ /** The basis of it all. Handles the changed/unchanged styling for the wrapped value */
export const HighlightedValue = ({ changed, children }) => ( export const HighlightedValue = ({ changed, children }) => (
<span className={changed ? 'text-info' : ''}> {children} </span> <span className={changed ? 'text-accent' : ''}> {children} </span>
) )
/** /**

View file

@ -11,6 +11,7 @@ import {
useMaterialList, useMaterialList,
useMaterialLength, useMaterialLength,
} from './hooks' } from './hooks'
import { V3Wip } from 'shared/components/v3-wip.mjs'
export const ns = [...menuNs, ...wrapperNs] export const ns = [...menuNs, ...wrapperNs]
@ -110,6 +111,8 @@ export const CutView = ({
</div> </div>
), ),
menu: ( menu: (
<>
<V3Wip />
<CutMenu <CutMenu
{...{ {...{
design, design,
@ -125,6 +128,7 @@ export const CutView = ({
setSettings, setSettings,
}} }}
/> />
</>
), ),
}} }}
/> />

View file

@ -4,12 +4,15 @@ import {
} from 'shared/components/workbench/menus/design-options/index.mjs' } from 'shared/components/workbench/menus/design-options/index.mjs'
import { import {
CoreSettings, CoreSettings,
ClearAllButton,
ns as coreMenuNs, ns as coreMenuNs,
} from 'shared/components/workbench/menus/core-settings/index.mjs' } from 'shared/components/workbench/menus/core-settings/index.mjs'
import { UiSettings, ns as uiNs } from 'shared/components/workbench/menus/ui-settings/index.mjs' import { UiSettings, ns as uiNs } from 'shared/components/workbench/menus/ui-settings/index.mjs'
import { useTranslation } from 'next-i18next'
import { nsMerge } from 'shared/utils.mjs'
import { SettingsIcon, OptionsIcon, DesktopIcon } from 'shared/components/icons.mjs'
import { Accordion } from 'shared/components/accordion.mjs'
export const ns = [...coreMenuNs, ...designMenuNs, ...uiNs] export const ns = nsMerge(coreMenuNs, designMenuNs, uiNs)
export const DraftMenu = ({ export const DraftMenu = ({
design, design,
@ -24,6 +27,7 @@ export const DraftMenu = ({
view, view,
setView, setView,
}) => { }) => {
const { t } = useTranslation()
const control = account.control const control = account.control
const menuProps = { const menuProps = {
design, design,
@ -36,12 +40,39 @@ export const DraftMenu = ({
control, control,
} }
const sections = [
{
name: 'designOptions',
ns: 'design-options',
icon: <OptionsIcon className="w-8 h-8" />,
menu: <DesignOptions {...menuProps} />,
},
{
name: 'coreSettings',
ns: 'core-settings',
icon: <SettingsIcon className="w-8 h-8" />,
menu: <CoreSettings {...menuProps} />,
},
{
name: 'uiSettings',
ns: 'ui-settings',
icon: <DesktopIcon className="w-8 h-8" />,
menu: <UiSettings {...menuProps} {...{ ui, view, setView }} />,
},
]
return ( return (
<nav> <Accordion
<DesignOptions {...menuProps} /> items={sections.map((section) => [
<CoreSettings {...menuProps} /> <>
<UiSettings {...menuProps} {...{ ui, view, setView }} /> <h5 className="flex flex-row gap-2 items-center justify-between w-full">
<ClearAllButton setSettings={setSettings} /> <span>{t(`${section.ns}:${section.name}.t`)}</span>
</nav> {section.icon}
</h5>
<p>{t(`${section.ns}:${section.name}.d`)}</p>
</>,
section.menu,
])}
/>
) )
} }

View file

@ -5,6 +5,7 @@ import { useTranslation } from 'next-i18next'
import { useToast } from 'shared/hooks/use-toast.mjs' import { useToast } from 'shared/hooks/use-toast.mjs'
import { CloseIcon } from 'shared/components/icons.mjs' import { CloseIcon } from 'shared/components/icons.mjs'
import { capitalize } from 'shared/utils.mjs' import { capitalize } from 'shared/utils.mjs'
import { V3Wip } from 'shared/components/v3-wip.mjs'
export const ns = ['wbedit'] export const ns = ['wbedit']
@ -52,6 +53,7 @@ export const EditView = ({ settings, setSettings, design, Design }) => {
return ( return (
<div className="max-w-screen-xl m-auto h-screen form-control mt-4 flex flex-col"> <div className="max-w-screen-xl m-auto h-screen form-control mt-4 flex flex-col">
<h2>{t('yamlEditViewTitleThing', { thing: capitalize(design) })}</h2> <h2>{t('yamlEditViewTitleThing', { thing: capitalize(design) })}</h2>
<V3Wip />
<div id="editor" className="h-2/3 my-2 overflow-auto flex flex-col"> <div id="editor" className="h-2/3 my-2 overflow-auto flex flex-col">
{error && ( {error && (
<div className={`w-full shadow bg-base-100 p-0 my-4`}> <div className={`w-full shadow bg-base-100 p-0 my-4`}>

View file

@ -9,6 +9,7 @@ import {
handleExport, handleExport,
ns as exportNs, ns as exportNs,
} from 'shared/components/workbench/exporting/export-handler.mjs' } from 'shared/components/workbench/exporting/export-handler.mjs'
import { V3Wip } from 'shared/components/v3-wip.mjs'
export const ns = ['exporting', exportNs] export const ns = ['exporting', exportNs]
@ -45,6 +46,7 @@ export const ExportView = ({ settings, ui, design, Design }) => {
return ( return (
<div className="max-w-screen-xl m-auto py-8"> <div className="max-w-screen-xl m-auto py-8">
<h2>{t('export')}</h2> <h2>{t('export')}</h2>
<V3Wip />
<p className="text-lg sm:text-xl">{t('exportPattern-txt')}</p> <p className="text-lg sm:text-xl">{t('exportPattern-txt')}</p>
{link && ( {link && (
<Popout link compact> <Popout link compact>

View file

@ -3,6 +3,7 @@ import { InspectorPattern } from './inspector/pattern.mjs'
import { DraftMenu, ns as menuNs } from './menu.mjs' import { DraftMenu, ns as menuNs } from './menu.mjs'
import { objUpdate } from 'shared/utils.mjs' import { objUpdate } from 'shared/utils.mjs'
import { PatternWithMenu, ns as wrapperNs } from '../pattern-with-menu.mjs' import { PatternWithMenu, ns as wrapperNs } from '../pattern-with-menu.mjs'
import { V3Wip } from 'shared/components/v3-wip.mjs'
export const ns = [...menuNs, ...wrapperNs] export const ns = [...menuNs, ...wrapperNs]
@ -76,6 +77,8 @@ export const InspectView = ({
setSettings, setSettings,
pattern: output, pattern: output,
menu: ( menu: (
<>
<V3Wip />
<DraftMenu <DraftMenu
{...{ {...{
design, design,
@ -94,6 +97,7 @@ export const InspectView = ({
setView, setView,
}} }}
/> />
</>
), ),
}} }}
/> />

View file

@ -5,6 +5,7 @@ import {
ClearAllButton, ClearAllButton,
ns as coreMenuNs, ns as coreMenuNs,
} from 'shared/components/workbench/menus/core-settings/index.mjs' } from 'shared/components/workbench/menus/core-settings/index.mjs'
import { V3Wip } from 'shared/components/v3-wip.mjs'
export const ns = ['logs', ...coreMenuNs] export const ns = ['logs', ...coreMenuNs]
@ -88,6 +89,7 @@ export const LogView = ({ pattern, settings, setSettings }) => {
<h1 className="grow">{t('logs')}</h1> <h1 className="grow">{t('logs')}</h1>
<ClearAllButton setSettings={setSettings} /> <ClearAllButton setSettings={setSettings} />
</div> </div>
<V3Wip />
{Object.entries(logs).map(([type, lines], key) => ( {Object.entries(logs).map(([type, lines], key) => (
<DraftLogs key={key} {...{ type, lines, t }} /> <DraftLogs key={key} {...{ type, lines, t }} />
))} ))}

View file

@ -13,6 +13,7 @@ import { PrintIcon, RightIcon } from 'shared/components/icons.mjs'
import { LoadingContext } from 'shared/context/loading-context.mjs' import { LoadingContext } from 'shared/context/loading-context.mjs'
import { useToast } from 'shared/hooks/use-toast.mjs' import { useToast } from 'shared/hooks/use-toast.mjs'
import { PatternWithMenu, ns as wrapperNs } from '../pattern-with-menu.mjs' import { PatternWithMenu, ns as wrapperNs } from '../pattern-with-menu.mjs'
import { V3Wip } from 'shared/components/v3-wip.mjs'
const viewNs = ['print', ...exportNs] const viewNs = ['print', ...exportNs]
export const ns = [...viewNs, ...menuNs, ...wrapperNs] export const ns = [...viewNs, ...menuNs, ...wrapperNs]
@ -114,6 +115,8 @@ export const PrintView = ({
/> />
), ),
menu: ( menu: (
<>
<V3Wip />
<PrintMenu <PrintMenu
{...{ {...{
design, design,
@ -129,6 +132,7 @@ export const PrintView = ({
exportIt, exportIt,
}} }}
/> />
</>
), ),
}} }}
/> />

View file

@ -10,6 +10,7 @@ import { useToast } from 'shared/hooks/use-toast.mjs'
import { LoadingContext } from 'shared/context/loading-context.mjs' import { LoadingContext } from 'shared/context/loading-context.mjs'
// Components // Components
import { Spinner } from 'shared/components/spinner.mjs' import { Spinner } from 'shared/components/spinner.mjs'
import { V3Wip } from 'shared/components/v3-wip.mjs'
export const ns = ['wbsave'] export const ns = ['wbsave']
@ -245,6 +246,7 @@ export const SaveView = ({ design, settings, from = false }) => {
return ( return (
<div className="m-auto mt-24"> <div className="m-auto mt-24">
<h1 className="max-w-6xl m-auto text-center">{t('wbsave:title')}</h1> <h1 className="max-w-6xl m-auto text-center">{t('wbsave:title')}</h1>
<V3Wip />
<div className="px-4 lg:px-12 flex flex-row flex-wrap gap-4 lg:gap-8 justify-around"> <div className="px-4 lg:px-12 flex flex-row flex-wrap gap-4 lg:gap-8 justify-around">
{info.new ? <SaveNewPattern {...saveProps} /> : null} {info.new ? <SaveNewPattern {...saveProps} /> : null}
{info.edit ? <SaveExistingPattern {...saveProps} from={from} /> : null} {info.edit ? <SaveExistingPattern {...saveProps} from={from} /> : null}

View file

@ -1,6 +1,29 @@
import { TestOptions, ns as optionsNs } from './options.mjs' import { ns as optionsNs } from './options.mjs'
import { TestMeasurements, ns as measieNs } from './measurements.mjs' import { ns as measieNs } from './measurements.mjs'
export const ns = [...optionsNs, ...measieNs] import { Accordion } from 'shared/components/accordion.mjs'
import { useTranslation } from 'next-i18next'
import { nsMerge } from 'shared/utils.mjs'
import { OptionsIcon, MeasieIcon, CommunityIcon } from 'shared/components/icons.mjs'
import { ListInput } from 'shared/components/inputs.mjs'
import { optionsMenuStructure } from 'shared/utils.mjs'
import { V3Wip } from 'shared/components/v3-wip.mjs'
export const ns = nsMerge('workbench', optionsNs, measieNs)
const flattenOptions = (options, list = false, path = []) => {
if (list === false) return flattenOptions(optionsMenuStructure(options), new Set())
for (const [key, option] of Object.entries(options)) {
if (key !== 'isGroup') {
if (!option.isGroup) list.add({ key, option, path })
else list = flattenOptions(option, list, [...path, key])
}
}
return list
}
const spacer = <span className="px-2 opacity-50">/</span>
export const TestMenu = ({ export const TestMenu = ({
design, design,
@ -11,22 +34,72 @@ export const TestMenu = ({
account, account,
DynamicDocs, DynamicDocs,
}) => { }) => {
const control = account.control const { t } = useTranslation(ns)
const menuProps = {
design, const allOptions = flattenOptions(patternConfig.options)
patternConfig,
settings,
update,
language,
account,
DynamicDocs,
control,
}
return ( return (
<nav> <Accordion
<TestOptions {...menuProps} /> items={[
<TestMeasurements {...menuProps} /> [
</nav> <>
<h5 className="flex flex-row gap-2 items-center justify-between w-full">
<span>{t('workbench:testOptions')}</span>
<OptionsIcon className="w-8 h-8" />
</h5>
<p>{t('workbench:testOptionsDesc')}</p>
</>,
<ListInput
list={[...allOptions].map((option) => ({
label: [
...option.path.map((p) => (
<>
<span>{t(`${p}.t`)}</span>
{spacer}
</>
)),
<span>{t(`${design}:${option.key}.t`)}</span>,
],
val: option.key,
}))}
update={(value) => {
if (value) update.settings(['sample'], { type: 'option', option: value })
else update.settings(['sample'])
}}
current={settings.sample.option}
/>,
],
[
<>
<h5 className="flex flex-row gap-2 items-center justify-between w-full">
<span>{t('workbench:testMeasurements')}</span>
<MeasieIcon className="w-8 h-8" />
</h5>
<p>{t('workbench:testOptionsDesc')}</p>
</>,
<ListInput
list={patternConfig.measurements.map((m) => ({
label: t(m),
val: m,
}))}
update={(value) => {
if (value) update.settings(['sample'], { type: 'measurement', measurement: value })
else update.settings(['sample'])
}}
current={settings.sample.measurement}
/>,
],
[
<>
<h5 className="flex flex-row gap-2 items-center justify-between w-full">
<span>{t('workbench:testSets')}</span>
<CommunityIcon className="w-8 h-8" />
</h5>
<p>{t('workbench:testSetsDesc')}</p>
</>,
<V3Wip />,
],
]}
/>
) )
} }

View file

@ -11,6 +11,7 @@ import {
DetailIcon, DetailIcon,
IconWrapper, IconWrapper,
ClearIcon, ClearIcon,
ResetIcon,
} from 'shared/components/icons.mjs' } from 'shared/components/icons.mjs'
import { ClearAllButton } from 'shared/components/workbench/menus/core-settings/index.mjs' import { ClearAllButton } from 'shared/components/workbench/menus/core-settings/index.mjs'
import { shownHeaderSelector } from 'shared/components/wrappers/header.mjs' import { shownHeaderSelector } from 'shared/components/wrappers/header.mjs'
@ -94,7 +95,7 @@ export const ViewHeader = ({ update, settings, ui, control, setSettings }) => {
'lg:top-24' 'lg:top-24'
)} transition-[top] duration-300 ease-in-out`} )} transition-[top] duration-300 ease-in-out`}
> >
<div className="hidden lg:flex flex-row flex-wrap gap-4 py-4 pt-4 w-full bg-neutral text-neutral-content items-center justify-center"> <div className="hidden lg:flex flex-row flex-wrap gap-4 py-4 pt-8 w-full bg-neutral text-neutral-content items-center justify-center">
{headerZoomButtons} {headerZoomButtons}
<Spacer /> <Spacer />
<div className="flex flex-row items-center gap-4"> <div className="flex flex-row items-center gap-4">
@ -156,7 +157,34 @@ export const ViewHeader = ({ update, settings, ui, control, setSettings }) => {
</div> </div>
<Spacer /> <Spacer />
<div className="flex flex-row items-center gap-4"> <div className="flex flex-row items-center gap-4">
<ClearAllButton setSettings={setSettings} compact /> <button
onClick={() => setSettings({ measurements: settings.measurements })}
className={`tooltip tooltip-primary tooltip-bottom flex flex-row items-center`}
data-tip={t('core-settings:clearSettingsNotMeasurements')}
disabled={typeof settings.options === 'undefined'}
>
<ResetIcon
stroke={3.5}
className={`w-6 h-6 ${
typeof settings.options === 'undefined' ? 'text-base-100 opacity-30' : 'text-accent'
}`}
/>
</button>
<button
onClick={() => setSettings({})}
className="tooltip tooltip-primary tooltip-bottom flex flex-row items-center text-warning"
data-tip={t('core-settings:clearSettingsAndMeasurements')}
disabled={!(settings.measurements && Object.keys(settings.measurements).length > 0)}
>
<ResetIcon
stroke={3.5}
className={`w-6 h-6 ${
!(settings.measurements && Object.keys(settings.measurements).length > 0)
? 'text-base-100 opacity-30'
: 'text-warning'
}`}
/>
</button>
</div> </div>
</div> </div>
</div> </div>