wip(lab): Work on workbench
- Moved the various tabs on the draft view to their own views. - Renames modes to views - Started to group various aspects of the workbench state under `_state` in the gist to prevent it from getting all mixed up with the core settings. - Updated events title to make it more clear not all events might be present - Removed valid state in measurements input since it was only getting updated on keyboard input but now when preloading measurements (which it does now)
This commit is contained in:
parent
bbb2b2c23f
commit
3653700572
12 changed files with 155 additions and 156 deletions
|
@ -121,7 +121,7 @@ const DefaultLayout = ({
|
|||
sm:flex-row-reverse
|
||||
${workbench && collapsePrimaryNav
|
||||
? 'sm:px-0 sm:w-16'
|
||||
: 'sm:px-1 md:px-4 lg:px-8 xl:px-16 2xl:px-32 sm:w-[38.2%]'
|
||||
: 'sm:px-1 md:px-4 lg:px-8 sm:w-[38.2%]'
|
||||
}
|
||||
`}>
|
||||
{workbench && (
|
||||
|
@ -144,7 +144,7 @@ const DefaultLayout = ({
|
|||
sm:px-1 md:px-4 lg:px-8
|
||||
${workbench && collapsePrimaryNav
|
||||
? ''
|
||||
: 'max-w-61.8% xl:px-16 2xl:px-32'
|
||||
: 'max-w-61.8%'
|
||||
}
|
||||
`}>
|
||||
<div className={workbench ? '' : "max-w-5xl"}>
|
||||
|
@ -164,7 +164,7 @@ const DefaultLayout = ({
|
|||
sm:flex-row
|
||||
${workbench && collapseAltMenu
|
||||
? 'sm:px-0 sm:w-16'
|
||||
: 'sm:px-1 md:px-4 lg:px-8 xl:px-16 2xl:px-32 sm:w-[38.2%]'
|
||||
: 'sm:px-1 md:px-4 lg:px-8 sm:w-[38.2%]'
|
||||
}
|
||||
`}>
|
||||
<div className={`hidden sm:flex`}>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Robot from 'shared/components/robot/index.js'
|
||||
import Events from './events.js'
|
||||
import Events from '../events.js'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const Error = props => {
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
import Markdown from 'react-markdown'
|
||||
import { linkClasses } from 'shared/components/navigation/primary.js'
|
||||
|
||||
const eventBlock = events => events.join(" \n")
|
||||
|
||||
const EventGroup = ({ type='info', events=[] }) => events.length > 0 ? (
|
||||
<div className="">
|
||||
<h3 className="capitalize" id={`events-${type}`}>{type}</h3>
|
||||
<div className="mdx">
|
||||
<Markdown>{eventBlock(events)}</Markdown>
|
||||
</div>
|
||||
</div>
|
||||
) : null
|
||||
|
||||
const order = [
|
||||
'error',
|
||||
'warning',
|
||||
'info',
|
||||
'debug'
|
||||
]
|
||||
|
||||
const Events = props => (
|
||||
<div className="flex flex-col">
|
||||
<ul className="flex flex-row row-wrap">
|
||||
{order.map(type => (
|
||||
<li key={type} className="">
|
||||
<a href={`#events-${type}`} className={`text-secondary font-bold capitalize text-xl`}>{type}</a>
|
||||
{type === 'debug' ? '' : <span className="px-2 font-bold">|</span>}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{order.map(type => <EventGroup type={type} events={props.events[type]} />)}
|
||||
</div>
|
||||
)
|
||||
|
||||
export default Events
|
|
@ -1,25 +1,12 @@
|
|||
import React, { useState } from 'react'
|
||||
import SvgWrapper from './svg-wrapper'
|
||||
import Error from './error.js'
|
||||
import Events from './events.js'
|
||||
import Json from 'shared/components/json.js'
|
||||
import Yaml from 'shared/components/yaml.js'
|
||||
import { capitalize } from 'shared/utils.js'
|
||||
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch"
|
||||
|
||||
const tabClasses = active => `
|
||||
tab tab-bordered font-bold text-4xl pb-12 capitalize
|
||||
${active && 'text-base-content tab-active'}
|
||||
`
|
||||
|
||||
const Wrap = props => <div className="max-w-screen-xl m-auto">{props.children}</div>
|
||||
|
||||
const LabDraft = props => {
|
||||
const { app, draft, pattern, gist, updateGist, unsetGist } = props
|
||||
if (!draft) return null
|
||||
|
||||
const [tab, setTab] = useState(props.pattern.config.name)
|
||||
|
||||
if (gist?.renderer === 'svg') {
|
||||
// Render as SVG
|
||||
let svg
|
||||
|
@ -41,24 +28,14 @@ const LabDraft = props => {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<div className="tabs my-8 mx-auto justify-center">
|
||||
{[props.pattern.config.name, 'events', 'yaml', 'json'].map(name => <button
|
||||
key={name}
|
||||
onClick={() => setTab(name)}
|
||||
className={tabClasses(tab === name)}
|
||||
>{name}</button>)}
|
||||
</div>
|
||||
{tab === 'events' && <Wrap><Events events={draft.events} /></Wrap>}
|
||||
{tab === 'json' && <Wrap><Json>{JSON.stringify(props.gist, null, 2)}</Json></Wrap>}
|
||||
{tab === 'yaml' && <Wrap><Yaml json={JSON.stringify(props.gist, null, 2)} /></Wrap>}
|
||||
{tab === props.pattern.config.name && <SvgWrapper
|
||||
<SvgWrapper
|
||||
draft={draft}
|
||||
patternProps={patternProps}
|
||||
gist={gist}
|
||||
updateGist={updateGist}
|
||||
unsetGist={unsetGist}
|
||||
app={app}
|
||||
/>}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
44
packages/freesewing.shared/components/workbench/events.js
Normal file
44
packages/freesewing.shared/components/workbench/events.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
import Markdown from 'react-markdown'
|
||||
import { linkClasses } from 'shared/components/navigation/primary.js'
|
||||
|
||||
const eventBlock = events => events.join(" \n")
|
||||
|
||||
const EventGroup = ({ type='info', events=[] }) => events.length > 0 ? (
|
||||
<div className="">
|
||||
<h3 className="capitalize" id={`events-${type}`}>{type}</h3>
|
||||
<div className="mdx ml-2">
|
||||
<Markdown>{eventBlock(events)}</Markdown>
|
||||
</div>
|
||||
</div>
|
||||
) : null
|
||||
|
||||
const order = [
|
||||
'error',
|
||||
'warning',
|
||||
'info',
|
||||
'debug'
|
||||
]
|
||||
const Events = props => (
|
||||
<div className="max-w-screen-xl m-auto">
|
||||
<div className="flex flex-col">
|
||||
<ul className="flex flex-row row-wrap">
|
||||
{order.map(type => (props.draft.events[type].length > 0)
|
||||
? (
|
||||
<li key={type} className="">
|
||||
<a href={`#events-${type}`} className={`text-secondary font-bold capitalize text-xl`}>{type}</a>
|
||||
{type === 'debug' ? '' : <span className="px-2 font-bold">|</span>}
|
||||
</li>
|
||||
) : (
|
||||
<li key={type} className="text-base-content font-bold capitalize text-xl">
|
||||
<span className="opacity-50">{type}</span>
|
||||
{type === 'debug' ? '' : <span className="px-2 font-bold">|</span>}
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
{order.map(type => <EventGroup type={type} events={props.draft.events[type]} />)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default Events
|
|
@ -13,15 +13,17 @@ const MeasurementInput = ({ m, gist, app, updateMeasurements }) => {
|
|||
const { t } = useTranslation(['app', 'measurements'])
|
||||
const prefix = (app.site === 'org') ? '' : 'https://freesewing.org'
|
||||
const title = t(`measurements:${m}`)
|
||||
const isValid = input => {
|
||||
if (input === '') return ''
|
||||
return !isNaN(input)
|
||||
}
|
||||
|
||||
const isValValid = val => (typeof val === 'undefined' || val === '')
|
||||
? null
|
||||
: !isNaN(val)
|
||||
const isValid = (newVal) => (typeof newVal === 'undefined')
|
||||
? isValValid(gist?.measurements?.[m])
|
||||
: isValValid(newVal)
|
||||
|
||||
const update = evt => {
|
||||
setVal(evt.target.value)
|
||||
const ok = isValid(evt.target.value)
|
||||
console.log({ok})
|
||||
if (ok) {
|
||||
setValid(true)
|
||||
updateMeasurements(evt.target.value*10, m)
|
||||
|
@ -29,10 +31,6 @@ const MeasurementInput = ({ m, gist, app, updateMeasurements }) => {
|
|||
}
|
||||
|
||||
const [val, setVal] = useState(gist?.measurements?.[m] || '')
|
||||
const [valid, setValid] = useState(typeof gist?.measurements?.[m] === 'undefined'
|
||||
? '' :
|
||||
isValid(gist.measurements[m])
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (gist?.measurements?.[m]) setVal(gist.measurements[m]/10)
|
||||
|
@ -40,6 +38,7 @@ const MeasurementInput = ({ m, gist, app, updateMeasurements }) => {
|
|||
|
||||
if (!m) return null
|
||||
|
||||
const valid = isValid()
|
||||
return (
|
||||
<div className="form-control mb-2" key={`wrap-${m}`}>
|
||||
<label className="label">
|
||||
|
@ -59,20 +58,19 @@ const MeasurementInput = ({ m, gist, app, updateMeasurements }) => {
|
|||
type="text"
|
||||
placeholder={title}
|
||||
className={`
|
||||
input input-lg input-bordered grow text-base-content
|
||||
${valid === false && 'input-error'}
|
||||
${valid === true && 'input-success'}
|
||||
border-r-0
|
||||
input input-lg input-bordered grow text-base-content border-r-0
|
||||
${isValid() === false && 'input-error'}
|
||||
${isValid() === true && 'input-success'}
|
||||
`}
|
||||
value={val}
|
||||
onChange={update}
|
||||
/>
|
||||
<span role="img" className={`bg-transparent border-y
|
||||
${valid === false && 'border-error text-neutral-content'}
|
||||
${valid === true && 'border-success text-neutral-content'}
|
||||
${valid === '' && 'border-base-200 text-base-content'}
|
||||
${isValid() === false && 'border-error text-neutral-content'}
|
||||
${isValid() === true && 'border-success text-neutral-content'}
|
||||
${isValid() === null && 'border-base-200 text-base-content'}
|
||||
`}>
|
||||
{valid === ''
|
||||
{isValid() === null
|
||||
? ''
|
||||
: valid
|
||||
? '👍'
|
||||
|
@ -82,7 +80,7 @@ const MeasurementInput = ({ m, gist, app, updateMeasurements }) => {
|
|||
<span className={`
|
||||
${valid === false && 'bg-error text-neutral-content'}
|
||||
${valid === true && 'bg-success text-neutral-content'}
|
||||
${valid === '' && 'bg-base-200 text-base-content'}
|
||||
${valid === null && 'bg-base-200 text-base-content'}
|
||||
`}>
|
||||
cm
|
||||
</span>
|
||||
|
|
9
packages/freesewing.shared/components/workbench/json.js
Normal file
9
packages/freesewing.shared/components/workbench/json.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import Json from 'shared/components/json.js'
|
||||
|
||||
const GistAsJson = props => (
|
||||
<div className="max-w-screen-xl m-auto">
|
||||
<Json>{JSON.stringify(props.gist, null, 2)}</Json>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default GistAsJson
|
|
@ -44,19 +44,19 @@ const WorkbenchMeasurements = ({ app, pattern, gist, updateGist }) => {
|
|||
const inputProps = { app, updateMeasurements, gist }
|
||||
|
||||
return (
|
||||
<div className="m-auto max-w-prose">
|
||||
<div className="m-auto max-w-2xl">
|
||||
<h1>
|
||||
<span className='capitalize mr-4 opacity-70'>
|
||||
{pattern.config.name}:
|
||||
</span>
|
||||
{t('measurements')}
|
||||
</h1>
|
||||
<details open>
|
||||
<summary><h2 className="inline-block">{t('cfp:preloadMeasurements')}</h2></summary>
|
||||
<details open className="my-2">
|
||||
<summary><h2 className="inline pl-1">{t('cfp:preloadMeasurements')}</h2></summary>
|
||||
<div className="ml-2 pl-4 border-l-2">
|
||||
{Object.keys(groups).map(group => (
|
||||
<details key={group}>
|
||||
<summary><h3 className="inline-block">{t(group)}</h3></summary>
|
||||
<summary><h3 className="inline pl-1">{t(group)}</h3></summary>
|
||||
<div className="ml-2 pl-4 border-l-2">
|
||||
{Object.keys(icons).map(type => (
|
||||
<React.Fragment key={type}>
|
||||
|
@ -86,8 +86,8 @@ const WorkbenchMeasurements = ({ app, pattern, gist, updateGist }) => {
|
|||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><h2 className="inline-block">{t('cfp:enterMeasurements')}</h2></summary>
|
||||
<details className="my-2">
|
||||
<summary><h2 className="inline pl-2">{t('cfp:enterMeasurements')}</h2></summary>
|
||||
<div className="ml-2 pl-4 border-l-2">
|
||||
{pattern.config.measurements && (
|
||||
<>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { linkClasses, Chevron } from 'shared/components/navigation/primary.js'
|
||||
import ModesMenu from './modes.js'
|
||||
import ViewMenu from './view.js'
|
||||
import DesignOptions from './design-options'
|
||||
import CoreSettings from './core-settings'
|
||||
import Xray from './xray'
|
||||
|
@ -84,8 +84,8 @@ export const SecText = props => props.raw
|
|||
const WorkbenchMenu = props => {
|
||||
return (
|
||||
<nav className="smmax-w-96 grow mb-12">
|
||||
<ModesMenu {...props} />
|
||||
{props.mode === 'draft' && (
|
||||
<ViewMenu {...props} />
|
||||
{props.gist?._state?.view === 'draft' && (
|
||||
<>
|
||||
<DesignOptions {...props} />
|
||||
<CoreSettings {...props} />
|
||||
|
|
|
@ -3,28 +3,43 @@ import OptionsIcon from 'shared/components/icons/options.js'
|
|||
import { linkClasses, Chevron } from 'shared/components/navigation/primary.js'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const Modes = props => {
|
||||
const View = props => {
|
||||
const { t } = useTranslation(['app'])
|
||||
const entries = [
|
||||
{
|
||||
name: 'measurements',
|
||||
title: t('measurements'),
|
||||
onClick: () => props.setMode('measurements')
|
||||
onClick: () => props.updateGist(['_state', 'view'], 'measurements')
|
||||
},
|
||||
{
|
||||
name: 'draft',
|
||||
title: t('draftPattern', { pattern: props.pattern.config.name }),
|
||||
onClick: () => props.setMode('draft')
|
||||
onClick: () => props.updateGist(['_state', 'view'], 'draft')
|
||||
},
|
||||
{
|
||||
name: 'test',
|
||||
title: t('testPattern', { pattern: props.pattern.config.name }),
|
||||
onClick: () => props.setMode('test')
|
||||
onClick: () => props.updateGist(['_state', 'view'], 'test')
|
||||
},
|
||||
{
|
||||
name: 'export',
|
||||
title: t('export'),
|
||||
onClick: () => props.setMode('export')
|
||||
onClick: () => props.updateGist(['_state', 'view'], 'export')
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
title: t('events'),
|
||||
onClick: () => props.updateGist(['_state', 'view'], 'events')
|
||||
},
|
||||
{
|
||||
name: 'yaml',
|
||||
title: t('YAML'),
|
||||
onClick: () => props.updateGist(['_state', 'view'], 'yaml')
|
||||
},
|
||||
{
|
||||
name: 'json',
|
||||
title: t('JSON'),
|
||||
onClick: () => props.updateGist(['_state', 'view'], 'json')
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -40,7 +55,7 @@ const Modes = props => {
|
|||
`}>
|
||||
<span className="text-secondary-focus mr-4"><MenuIcon /></span>
|
||||
<span className={`grow ${linkClasses} hover:cursor-resize`}>
|
||||
{t('modes')}
|
||||
{t('view')}
|
||||
</span>
|
||||
<Chevron />
|
||||
</summary>
|
||||
|
@ -55,21 +70,21 @@ const Modes = props => {
|
|||
sm:hover:border-secondary-focus
|
||||
text-left
|
||||
capitalize
|
||||
${entry.name === props.mode
|
||||
${entry.name === props.gist?._state?.view
|
||||
? 'text-secondary border-secondary sm:text-secondary-focus sm:border-secondary-focus'
|
||||
: 'text-base-content sm:text-neutral-content'
|
||||
}
|
||||
`} onClick={entry.onClick}>
|
||||
<span className={`
|
||||
text-3xl mr-2 inline-block p-0 leading-3
|
||||
${entry.name === props.mode
|
||||
${entry.name === props.gist?._state?.view
|
||||
? 'text-secondary sm:text-secondary-focus translate-y-1'
|
||||
: 'translate-y-3'
|
||||
}
|
||||
`}>
|
||||
{entry.name === props.mode ? <>•</> : <>°</>}
|
||||
{entry.name === props.gist?._state?.view ? <>•</> : <>°</>}
|
||||
</span>
|
||||
<span className={entry.name === props.mode ? 'font-bold' : ''}>
|
||||
<span className={entry.name === props.gist?._state?.view ? 'font-bold' : ''}>
|
||||
{ entry.title }
|
||||
</span>
|
||||
</button>
|
||||
|
@ -80,4 +95,4 @@ const Modes = props => {
|
|||
)
|
||||
}
|
||||
|
||||
export default Modes
|
||||
export default View
|
9
packages/freesewing.shared/components/workbench/yaml.js
Normal file
9
packages/freesewing.shared/components/workbench/yaml.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import Yaml from 'shared/components/yaml.js'
|
||||
|
||||
const GistAsYaml = props => (
|
||||
<div className="max-w-screen-xl m-auto">
|
||||
<Yaml json={JSON.stringify(props.gist, null, 2)} />
|
||||
</div>
|
||||
)
|
||||
|
||||
export default GistAsYaml
|
|
@ -2,14 +2,29 @@ import { useState, useEffect } from 'react'
|
|||
import useLocalStorage from 'shared/hooks/useLocalStorage.js'
|
||||
import Layout from 'shared/components/layouts/default'
|
||||
import Menu from 'shared/components/workbench/menu/index.js'
|
||||
import Measurements, { Input } from 'shared/components/workbench/measurements/index.js'
|
||||
import LabDraft from 'shared/components/workbench/draft/index.js'
|
||||
import set from 'lodash.set'
|
||||
import unset from 'lodash.unset'
|
||||
import defaultSettings from 'shared/components/workbench/default-settings.js'
|
||||
import DraftError from 'shared/components/workbench/draft/error.js'
|
||||
import theme from 'pkgs/plugin-theme/src/index.js'
|
||||
|
||||
// Views
|
||||
import Measurements, { Input } from 'shared/components/workbench/measurements/index.js'
|
||||
import LabDraft from 'shared/components/workbench/draft/index.js'
|
||||
import GistAsJson from 'shared/components/workbench/json.js'
|
||||
import GistAsYaml from 'shared/components/workbench/yaml.js'
|
||||
import DraftEvents from 'shared/components/workbench/events.js'
|
||||
|
||||
const views = {
|
||||
measurements: Measurements,
|
||||
draft: LabDraft,
|
||||
test: () => <p>TODO</p>,
|
||||
export: () => <p>TODO</p>,
|
||||
events: DraftEvents,
|
||||
yaml: GistAsYaml,
|
||||
json: GistAsJson,
|
||||
welcome: () => <p>TODO</p>,
|
||||
}
|
||||
|
||||
// Generates a default pattern gist to start from
|
||||
const defaultGist = (pattern, locale='en') => {
|
||||
|
@ -33,30 +48,24 @@ const hasRequiredMeasurements = (pattern, gist) => {
|
|||
|
||||
/*
|
||||
* This component wraps the workbench and is in charge of
|
||||
* keeping the mode & gist state, which will trickly down
|
||||
* keeping the gist state, which will trickly down
|
||||
* to all workbench subcomponents
|
||||
*
|
||||
* mode: What to display (draft, sample, measurements, ...)
|
||||
* gist: The runtime pattern configuration
|
||||
*/
|
||||
const WorkbenchWrapper = ({ app, pattern }) => {
|
||||
|
||||
// State for display mode and gist
|
||||
const [mode, setMode] = useState('measurements')
|
||||
const [gist, setGist] = useLocalStorage('gist', defaultGist(pattern, app.locale))
|
||||
// State for gist
|
||||
const [gist, setGist] = useLocalStorage(`${pattern.config.name}_gist`, defaultGist(pattern, app.locale))
|
||||
|
||||
// If we don't have the requiremed measurements,
|
||||
// force mode to measurements
|
||||
// If we don't have the required measurements,
|
||||
// force view to measurements
|
||||
useEffect(() => {
|
||||
if (
|
||||
mode !== 'measurements'
|
||||
gist?._state?.view !== 'measurements'
|
||||
&& !hasRequiredMeasurements(pattern, gist)
|
||||
) setMode('measurements')
|
||||
) updateGist(['_state', 'view'], 'measurements')
|
||||
})
|
||||
|
||||
/*
|
||||
* Update gist method. See lodash.set
|
||||
*/
|
||||
// Helper methods to manage the gist state
|
||||
const updateGist = (path, content) => {
|
||||
const newGist = {...gist}
|
||||
set(newGist, path, content)
|
||||
|
@ -68,10 +77,9 @@ const WorkbenchWrapper = ({ app, pattern }) => {
|
|||
setGist(newGist)
|
||||
}
|
||||
|
||||
// Generate the draft here so we can pass it to both Menu
|
||||
// and LabDraft
|
||||
// Generate the draft here so we can pass it down
|
||||
let draft = false
|
||||
if (mode === 'draft') {
|
||||
if (['draft', 'events'].indexOf(gist?._state?.view) !== -1) {
|
||||
draft = new pattern(gist)
|
||||
if (gist?.renderer === 'svg') draft.use(theme)
|
||||
try { draft.draft() }
|
||||
|
@ -81,48 +89,23 @@ const WorkbenchWrapper = ({ app, pattern }) => {
|
|||
}
|
||||
}
|
||||
|
||||
// Props to pass down
|
||||
const componentProps = { app, pattern, gist, updateGist, unsetGist, setGist, draft }
|
||||
// Required props for layout
|
||||
const layoutProps = {
|
||||
app: app,
|
||||
noSearch: true,
|
||||
workbench: true,
|
||||
AltMenu: <Menu
|
||||
app={app}
|
||||
pattern={pattern}
|
||||
mode={mode}
|
||||
setMode={setMode}
|
||||
gist={gist}
|
||||
updateGist={updateGist}
|
||||
unsetGist={unsetGist}
|
||||
setGist={setGist}
|
||||
draft={draft}
|
||||
/>
|
||||
AltMenu: <Menu {...componentProps }/>
|
||||
}
|
||||
|
||||
const Component = views[gist?._state?.view]
|
||||
? views[gist._state.view]
|
||||
: views.welcome
|
||||
|
||||
|
||||
return (
|
||||
<Layout {...layoutProps}>
|
||||
{mode === 'measurements' && (
|
||||
<Measurements
|
||||
app={app}
|
||||
pattern={pattern}
|
||||
gist={gist}
|
||||
updateGist={updateGist}
|
||||
/>
|
||||
)}
|
||||
{mode === 'draft' && (
|
||||
<LabDraft
|
||||
app={app}
|
||||
pattern={pattern}
|
||||
draft={draft}
|
||||
gist={gist}
|
||||
updateGist={updateGist}
|
||||
unsetGist={unsetGist}
|
||||
/>
|
||||
)}
|
||||
return <Layout {...layoutProps}>
|
||||
<Component {...componentProps} />
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
export default WorkbenchWrapper
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue