Merge branch 'develop' into eriese-imperial
This commit is contained in:
commit
7476d45f54
575 changed files with 2464 additions and 1196 deletions
|
@ -0,0 +1,28 @@
|
|||
import { useState } from 'react'
|
||||
import { SecText, SumButton, Li, SumDiv, Deg } from 'shared/components/workbench/menu/index.js'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const CoreSettingBool = props => {
|
||||
|
||||
const { t } = useTranslation(['app'])
|
||||
const [value, setValue] = useState(props.gist[props.setting])
|
||||
|
||||
const toggle = (evt) => {
|
||||
props.updateGist([props.setting], !value)
|
||||
setValue(!value)
|
||||
}
|
||||
|
||||
return (
|
||||
<Li>
|
||||
<SumButton onClick={toggle}>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
<span>{ t(`settings:${props.setting}.t`) }</span>
|
||||
</SumDiv>
|
||||
<SecText>{ t(value ? 'yes' : 'no')}</SecText>
|
||||
</SumButton>
|
||||
</Li>
|
||||
)
|
||||
}
|
||||
|
||||
export default CoreSettingBool
|
|
@ -0,0 +1,51 @@
|
|||
import { useState } from 'react'
|
||||
import { Deg } from 'shared/components/workbench/menu/index.js'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const CoreSettingList = props => {
|
||||
const { t } = useTranslation(['settings'])
|
||||
const { dflt, list } = props
|
||||
const val = props.gist?.[props.setting]
|
||||
|
||||
const [value, setValue] = useState(val)
|
||||
|
||||
const handleChange = (newVal) => {
|
||||
if (newVal === dflt) reset()
|
||||
else {
|
||||
setValue(newVal)
|
||||
props.updateGist([props.setting], newVal)
|
||||
}
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
setValue(props.dflt)
|
||||
props.updateGist([props.setting], props.dflt)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="py-4 mx-6 border-l-2 pl-2">
|
||||
<p className="m-0 p-0 px-2 mb-2 text-base-content opacity-60 italic">
|
||||
{t(`settings:${props.setting}.d`)}
|
||||
</p>
|
||||
<div className="flex flex-row">
|
||||
<div className="grow">
|
||||
{props.list.map(entry => (
|
||||
<button
|
||||
key={entry.key}
|
||||
onClick={() => handleChange(entry.key)}
|
||||
className={`
|
||||
mr-1 mb-1 text-left text-lg w-full hover:text-secondary-focus px-2
|
||||
${entry.key === value && 'font-bold text-secondary'}
|
||||
`}
|
||||
>
|
||||
<Deg />
|
||||
{entry.title}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CoreSettingList
|
|
@ -0,0 +1,74 @@
|
|||
import { useState } from 'react'
|
||||
import { formatMm } from 'shared/utils'
|
||||
import ClearIcon from 'shared/components/icons/clear'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const CoreSettingMm = props => {
|
||||
const { t } = useTranslation(['app', 'settings'])
|
||||
const { dflt, min, max } = props
|
||||
const val = props.gist?.[props.setting]
|
||||
|
||||
const [value, setValue] = useState(val)
|
||||
|
||||
const handleChange = evt => {
|
||||
const newVal = parseFloat(evt.target.value)
|
||||
|
||||
if (newVal === dflt) reset()
|
||||
else {
|
||||
setValue(newVal)
|
||||
props.updateGist([props.setting], newVal)
|
||||
}
|
||||
}
|
||||
const reset = () => {
|
||||
setValue(props.dflt)
|
||||
props.updateGist([props.setting], props.dflt)
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="py-4 mx-6 border-l-2 pl-2">
|
||||
<p className="m-0 p-0 px-2 mb-2 text-base-content opacity-60 italic">
|
||||
{t(`settings:${props.setting}.d`)}
|
||||
</p>
|
||||
<div className="flex flex-row justify-between">
|
||||
<span
|
||||
className="opacity-50"
|
||||
dangerouslySetInnerHTML={{__html: formatMm(min, props.gist.units)}}
|
||||
/>
|
||||
<span
|
||||
className={`font-bold ${val===dflt ? 'text-secondary-focus' : 'text-accent'}`}
|
||||
dangerouslySetInnerHTML={{__html: formatMm(val, props.gist.units)}}
|
||||
/>
|
||||
<span
|
||||
className="opacity-50"
|
||||
dangerouslySetInnerHTML={{__html: formatMm(max, props.gist.units)}}
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
max={max}
|
||||
min={min}
|
||||
step={0.1}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
className={`
|
||||
range range-sm mt-1
|
||||
${val === dflt ? 'range-secondary' : 'range-accent'}
|
||||
`}
|
||||
/>
|
||||
<div className="flex flex-row justify-between">
|
||||
<span />
|
||||
<button
|
||||
title={t('reset')}
|
||||
className="btn btn-ghost btn-xs text-accent"
|
||||
disabled={val === dflt}
|
||||
onClick={reset}
|
||||
>
|
||||
<ClearIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CoreSettingMm
|
|
@ -0,0 +1,71 @@
|
|||
import { useState } from 'react'
|
||||
import ClearIcon from 'shared/components/icons/clear.js'
|
||||
import EditIcon from 'shared/components/icons/edit.js'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const CoreSettingNr = props => {
|
||||
const { t } = useTranslation(['app', 'settings'])
|
||||
const { dflt, min, max } = props
|
||||
const val = props.gist?.[props.setting]
|
||||
|
||||
const [value, setValue] = useState(val)
|
||||
|
||||
const handleChange = evt => {
|
||||
const newVal = parseFloat(evt.target.value)
|
||||
|
||||
if (newVal === dflt) reset()
|
||||
else {
|
||||
setValue(newVal)
|
||||
props.updateGist([props.setting], newVal)
|
||||
}
|
||||
}
|
||||
const reset = () => {
|
||||
setValue(props.dflt)
|
||||
props.updateGist([props.setting], props.dflt)
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="py-4 mx-6 border-l-2 pl-2">
|
||||
<p className="m-0 p-0 px-2 mb-2 text-base-content opacity-60 italic">
|
||||
{t(`settings:${props.setting}.d`)}
|
||||
</p>
|
||||
<div className="flex flex-row justify-between">
|
||||
<span className="opacity-50">
|
||||
{min}
|
||||
</span>
|
||||
<span className={`font-bold ${val===dflt ? 'text-secondary-focus' : 'text-accent'}`}>
|
||||
{val}
|
||||
</span>
|
||||
<span className="opacity-50">
|
||||
{max}
|
||||
</span>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
max={max}
|
||||
min={min}
|
||||
step={0.1}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
className={`
|
||||
range range-sm mt-1
|
||||
${val === dflt ? 'range-secondary' : 'range-accent'}
|
||||
`}
|
||||
/>
|
||||
<div className="flex flex-row justify-between">
|
||||
<span />
|
||||
<button
|
||||
title={t('reset')}
|
||||
className="btn btn-ghost btn-xs text-accent"
|
||||
disabled={val === dflt}
|
||||
onClick={reset}
|
||||
>
|
||||
<ClearIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CoreSettingNr
|
|
@ -0,0 +1,66 @@
|
|||
import ClearIcon from 'shared/components/icons/clear.js'
|
||||
import orderBy from 'lodash.orderby'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const CoreSettingOnly = props => {
|
||||
const { t } = useTranslation(['app', 'parts', 'settings'])
|
||||
const list = props.design.config.draftOrder
|
||||
const partNames = list.map(part => ({ id: part, name: t(`parts:${part}`) }))
|
||||
|
||||
const togglePart = part => {
|
||||
const parts = props.gist.only || []
|
||||
const newParts = new Set(parts)
|
||||
if (newParts.has(part)) newParts.delete(part)
|
||||
else newParts.add(part)
|
||||
if (newParts.size < 1) reset()
|
||||
else props.updateGist(['only'], [...newParts])
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
props.unsetGist(['only'])
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="py-4 mx-6 border-l-2 pl-2">
|
||||
<p className="m-0 p-0 px-2 mb-2 text-base-content opacity-60 italic">
|
||||
{t(`settings:only.d`)}
|
||||
</p>
|
||||
<div className="flex flex-row">
|
||||
<div className="grow">
|
||||
{orderBy(partNames, ['name'], ['asc']).map(part => (
|
||||
<button
|
||||
key={part.id}
|
||||
onClick={() => togglePart(part.id)}
|
||||
className={`
|
||||
mr-1 mb-1 text-left text-lg w-full hover:text-secondary-focus px-2
|
||||
${props.gist?.only && props.gist.only.indexOf(part.id) !== -1 && 'font-bold text-secondary-focus'}
|
||||
`}
|
||||
>
|
||||
<span className={`
|
||||
text-3xl mr-2 inline-block p-0 leading-3
|
||||
translate-y-3
|
||||
`}>
|
||||
<>°</>
|
||||
</span>
|
||||
{part.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row-reverse">
|
||||
<button
|
||||
title={t('reset')}
|
||||
className="btn btn-ghost btn-xs text-accent"
|
||||
disabled={!props.gist.only || props.gist.only.length < 1}
|
||||
onClick={reset}
|
||||
>
|
||||
<ClearIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CoreSettingOnly
|
||||
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import { useState } from 'react'
|
||||
import { SecText, SumButton, Li, SumDiv, Deg } from 'shared/components/workbench/menu/index.js'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const CoreSettingSaBool = props => {
|
||||
|
||||
const { t } = useTranslation(['app', 'settings'])
|
||||
const [value, setValue] = useState(props.gist.saBool || false)
|
||||
|
||||
const toggle = () => {
|
||||
props.setGist({
|
||||
...props.gist,
|
||||
saBool: !value,
|
||||
sa: value ? 0 : props.gist.saMm
|
||||
})
|
||||
setValue(!value)
|
||||
}
|
||||
|
||||
return (
|
||||
<Li>
|
||||
<SumButton onClick={toggle}>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
<span>{ t('settings:sa.t') }</span>
|
||||
<span className="ml-4 opacity-50">
|
||||
[ { t(`yes`) }/
|
||||
{ t(`no`) } ]
|
||||
</span>
|
||||
</SumDiv>
|
||||
<SecText>{t(value ? 'yes' : 'no')}</SecText>
|
||||
</SumButton>
|
||||
</Li>
|
||||
)
|
||||
}
|
||||
|
||||
export default CoreSettingSaBool
|
|
@ -0,0 +1,76 @@
|
|||
import { useState } from 'react'
|
||||
import { formatMm } from 'shared/utils'
|
||||
import ClearIcon from 'shared/components/icons/clear'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const CoreSettingMm = props => {
|
||||
const { t } = useTranslation(['app', 'settings'])
|
||||
const { dflt, min, max } = props
|
||||
const val = props.gist?.[props.setting]
|
||||
|
||||
const [value, setValue] = useState(val)
|
||||
|
||||
const handleChange = evt => {
|
||||
const newVal = parseFloat(evt.target.value)
|
||||
|
||||
setValue(newVal)
|
||||
if (props.gist.sa) props.setGist({
|
||||
...props.gist,
|
||||
saMm: newVal,
|
||||
sa: newVal,
|
||||
})
|
||||
else props.updateGist(['saMm'], newVal)
|
||||
}
|
||||
const reset = () => {
|
||||
setValue(dflt)
|
||||
props.updateGist(['saMm'], dflt)
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="py-4 mx-6 border-l-2 pl-2">
|
||||
<p className="m-0 p-0 px-2 mb-2 text-base-content opacity-60 italic">
|
||||
{t(`settings:sa.d`)}
|
||||
</p>
|
||||
<div className="flex flex-row justify-between">
|
||||
<span
|
||||
className="opacity-50"
|
||||
dangerouslySetInnerHTML={{__html: formatMm(min, props.gist.units)}}
|
||||
/>
|
||||
<span
|
||||
className={`font-bold ${val===dflt ? 'text-secondary-focus' : 'text-accent'}`}
|
||||
dangerouslySetInnerHTML={{__html: formatMm(val, props.gist.units)}}
|
||||
/>
|
||||
<span
|
||||
className="opacity-50"
|
||||
dangerouslySetInnerHTML={{__html: formatMm(max, props.gist.units)}}
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
max={max}
|
||||
min={min}
|
||||
step={0.1}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
className={`
|
||||
range range-sm mt-1
|
||||
${val === dflt ? 'range-secondary' : 'range-accent'}
|
||||
`}
|
||||
/>
|
||||
<div className="flex flex-row justify-between">
|
||||
<span />
|
||||
<button
|
||||
title={t('reset')}
|
||||
className="btn btn-ghost btn-xs text-accent"
|
||||
disabled={val === dflt}
|
||||
onClick={reset}
|
||||
>
|
||||
<ClearIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CoreSettingMm
|
|
@ -0,0 +1,69 @@
|
|||
import SettingsIcon from 'shared/components/icons/settings.js'
|
||||
import { linkClasses, Chevron } from 'shared/components/navigation/primary.js'
|
||||
import Setting from './setting.js'
|
||||
import { Ul, Details, TopSummary, TopSumTitle } from '../index.js'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
export const settings = {
|
||||
paperless: {
|
||||
dflt: false,
|
||||
},
|
||||
saBool: {
|
||||
dflt: false,
|
||||
},
|
||||
saMm: {
|
||||
min: 0,
|
||||
max: 25,
|
||||
dflt: 10,
|
||||
},
|
||||
complete: {
|
||||
dflt: false,
|
||||
},
|
||||
only: { },
|
||||
locale: {
|
||||
list: ['de', 'en', 'es', 'fr', 'nl'],
|
||||
},
|
||||
units: {
|
||||
list: ['metric', 'imperial'],
|
||||
},
|
||||
margin: {
|
||||
min: 0,
|
||||
max: 25,
|
||||
dflt: 2,
|
||||
},
|
||||
scale: {
|
||||
min: 0.1,
|
||||
max: 5,
|
||||
dflt: 1,
|
||||
},
|
||||
renderer: {
|
||||
list: ['react', 'svg'],
|
||||
titles: {
|
||||
react: '<Draft /> (React)',
|
||||
svg: '@freesewing/core (SVG)'
|
||||
}
|
||||
},
|
||||
debug: {
|
||||
dflt: false,
|
||||
},
|
||||
}
|
||||
|
||||
const CoreSettings = props => {
|
||||
const { t } = useTranslation(['app'])
|
||||
|
||||
return (
|
||||
<Details open>
|
||||
<TopSummary icon={<SettingsIcon />}>
|
||||
<TopSumTitle>{t('settings')}</TopSumTitle>
|
||||
<Chevron />
|
||||
</TopSummary>
|
||||
<Ul>
|
||||
{Object.keys(settings).map(setting => (
|
||||
<Setting key={setting} setting={setting} config={settings[setting]} {...props} />
|
||||
))}
|
||||
</Ul>
|
||||
</Details>
|
||||
)
|
||||
}
|
||||
|
||||
export default CoreSettings
|
118
sites/shared/components/workbench/menu/core-settings/setting.js
Normal file
118
sites/shared/components/workbench/menu/core-settings/setting.js
Normal file
|
@ -0,0 +1,118 @@
|
|||
import { Chevron } from 'shared/components/navigation/primary'
|
||||
import PctDegOption from 'shared/components/workbench/inputs/design-option-pct-deg'
|
||||
import ListSetting from './core-setting-list'
|
||||
import OnlySetting from './core-setting-only'
|
||||
import MmSetting from './core-setting-mm'
|
||||
import NrSetting from './core-setting-nr'
|
||||
import BoolSetting from './core-setting-bool'
|
||||
import SaBoolSetting from './core-setting-sa-bool'
|
||||
import SaMmSetting from './core-setting-sa-mm'
|
||||
import { formatMm } from 'shared/utils'
|
||||
import { SecText, Li, Details, Summary, SumDiv, Deg } from 'shared/components/workbench/menu/index'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const settings = {
|
||||
paperless: props => (
|
||||
<SecText>
|
||||
{props.t(props.gist.paperless ? 'yes' : 'no')}
|
||||
</SecText>
|
||||
),
|
||||
complete: props => (
|
||||
<SecText>
|
||||
{props.t(props.gist.complete ? 'yes' : 'no')}
|
||||
</SecText>
|
||||
),
|
||||
debug: props => (
|
||||
<SecText>
|
||||
{props.t(props.gist.debug ? 'yes' : 'no')}
|
||||
</SecText>
|
||||
),
|
||||
locale: props => (
|
||||
<SecText>
|
||||
{props.t(`i18n:${props.gist.locale}`)}
|
||||
</SecText>
|
||||
),
|
||||
units: props => (
|
||||
<SecText>
|
||||
{props.t(`${props.gist.units}Units`)}
|
||||
</SecText>
|
||||
),
|
||||
margin: props => <SecText raw={formatMm(props.gist.margin, props.gist.units)} />,
|
||||
scale: props => props.gist.scale === 1
|
||||
? <SecText>{props.gist.scale}</SecText>
|
||||
: <span className="text-accent">{props.gist.scale}</span>,
|
||||
saMm: props => <SecText raw={formatMm(props.gist.saMm, props.gist.units)} />,
|
||||
renderer: props => (
|
||||
<SecText>
|
||||
{props.config.titles[props.gist.renderer]}
|
||||
</SecText>
|
||||
),
|
||||
only: props => (props.gist?.only && props.gist.only.length > 0)
|
||||
? <SecText>{props.gist.only.length}</SecText>
|
||||
: <span className="text-secondary-focus">{props.t('default')}</span>
|
||||
}
|
||||
|
||||
const inputs = {
|
||||
locale: props => <ListSetting
|
||||
{...props}
|
||||
list={props.config.list.map(key => ({
|
||||
key,
|
||||
title: props.t(`i18n:${key}`)
|
||||
}))}
|
||||
/>,
|
||||
units: props => <ListSetting
|
||||
{...props}
|
||||
list={props.config.list.map(key => ({
|
||||
key,
|
||||
title: props.t(`${key}Units`)
|
||||
}))}
|
||||
/>,
|
||||
margin: props => <MmSetting {...props} {...props.config} />,
|
||||
scale: props => <NrSetting {...props} {...props.config} />,
|
||||
saMm: props => <SaMmSetting {...props} {...props.config} />,
|
||||
renderer: props => <ListSetting
|
||||
{...props}
|
||||
list={props.config.list.map(key => ({
|
||||
key,
|
||||
title: props.config.titles[key]
|
||||
}))}
|
||||
/>,
|
||||
only: props => <OnlySetting {...props} />
|
||||
}
|
||||
|
||||
const Setting = props => {
|
||||
const { t } = useTranslation(['app', 'i18n', 'settings'])
|
||||
if (props.setting === 'saBool')
|
||||
return <SaBoolSetting {...props} {...props.config} />
|
||||
if (['paperless', 'complete', 'debug', 'xray'].indexOf(props.setting) !== -1)
|
||||
return <BoolSetting {...props} {...props.config} />
|
||||
|
||||
const Input = inputs[props.setting]
|
||||
const Value = settings[props.setting]
|
||||
|
||||
return (
|
||||
<Li>
|
||||
<Details>
|
||||
<Summary>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
{props.setting === 'saMm'
|
||||
? (
|
||||
<>
|
||||
<span>{t(`settings:sa.t`)}</span>
|
||||
<span className="ml-4 opacity-50">[ {t(`size`)} ]</span>
|
||||
</>
|
||||
)
|
||||
: <span>{t(`settings:${props.setting}.t`)}</span>
|
||||
}
|
||||
</SumDiv>
|
||||
<Value setting={props.setting} {...props} t={t}/>
|
||||
<Chevron />
|
||||
</Summary>
|
||||
<Input {...props} t={t} />
|
||||
</Details>
|
||||
</Li>
|
||||
)
|
||||
}
|
||||
|
||||
export default Setting
|
|
@ -0,0 +1,25 @@
|
|||
import OptionsIcon from 'shared/components/icons/options.js'
|
||||
import { Chevron } from 'shared/components/navigation/primary.js'
|
||||
import OptionGroup from './option-group'
|
||||
import { Ul, Details, TopSummary, TopSumTitle } from 'shared/components/workbench/menu'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const DesignOptions = props => {
|
||||
const { t } = useTranslation(['app'])
|
||||
|
||||
return (
|
||||
<Details open>
|
||||
<TopSummary icon={<OptionsIcon />}>
|
||||
<TopSumTitle>{t('designOptions')}</TopSumTitle>
|
||||
<Chevron />
|
||||
</TopSummary>
|
||||
<Ul className="pl-5 list-inside">
|
||||
{Object.keys(props.design.config.optionGroups).map(group => (
|
||||
<OptionGroup {...props} group={group} key={group} />
|
||||
))}
|
||||
</Ul>
|
||||
</Details>
|
||||
)
|
||||
}
|
||||
|
||||
export default DesignOptions
|
|
@ -0,0 +1,32 @@
|
|||
import { Chevron } from 'shared/components/navigation/primary.js'
|
||||
import Option from './option'
|
||||
import { Li, Ul, Details, Summary, SumDiv, Deg } from 'shared/components/workbench/menu'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const OptionGroup = props => {
|
||||
const { t } = useTranslation(['optiongroups'])
|
||||
const config = props.config || props.design.config.optionGroups[props.group]
|
||||
return (
|
||||
<Li>
|
||||
<Details>
|
||||
<Summary>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
<span className="font-bold">
|
||||
{ t(props.group) }
|
||||
</span>
|
||||
</SumDiv>
|
||||
<Chevron />
|
||||
</Summary>
|
||||
<Ul>
|
||||
{config.map(option =>
|
||||
typeof option === 'string' ? <Option {...props} option={option} key={option} />
|
||||
: Object.keys(option).map((sub) => <OptionGroup {...props} config={option[sub]} group={sub} key={sub}/>)
|
||||
)}
|
||||
</Ul>
|
||||
</Details>
|
||||
</Li>
|
||||
)
|
||||
}
|
||||
|
||||
export default OptionGroup
|
|
@ -0,0 +1,14 @@
|
|||
import PctDegOption from 'shared/components/workbench/inputs/design-option-pct-deg'
|
||||
import CountOption from 'shared/components/workbench/inputs/design-option-count'
|
||||
import ListOption from 'shared/components/workbench/inputs/design-option-list'
|
||||
|
||||
export const Tmp = props => <p>not yet</p>
|
||||
|
||||
export const inputs = {
|
||||
pct: PctDegOption,
|
||||
count: CountOption,
|
||||
deg: props => (<PctDegOption {...props} type='deg' />),
|
||||
list: ListOption,
|
||||
mm: (<p>Mm options are not supported. Please report this.</p>),
|
||||
constant: Tmp,
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
import { useTranslation } from 'next-i18next'
|
||||
import { formatMm, formatPercentage} from 'shared/utils'
|
||||
|
||||
export const values = {
|
||||
pct: props => {
|
||||
const val = (typeof props.gist?.options?.[props.option] === 'undefined')
|
||||
? props.design.config.options[props.option].pct/100
|
||||
: props.gist.options[props.option]
|
||||
return (
|
||||
<span className={
|
||||
val=== props.design.config.options[props.option].pct/100
|
||||
? 'text-secondary-focus'
|
||||
: 'text-accent'
|
||||
}>
|
||||
{formatPercentage(val)}
|
||||
{props.design.config.options[props.option]?.toAbs
|
||||
? ' | ' +formatMm(props.design.config.options[props.option]?.toAbs(val, props.gist))
|
||||
: null
|
||||
}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
bool: props => {
|
||||
const { t } = useTranslation(['app'])
|
||||
const dflt = props.design.config.options[props.option].bool
|
||||
let current = props.gist?.options?.[props.option]
|
||||
current = current === undefined ? dflt : current;
|
||||
return (
|
||||
<span className={
|
||||
(dflt==current || typeof current === 'undefined')
|
||||
? 'text-secondary-focus'
|
||||
: 'text-accent'
|
||||
}>
|
||||
{current
|
||||
? t('yes')
|
||||
: t('no')
|
||||
}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
count: props => {
|
||||
const dflt = props.design.config.options[props.option].count
|
||||
const current = props.gist?.options?.[props.option]
|
||||
return (dflt==current || typeof current === 'undefined')
|
||||
? (<span className="text-secondary-focus">{dflt}</span>)
|
||||
: (<span className="text-accent">{current}</span>)
|
||||
},
|
||||
list: props => {
|
||||
const dflt = props.design.config.options[props.option].dflt
|
||||
const current = props.gist?.options?.[props.option]
|
||||
const prefix = `${props.option}.o.`
|
||||
return (dflt==current || typeof current === 'undefined')
|
||||
? (<span className="text-secondary-focus">{props.t(prefix+dflt)}</span>)
|
||||
: (<span className="text-accent">{props.t(prefix+current)}</span>)
|
||||
},
|
||||
deg: props => {
|
||||
const dflt = props.design.config.options[props.option].deg
|
||||
const current = props.gist?.options?.[props.option]
|
||||
return (dflt==current || typeof current === 'undefined')
|
||||
? (<span className="text-secondary-focus">{dflt}°</span>)
|
||||
: (<span className="text-accent">{current}°</span>)
|
||||
},
|
||||
mm: props => {
|
||||
return (<p>No mm val yet</p>)
|
||||
},
|
||||
constant: props => {
|
||||
return (<p>No constant val yet</p>)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
import { Chevron } from 'shared/components/navigation/primary'
|
||||
import { optionType } from 'shared/utils'
|
||||
import { Li, Ul, Details, Summary, SumButton, SumDiv, Deg } from 'shared/components/workbench/menu'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import {values} from 'shared/components/workbench/menu/design-options/option-value'
|
||||
import {inputs} from 'shared/components/workbench/menu/design-options/option-input'
|
||||
|
||||
const Option = props => {
|
||||
const { t } = useTranslation([`o_${props.design.config.name}`])
|
||||
const opt = props.design.config.options[props.option];
|
||||
const type = optionType(opt)
|
||||
const Input = inputs[type]
|
||||
const Value = values[type]
|
||||
const hide = opt.hide && opt.hide(props.draft.settings.options);
|
||||
|
||||
if (hide) {
|
||||
return <Li></Li>
|
||||
}
|
||||
|
||||
if (type === 'bool') {
|
||||
const toggleBoolean = () => {
|
||||
const dflt = opt.bool
|
||||
const current = props.gist?.options?.[props.option]
|
||||
if (typeof current === 'undefined')
|
||||
props.updateGist(['options', props.option], !dflt)
|
||||
else props.unsetGist(['options', props.option])
|
||||
}
|
||||
|
||||
return (
|
||||
<Li>
|
||||
<SumButton onClick={toggleBoolean}>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
<span>{t(`${props.option}.t`) }</span>
|
||||
</SumDiv>
|
||||
<Value type={type} {...props} t={t} />
|
||||
</SumButton>
|
||||
</Li>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Li>
|
||||
<Details>
|
||||
<Summary>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
<span>{t(`${props.option}.t`)}</span>
|
||||
</SumDiv>
|
||||
<Value type={type} {...props} t={t} />
|
||||
<Chevron w={6} m={3}/>
|
||||
</Summary>
|
||||
<Input {...props} ot={t} />
|
||||
</Details>
|
||||
</Li>
|
||||
)
|
||||
}
|
||||
|
||||
export default Option
|
101
sites/shared/components/workbench/menu/index.js
Normal file
101
sites/shared/components/workbench/menu/index.js
Normal file
|
@ -0,0 +1,101 @@
|
|||
import { linkClasses, Chevron } from 'shared/components/navigation/primary.js'
|
||||
import ViewMenu from './view.js'
|
||||
import DesignOptions from './design-options'
|
||||
import CoreSettings from './core-settings'
|
||||
import Xray from './xray'
|
||||
import TestDesignOptions from './test-design-options'
|
||||
|
||||
export const Ul = props => <ul className="pl-5 list-inside">{props.children}</ul>
|
||||
export const Li = props => (
|
||||
<li className="flex flex-row hover:border-r-2 hover:border-r-secondary">
|
||||
{props.children}
|
||||
</li>
|
||||
)
|
||||
export const Details = props => (
|
||||
<details className="grow" open={props.open || false}>
|
||||
{props.children}
|
||||
</details>
|
||||
)
|
||||
export const Deg = props => <span className="text-3xl inline-block p-0 leading-3 px-2 translate-y-3">°</span>
|
||||
export const NoSumDiv = props => (
|
||||
<div className={`
|
||||
grow px-2 ml-2 border-l-2
|
||||
${linkClasses}
|
||||
hover:cursor-resize
|
||||
hover:border-secondary
|
||||
sm:hover:border-secondary-focus
|
||||
text-base-content sm:text-base-content
|
||||
`}>{props.children}</div>
|
||||
)
|
||||
export const SumDiv = props => (
|
||||
<div className={`
|
||||
grow pl-2 border-l-2
|
||||
${linkClasses}
|
||||
hover:cursor-resize
|
||||
hover:border-secondary
|
||||
sm:hover:border-secondary-focus
|
||||
text-base-content sm:text-base-content
|
||||
`}>{props.children}</div>
|
||||
)
|
||||
export const Summary = props => (
|
||||
<summary className={`
|
||||
flex flex-row
|
||||
px-2
|
||||
text-base-content
|
||||
sm:text-base-content
|
||||
hover:cursor-row-resize
|
||||
items-center
|
||||
`}>{props.children}</summary>
|
||||
)
|
||||
export const TopSummary = props => (
|
||||
<summary className={`
|
||||
flex flex-row gap-4 text-lg
|
||||
hover:cursor-row-resize
|
||||
p-2
|
||||
text-base-content
|
||||
sm:text-base-content
|
||||
items-center
|
||||
`}>
|
||||
<span className="text-secondary-focus mr-4">{props.icon || null}</span>
|
||||
{props.children}
|
||||
</summary>
|
||||
)
|
||||
export const SumButton = props => (
|
||||
<button className={`
|
||||
flex flex-row
|
||||
px-2
|
||||
w-full justify-between
|
||||
text-left
|
||||
text-base-content
|
||||
sm:text-base-content
|
||||
hover:cursor-pointer
|
||||
items-center
|
||||
mr-4
|
||||
`} onClick={props.onClick}>{props.children}</button>
|
||||
)
|
||||
export const TopSumTitle = props => (
|
||||
<span className={`grow ${linkClasses} hover:cursor-resize font-bold uppercase`}>
|
||||
{props.children}
|
||||
</span>
|
||||
)
|
||||
export const SecText = props => props.raw
|
||||
? <span className="text-secondary-focus" dangerouslySetInnerHTML={{__html: props.raw}} />
|
||||
: <span className="text-secondary-focus">{props.children}</span>
|
||||
|
||||
const WorkbenchMenu = props => {
|
||||
return (
|
||||
<nav className="grow mb-12">
|
||||
<ViewMenu {...props} />
|
||||
{props.gist?._state?.view === 'draft' && (
|
||||
<>
|
||||
<DesignOptions {...props} />
|
||||
<CoreSettings {...props} />
|
||||
{props.gist.renderer === 'react' && <Xray {...props} />}
|
||||
</>
|
||||
)}
|
||||
{props.gist?._state?.view === 'test' && <TestDesignOptions {...props} />}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
export default WorkbenchMenu
|
|
@ -0,0 +1,25 @@
|
|||
import OptionsIcon from 'shared/components/icons/options.js'
|
||||
import { Chevron } from 'shared/components/navigation/primary.js'
|
||||
import OptionGroup from './option-group'
|
||||
import { Ul, Details, TopSummary, TopSumTitle } from 'shared/components/workbench/menu'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const DesignOptions = props => {
|
||||
const { t } = useTranslation(['app'])
|
||||
|
||||
return (
|
||||
<Details open>
|
||||
<TopSummary icon={<OptionsIcon />}>
|
||||
<TopSumTitle>{t('app:designOptions')}</TopSumTitle>
|
||||
<Chevron />
|
||||
</TopSummary>
|
||||
<Ul className="pl-5 list-inside">
|
||||
{Object.keys(props.design.config.optionGroups).map(group => (
|
||||
<OptionGroup {...props} group={group} key={group} />
|
||||
))}
|
||||
</Ul>
|
||||
</Details>
|
||||
)
|
||||
}
|
||||
|
||||
export default DesignOptions
|
|
@ -0,0 +1,33 @@
|
|||
import { Chevron } from 'shared/components/navigation/primary.js'
|
||||
import Option from './option'
|
||||
import OptionSubGroup from './option-sub-group'
|
||||
import { Li, Ul, Details, Summary, SumDiv, Deg } from 'shared/components/workbench/menu'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const OptionGroup = props => {
|
||||
const { t } = useTranslation(['optiongroups'])
|
||||
const config = props.config || props.design.config.optionGroups[props.group]
|
||||
return (
|
||||
<Li>
|
||||
<Details>
|
||||
<Summary>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
<span className="font-bold">
|
||||
{ t(props.group) }
|
||||
</span>
|
||||
</SumDiv>
|
||||
<Chevron />
|
||||
</Summary>
|
||||
<Ul>
|
||||
{config.map(option => typeof option === 'string'
|
||||
? <Option {...props} option={option} key={option} />
|
||||
: <OptionSubGroup {...props} sub={option} config={config} />
|
||||
)}
|
||||
</Ul>
|
||||
</Details>
|
||||
</Li>
|
||||
)
|
||||
}
|
||||
|
||||
export default OptionGroup
|
|
@ -0,0 +1,29 @@
|
|||
import { Chevron } from 'shared/components/navigation/primary.js'
|
||||
import Option from './option'
|
||||
import { Li, Ul, Details, Summary, SumDiv, Deg } from 'shared/components/workbench/menu'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const OptionSubGroup = props => {
|
||||
const { t } = useTranslation(['optiongroups'])
|
||||
return Object.keys(props.sub).map(name => (
|
||||
<Li>
|
||||
<Details>
|
||||
<Summary>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
<span className="font-bold">{ t(name) }</span>
|
||||
</SumDiv>
|
||||
<Chevron />
|
||||
</Summary>
|
||||
<Ul>
|
||||
{props.sub[name].map(option => typeof option === 'string'
|
||||
? <Option {...props} option={option} key={option} />
|
||||
: <OptionSubGroup {...props} sub={option} config={config} />
|
||||
)}
|
||||
</Ul>
|
||||
</Details>
|
||||
</Li>
|
||||
))
|
||||
}
|
||||
|
||||
export default OptionSubGroup
|
|
@ -0,0 +1,67 @@
|
|||
import { linkClasses } from 'shared/components/navigation/primary.js'
|
||||
import { Li } from 'shared/components/workbench/menu'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const SumButton = props => (
|
||||
<button className={`
|
||||
flex flex-row
|
||||
px-2
|
||||
w-full justify-between
|
||||
text-left
|
||||
text-base-content
|
||||
sm:text-base-content
|
||||
hover:cursor-pointer
|
||||
items-center
|
||||
mr-4
|
||||
`} onClick={props.onClick}>{props.children}</button>
|
||||
)
|
||||
const SumDiv = (props) => (
|
||||
<div className={`
|
||||
grow pl-2 border-l-2
|
||||
${linkClasses}
|
||||
hover:cursor-resize
|
||||
hover:border-secondary
|
||||
sm:hover:border-secondary-focus
|
||||
text-base-content sm:text-base-content
|
||||
${props.active && 'border-secondary-focus'}
|
||||
|
||||
`}>{props.children}</div>
|
||||
)
|
||||
|
||||
const Option = props => {
|
||||
const { t } = useTranslation([`o_${props.design.config.name}`, 'workbench'])
|
||||
const active = (
|
||||
props.gist.sample?.type === 'option' &&
|
||||
props.gist.sample?.option === props.option
|
||||
)
|
||||
|
||||
return (
|
||||
<Li>
|
||||
<SumButton onClick={() => props.updateGist(
|
||||
['sample'],
|
||||
{
|
||||
type: 'option',
|
||||
option: props.option
|
||||
},
|
||||
true // Close navigation on mobile
|
||||
)}>
|
||||
<SumDiv active={active}>
|
||||
<span className={`
|
||||
text-3xl inline-block p-0 leading-3 px-2
|
||||
${active
|
||||
? 'text-secondary sm:text-secondary-focus translate-y-1 font-bold'
|
||||
: 'translate-y-3'
|
||||
}`}
|
||||
>
|
||||
{active ? <span>•</span> : <span>°</span>}
|
||||
</span>
|
||||
<span className={active ? 'text-secondary font-bold' : ''}>
|
||||
{t(`o_${props.design.config.name}:${props.option}.t`)}
|
||||
</span>
|
||||
</SumDiv>
|
||||
</SumButton>
|
||||
</Li>
|
||||
)
|
||||
}
|
||||
|
||||
export default Option
|
120
sites/shared/components/workbench/menu/view.js
Normal file
120
sites/shared/components/workbench/menu/view.js
Normal file
|
@ -0,0 +1,120 @@
|
|||
import MenuIcon from 'shared/components/icons/menu.js'
|
||||
import { linkClasses, Chevron } from 'shared/components/navigation/primary.js'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import defaultSettings from '../default-settings'
|
||||
|
||||
const View = props => {
|
||||
const { t } = useTranslation(['app'])
|
||||
const entries = [
|
||||
{
|
||||
name: 'measurements',
|
||||
title: t('measurements'),
|
||||
onClick: () => props.updateGist(['_state', 'view'], 'measurements', true)
|
||||
},
|
||||
{
|
||||
name: 'draft',
|
||||
title: t('draftDesign', { design: props.design.config.name }),
|
||||
onClick: () => props.updateGist(['_state', 'view'], 'draft', true)
|
||||
},
|
||||
{
|
||||
name: 'test',
|
||||
title: t('testDesign', { design: props.design.config.name }),
|
||||
onClick: () => props.updateGist(['_state', 'view'], 'test', true)
|
||||
},
|
||||
{
|
||||
name: 'printingLayout',
|
||||
title: t('layoutThing', { thing: props.design.config.name })
|
||||
+ ': ' + t('forPrinting'),
|
||||
onClick: () => props.updateGist(['_state', 'view'], 'printingLayout', true)
|
||||
},
|
||||
{
|
||||
name: 'cuttingLayout',
|
||||
title: t('layoutThing', { thing: props.design.config.name })
|
||||
+ ': ' + t('forCutting'),
|
||||
onClick: () => props.updateGist(['_state', 'view'], 'cuttingLayout', true)
|
||||
},
|
||||
{
|
||||
name: 'export',
|
||||
title: t('exportThing', { thing: props.design.config.name }),
|
||||
onClick: () => props.updateGist(['_state', 'view'], 'export', true)
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
title: t('events'),
|
||||
onClick: () => props.updateGist(['_state', 'view'], 'events', true)
|
||||
},
|
||||
{
|
||||
name: 'yaml',
|
||||
title: t('YAML'),
|
||||
onClick: () => props.updateGist(['_state', 'view'], 'yaml', true)
|
||||
},
|
||||
{
|
||||
name: 'json',
|
||||
title: t('JSON'),
|
||||
onClick: () => props.updateGist(['_state', 'view'], 'json', true)
|
||||
},
|
||||
{
|
||||
name: 'edit',
|
||||
title: t('editThing', { thing: 'YAML' }),
|
||||
onClick: () => props.updateGist(['_state', 'view'], 'edit', true)
|
||||
},
|
||||
{
|
||||
name: 'clear',
|
||||
title: t('clearThing', { thing: 'YAML' }),
|
||||
onClick: () => props.setGist(defaultSettings)
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<details className='py-1' open>
|
||||
<summary className={`
|
||||
flex flex-row uppercase gap-4 font-bold text-lg
|
||||
hover:cursor-row-resize
|
||||
p-2
|
||||
text-base-content
|
||||
sm:text-base-content
|
||||
items-center
|
||||
`}>
|
||||
<span className="text-secondary-focus mr-4"><MenuIcon /></span>
|
||||
<span className={`grow ${linkClasses} hover:cursor-resize`}>
|
||||
{t('view')}
|
||||
</span>
|
||||
<Chevron />
|
||||
</summary>
|
||||
<ul className="pl-5 list-inside">
|
||||
{entries.map(entry => (
|
||||
<li key={entry.title} className="flex flex-row">
|
||||
<button title={entry.title} className={`
|
||||
grow pl-2 border-l-2
|
||||
${linkClasses}
|
||||
hover:cursor-pointer
|
||||
hover:border-secondary
|
||||
sm:hover:border-secondary-focus
|
||||
text-left
|
||||
capitalize
|
||||
${entry.name === props.gist?._state?.view
|
||||
? 'text-secondary border-secondary sm:text-secondary-focus sm:border-secondary-focus'
|
||||
: 'text-base-content sm:text-base-content'
|
||||
}
|
||||
`} onClick={entry.onClick}>
|
||||
<span className={`
|
||||
text-3xl mr-2 inline-block p-0 leading-3
|
||||
${entry.name === props.gist?._state?.view
|
||||
? 'text-secondary sm:text-secondary-focus translate-y-1 font-bold'
|
||||
: 'translate-y-3'
|
||||
}
|
||||
`}>
|
||||
{entry.name === props.gist?._state?.view ? <>•</> : <>°</>}
|
||||
</span>
|
||||
<span className={entry.name === props.gist?._state?.view ? 'font-bold' : ''}>
|
||||
{ entry.title }
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</details>
|
||||
)
|
||||
}
|
||||
|
||||
export default View
|
51
sites/shared/components/workbench/menu/xray/attributes.js
Normal file
51
sites/shared/components/workbench/menu/xray/attributes.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
import { Chevron } from 'shared/components/navigation/primary'
|
||||
import { Ul, Li, Details, Summary, SumDiv, NoSumDiv, Deg } from 'shared/components/workbench/menu'
|
||||
import { round } from 'shared/utils'
|
||||
|
||||
const XrayAttributes = ({ attr=false, t }) => {
|
||||
if (!attr || !attr.list || Object.keys(attr.list).length < 1) return null
|
||||
|
||||
return (
|
||||
<Li>
|
||||
<Details>
|
||||
<Summary>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
Attributes
|
||||
</SumDiv>
|
||||
<Chevron />
|
||||
</Summary>
|
||||
<Ul>
|
||||
{Object.keys(attr.list).map(at => (
|
||||
<Li key={at}>
|
||||
<Details>
|
||||
<Summary>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
{at}
|
||||
</SumDiv>
|
||||
<Chevron />
|
||||
</Summary>
|
||||
<Ul>
|
||||
{attr.list[at].map(val => (
|
||||
<Li key={val}>
|
||||
<NoSumDiv>
|
||||
<Deg />
|
||||
<span>{val === true
|
||||
? t('app.yes')
|
||||
: val
|
||||
}</span>
|
||||
</NoSumDiv>
|
||||
</Li>
|
||||
))}
|
||||
</Ul>
|
||||
</Details>
|
||||
</Li>
|
||||
))}
|
||||
</Ul>
|
||||
</Details>
|
||||
</Li>
|
||||
)
|
||||
}
|
||||
|
||||
export default XrayAttributes
|
21
sites/shared/components/workbench/menu/xray/disable.js
Normal file
21
sites/shared/components/workbench/menu/xray/disable.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { Li, SumButton, SumDiv, Deg } from 'shared/components/workbench/menu'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const DisableXray = props => {
|
||||
const { t } = useTranslation(['cfp', 'settings'])
|
||||
|
||||
return (
|
||||
<Li>
|
||||
<SumButton onClick={() => props.updateGist(['_state', 'xray', 'enabled'], false)}>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
<span>
|
||||
{t('cfp:thingIsEnabled', { thing: t('settings:xray.t') })}
|
||||
</span>
|
||||
</SumDiv>
|
||||
</SumButton>
|
||||
</Li>
|
||||
)
|
||||
}
|
||||
|
||||
export default DisableXray
|
54
sites/shared/components/workbench/menu/xray/index.js
Normal file
54
sites/shared/components/workbench/menu/xray/index.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
import XrayIcon from 'shared/components/icons/xray.js'
|
||||
import { linkClasses, Chevron } from 'shared/components/navigation/primary.js'
|
||||
import Log from './log.js'
|
||||
import Reset from './reset.js'
|
||||
import Disable from './disable.js'
|
||||
import List from './list.js'
|
||||
import { Ul, Details, TopSummary } from 'shared/components/workbench/menu'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const Xray = props => {
|
||||
const { t } = useTranslation(['app', 'settings'])
|
||||
|
||||
return (
|
||||
<Details open>
|
||||
<TopSummary icon={<XrayIcon />}>
|
||||
{props.gist?._state?.xray?.enabled
|
||||
? (
|
||||
<>
|
||||
<span className={`grow ${linkClasses} hover:cursor-resize font-bold uppercase`}>
|
||||
{t('settings:xray.t')}
|
||||
</span>
|
||||
<Chevron />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<button
|
||||
className={`grow ${linkClasses} hover:cursor-resize uppercase font-bold text-left`}
|
||||
onClick={() => props.updateGist(['_state', 'xray', 'enabled'], true)}
|
||||
>
|
||||
{t('settings:xray.t')}
|
||||
</button>
|
||||
<span className="text-normal text-secondary">
|
||||
{t('cfp:thingIsDisabled', { thing: t('settings:xray.t') })}
|
||||
</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</TopSummary>
|
||||
{props.gist?._state?.xray?.enabled && (
|
||||
<Ul>
|
||||
<Disable {...props} />
|
||||
<Log {...props} />
|
||||
<Reset {...props} />
|
||||
{
|
||||
props.gist?._state?.xray?.parts &&
|
||||
Object.keys(props.gist._state.xray.parts).map(partName => <List {...props} partName={partName} />)
|
||||
}
|
||||
</Ul>
|
||||
)}
|
||||
</Details>
|
||||
)
|
||||
}
|
||||
|
||||
export default Xray
|
138
sites/shared/components/workbench/menu/xray/list.js
Normal file
138
sites/shared/components/workbench/menu/xray/list.js
Normal file
|
@ -0,0 +1,138 @@
|
|||
import { Chevron } from 'shared/components/navigation/primary.js'
|
||||
import ClearIcon from 'shared/components/icons/clear.js'
|
||||
import FilterIcon from 'shared/components/icons/filter.js'
|
||||
import SearchIcon from 'shared/components/icons/search.js'
|
||||
import { Ul, Li, Details, Summary, SumDiv, Deg } from 'shared/components/workbench/menu'
|
||||
import Path from './path.js'
|
||||
import Point from './point.js'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const types = {
|
||||
paths: Path,
|
||||
points: Point
|
||||
}
|
||||
|
||||
const XrayList = props => {
|
||||
const { t } = useTranslation(['app', 'parts'])
|
||||
|
||||
const title = t(`parts:${props.partName}`) + ` (${props.partName})`
|
||||
|
||||
const part = props.gist._state.xray.parts[props.partName]
|
||||
|
||||
// Is this the only part on display?
|
||||
const only = (
|
||||
props.gist.only &&
|
||||
props.gist.only.length === 1 &&
|
||||
props.gist.only[0] === props.partName
|
||||
)
|
||||
|
||||
return (
|
||||
<Li>
|
||||
<Details>
|
||||
<Summary>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
<span>{title}</span>
|
||||
<span className="ml-2 opacity-60">[{props.partName}]</span>
|
||||
</SumDiv>
|
||||
<button
|
||||
className={`px-3 hover:text-secondary-focus ${only ? 'text-accent' : 'text-secondary'}`}
|
||||
title={t('filter')}
|
||||
onClick={only
|
||||
? () => props.unsetGist(['only'])
|
||||
: () => props.updateGist(['only'], [props.partName])
|
||||
}
|
||||
>
|
||||
<FilterIcon />
|
||||
</button>
|
||||
<button
|
||||
className="text-accent px-3 hover:text-secondary-focus"
|
||||
onClick={() => props.unsetGist(['_state', 'xray', 'parts', props.partName])}
|
||||
>
|
||||
<ClearIcon />
|
||||
</button>
|
||||
<Chevron w={6} m={3}/>
|
||||
</Summary>
|
||||
{Object.keys(types).map(type => part[type] && (
|
||||
<Ul>
|
||||
<Li>
|
||||
<Details>
|
||||
<Summary>
|
||||
<SumDiv>
|
||||
<span className="capitalize">{type}</span>
|
||||
</SumDiv>
|
||||
<button
|
||||
className="text-accent px-3 hover:text-secondary-focus"
|
||||
onClick={() => props.unsetGist(['_state', 'xray', 'parts', props.partName, type])}
|
||||
>
|
||||
<ClearIcon />
|
||||
</button>
|
||||
<Chevron />
|
||||
</Summary>
|
||||
<Ul>
|
||||
{Object.keys(part[type])
|
||||
.map(id => (
|
||||
<Li>
|
||||
<Details>
|
||||
<Summary>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
<span>{id}</span>
|
||||
</SumDiv>
|
||||
<button
|
||||
className={`px-3 hover:text-secondary-focus"
|
||||
${props.gist._state?.xray?.reveal?.[props.partName]?.[type]?.[id]
|
||||
? 'text-accent'
|
||||
: 'text-secondary'
|
||||
}`}
|
||||
onClick={props.gist._state?.xray?.reveal?.[props.partName]?.[type]?.[id]
|
||||
? () => props.unsetGist(
|
||||
['_state', 'xray', 'reveal', props.partName, type, id]
|
||||
)
|
||||
: () => props.updateGist(
|
||||
['_state', 'xray', 'reveal', props.partName, type, id],
|
||||
id
|
||||
)
|
||||
}
|
||||
>
|
||||
<SearchIcon />
|
||||
</button>
|
||||
<button
|
||||
className="text-accent px-3 hover:text-secondary-focus"
|
||||
onClick={() => {
|
||||
props.unsetGist(['_state', 'xray', 'parts', props.partName, type, id])
|
||||
props.unsetGist(['_state', 'xray', 'reveal', props.partName, type, id])
|
||||
}}
|
||||
>
|
||||
<ClearIcon />
|
||||
</button>
|
||||
<Chevron />
|
||||
</Summary>
|
||||
{type === 'paths' && <Path
|
||||
pathName={id}
|
||||
partName={props.partName}
|
||||
draft={props.draft}
|
||||
t={t}
|
||||
units={props.gist.units}
|
||||
/>}
|
||||
{type === 'points' && <Point
|
||||
pointName={id}
|
||||
partName={props.partName}
|
||||
draft={props.draft}
|
||||
t={t}
|
||||
/>}
|
||||
</Details>
|
||||
</Li>
|
||||
))
|
||||
}
|
||||
</Ul>
|
||||
</Details>
|
||||
</Li>
|
||||
</Ul>
|
||||
))}
|
||||
</Details>
|
||||
</Li>
|
||||
)
|
||||
}
|
||||
|
||||
export default XrayList
|
33
sites/shared/components/workbench/menu/xray/log.js
Normal file
33
sites/shared/components/workbench/menu/xray/log.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { Chevron } from 'shared/components/navigation/primary.js'
|
||||
import { Ul, Li, Details, Summary, SumButton, SumDiv, Deg } from 'shared/components/workbench/menu'
|
||||
|
||||
const ConsoleLog = props => (
|
||||
<Li>
|
||||
<Details>
|
||||
<Summary>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
<span>console.log()</span>
|
||||
</SumDiv>
|
||||
<Chevron />
|
||||
</Summary>
|
||||
<Ul>
|
||||
{['config', 'gist', 'draft'].map(it => (
|
||||
<Li key={it}>
|
||||
<SumButton onClick={() => console.log(it === 'config'
|
||||
? props.design.config
|
||||
: props[it]
|
||||
)}>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
<span>{it}</span>
|
||||
</SumDiv>
|
||||
</SumButton>
|
||||
</Li>
|
||||
))}
|
||||
</Ul>
|
||||
</Details>
|
||||
</Li>
|
||||
)
|
||||
|
||||
export default ConsoleLog
|
76
sites/shared/components/workbench/menu/xray/path-ops.js
Normal file
76
sites/shared/components/workbench/menu/xray/path-ops.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
import { Chevron } from 'shared/components/navigation/primary'
|
||||
import { Ul, Li, Details, Summary, SumDiv, NoSumDiv, Deg } from 'shared/components/workbench/menu'
|
||||
import { round } from 'shared/utils'
|
||||
import Point from './point'
|
||||
|
||||
const MoveLine = ({ op }) => <Point point={op.to} />
|
||||
const Curve = ({ op }) => ['cp1', 'cp2', 'to'].map(pnt => (
|
||||
<Li key={pnt}>
|
||||
<Details>
|
||||
<Summary>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
<span className="font-bold">{pnt}</span>
|
||||
</SumDiv>
|
||||
<Chevron />
|
||||
</Summary>
|
||||
<Point point={op[pnt]} />
|
||||
</Details>
|
||||
</Li>
|
||||
))
|
||||
const Close = () => (
|
||||
<p>Close</p>
|
||||
)
|
||||
|
||||
const XrayPathOp = ({ op }) => (
|
||||
<Li>
|
||||
{op.type === 'close'
|
||||
? (
|
||||
<NoSumDiv>
|
||||
<Deg />
|
||||
<span className="font-bold">{op.type}</span>
|
||||
</NoSumDiv>
|
||||
) : (
|
||||
<Details>
|
||||
<Summary>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
<span className="font-bold">{op.type}</span>
|
||||
<span>To</span>
|
||||
</SumDiv>
|
||||
<Chevron />
|
||||
</Summary>
|
||||
<Ul>
|
||||
{op.type === 'curve'
|
||||
? <Curve op={op} />
|
||||
: <MoveLine op={op} />
|
||||
}
|
||||
</Ul>
|
||||
</Details>
|
||||
)
|
||||
}
|
||||
</Li>
|
||||
)
|
||||
|
||||
const XrayPathOps = ({ ops=false }) => {
|
||||
if (!ops || ops.length < 1) return null
|
||||
|
||||
return (
|
||||
<Li>
|
||||
<Details>
|
||||
<Summary>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
<span className="font-bold">path.ops</span>
|
||||
</SumDiv>
|
||||
<Chevron />
|
||||
</Summary>
|
||||
<Ul>
|
||||
{ops.map(op => <XrayPathOp op={op} />)}
|
||||
</Ul>
|
||||
</Details>
|
||||
</Li>
|
||||
)
|
||||
}
|
||||
|
||||
export default XrayPathOps
|
34
sites/shared/components/workbench/menu/xray/path.js
Normal file
34
sites/shared/components/workbench/menu/xray/path.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { Ul, Li, Details, Summary, NoSumDiv, Deg } from 'shared/components/workbench/menu'
|
||||
import { formatMm } from 'shared/utils'
|
||||
import Attributes from './attributes'
|
||||
import Ops from './path-ops'
|
||||
|
||||
const XrayPath = ({ pathName, partName, draft, t, units }) => {
|
||||
const path = draft?.parts?.[partName]?.paths?.[pathName]
|
||||
|
||||
if (!path) return null
|
||||
return (
|
||||
<Ul>
|
||||
<Attributes attr={path.attributes} />
|
||||
<Li>
|
||||
<NoSumDiv>
|
||||
<Deg />
|
||||
<span className="font-bold mr-2">path.render =</span>
|
||||
<span>{JSON.stringify(path.render)}</span>
|
||||
</NoSumDiv>
|
||||
</Li>
|
||||
<Li>
|
||||
<NoSumDiv>
|
||||
<Deg />
|
||||
<span className="font-bold mr-2">path.length() =</span>
|
||||
<span dangerouslySetInnerHTML={{
|
||||
__html: formatMm(path.length(), units)
|
||||
}} />
|
||||
</NoSumDiv>
|
||||
</Li>
|
||||
<Ops ops={path.ops} />
|
||||
</Ul>
|
||||
)
|
||||
}
|
||||
|
||||
export default XrayPath
|
25
sites/shared/components/workbench/menu/xray/point.js
Normal file
25
sites/shared/components/workbench/menu/xray/point.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { Ul, Li, Details, Summary, NoSumDiv, Deg } from 'shared/components/workbench/menu'
|
||||
import { round } from 'shared/utils'
|
||||
import Attributes from './attributes'
|
||||
|
||||
const XrayPoint = ({ pointName, partName, draft, t }) => {
|
||||
const point = draft?.parts?.[partName]?.points?.[pointName]
|
||||
|
||||
return point
|
||||
? (
|
||||
<Ul>
|
||||
{['x', 'y'].map(coord => (
|
||||
<Li key={coord}>
|
||||
<NoSumDiv>
|
||||
<Deg />
|
||||
<span className="font-bold mr-2">{coord} =</span>
|
||||
<span>{round(point[coord])}</span>
|
||||
</NoSumDiv>
|
||||
</Li>
|
||||
))}
|
||||
<Attributes attr={point.attributes} t={t} />
|
||||
</Ul>
|
||||
) : null
|
||||
}
|
||||
|
||||
export default XrayPoint
|
19
sites/shared/components/workbench/menu/xray/reset.js
Normal file
19
sites/shared/components/workbench/menu/xray/reset.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { Li, SumButton, SumDiv, Deg } from 'shared/components/workbench/menu'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const ResetXray = props => {
|
||||
const { t } = useTranslation(['app'])
|
||||
|
||||
return (
|
||||
<Li>
|
||||
<SumButton onClick={() => props.updateGist(['_state', 'xray'], { enabled: true })}>
|
||||
<SumDiv>
|
||||
<Deg />
|
||||
<span>{ t(`reset`) }</span>
|
||||
</SumDiv>
|
||||
</SumButton>
|
||||
</Li>
|
||||
)
|
||||
}
|
||||
|
||||
export default ResetXray
|
Loading…
Add table
Add a link
Reference in a new issue