2023-06-20 17:30:18 +02:00
|
|
|
// Hooks
|
2023-07-26 16:38:51 -06:00
|
|
|
import { useEffect, useState, useCallback, useMemo } from 'react'
|
2023-06-20 17:30:18 +02:00
|
|
|
import { useTranslation } from 'next-i18next'
|
|
|
|
import { useView } from 'shared/hooks/use-view.mjs'
|
|
|
|
import { usePatternSettings } from 'shared/hooks/use-pattern-settings.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'
|
2023-09-09 17:58:44 +02:00
|
|
|
import { objUpdate, hasRequiredMeasurements, nsMerge } from 'shared/utils.mjs'
|
2023-06-20 17:30:18 +02:00
|
|
|
// Components
|
2023-09-09 17:58:44 +02:00
|
|
|
import { Header, ns as headerNs } from 'site/components/header/index.mjs'
|
2023-06-20 17:30:18 +02:00
|
|
|
import { WorkbenchHeader } from './header.mjs'
|
|
|
|
import { ErrorView } from 'shared/components/error/view.mjs'
|
2023-06-29 14:35:46 +00:00
|
|
|
import { MobileMenubar } from './menus/mobile-menubar.mjs'
|
2023-06-20 17:30:18 +02:00
|
|
|
// Views
|
|
|
|
import { DraftView, ns as draftNs } from 'shared/components/workbench/views/draft/index.mjs'
|
|
|
|
import { SaveView, ns as saveNs } from 'shared/components/workbench/views/save/index.mjs'
|
|
|
|
import { PrintView, ns as printNs } from 'shared/components/workbench/views/print/index.mjs'
|
|
|
|
import { CutView, ns as cutNs } from 'shared/components/workbench/views/cut/index.mjs'
|
|
|
|
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'
|
|
|
|
import { MeasiesView, ns as measiesNs } from 'shared/components/workbench/views/measies/index.mjs'
|
|
|
|
|
2023-09-10 11:08:31 +02:00
|
|
|
export const ns = nsMerge(
|
2023-06-20 17:30:18 +02:00
|
|
|
'account',
|
|
|
|
'workbench',
|
2023-09-07 10:29:19 +02:00
|
|
|
'flag',
|
2023-09-06 10:30:52 +02:00
|
|
|
'plugin-annotations',
|
2023-09-10 11:08:31 +02:00
|
|
|
draftNs,
|
|
|
|
saveNs,
|
|
|
|
printNs,
|
|
|
|
cutNs,
|
|
|
|
editNs,
|
|
|
|
testNs,
|
|
|
|
exportNs,
|
|
|
|
logNs,
|
|
|
|
inspectNs,
|
|
|
|
measiesNs,
|
|
|
|
headerNs
|
|
|
|
)
|
2023-06-20 17:30:18 +02:00
|
|
|
|
|
|
|
const defaultUi = {
|
|
|
|
renderer: 'react',
|
2023-09-09 17:58:44 +02:00
|
|
|
kiosk: false,
|
2023-06-20 17:30:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const views = {
|
|
|
|
draft: DraftView,
|
|
|
|
print: PrintView,
|
|
|
|
cut: CutView,
|
|
|
|
export: ExportView,
|
|
|
|
edit: EditView,
|
|
|
|
test: TestView,
|
|
|
|
logs: LogView,
|
|
|
|
inspect: InspectView,
|
|
|
|
measies: MeasiesView,
|
|
|
|
}
|
|
|
|
|
|
|
|
const draftViews = ['draft', 'inspect']
|
|
|
|
|
2023-09-09 17:58:44 +02:00
|
|
|
const kioskClasses = 'z-30 w-screen h-screen fixed top-0 left-0 bg-base-100'
|
|
|
|
|
2023-09-27 12:04:26 +02:00
|
|
|
export const Workbench = ({ design, Design, DynamicDocs, saveAs = false, preload = false }) => {
|
2023-06-20 17:30:18 +02:00
|
|
|
// Hooks
|
2023-07-02 19:41:44 +02:00
|
|
|
const { t, i18n } = useTranslation([...ns, design])
|
2023-06-20 17:30:18 +02:00
|
|
|
const { language } = i18n
|
|
|
|
const { account } = useAccount()
|
|
|
|
const controlState = useControlState()
|
|
|
|
|
|
|
|
// State
|
2023-06-29 14:35:46 +00:00
|
|
|
const [view, _setView] = useView()
|
2023-09-27 12:04:26 +02:00
|
|
|
const [settings, setSettings] = usePatternSettings(preload)
|
2023-06-20 17:30:18 +02:00
|
|
|
const [ui, setUi] = useState(defaultUi)
|
|
|
|
const [error, setError] = useState(false)
|
|
|
|
const [mounted, setMounted] = useState(false)
|
|
|
|
const [missingMeasurements, setMissingMeasurements] = useState(false)
|
2023-09-27 12:04:26 +02:00
|
|
|
const [preloaded, setPreloaded] = useState(0)
|
2023-06-20 17:30:18 +02:00
|
|
|
|
2023-06-29 14:35:46 +00:00
|
|
|
const setView = useCallback(
|
|
|
|
(newView) => {
|
2023-06-29 17:29:13 +00:00
|
|
|
// hacky little way to scroll to the top but keep the menu hidden if it was hidden
|
2023-06-29 14:35:46 +00:00
|
|
|
const endScroll = Math.min(window.scrollY, 21)
|
|
|
|
window.scrollTo({ top: 0, behavior: 'instant' })
|
|
|
|
_setView(newView)
|
|
|
|
window.scroll({ top: endScroll })
|
|
|
|
},
|
|
|
|
[_setView]
|
|
|
|
)
|
|
|
|
|
2023-06-20 14:27:00 -05:00
|
|
|
// set mounted on mount
|
|
|
|
useEffect(() => setMounted(true), [setMounted])
|
|
|
|
|
2023-09-27 12:04:26 +02:00
|
|
|
// Handle preload
|
|
|
|
useEffect(() => {
|
|
|
|
if (preload) {
|
|
|
|
// This will run a few times while variouos things bootstrap
|
|
|
|
// but should not run after that.
|
|
|
|
if (preload.settings && preloaded < 3) {
|
|
|
|
console.log('preloading settings', { mounted, preloaded })
|
|
|
|
setSettings(preload.settings)
|
|
|
|
setView('draft')
|
|
|
|
setPreloaded(preloaded + 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, [preload])
|
|
|
|
|
2023-06-20 17:30:18 +02:00
|
|
|
useEffect(() => {
|
2023-06-20 14:27:00 -05:00
|
|
|
// protect against loops
|
|
|
|
if (!mounted) return
|
|
|
|
|
2023-06-28 13:04:45 -05:00
|
|
|
setMounted(true)
|
2023-06-20 17:30:18 +02:00
|
|
|
const [ok, missing] = hasRequiredMeasurements(Design, settings.measurements)
|
|
|
|
if (ok) setMissingMeasurements(false)
|
2023-06-20 14:27:00 -05:00
|
|
|
// Force the measurements view if we have missing measurements
|
2023-06-20 17:30:18 +02:00
|
|
|
else {
|
2023-06-20 14:27:00 -05:00
|
|
|
setMissingMeasurements(missing)
|
2023-06-20 17:30:18 +02:00
|
|
|
if (view !== 'measies') setView('measies')
|
|
|
|
}
|
2023-06-22 10:52:02 -05:00
|
|
|
}, [Design, settings.measurements, mounted, view, setView])
|
2023-06-20 17:30:18 +02:00
|
|
|
|
|
|
|
// Helper methods for settings/ui updates
|
2023-07-26 16:38:51 -06:00
|
|
|
const update = useMemo(
|
|
|
|
() => ({
|
|
|
|
settings: (path, val) =>
|
|
|
|
setSettings((curSettings) => objUpdate({ ...curSettings }, path, val)),
|
|
|
|
ui: (path, val) => setUi((curUi) => objUpdate({ ...curUi }, path, val)),
|
|
|
|
toggleSa: () => {
|
|
|
|
setSettings((curSettings) => {
|
|
|
|
const sa = curSettings.samm || (account.imperial ? 15.3125 : 10)
|
|
|
|
|
|
|
|
if (curSettings.sabool)
|
|
|
|
return objUpdate({ ...curSettings }, [
|
|
|
|
[['sabool'], 0],
|
|
|
|
[['sa'], 0],
|
|
|
|
[['samm'], sa],
|
|
|
|
])
|
|
|
|
else {
|
|
|
|
return objUpdate({ ...curSettings }, [
|
|
|
|
[['sabool'], 1],
|
|
|
|
[['sa'], sa],
|
|
|
|
[['samm'], sa],
|
|
|
|
])
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
setControl: controlState.update,
|
|
|
|
}),
|
|
|
|
[setSettings, setUi, account, controlState]
|
|
|
|
)
|
2023-06-20 17:30:18 +02:00
|
|
|
|
2023-06-28 13:04:45 -05:00
|
|
|
// wait for mount. this helps prevent hydration issues
|
2023-09-27 12:04:26 +02:00
|
|
|
if (!mounted) return <p>wait for it...</p> //<ModalSpinner />
|
2023-06-28 13:04:45 -05:00
|
|
|
|
|
|
|
// Warn that the design is somehow missing
|
|
|
|
if (!Design) return <ErrorView>{t('workbench.noDesignFound')}</ErrorView>
|
2023-06-20 17:30:18 +02:00
|
|
|
|
|
|
|
// Short-circuit errors early
|
|
|
|
if (error)
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<WorkbenchHeader {...{ view, setView, update }} />
|
|
|
|
{error}
|
2023-06-29 14:35:46 +00:00
|
|
|
<MobileMenubar />
|
2023-06-20 17:30:18 +02:00
|
|
|
</>
|
|
|
|
)
|
|
|
|
|
|
|
|
// Deal with each view
|
|
|
|
const viewProps = {
|
|
|
|
account,
|
|
|
|
design,
|
|
|
|
view,
|
|
|
|
setView,
|
|
|
|
update,
|
|
|
|
settings,
|
2023-06-21 22:14:57 -05:00
|
|
|
setSettings,
|
2023-06-20 17:30:18 +02:00
|
|
|
ui,
|
|
|
|
language,
|
|
|
|
DynamicDocs,
|
|
|
|
Design,
|
2023-09-27 12:04:26 +02:00
|
|
|
saveAs,
|
2023-06-20 17:30:18 +02:00
|
|
|
}
|
|
|
|
let viewContent = null
|
|
|
|
|
|
|
|
switch (view) {
|
|
|
|
// Save view
|
|
|
|
case 'save':
|
2023-06-21 12:29:19 +02:00
|
|
|
viewContent = <SaveView {...viewProps} />
|
2023-06-20 17:30:18 +02:00
|
|
|
break
|
|
|
|
case 'export':
|
|
|
|
viewContent = <ExportView {...viewProps} />
|
|
|
|
break
|
|
|
|
case 'edit':
|
|
|
|
viewContent = <EditView {...viewProps} setSettings={setSettings} />
|
|
|
|
break
|
|
|
|
case 'measies':
|
2023-06-20 20:19:31 +02:00
|
|
|
viewContent = <MeasiesView {...viewProps} {...{ missingMeasurements }} />
|
2023-06-20 17:30:18 +02:00
|
|
|
break
|
|
|
|
default: {
|
|
|
|
const layout = ui.layouts?.[view] || settings.layout || true
|
|
|
|
// Generate the pattern here so we can pass it down to both the view and the options menu
|
2023-06-24 15:00:46 -05:00
|
|
|
const pattern =
|
2023-06-28 13:04:45 -05:00
|
|
|
(Design.patternConfig.measurements.length === 0 || settings.measurements !== undefined) &&
|
|
|
|
new Design({ layout, embed: true, ...settings })
|
2023-06-20 17:30:18 +02:00
|
|
|
// Return early if the pattern is not initialized yet
|
|
|
|
if (typeof pattern.getConfig !== 'function') return null
|
|
|
|
|
|
|
|
const patternConfig = pattern.getConfig()
|
|
|
|
if (ui.renderer === 'svg') {
|
|
|
|
// Add theme to svg renderer
|
2023-09-06 10:30:52 +02:00
|
|
|
pattern.use(pluginI18n, (key) => t(key))
|
2023-06-20 17:30:18 +02:00
|
|
|
pattern.use(pluginTheme, { skipGrid: ['pages'] })
|
|
|
|
}
|
|
|
|
|
|
|
|
if (draftViews.includes(view)) {
|
|
|
|
// Draft the pattern or die trying
|
|
|
|
try {
|
|
|
|
pattern.draft()
|
|
|
|
const errors = [...pattern.store.logs.error]
|
|
|
|
for (const store of pattern.setStores) errors.push(...store.logs.error)
|
|
|
|
if (errors.length > 0) setView('logs')
|
|
|
|
} catch (error) {
|
|
|
|
console.log(error)
|
|
|
|
setError(<ErrorView>{JSON.stringify(error)}</ErrorView>)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const View = views[view]
|
|
|
|
viewContent = <View {...{ ...viewProps, pattern, patternConfig }} />
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2023-09-09 17:58:44 +02:00
|
|
|
<>
|
|
|
|
{!ui.kiosk && <Header />}
|
|
|
|
<div className={`flex flex-row min-h-screen ${ui.kiosk ? kioskClasses : ''}`}>
|
2023-09-27 12:04:26 +02:00
|
|
|
<WorkbenchHeader {...{ view, setView, update, saveAs }} />
|
2023-09-09 17:58:44 +02:00
|
|
|
<div className="grow">{viewContent}</div>
|
|
|
|
<MobileMenubar />
|
|
|
|
</div>
|
|
|
|
</>
|
2023-06-20 17:30:18 +02:00
|
|
|
)
|
|
|
|
}
|