Add proper menu

related to #360
This commit is contained in:
Kurt Hutten
2021-06-12 19:20:24 +10:00
parent 6ad731d158
commit 3c18a24cb6
11 changed files with 242 additions and 127 deletions

View File

@@ -1,6 +1,20 @@
import { useContext } from 'react'
import { Menu } from '@headlessui/react'
import { IdeContext } from 'src/components/IdeToolbarNew/IdeToolbarNew'
import Svg from 'src/components/Svg/Svg'
import { useRender } from 'src/components/IdeToolbarNew/useRender'
import {makeStlDownloadHandler, PullTitleFromFirstLine} from './helpers'
const EditorMenu = () => {
const handleRender = useRender()
const { state, thunkDispatch } = useContext(IdeContext)
const handleStlDownload = makeStlDownloadHandler({
type: state.objectData?.type,
geometry: state.objectData?.data,
fileName: PullTitleFromFirstLine(state.code || ''),
thunkDispatch,
})
return (
<div className="bg-gray-500 flex items-center h-9 w-full cursor-grab">
<div className=" text-gray-500 bg-gray-300 cursor-grab px-2 h-full flex items-center">
@@ -14,9 +28,10 @@ const EditorMenu = () => {
</button>
<div className="w-px h-full bg-gray-300"/>
<div className="flex gap-6 px-6">
<button className="text-gray-100">
File
</button>
<FileDropdown
handleRender={handleRender}
handleStlDownload={handleStlDownload}
/>
<button className="text-gray-100">
Edit
</button>
@@ -29,3 +44,37 @@ const EditorMenu = () => {
}
export default EditorMenu
function FileDropdown({handleRender, handleStlDownload}) {
return (
<Menu>
<Menu.Button className="text-gray-100">File</Menu.Button>
<Menu.Items className="absolute flex flex-col mt-10 bg-gray-500 rounded shadow-md text-gray-100">
<Menu.Item>
{({ active }) => (
<button
className={`${active && 'bg-gray-600'} px-2 py-1`}
onClick={handleRender}
>
Save &amp; Render <span className="text-gray-400 pl-4">{
/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) ?
<><Svg name="mac-cmd-key" className="h-3 w-3 inline-block text-left" />S</> :
'Ctrl S'
}</span>
</button>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<button
className={`${active && 'bg-gray-600'} px-2 py-1 text-left`}
onClick={handleStlDownload}
>
Download STL
</button>
)}
</Menu.Item>
</Menu.Items>
</Menu>
)
}

View File

@@ -0,0 +1,59 @@
import { flow, identity } from 'lodash/fp'
import { fileSave } from 'browser-fs-access'
import { MeshBasicMaterial, Mesh, Scene } from 'three'
import { STLExporter } from 'three/examples/jsm/exporters/STLExporter'
import { requestRender } from 'src/helpers/hooks/useIdeState'
export const PullTitleFromFirstLine = (code: string = '') => {
const firstLine = code.split('\n').filter(identity)[0] || ''
if (!(firstLine.startsWith('//') || firstLine.startsWith('#'))) {
return 'object.stl'
}
return (
(firstLine.replace(/^(\/\/|#)\s*(.+)/, (_, __, titleWithSpaces) =>
titleWithSpaces.replaceAll(/\s/g, '-')
) || 'object') + '.stl'
)
}
export const makeStlDownloadHandler = (({ geometry, fileName, type, thunkDispatch}) => () => {
const makeStlBlobFromGeo = flow(
(geo) => new Mesh(geo, new MeshBasicMaterial()),
(mesh) => new Scene().add(mesh),
(scene) => new STLExporter().parse(scene),
(stl) =>
new Blob([stl], {
type: 'text/plain',
})
)
const saveFile = (geometry) => {
const blob = makeStlBlobFromGeo(geometry)
fileSave(blob, {
fileName,
extensions: ['.stl'],
})
}
if (geometry) {
if (type === 'geometry') {
saveFile(geometry)
} else {
thunkDispatch((dispatch, getState) => {
const state = getState()
if (state.ideType === 'openScad') {
thunkDispatch((dispatch, getState) => {
const state = getState()
dispatch({ type: 'setLoading' })
requestRender({
state,
dispatch,
code: state.code,
viewerSize: state.viewerSize,
camera: state.camera,
specialCadProcess: 'stl',
}).then((result) => result && saveFile(result.data))
})
}
})
}
}
})