1
0
Fork 0

wip(shared): Work on workbench views

This commit is contained in:
joostdecock 2023-05-31 17:56:58 +02:00
parent cab7f5d2c5
commit c53ff25053
25 changed files with 386 additions and 96 deletions

View file

@ -12,7 +12,7 @@ const OpenTitleButton = ({
<div <div
role="button" role="button"
className={`flex flex-row items-center justify-between w-full ${ className={`flex flex-row items-center justify-between w-full ${
top ? 'lg:rounded-t-lg' : 'lg:rounded-b-lg' bottom ? 'lg:rounded-b-lg' : 'lg:rounded-t-lg'
} }
bg-${color} text-${color}-content px-4 py-1 text-lg font-medium`} bg-${color} text-${color}-content px-4 py-1 text-lg font-medium`}
onClick={toggle} onClick={toggle}

View file

@ -8,10 +8,10 @@ import { LoadingContext } from 'shared/context/loading-context.mjs'
import { import {
BeakerIcon, BeakerIcon,
BriefcaseIcon, BriefcaseIcon,
ClearIcon,
CodeIcon, CodeIcon,
CutIcon, CutIcon,
FingerprintIcon, HelpIcon,
HomeIcon,
MenuIcon, MenuIcon,
OptionsIcon, OptionsIcon,
PrintIcon, PrintIcon,
@ -20,8 +20,6 @@ import {
import { Ribbon } from 'shared/components/ribbon.mjs' import { Ribbon } from 'shared/components/ribbon.mjs'
import Link from 'next/link' import Link from 'next/link'
import { ModalMenu } from 'site/components/navigation/modal-menu.mjs' import { ModalMenu } from 'site/components/navigation/modal-menu.mjs'
import { ModalWrapper } from 'shared/components/wrappers/modal.mjs'
import { ControlSettings } from 'shared/components/account/control.mjs'
export const ns = ['workbench', 'sections'] export const ns = ['workbench', 'sections']
@ -40,6 +38,7 @@ export const NavButton = ({
active ? 'font-heavy' : '' active ? 'font-heavy' : ''
}` }`
const span = <span className="block font-bold hidden 2xl:block">{label}</span> const span = <span className="block font-bold hidden 2xl:block">{label}</span>
console.log('in button', label, onClick)
return onClick ? ( return onClick ? (
<button {...{ onClick, className }} title={label}> <button {...{ onClick, className }} title={label}>
@ -143,37 +142,27 @@ const NavIcons = ({ setModal, setView, view }) => {
> >
<CodeIcon className={iconSize} /> <CodeIcon className={iconSize} />
</NavButton> </NavButton>
<NavButton
onClick={() => setView('clear')}
label={t('workbench:clear')}
color={colors[8]}
extraClasses="hidden lg:flex"
>
<ClearIcon className={iconSize} />
</NavButton>
<NavSpacer /> <NavSpacer />
<NavButton <NavButton
label={t('workbench:control')} label={t('workbench:help')}
color={colors[9]} color={colors[8]}
onClick={() => href="/docs/site/draft"
setModal( extraClasses="hidden lg:flex"
<ModalWrapper>
<ControlSettings noBack title />
<div className="mb-3"></div>
</ModalWrapper>
)
}
> >
<FingerprintIcon className={iconSize} /> <HelpIcon className={iconSize} />
</NavButton>
<NavButton label={t('workbench:home')} color={colors[9]} href="/">
<HomeIcon className={iconSize} />
</NavButton> </NavButton>
</> </>
) )
} }
export const WorkbenchHeader = ({ view, setView }) => { export const WorkbenchHeader = ({ view, setView, update }) => {
const { setModal } = useContext(ModalContext) const { setModal } = useContext(ModalContext)
const { loading } = useContext(LoadingContext) const { loading } = useContext(LoadingContext)
const [show, setShow] = useState(true) const [show, setShow] = useState(true)
console.log('header', update)
return ( return (
<header <header
@ -196,7 +185,7 @@ export const WorkbenchHeader = ({ view, setView }) => {
{/* Mobile content */} {/* Mobile content */}
<div className="flex lg:hidden flex-row items-center justify-between w-full"> <div className="flex lg:hidden flex-row items-center justify-between w-full">
<NavIcons setModal={setModal} setView={setView} /> <NavIcons {...{ setModal, setView, update }} />
</div> </div>
</div> </div>
</div> </div>

View file

@ -38,6 +38,7 @@ export const Workbench = ({ design, Design, baseSettings, DynamicDocs, from }) =
// Effect // Effect
useEffect(() => { useEffect(() => {
// Force re-render when baseSettings changes. Required when they are loaded async. // Force re-render when baseSettings changes. Required when they are loaded async.
console.log('in effect')
setSettings({ ...baseSettings, embed: true }) setSettings({ ...baseSettings, embed: true })
}, [baseSettings]) }, [baseSettings])
@ -54,11 +55,11 @@ export const Workbench = ({ design, Design, baseSettings, DynamicDocs, from }) =
if (error) if (error)
return ( return (
<> <>
<WorkbenchHeader setView={setView} /> <WorkbenchHeader {...{ view, setView, update }} />
{error} {error}
</> </>
) )
console.log(baseSettings)
// Deal with each view // Deal with each view
const viewProps = { const viewProps = {
account, account,
@ -102,7 +103,7 @@ export const Workbench = ({ design, Design, baseSettings, DynamicDocs, from }) =
return ( return (
<> <>
<WorkbenchHeader setView={setView} view={view} /> <WorkbenchHeader {...{ view, setView, update }} />
{viewContent} {viewContent}
</> </>
) )

View file

@ -111,18 +111,4 @@ export const loadSettingsConfig = ({
step: 1, step: 1,
emoji: '🔲', emoji: '🔲',
}, },
renderer: {
control: 4, // Show when control > 3
list: ['react', 'svg'],
choiceTitles: {
react: 'renderWithReact',
svg: 'renderWithCore',
},
valueTitles: {
react: 'React',
svg: 'SVG',
},
dflt: 'react',
emoji: '🚀',
},
}) })

View file

@ -19,12 +19,6 @@ margin.t: Margin
margin.d: Controls the margin around pattern parts margin.d: Controls the margin around pattern parts
scale.t: Scale scale.t: Scale
scale.d: Controls the overall line width, font size, and other elements that do not scale with the pattern's measurements scale.d: Controls the overall line width, font size, and other elements that do not scale with the pattern's measurements
renderer.t: Render Engine
renderer.d: Controls how the pattern is rendered (drawn) on the screen
renderWithReact.t: Render with FreeSewing's React components
renderWithReact.d: Render as SVG through our React components. Allows interactivity and is optimized for screen. Use this if you are not sure what to pick.
renderWithCore.t: Render with Freesewing's Core library
renderWithCore.d: Render directly to SVG from Core. Allows no interactivity and is optimized for print. Use this if you want to know what it will look like when exported.
de.t: German de.t: German
de.d: Use this to generate a German pattern de.d: Use this to generate a German pattern
en.t: English en.t: English

View file

@ -62,7 +62,7 @@ const inputs = {
units: UnitsSettingInput, units: UnitsSettingInput,
} }
const CoreTitle = ({ name, t, changed, current = null, open = false, emoji = '' }) => ( export const CoreTitle = ({ name, t, changed, current = null, open = false, emoji = '' }) => (
<div className={`flex flex-row gap-1 items-center w-full ${open ? '' : 'justify-between'}`}> <div className={`flex flex-row gap-1 items-center w-full ${open ? '' : 'justify-between'}`}>
<span className="font-medium"> <span className="font-medium">
<span role="img" className="pr-2"> <span role="img" className="pr-2">
@ -94,6 +94,7 @@ export const Setting = ({
settingsConfig, settingsConfig,
changed, changed,
loadDocs, loadDocs,
control,
}) => { }) => {
const drillProps = { name, config, current, update, t, units, changed } const drillProps = { name, config, current, update, t, units, changed }
@ -144,6 +145,36 @@ export const Setting = ({
const titleProps = { name, t, current: <Value {...drillProps} />, emoji: config.emoji } const titleProps = { name, t, current: <Value {...drillProps} />, emoji: config.emoji }
const boolSettings = ['sabool', 'paperless', 'details']
if (control > 4) {
// Save gurus some clicks
if (boolSettings.includes(name))
return (
<Collapse
color={changed ? 'accent' : 'primary'}
title={<CoreTitle {...titleProps} />}
onClick={() => update.settings([name], current ? 0 : 1)}
/>
)
if (name === 'units')
return (
<Collapse
color={changed ? 'accent' : 'primary'}
title={<CoreTitle {...titleProps} />}
onClick={() => update.settings([name], current === 'metric' ? 'imperial' : 'metric')}
/>
)
if (name === 'renderer')
return (
<Collapse
color={changed ? 'accent' : 'primary'}
title={<CoreTitle {...titleProps} />}
onClick={() => update.ui([name], current === 'svg' ? 'react' : 'svg')}
/>
)
}
return ( return (
<Collapse <Collapse
color={changed ? 'accent' : 'primary'} color={changed ? 'accent' : 'primary'}
@ -230,7 +261,7 @@ export const CoreSettings = ({
.map((name) => ( .map((name) => (
<Setting <Setting
key={name} key={name}
{...{ name, design, update, t, patternConfig, loadDocs }} {...{ name, design, update, t, patternConfig, loadDocs, control }}
config={settingsConfig[name]} config={settingsConfig[name]}
current={settings[name]} current={settings[name]}
changed={wasChanged(settings[name], name, settingsConfig)} changed={wasChanged(settings[name], name, settingsConfig)}

View file

@ -4,7 +4,7 @@ import { ChoiceButton } from 'shared/components/choice-button.mjs'
import orderBy from 'lodash.orderby' import orderBy from 'lodash.orderby'
// Shared input for list inputs // Shared input for list inputs
export const ListSetting = ({ name, list, config, current, update, t }) => { export const ListSetting = ({ name, list, config, current, update, t, setUi }) => {
if (typeof current === 'undefined') current = config.dflt if (typeof current === 'undefined') current = config.dflt
const [value, setValue] = useState(current) const [value, setValue] = useState(current)
@ -12,7 +12,8 @@ export const ListSetting = ({ name, list, config, current, update, t }) => {
const handleChange = (newCurrent) => { const handleChange = (newCurrent) => {
if (newCurrent === config.dflt) reset() if (newCurrent === config.dflt) reset()
else { else {
update.settings([name], newCurrent) if (setUi) update.ui(setUi, newCurrent)
else update.settings([name], newCurrent)
setValue(newCurrent) setValue(newCurrent)
} }
} }
@ -139,7 +140,6 @@ export const UnitsSettingInputs = ({ name, config, current, update, t }) => (
export const MarginSettingInput = (props) => <MmSetting {...props} /> export const MarginSettingInput = (props) => <MmSetting {...props} />
export const ScaleSettingInput = (props) => <NrSetting {...props} /> export const ScaleSettingInput = (props) => <NrSetting {...props} />
export const RendererSettingInput = (props) => <ListSetting {...props} />
export const CompleteSettingInput = (props) => <ListSetting {...props} /> export const CompleteSettingInput = (props) => <ListSetting {...props} />
export const PaperlessSettingInput = (props) => <ListSetting {...props} /> export const PaperlessSettingInput = (props) => <ListSetting {...props} />

View file

@ -5,7 +5,6 @@ const ListValue = ({ current, t, config, changed }) =>
? t(`core-settings:${config.valueTitles[current]}`) ? t(`core-settings:${config.valueTitles[current]}`)
: t(`core-settings:${config.valueTitles[config.dflt]}`) : t(`core-settings:${config.valueTitles[config.dflt]}`)
export const RendererSettingValue = ListValue
export const LocaleSettingValue = ListValue export const LocaleSettingValue = ListValue
export const CompleteSettingValue = ListValue export const CompleteSettingValue = ListValue
export const PaperlessSettingValue = ListValue export const PaperlessSettingValue = ListValue

View file

@ -0,0 +1,36 @@
export const loadSettingsConfig = () => ({
control: {
control: 1, // Show when control > 0
list: ['1', '2', '3', '4', '5'],
choiceTitles: {
1: 'renderWithReact',
2: 'renderWithCore',
3: 'renderWithCore',
4: 'renderWithCore',
5: 'renderWithCore',
},
valueTitles: {
1: 'renderWithReact',
2: 'renderWithCore',
3: 'renderWithCore',
4: 'renderWithCore',
5: 'renderWithCore',
},
dflt: '2',
emoji: '🖥️',
},
renderer: {
control: 4, // Show when control > 3
list: ['react', 'svg'],
choiceTitles: {
react: 'renderWithReact',
svg: 'renderWithCore',
},
valueTitles: {
react: 'React',
svg: 'SVG',
},
dflt: 'react',
emoji: '🚀',
},
})

View file

@ -0,0 +1,210 @@
import { XrayIcon } from 'shared/components/icons.mjs'
//import { ConsoleLog } from './log.mjs'
//import { XrayReset } from './reset.mjs'
//import { XrayList } from './list.mjs'
import { useTranslation } from 'next-i18next'
import { Popout } from 'shared/components/popout.mjs'
//Dependencies
import { loadSettingsConfig } from './config.mjs'
// Components
import { Collapse } from 'shared/components/collapse.mjs'
import { HelpIcon } from 'shared/components/icons.mjs'
import { ControlSettingInput, RendererSettingInput, XRaySettingInput } from './inputs.mjs'
import { ControlSettingValue, RendererSettingValue, XRaySettingValue } from './values.mjs'
export const ns = ['ui-settings']
// Facilitate lookup of the value component
const values = {
control: ControlSettingValue,
renderer: RendererSettingValue,
xray: XRaySettingValue,
}
// Facilitate lookup of the input component
const inputs = {
control: ControlSettingInput,
renderer: RendererSettingInput,
xray: XRaySettingInput,
}
const wasChanged = (current, name, settingsConfig) => {
if (typeof current === 'undefined') return false
if (current === settingsConfig[name].dflt) return false
return true
}
export const UiTitle = ({ name, t, changed, current = null, open = false, emoji = '' }) => (
<div className={`flex flex-row gap-1 items-center w-full ${open ? '' : 'justify-between'}`}>
<span className="font-medium">
<span role="img" className="pr-2">
{emoji}
</span>
{t(`ui-settings:${name}.t`)}
{open ? ':' : ''}
</span>
<span className="font-bold">{current}</span>
</div>
)
export const Setting = ({
name,
config,
current,
update,
t,
settingsConfig,
changed,
loadDocs,
control,
ui,
}) => {
const drillProps = { name, config, current, update, t, changed, control }
// Don't bother with X-Ray in SVG mode
if (name === 'xray' && ui.renderer === 'svg') return null
const Input = inputs[name]
const Value = values[name]
const buttons = []
const openButtons = []
if (loadDocs)
openButtons.push(
<button
className="btn btn-xs btn-ghost px-0"
key="help"
onClick={(evt) => loadDocs(evt, name)}
>
<HelpIcon className="w-4 h-4" />
</button>
)
if (changed) {
buttons.push(
<button
className="btn btn-accent"
key="clear"
onClick={(evt) => {
evt.stopPropagation()
update.settings([name], config.dflt)
}}
>
<ClearIcon />
</button>
)
openButtons.push(
<button
className="btn btn-ghost btn-xs px-0"
key="clear"
onClick={(evt) => {
evt.stopPropagation()
update.settings([name], config.dflt)
}}
>
<ClearIcon />
</button>
)
}
const titleProps = { name, t, current: <Value {...drillProps} />, emoji: config.emoji }
const boolSettings = ['sabool', 'paperless', 'details']
if (control > 4) {
// Save gurus some clicks
if (name === 'renderer')
return (
<Collapse
color={changed ? 'accent' : 'primary'}
title={<UiTitle {...titleProps} />}
onClick={() => (current === 'svg' ? update.ui([name]) : update.ui([name], 'svg'))}
/>
)
}
return (
<Collapse
color={changed ? 'accent' : 'primary'}
openTitle={<UiTitle open {...titleProps} />}
title={<UiTitle {...titleProps} />}
buttons={buttons}
openButtons={openButtons}
>
<Input {...drillProps} />
</Collapse>
)
}
export const UiSettings = ({
design,
update,
settings,
ui,
account,
control,
language,
DynamicDocs,
}) => {
const { t } = useTranslation(ns)
const settingsConfig = loadSettingsConfig()
const loadDocs = DynamicDocs
? (evt, setting = false) => {
evt.stopPropagation()
let path = `site/draft/ui-settings`
if (setting) path += `/${setting}`
setModal(
<ModalWrapper>
<div className="max-w-prose">
<DynamicDocs path={path} language={language} />
</div>
</ModalWrapper>
)
}
: false
const openButtons = []
if (loadDocs)
openButtons.push(
<button
className="btn btn-xs btn-ghost px-0 z-10"
key="help"
onClick={(evt) => loadDocs(evt)}
>
<HelpIcon className="w-4 h-4" />
</button>
)
const toggleXray = () => update.ui(['xray', 'enabled'], ui?.xray?.enabled ? false : true)
return (
<>
<div className="px-2 mt-8">
{control > 4 ? (
<div className="border-t border-solid border-base-300 pb-2 mx-36"></div>
) : (
<>
<h5 className="flex flex-row gap-2 items-center">
<XrayIcon />
<span>{t('ui-settings:uiSettings')}</span>
</h5>
<p>{t('ui-settings:uiSettings.d')}</p>
</>
)}
</div>
{Object.keys(settingsConfig)
.filter((name) => settingsConfig[name].control <= control)
.map((name) => (
<Setting
key={name}
{...{ name, design, update, t, loadDocs, control }}
config={settingsConfig[name]}
current={ui[name]}
changed={wasChanged(settings[name], name, settingsConfig)}
/>
))}
</>
)
}

View file

@ -0,0 +1,59 @@
import { useState } from 'react'
import { ChoiceButton } from 'shared/components/choice-button.mjs'
import { ControlSettings } from 'shared/components/account/control.mjs'
// Shared input for list inputs
export const ListSetting = ({ name, list, config, current, update, t }) => {
if (typeof current === 'undefined') current = config.dflt
const [value, setValue] = useState(current)
const handleChange = (newCurrent) => {
if (newCurrent === config.dflt) reset()
else {
update.ui([name], newCurrent)
setValue(newCurrent)
}
}
const reset = () => {
update.ui([name])
setValue(config.dflt)
}
return (
<>
<p>{t(`ui-settings:${name}.d`)}</p>
{config.list.map((entry) => (
<ChoiceButton
key={entry}
title={t(`ui-settings:${config.choiceTitles[entry]}.t`)}
color={entry === config.dflt ? 'primary' : 'accent'}
active={current === entry}
onClick={() => handleChange(entry)}
>
{t(`ui-settings:${config.choiceTitles[entry]}.d`)}
</ChoiceButton>
))}
</>
)
}
export const ControlSettingInput = ({ t, name }) => (
<>
<p>{t(`ui-settings:${name}.d`)}</p>
<ControlSettings noBack />
</>
)
export const RendererSettingInput = ({ name, config, current, update, t }) => (
<ListSetting
{...{ name, config, current, update, t }}
list={config.list.map((key) => ({
key,
title: key,
}))}
/>
)
export const XRaySettingInput = (props) => <ListSetting {...props} />

View file

@ -0,0 +1,11 @@
uiSettings.t: UI Preferences
uiSettings.d: These preferences control the UI (User Interface) aspects of our online pattern drafting environment.
renderer.t: Render Engine
renderer.d: Controls how the pattern is rendered (drawn) on the screen
renderWithReact.t: Render with FreeSewing's React components
renderWithReact.d: Render as SVG through our React components. Allows interactivity and is optimized for screen. Use this if you are not sure what to pick.
renderWithCore.t: Render with Freesewing's Core library
renderWithCore.d: Render directly to SVG from Core. Allows no interactivity and is optimized for print. Use this if you want to know what it will look like when exported.
control.t: User Experience
control.d: Which user experience do you prefer? Please note that this is an account setting, so it will impact the entire website.

View file

@ -0,0 +1,10 @@
import { Difficulty } from 'shared/components/designs/difficulty.mjs'
const ListValue = ({ current, t, config, changed }) =>
changed
? t(`ui-settings:${config.valueTitles[current]}`)
: t(`ui-settings:${config.valueTitles[config.dflt]}`)
export const RendererSettingValue = ListValue
export const XRaysValue = ListValue
export const ControlSettingValue = ({ control }) => <Difficulty score={control} />

View file

@ -1,35 +0,0 @@
import { XrayIcon } from 'shared/components/icons.mjs'
//import { ConsoleLog } from './log.mjs'
//import { XrayReset } from './reset.mjs'
//import { XrayList } from './list.mjs'
import { useTranslation } from 'next-i18next'
import { Popout } from 'shared/components/popout.mjs'
export const ns = ['xray']
export const XrayMenu = ({ design, update, settings, ui, account, control }) => {
const { t } = useTranslation(ns)
if (ui.renderer !== 'react' || control < 4) return null
const toggleXray = () => update.ui(['xray', 'enabled'], ui?.xray?.enabled ? false : true)
return (
<>
<div className="px-2 mt-8">
{control > 4 ? (
<div className="border-t border-solid border-base-300 pb-2 mx-36"></div>
) : (
<>
<h5 className="flex flex-row gap-2 items-center">
<XrayIcon />
<span>{t('xray:xrayPattern')}</span>
</h5>
<p>{t('core-settings:coreSettings.d')}</p>
</>
)}
</div>
<Popout fixme>Implement X-Ray</Popout>
</>
)
}

View file

@ -5,7 +5,7 @@ export const Pattern = ({ pattern, setView, settings, ui, update }) => {
// Render as SVG // Render as SVG
return ui.renderer === 'svg' ? ( return ui.renderer === 'svg' ? (
<div dangerouslySetInnerHTML={{ __html: patern.render() }} /> <div dangerouslySetInnerHTML={{ __html: pattern.render() }} />
) : ( ) : (
<SvgWrapper renderProps={pattern.getRenderProps()} {...{ update, settings, ui }} /> <SvgWrapper renderProps={pattern.getRenderProps()} {...{ update, settings, ui }} />
) )

View file

@ -6,9 +6,9 @@ import {
CoreSettings, CoreSettings,
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 { XrayMenu, ns as xrayNs } from 'shared/components/workbench/menus/xray/index.mjs' import { UiSettings, ns as uiNs } from 'shared/components/workbench/menus/ui-settings/index.mjs'
export const ns = [...coreMenuNs, ...designMenuNs, ...xrayNs] export const ns = [...coreMenuNs, ...designMenuNs, ...uiNs]
export const DraftMenu = ({ export const DraftMenu = ({
design, design,
@ -38,7 +38,7 @@ export const DraftMenu = ({
<nav className="grow mb-12"> <nav className="grow mb-12">
<DesignOptions {...menuProps} /> <DesignOptions {...menuProps} />
<CoreSettings {...menuProps} /> <CoreSettings {...menuProps} />
<XrayMenu {...menuProps} ui={ui} /> <UiSettings {...menuProps} ui={ui} />
</nav> </nav>
) )
} }

View file

@ -6,7 +6,7 @@ cutLayout: Cut Layout
save: Save save: Save
export: Export export: Export
edit: Edit edit: Edit
clear: Clear reset: Reset
help: Help help: Help
name: Name name: Name
width: Width width: Width
@ -20,4 +20,3 @@ showOnlyThisPart: Show only this pattern part
partInfo: Pattern part info partInfo: Pattern part info
pathInfo: Path info pathInfo: Path info
part: Pattern part part: Pattern part
control: Experience