1
0
Fork 0

feat(shared): Better workbend nav. Closes #4272

This commit is contained in:
joostdecock 2023-06-15 09:23:49 +02:00
parent f2cc48039d
commit 7daad74964
18 changed files with 355 additions and 147 deletions

View file

@ -163,6 +163,16 @@ export const DesktopIcon = (props) => (
</IconWrapper>
)
export const DetailIcon = (props) => (
<IconWrapper {...props}>
<path d="M 12,3.8533453 V 20.547919 M 1.5305595,3.53056 H 22.46944 V 20.469444 H 1.5305595 Z" />
<path
d="m 14.415354,11.5 h 5.669292 m -5.669292,3 h 5.669292 m -5.669292,-6 h 5.669292"
strokeWidth={props.stroke / 2 || 1.1}
/>
</IconWrapper>
)
export const DiscordIcon = (props) => (
<IconWrapper {...props} stroke={0} fill>
<path d="M20.222 0c1.406 0 2.54 1.137 2.607 2.475V24l-2.677-2.273-1.47-1.338-1.604-1.398.67 2.205H3.71c-1.402 0-2.54-1.065-2.54-2.476V2.48C1.17 1.142 2.31.003 3.715.003h16.5L20.222 0zm-6.118 5.683h-.03l-.202.2c2.073.6 3.076 1.537 3.076 1.537-1.336-.668-2.54-1.002-3.744-1.137-.87-.135-1.74-.064-2.475 0h-.2c-.47 0-1.47.2-2.81.735-.467.203-.735.336-.735.336s1.002-1.002 3.21-1.537l-.135-.135s-1.672-.064-3.477 1.27c0 0-1.805 3.144-1.805 7.02 0 0 1 1.74 3.743 1.806 0 0 .4-.533.805-1.002-1.54-.468-2.14-1.404-2.14-1.404s.134.066.335.2h.06c.03 0 .044.015.06.03v.006c.016.016.03.03.06.03.33.136.66.27.93.4.466.202 1.065.403 1.8.536.93.135 1.996.2 3.21 0 .6-.135 1.2-.267 1.8-.535.39-.2.87-.4 1.397-.737 0 0-.6.936-2.205 1.404.33.466.795 1 .795 1 2.744-.06 3.81-1.8 3.87-1.726 0-3.87-1.815-7.02-1.815-7.02-1.635-1.214-3.165-1.26-3.435-1.26l.056-.02zm.168 4.413c.703 0 1.27.6 1.27 1.335 0 .74-.57 1.34-1.27 1.34-.7 0-1.27-.6-1.27-1.334.002-.74.573-1.338 1.27-1.338zm-4.543 0c.7 0 1.266.6 1.266 1.335 0 .74-.57 1.34-1.27 1.34-.7 0-1.27-.6-1.27-1.334 0-.74.57-1.338 1.27-1.338z" />
@ -306,8 +316,12 @@ export const LinkIcon = (props) => (
)
export const MeasureIcon = (props) => (
<IconWrapper stroke={0} fill={true} {...props}>
<path d="M 11.989586,0.20078596 A 1.8530064,1.8530064 0 0 0 10.41772,1.0808978 L 10.41636,1.0818822 2.6101375,12.790213 C 0.31198815,17.524132 3.4857314,23.088241 8.7331975,23.524984 5.2700505,21.502387 3.9559618,17.15205 5.7209237,13.552712 l 6.2686253,-9.4286018 0.102112,0.1534989 6.16666,9.2751139 c 1.764998,3.59936 0.450799,7.949697 -3.012384,9.972272 2.463292,-0.204996 4.667189,-1.610082 5.89114,-3.755908 1.223988,-2.145826 1.31071,-4.756633 0.231773,-6.978863 L 13.577211,1.0990319 13.576697,1.0987858 A 1.8530064,1.8530064 0 0 0 11.989439,0.20079698 Z m -0.0036,0.56327746 A 1.333887,1.333887 0 0 1 13.31991,2.0979723 1.333887,1.333887 0 0 1 11.985987,3.4318811 1.333887,1.333887 0 0 1 10.6521,2.0979723 1.333887,1.333887 0 0 1 11.985987,0.76406342 Z" />
<IconWrapper {...props}>
<path d="m 1.5,4.5 h 21 v 15 h -21 z" />
<path
d="m 3.5,19.316406 v -3.708984 z m 2.1035156,0 v -3.708984 z m 2.1035156,0 v -3.708984 z m 2.1035157,0 v -3.708984 z m 4.3789061,0 v -3.708984 z m 2.103516,0 v -3.708984 z m 2.103515,0 v -3.708984 z m 2.103516,0 V 15.607422 Z M 12,19.130859 v -5.082031 z m 0,-8.986328 V 5.0625001 Z M 5.6035156,8.5859371 v -3.708984 z m 12.7929684,0 v -3.708984 z"
strokeWidth={props.stroke / 2 || 1.1}
/>
</IconWrapper>
)
@ -385,6 +399,16 @@ export const PageSizeIcon = (props) => (
</IconWrapper>
)
export const PaperlessIcon = (props) => (
<IconWrapper {...props}>
<path d="M 1.5867219,1.58672 H 22.413278 V 22.41328 H 1.5867219 Z" />
<path
d="M 22.007133,15.500122 H 1.97864 m 20.028493,-7 H 1.97864 M 15.492887,1.9858756 V 22.014369 m -7,-20.0284934 V 22.014369"
strokeWidth={props.stroke / 2 || 1.1}
/>
</IconWrapper>
)
export const PluginIcon = (props) => (
<IconWrapper {...props}>
<path d="M14.25 6.087c0-.355.186-.676.401-.959.221-.29.349-.634.349-1.003 0-1.036-1.007-1.875-2.25-1.875s-2.25.84-2.25 1.875c0 .369.128.713.349 1.003.215.283.401.604.401.959v0a.64.64 0 01-.657.643 48.39 48.39 0 01-4.163-.3c.186 1.613.293 3.25.315 4.907a.656.656 0 01-.658.663v0c-.355 0-.676-.186-.959-.401a1.647 1.647 0 00-1.003-.349c-1.036 0-1.875 1.007-1.875 2.25s.84 2.25 1.875 2.25c.369 0 .713-.128 1.003-.349.283-.215.604-.401.959-.401v0c.31 0 .555.26.532.57a48.039 48.039 0 01-.642 5.056c1.518.19 3.058.309 4.616.354a.64.64 0 00.657-.643v0c0-.355-.186-.676-.401-.959a1.647 1.647 0 01-.349-1.003c0-1.035 1.008-1.875 2.25-1.875 1.243 0 2.25.84 2.25 1.875 0 .369-.128.713-.349 1.003-.215.283-.4.604-.4.959v0c0 .333.277.599.61.58a48.1 48.1 0 005.427-.63 48.05 48.05 0 00.582-4.717.532.532 0 00-.533-.57v0c-.355 0-.676.186-.959.401-.29.221-.634.349-1.003.349-1.035 0-1.875-1.007-1.875-2.25s.84-2.25 1.875-2.25c.37 0 .713.128 1.003.349.283.215.604.401.96.401v0a.656.656 0 00.658-.663 48.422 48.422 0 00-.37-5.36c-1.886.342-3.81.574-5.766.689a.578.578 0 01-.61-.58v0z" />
@ -415,12 +439,25 @@ export const RightIcon = (props) => (
</IconWrapper>
)
export const RocketIcon = (props) => (
<IconWrapper {...props}>
<path d="M15.59 14.37a6 6 0 01-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 006.16-12.12A14.98 14.98 0 009.631 8.41m5.96 5.96a14.926 14.926 0 01-5.841 2.58m-.119-8.54a6 6 0 00-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 00-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 01-2.448-2.448 14.9 14.9 0 01.06-.312m-2.24 2.39a4.493 4.493 0 00-1.757 4.306 4.493 4.493 0 004.306-1.758M16.5 9a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0z" />
</IconWrapper>
)
export const RssIcon = (props) => (
<IconWrapper {...props}>
<path d="M6 5c7.18 0 13 5.82 13 13M6 11a7 7 0 017 7m-6 0a1 1 0 11-2 0 1 1 0 012 0z" />
</IconWrapper>
)
export const SaIcon = (props) => (
<IconWrapper {...props}>
<circle cx="12" cy="12" r="5" />
<circle cx="12" cy="12" r="10" strokeDasharray="2 4" />
</IconWrapper>
)
export const SearchIcon = (props) => (
<IconWrapper {...props}>
<path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />

View file

@ -60,7 +60,7 @@ const NavIcons = ({ setModal, setView, setDense, dense, view }) => {
<>
<NavButton
onClick={() => setDense(!dense)}
label={t('workbench:collapse')}
label={t('workbench:viewMenu')}
extraClasses="text-success bg-neutral hover:bg-success hover:text-neutral"
>
{dense ? (
@ -71,14 +71,14 @@ const NavIcons = ({ setModal, setView, setDense, dense, view }) => {
</NavButton>
<NavButton
onClick={() => setView('draft')}
label={t('workbench:draft')}
label={t('workbench:draftPattern')}
active={view === 'draft'}
>
<OptionsIcon className={iconSize} />
</NavButton>
<NavButton
onClick={() => setView('test')}
label={t('workbench:test')}
label={t('workbench:testPattern')}
active={view === 'test'}
>
<BeakerIcon className={iconSize} />
@ -99,40 +99,40 @@ const NavIcons = ({ setModal, setView, setDense, dense, view }) => {
</NavButton>
<NavButton
onClick={() => setView('save')}
label={t('workbench:save')}
label={t('workbench:savePattern')}
active={view === 'save'}
>
<UploadIcon className={iconSize} />
</NavButton>
<NavButton
onClick={() => setView('export')}
label={t('workbench:export')}
label={t('workbench:exportPattern')}
active={view === 'export'}
>
<BriefcaseIcon className={iconSize} />
</NavButton>
<NavButton
onClick={() => setView('edit')}
label={t('workbench:edit')}
label={t('workbench:editSettings')}
active={view === 'edit'}
>
<CodeIcon className={iconSize} />
</NavButton>
<NavButton
onClick={() => setView('logs')}
label={t('workbench:logs')}
label={t('workbench:patternLogs')}
active={view === 'logs'}
>
<DocsIcon className={iconSize} />
</NavButton>
<NavButton
onClick={() => setView('inspect')}
label={t('workbench:inspector')}
label={t('workbench:patternInspector')}
active={view === 'inspect'}
>
<SearchIcon className={iconSize} />
</NavButton>
<NavButton label={t('workbench:help')} href="/docs/site/draft">
<NavButton label={t('workbench:docs')} href="/docs/site/draft">
<HelpIcon className={iconSize} />
</NavButton>
</>

View file

@ -3,6 +3,7 @@ import { useEffect, useState } from 'react'
import { useTranslation } from 'next-i18next'
import { useView } from 'shared/hooks/use-view.mjs'
import { useAccount } from 'shared/hooks/use-account.mjs'
import { useControlState } from 'shared/components/account/control.mjs'
// Dependencies
import { pluginTheme } from '@freesewing/plugin-theme'
import { pluginI18n } from '@freesewing/plugin-i18n'
@ -20,6 +21,7 @@ import { EditView, ns as editNs } from './views/edit/index.mjs'
import { TestView, ns as testNs } from 'shared/components/workbench/views/test/index.mjs'
import { ExportView, ns as exportNs } from 'shared/components/workbench/views/exporting/index.mjs'
import { LogView, ns as logNs } from 'shared/components/workbench/views/logs/index.mjs'
import { InspectView, ns as inspectNs } from 'shared/components/workbench/views/inspect/index.mjs'
export const ns = [
'account',
@ -46,15 +48,17 @@ const views = {
edit: EditView,
test: TestView,
logs: LogView,
inspect: InspectView,
}
const draftViews = ['draft']
const draftViews = ['draft', 'inspect']
export const Workbench = ({ design, Design, baseSettings, DynamicDocs, from }) => {
// Hooks
const { t, i18n } = useTranslation(ns)
const { language } = i18n
const { account } = useAccount()
const controlState = useControlState()
// State
const [view, setView] = useView()
@ -72,6 +76,17 @@ export const Workbench = ({ design, Design, baseSettings, DynamicDocs, from }) =
const update = {
settings: (path, val) => setSettings(objUpdate({ ...settings }, path, val)),
ui: (path, val) => setUi(objUpdate({ ...ui }, path, val)),
toggleSa: () => {
if (settings.sabool) {
const mm = settings.sa
setSettings(objUpdate({ ...settings }, ['sabool'], 0))
setSettings(objUpdate({ ...settings }, ['sa'], 0))
} else {
setSettings(objUpdate({ ...settings }, ['sabool'], 1))
setSettings(objUpdate({ ...settings }, ['sa'], settings.samm))
}
},
setControl: controlState.update,
}
// Don't bother without a Design

View file

@ -20,44 +20,6 @@ export const loadSettingsConfig = (settings, view) => {
dflt: 'react',
emoji: '🚀',
},
inspect: settings.renderer !== 'svg' && {
control: 4, // Show when control > 3
list: [0, 1],
choiceTitles: {
0: 'inspectNo',
1: 'inspectYes',
},
valueTitles: {
0: 'no',
1: 'yes',
},
dflt: 0,
emoji: '🔬',
},
view: {
control: 3,
list: ['draft', 'test', 'print', 'cut', 'save', 'export', 'logs'],
dflt: view,
emoji: '👀',
choiceTitles: {
draft: 'draft',
test: 'test',
print: 'print',
cut: 'cut',
save: 'save',
export: 'export',
logs: 'logs',
},
valueTitles: {
draft: 'draft',
test: 'test',
print: 'print',
cut: 'cut',
save: 'save',
export: 'export',
logs: 'logs',
},
},
}
uiSettings.control.list.forEach(

View file

@ -15,20 +15,7 @@ export const ControlSettingInput = (props) => {
)
}
const ViewInput = (props) => {
props.config.dflt = props.view
return (
<ListInput
{...props}
updateFunc={(path, newVal) => props.setView(newVal)}
current={props.view}
/>
)
}
export const inputs = {
renderer: ListInput,
inspect: BoolInput,
control: ControlSettingInput,
view: ViewInput,
}

View file

@ -3,7 +3,5 @@ import { ListValue } from '../shared/values.mjs'
export const values = {
renderer: ListValue,
inspect: ListValue,
control: ({ control }) => <Difficulty score={control} color="primary" />,
view: ListValue,
}

View file

@ -1,11 +1,87 @@
import { useState } from 'react'
import { PanZoomPattern as ShowPattern } from 'shared/components/workbench/pan-zoom-pattern.mjs'
import { InspectorPattern } from './inspector/pattern.mjs'
import { DraftMenu, ns as menuNs } from './menu.mjs'
import { objUpdate } from 'shared/utils.mjs'
import {
SettingsIcon,
PaperlessIcon,
SaIcon,
DesktopIcon,
RocketIcon,
BulletIcon,
MeasureIcon,
DetailIcon,
} from 'shared/components/icons.mjs'
export const ns = menuNs
const IconButton = ({ Icon, onClick, dflt = true }) => (
<button
onClick={onClick}
className={`text-${dflt ? 'neutral-content' : 'accent'} hover:text-secondary-focus`}
>
<Icon />
</button>
)
const Spacer = () => <span className="opacity-50">|</span>
export const DraftViewHeader = ({ update, settings, ui, control }) => {
return (
<div className="flex flex-row gap-4 py-2 w-full bg-neutral text-neutral-content items-center justify-center">
<div className="flex flex-row items-center gap-4">
<IconButton
Icon={SaIcon}
dflt={settings.sabool ? false : true}
onClick={() => update.toggleSa()}
/>
<IconButton
Icon={PaperlessIcon}
dflt={settings.paperless ? false : true}
onClick={() => update.settings(['paperless'], !settings.paperless)}
/>
<IconButton
Icon={DetailIcon}
dflt={settings.complete}
onClick={() =>
update.settings(
['complete'],
typeof settings.complete === 'undefined' ? 0 : settings.complete ? 0 : 1
)
}
/>
<IconButton
Icon={
settings.units !== 'imperial'
? MeasureIcon
: ({ className }) => <MeasureIcon className={`${className} rotate-180 w-6 h-6`} />
}
dflt={settings.units !== 'imperial'}
onClick={() =>
update.settings(['units'], settings.units === 'imperial' ? 'metric' : 'imperial')
}
/>
</div>
<Spacer />
<div className="flex flex-row items-center">
{[1, 2, 3, 4, 5].map((score) => (
<button onClick={() => update.setControl(score)} className="text-primary">
<BulletIcon fill={control >= score ? true : false} />
</button>
))}
</div>
<Spacer />
<div className="flex flex-row items-center gap-4">
<IconButton
Icon={RocketIcon}
dflt={ui.renderer !== 'svg'}
onClick={() => update.ui(['renderer'], ui.renderer === 'react' ? 'svg' : 'react')}
/>
</div>
</div>
)
}
export const DraftView = ({
design,
pattern,
@ -19,38 +95,6 @@ export const DraftView = ({
setView,
view,
}) => {
// State for inspector
const [inspect, setInspect] = useState({
show: {},
reveal: {},
})
const inspector = {
show: (data) => {
const newInspect = { ...inspect }
newInspect.show[data.id] = data
setInspect(newInspect)
},
hide: (id) => {
const newInspect = { ...inspect }
delete newInspect.show[id]
delete newInspect.reveal[id]
setInspect(newInspect)
},
reveal: (id) => {
const newInspect = { ...inspect }
if (newInspect.reveal[id]) delete newInspect.reveal[id]
else newInspect.reveal[id] = 1
setInspect(newInspect)
},
update: (path, val) => {
const newInspect = objUpdate({ ...inspect }, path, val)
setInspect(newInspect)
},
data: inspect,
pattern: pattern,
}
let output = null
let renderProps = false
if (ui.renderer === 'svg') {
@ -62,14 +106,19 @@ export const DraftView = ({
}
} else {
renderProps = pattern.getRenderProps()
output = ui.inspect ? (
<InspectorPattern {...{ renderProps, inspector }} />
) : (
<ShowPattern {...{ renderProps, inspector }} />
)
output = <ShowPattern {...{ renderProps }} />
}
return (
<div className="flex flex-col">
<DraftViewHeader
{...{
settings,
ui,
update,
control: account.control,
}}
/>
<div className="flex flex-row">
<div className="w-2/3 shrink-0 grow lg:p-4 sticky top-0">{output}</div>
<div className="w-1/3 shrink grow-0 lg:p-4 max-w-2xl h-screen overflow-scroll">
@ -84,7 +133,6 @@ export const DraftView = ({
language,
account,
DynamicDocs,
inspector,
renderProps,
view,
setView,
@ -92,5 +140,6 @@ export const DraftView = ({
/>
</div>
</div>
</div>
)
}

View file

@ -7,9 +7,8 @@ import {
ns as coreMenuNs,
} from 'shared/components/workbench/menus/core-settings/index.mjs'
import { UiSettings, ns as uiNs } from 'shared/components/workbench/menus/ui-settings/index.mjs'
import { Inspector, ns as inspectorNs } from './inspector/menu.mjs'
export const ns = [...coreMenuNs, ...designMenuNs, ...uiNs, inspectorNs]
export const ns = [...coreMenuNs, ...designMenuNs, ...uiNs]
export const DraftMenu = ({
design,
@ -20,7 +19,6 @@ export const DraftMenu = ({
language,
account,
DynamicDocs,
inspector = false,
renderProps,
view,
setView,
@ -39,7 +37,6 @@ export const DraftMenu = ({
return (
<nav className="grow mb-12">
{ui.inspect ? <Inspector {...menuProps} {...{ ui, inspector, renderProps }} /> : null}
<DesignOptions {...menuProps} />
<CoreSettings {...menuProps} />
<UiSettings {...menuProps} {...{ ui, view, setView }} />

View file

@ -0,0 +1,96 @@
import { useState } from 'react'
import { PanZoomPattern as ShowPattern } from 'shared/components/workbench/pan-zoom-pattern.mjs'
import { InspectorPattern } from './inspector/pattern.mjs'
import { DraftMenu, ns as menuNs } from './menu.mjs'
import { objUpdate } from 'shared/utils.mjs'
export const ns = menuNs
export const DraftView = ({
design,
pattern,
patternConfig,
settings,
ui,
update,
language,
account,
DynamicDocs,
setView,
view,
}) => {
// State for inspector
const [inspect, setInspect] = useState({
show: {},
reveal: {},
})
const inspector = {
show: (data) => {
const newInspect = { ...inspect }
newInspect.show[data.id] = data
setInspect(newInspect)
},
hide: (id) => {
const newInspect = { ...inspect }
delete newInspect.show[id]
delete newInspect.reveal[id]
setInspect(newInspect)
},
reveal: (id) => {
const newInspect = { ...inspect }
if (newInspect.reveal[id]) delete newInspect.reveal[id]
else newInspect.reveal[id] = 1
setInspect(newInspect)
},
update: (path, val) => {
const newInspect = objUpdate({ ...inspect }, path, val)
setInspect(newInspect)
},
data: inspect,
pattern: pattern,
}
let output = null
let renderProps = false
if (ui.renderer === 'svg') {
try {
const __html = pattern.render()
output = <div dangerouslySetInnerHTML={{ __html }} />
} catch (err) {
console.log(err)
}
} else {
renderProps = pattern.getRenderProps()
output = ui.inspect ? (
<InspectorPattern {...{ renderProps, inspector }} />
) : (
<ShowPattern {...{ renderProps, inspector }} />
)
}
return (
<div className="flex flex-row">
<div className="w-2/3 shrink-0 grow lg:p-4 sticky top-0">{output}</div>
<div className="w-1/3 shrink grow-0 lg:p-4 max-w-2xl h-screen overflow-scroll">
<DraftMenu
{...{
design,
pattern,
patternConfig,
settings,
ui,
update,
language,
account,
DynamicDocs,
inspector,
renderProps,
view,
setView,
}}
/>
</div>
</div>
)
}

View file

@ -0,0 +1,48 @@
import {
DesignOptions,
ns as designMenuNs,
} from 'shared/components/workbench/menus/design-options/index.mjs'
import {
CoreSettings,
ns as coreMenuNs,
} from 'shared/components/workbench/menus/core-settings/index.mjs'
import { UiSettings, ns as uiNs } from 'shared/components/workbench/menus/ui-settings/index.mjs'
import { Inspector, ns as inspectorNs } from './inspector/menu.mjs'
export const ns = [...coreMenuNs, ...designMenuNs, ...uiNs, inspectorNs]
export const DraftMenu = ({
design,
patternConfig,
settings,
ui,
update,
language,
account,
DynamicDocs,
inspector = false,
renderProps,
view,
setView,
}) => {
const control = account.control
const menuProps = {
design,
patternConfig,
settings,
update,
language,
account,
DynamicDocs,
control,
}
return (
<nav className="grow mb-12">
{ui.inspect ? <Inspector {...menuProps} {...{ ui, inspector, renderProps }} /> : null}
<DesignOptions {...menuProps} />
<CoreSettings {...menuProps} />
<UiSettings {...menuProps} {...{ ui, view, setView }} />
</nav>
)
}

View file

@ -1,6 +1,7 @@
import { useTranslation } from 'next-i18next'
import { PanZoomPattern } from 'shared/components/workbench/pan-zoom-pattern.mjs'
import { TestMenu, ns as menuNs } from './menu.mjs'
import { DraftViewHeader } from '../draft/index.mjs'
export const ns = menuNs
@ -24,6 +25,15 @@ export const TestView = ({
const title = t('testThing', { design, thing: t(settings.sample?.[settings.sample.type]) })
return (
<div className="flex flex-col">
<DraftViewHeader
{...{
settings,
ui,
update,
control: account.control,
}}
/>
<div className="flex flex-row ml-0 lg:ml-10">
<div className="w-2/3 shrink-0 grow lg:p-4 sticky top-0">
<h2 className="capitalize">{title}</h2>
@ -46,5 +56,6 @@ export const TestView = ({
/>
</div>
</div>
</div>
)
}

View file

@ -21,3 +21,11 @@ showMovableButtons: Buttons
partInfo: Pattern part info
pathInfo: Path info
part: Pattern part
draftPattern: Draft pattern
testPattern: Test pattern
savePattern: Save pattern
exportPattern: Export pattern
editSettings: Edit settings
patternLogs: Pattern logs
patternInspector: Pattern Inspector
docs: Documentation