color picker floating above viewer
This commit is contained in:
parent
c95616210b
commit
2ae038020b
9 changed files with 41 additions and 11 deletions
|
|
@ -22,12 +22,14 @@ Licenses: see [LICENSES](./LICENSE).
|
|||
## Roadmap
|
||||
|
||||
- Add tests!
|
||||
- Better names for downloads (matching source)
|
||||
- Model /home fs in shared state. have two clear paths: /libraries for builtins, and /home for user data. State pointing to /libraries paths needs not store the data except if there's overrides (flagged as modifications in the file picker)
|
||||
- Setup [OPENSCADPATH](https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Libraries#Setting_OPENSCADPATH) env var w/ Emscripten to ensure examples will run fine.
|
||||
- Bundle some official demos and more examples from each library!
|
||||
- Ask users to contribute recipes to include more libraries, and cool examples that work in the environment
|
||||
- Drag and drop of files (SCAD, STL, etc) and Zip archives. For assets, auto insert the corresponding import.
|
||||
- Proper PWA w/ File opening / association to *.scad files
|
||||
- Animation rendering (And other formats than STL)
|
||||
- Compress URL fragment
|
||||
- Customizer support. Probably by adding --export-json or --export-format=customizer-json to OpenSCAD.
|
||||
- Mobile (iOS) editing support: switch to https://www.npmjs.com/package/react-codemirror ?
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ export default function Footer({style}: {style?: CSSProperties}) {
|
|||
|
||||
{state.output && (
|
||||
<Button icon='pi pi-download'
|
||||
title={`Download ${state.output.isPreview ? "preview.stl" : "render.stl"} (${state.output.formattedStlFileSize})`}
|
||||
title={`Download ${state.output.isPreview ? "preview.stl" : "render.stl"} (${state.output.formattedStlFileSize})\nGenerated in ${state.output.formattedElapsedMillis}`}
|
||||
severity="secondary"
|
||||
text
|
||||
// label={state.output.isPreview ? "preview.stl" : "render.stl"}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ export default function PanelSwitcher() {
|
|||
}
|
||||
<SettingsMenu style={{
|
||||
position: 'absolute',
|
||||
marginTop: '5px',
|
||||
right: 0,
|
||||
top: 0,
|
||||
}} />
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
import { CSSProperties, forwardRef, useContext, useEffect, useRef, useState } from 'react';
|
||||
import { ModelContext } from './contexts';
|
||||
import { StlViewer} from "react-stl-viewer";
|
||||
import { ColorPicker } from 'primereact/colorpicker';
|
||||
import { defaultModelColor } from '../state/initial-state';
|
||||
|
||||
export default function ViewerPanel({className, style}: {className?: string, style?: CSSProperties}) {
|
||||
const model = useContext(ModelContext);
|
||||
|
|
@ -21,8 +23,10 @@ export default function ViewerPanel({className, style}: {className?: string, sty
|
|||
}}>
|
||||
{state.output?.stlFileURL &&
|
||||
<StlViewer
|
||||
className='absolute-fill'
|
||||
style={{
|
||||
flex: 1
|
||||
zIndex: 0,
|
||||
// flex: 1,
|
||||
// width: '100%'
|
||||
}}
|
||||
// ref={stlModelRef}
|
||||
|
|
@ -30,12 +34,22 @@ export default function ViewerPanel({className, style}: {className?: string, sty
|
|||
orbitControls
|
||||
shadows={state.view.showShadows}
|
||||
modelProps={{
|
||||
color: '#f9d72c',
|
||||
|
||||
color: model.state.view.color,
|
||||
}}
|
||||
url={state.output?.stlFileURL ?? ''}
|
||||
/>
|
||||
}
|
||||
/>}
|
||||
|
||||
<ColorPicker
|
||||
className={`opacity-animated ${!model.isComponentFullyVisible('viewer') ? 'opacity-0' : ''}`}
|
||||
value={model.state.view.color}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '12px',
|
||||
left: '12px',
|
||||
// pointerEvents: model.isComponentFullyVisible('viewer') ? undefined : 'none',
|
||||
}}
|
||||
onChange={(e) => model.mutate(s => s.view.color = `#${e.value ?? defaultModelColor}`)} />
|
||||
|
||||
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export interface State {
|
|||
mode: 'multi',
|
||||
} & { [K in MultiLayoutComponentId]: boolean })
|
||||
|
||||
color: string,
|
||||
showAxes?: boolean,
|
||||
showShadows?: boolean,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import { State } from "./app-state";
|
||||
import { validateArray, validateBoolean, validateString, validateStringEnum } from "../utils";
|
||||
import { defaultModelColor } from "./initial-state";
|
||||
|
||||
export function buildUrlForStateParams(state: State) {//partialState: {params: State['params'], view: State['view']}) {
|
||||
return `${location.protocol}//${location.host}${location.pathname}#${encodeStateParamsAsFragment(state)}`;
|
||||
|
|
@ -33,6 +34,7 @@ export function readStateFromFragment(): State | null {
|
|||
viewer: validateBoolean(view?.layout['viewer']),
|
||||
customizer: validateBoolean(view?.layout['customizer']),
|
||||
},
|
||||
color: validateString(view?.color, () => defaultModelColor),
|
||||
showAxes: validateBoolean(view?.layout?.showAxis),
|
||||
showShadows: validateBoolean(view?.layout?.showShadow),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import defaultScad from './default-scad';
|
|||
import { State } from './app-state';
|
||||
|
||||
export const defaultSourcePath = '/playground.scad';
|
||||
export const defaultModelColor = '#f9d72c';
|
||||
|
||||
export const blankProjectState: State = {
|
||||
params: {
|
||||
|
|
@ -12,6 +13,7 @@ export const blankProjectState: State = {
|
|||
features: [],
|
||||
},
|
||||
view: {
|
||||
color: defaultModelColor,
|
||||
layout: {
|
||||
mode: 'single',
|
||||
focus: 'editor'
|
||||
|
|
@ -37,7 +39,9 @@ export function createInitialState(fs: any, state: State | null) {
|
|||
editor: true,
|
||||
viewer: true,
|
||||
customizer: false,
|
||||
} as any
|
||||
} as any,
|
||||
|
||||
color: defaultModelColor,
|
||||
},
|
||||
...(state ?? {})
|
||||
};
|
||||
|
|
|
|||
|
|
@ -51,6 +51,14 @@ export class Model {
|
|||
this.mutate(s => s.view.logs = value);
|
||||
}
|
||||
|
||||
isComponentFullyVisible(id: SingleLayoutComponentId) {
|
||||
if (this.state.view.layout.mode === 'multi') {
|
||||
return this.state.view.layout[id];
|
||||
} else {
|
||||
return this.state.view.layout.focus === id;
|
||||
}
|
||||
}
|
||||
|
||||
changeLayout(mode: 'multi' | 'single') {
|
||||
if (this.state.view.layout.mode === mode) return;
|
||||
this.mutate(s => {
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ export function formatBytes(n: number) {
|
|||
|
||||
export function formatMillis(n: number) {
|
||||
if (n < 1000)
|
||||
return `${Math.floor(n)} millis`;
|
||||
return `${Math.floor(n)}ms`;
|
||||
|
||||
return `${Math.floor(n / 100) / 10}sec`;
|
||||
}
|
||||
Loading…
Reference in a new issue