1
0
Fork 0
freesewing/sites/shared/components/workbench/new.mjs
Joost De Cock 86d984e8c5 wip(shared): Split new design pages per design to split bundle
This adds page templates that will auto-generate pages for each design.
This not only allows us to split the bundle for these individual
designs, we are also now passing the inline docs for a design as static
props, as that will limit the memory footprint of webpack.

Remains to be seen how this will all come together, but it's better than
throwing everything at webpack and watching the build fail.
2023-10-08 14:26:58 +02:00

251 lines
7.9 KiB
JavaScript

// __SDEFILE__ - This file is a dependency for the stand-alone environment
// Hooks
import { useEffect, useState, useCallback, useMemo } from 'react'
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'
import { objUpdate, hasRequiredMeasurements, nsMerge } from 'shared/utils.mjs'
// Components
import { Header, ns as headerNs } from 'site/components/header/index.mjs'
import { WorkbenchHeader } from './header.mjs'
import { ErrorView } from 'shared/components/error/view.mjs'
import { MobileMenubar } from './menus/mobile-menubar.mjs'
// 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'
import { DocsView, ns as docsNs } from 'shared/components/workbench/views/docs/index.mjs'
export const ns = nsMerge(
'account',
'workbench',
'flag',
'plugin-annotations',
draftNs,
saveNs,
printNs,
cutNs,
editNs,
testNs,
exportNs,
logNs,
inspectNs,
measiesNs,
headerNs,
docsNs
)
const defaultUi = {
renderer: 'react',
kiosk: false,
}
const views = {
draft: DraftView,
print: PrintView,
cut: CutView,
export: ExportView,
edit: EditView,
test: TestView,
logs: LogView,
inspect: InspectView,
measies: MeasiesView,
docs: DocsView,
}
const draftViews = ['draft', 'inspect']
const kioskClasses = 'z-30 w-screen h-screen fixed top-0 left-0 bg-base-100'
const noDocs = { measurements: {}, options: {} }
export const Workbench = ({ design, Design, docs = noDocs, saveAs = false, preload = false }) => {
// Hooks
const { t, i18n } = useTranslation([...ns, design])
const { language } = i18n
const { account } = useAccount()
const controlState = useControlState()
// State
const [view, _setView] = useView()
const [settings, setSettings] = usePatternSettings(preload)
const [ui, setUi] = useState(defaultUi)
const [error, setError] = useState(false)
const [mounted, setMounted] = useState(false)
const [missingMeasurements, setMissingMeasurements] = useState(false)
const [preloaded, setPreloaded] = useState(0)
const setView = useCallback(
(newView) => {
// hacky little way to scroll to the top but keep the menu hidden if it was hidden
const endScroll = Math.min(window.scrollY, 21)
window.scrollTo({ top: 0, behavior: 'instant' })
_setView(newView)
window.scroll({ top: endScroll })
},
[_setView]
)
// set mounted on mount
useEffect(() => setMounted(true), [setMounted])
// 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])
useEffect(() => {
// protect against loops
if (!mounted) return
setMounted(true)
const [ok, missing] = hasRequiredMeasurements(Design, settings.measurements)
if (ok) setMissingMeasurements(false)
// Force the measurements view if we have missing measurements
else {
setMissingMeasurements(missing)
if (view !== 'measies') setView('measies')
}
}, [Design, settings.measurements, mounted, view, setView])
// Helper methods for settings/ui updates
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]
)
// wait for mount. this helps prevent hydration issues
if (!mounted) return <p>wait for it...</p> //<ModalSpinner />
// Warn that the design is somehow missing
if (!Design) return <ErrorView>{t('workbench.noDesignFound')}</ErrorView>
// Short-circuit errors early
if (error)
return (
<>
<WorkbenchHeader {...{ view, setView, update }} control={account.control} />
{error}
<MobileMenubar />
</>
)
// Deal with each view
const viewProps = {
account,
design,
view,
setView,
update,
settings,
setSettings,
ui,
language,
docs,
Design,
saveAs,
}
let viewContent = null
switch (view) {
// Save view
case 'save':
viewContent = <SaveView {...viewProps} />
break
case 'export':
viewContent = <ExportView {...viewProps} />
break
case 'edit':
viewContent = <EditView {...viewProps} setSettings={setSettings} />
break
case 'measies':
viewContent = <MeasiesView {...viewProps} {...{ missingMeasurements }} />
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
const pattern =
(Design.patternConfig.measurements.length === 0 || settings.measurements !== undefined) &&
new Design({ layout, embed: true, ...settings })
// 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
pattern.use(pluginI18n, (key) => t(key))
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 (
<>
{!ui.kiosk && <Header />}
<div className={`flex flex-row min-h-screen ${ui.kiosk ? kioskClasses : ''}`}>
<WorkbenchHeader {...{ view, setView, update, saveAs }} control={account.control} />
<div className="grow">{viewContent}</div>
<MobileMenubar />
</div>
</>
)
}