From ceb9b89616616b11d6d15c50d75b17fa891f8357 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 19 Jan 2018 01:25:26 -0800 Subject: [PATCH] wizard react support --- modules/bus/index.js | 4 + modules/gems/camelCaseSplit.js | 25 ++++ modules/gems/shallowEqual.js | 23 +++ modules/scene/geoms.js | 20 +++ modules/scene/objects/mesh.js | 9 ++ modules/scene/sceneSetup.js | 3 +- modules/ui/components/Window.jsx | 3 +- modules/ui/components/controls/Button.jsx | 4 +- .../ui/components/controls/InputControl.jsx | 2 +- .../ui/components/controls/NumberControl.jsx | 7 +- .../ui/components/controls/TextControl.jsx | 16 --- modules/ui/connect.jsx | 19 ++- web/app/cad/actions/operationActions.js | 8 +- .../cad/craft/brep/wizards/preview-wizard.js | 136 ------------------ web/app/cad/craft/craftPlugin.js | 67 +++++++++ web/app/cad/craft/operationPlugin.js | 56 ++++++++ web/app/cad/craft/operations.js | 4 +- web/app/cad/craft/primitives/boxOperation.js | 28 ++++ web/app/cad/craft/wizard/wizardPlugin.js | 26 ++++ web/app/cad/dom/actionInfo/ActionInfo.jsx | 7 +- .../cad/dom/components/PlugableControlBar.jsx | 19 ++- .../cad/dom/components/PlugableToolbar.jsx | 13 +- web/app/cad/dom/components/UISystem.jsx | 9 +- web/app/cad/dom/components/View3d.jsx | 10 +- web/app/cad/dom/components/WebApplication.jsx | 9 +- web/app/cad/dom/components/wizard/Wizard.jsx | 81 +++++++++++ .../dom/components/wizard/WizardManager.jsx | 32 +++++ web/app/cad/dom/menu/MenuHolder.jsx | 22 +-- web/app/cad/dom/startReact.jsx | 4 +- web/app/cad/init/startApplication.js | 10 +- web/app/cad/modeler-app.js | 1 - web/app/cad/part/partModellerPlugin.js | 31 ++-- web/app/cad/part/partOperationsPlugin.js | 7 + web/app/cad/part/uiConfigPlugin.js | 25 ++++ web/app/cad/preview/scenePreviewer.js | 76 ++++++++++ web/app/utils/utils.js | 26 ---- 36 files changed, 580 insertions(+), 262 deletions(-) create mode 100644 modules/gems/camelCaseSplit.js create mode 100644 modules/gems/shallowEqual.js create mode 100644 modules/scene/geoms.js create mode 100644 modules/scene/objects/mesh.js delete mode 100644 web/app/cad/craft/brep/wizards/preview-wizard.js create mode 100644 web/app/cad/craft/craftPlugin.js create mode 100644 web/app/cad/craft/operationPlugin.js create mode 100644 web/app/cad/craft/primitives/boxOperation.js create mode 100644 web/app/cad/craft/wizard/wizardPlugin.js create mode 100644 web/app/cad/dom/components/wizard/Wizard.jsx create mode 100644 web/app/cad/dom/components/wizard/WizardManager.jsx create mode 100644 web/app/cad/part/partOperationsPlugin.js create mode 100644 web/app/cad/part/uiConfigPlugin.js create mode 100644 web/app/cad/preview/scenePreviewer.js diff --git a/modules/bus/index.js b/modules/bus/index.js index eb5e5b6d..86669384 100644 --- a/modules/bus/index.js +++ b/modules/bus/index.js @@ -105,6 +105,10 @@ export default class Bus { this.state[forToken] = initValue; } + disableState(forToken) { + this.keepStateFor.delete(forToken); + delete this.state[forToken]; + } } export function createToken(...fqn) { diff --git a/modules/gems/camelCaseSplit.js b/modules/gems/camelCaseSplit.js new file mode 100644 index 00000000..e1803979 --- /dev/null +++ b/modules/gems/camelCaseSplit.js @@ -0,0 +1,25 @@ +export default function camelCaseSplit(str) { + function isUpperCase(str) { + return str.toUpperCase() === str; + } + + const words = []; + let word = ''; + + for (let i = 0; i < str.length; i++) { + const c = str.charAt(i); + if (c === '_' || c === '-') { + continue; + } + const dot = c === '.'; + if ((dot || isUpperCase(c)) && word.length !== 0) { + words.push(word); + word = ''; + } + if (!dot) word += c; + } + if (word.length !== 0){ + words.push(word); + } + return words; +} diff --git a/modules/gems/shallowEqual.js b/modules/gems/shallowEqual.js new file mode 100644 index 00000000..6485cff7 --- /dev/null +++ b/modules/gems/shallowEqual.js @@ -0,0 +1,23 @@ +export default function shallowEqual(objA, objB) { + if (objA === objB) { + return true; + } + + let aKeys = Object.keys(objA); + let bKeys = Object.keys(objB); + let len = aKeys.length; + + if (bKeys.length !== len) { + return false; + } + + for (let i = 0; i < len; i++) { + let key = aKeys[i]; + + if (objA[key] !== objB[key]) { + return false; + } + } + + return true; +}; \ No newline at end of file diff --git a/modules/scene/geoms.js b/modules/scene/geoms.js new file mode 100644 index 00000000..5972ef71 --- /dev/null +++ b/modules/scene/geoms.js @@ -0,0 +1,20 @@ + +export function createBoxGeometry(width, height, depth) { + return new THREE.BoxGeometry(width, height, depth); +} + +export function createMeshGeometry(triangles) { + const geometry = new THREE.Geometry(); + + for (let tr of triangles) { + const a = geometry.vertices.length; + const b = a + 1; + const c = a + 2; + const face = new THREE.Face3(a, b, c); + tr.forEach(v => geometry.vertices.push(v.three())); + geometry.faces.push(face); + } + geometry.mergeVertices(); + geometry.computeFaceNormals(); + return geometry; +} \ No newline at end of file diff --git a/modules/scene/objects/mesh.js b/modules/scene/objects/mesh.js new file mode 100644 index 00000000..0c0f29b2 --- /dev/null +++ b/modules/scene/objects/mesh.js @@ -0,0 +1,9 @@ +import {createMeshGeometry} from '../geoms'; + +export function createMesh(geometry, material) { + return new THREE.Mesh(geometry, material); +} + +export function createMeshFromTriangles(triangles, material) { + return createMesh(createMeshGeometry(triangles), material); +} diff --git a/modules/scene/sceneSetup.js b/modules/scene/sceneSetup.js index 204d64f4..4fd82f8e 100644 --- a/modules/scene/sceneSetup.js +++ b/modules/scene/sceneSetup.js @@ -1,5 +1,6 @@ import DPR from 'dpr'; -import './utils/threeLoader' +import './utils/threeLoader'; +import './utils/vectorThreeEnhancement'; export default class SceneSetUp { diff --git a/modules/ui/components/Window.jsx b/modules/ui/components/Window.jsx index 7505950d..99cdc932 100644 --- a/modules/ui/components/Window.jsx +++ b/modules/ui/components/Window.jsx @@ -35,7 +35,8 @@ export default class Window extends React.Component { width: this.state.width, height: this.state.height, left: this.state.left, - top: this.state.top + top: this.state.top, + zIndex: 1 } } } diff --git a/modules/ui/components/controls/Button.jsx b/modules/ui/components/controls/Button.jsx index 2b110bdf..7370ccb8 100644 --- a/modules/ui/components/controls/Button.jsx +++ b/modules/ui/components/controls/Button.jsx @@ -2,9 +2,9 @@ import React from 'react'; import ls from './Button.less' -export default function Button({text, type}) { +export default function Button({text, type, onClick}) { - return + return } diff --git a/modules/ui/components/controls/InputControl.jsx b/modules/ui/components/controls/InputControl.jsx index f235af07..499e1d8d 100644 --- a/modules/ui/components/controls/InputControl.jsx +++ b/modules/ui/components/controls/InputControl.jsx @@ -9,7 +9,7 @@ export default class InputControl extends React.Component { let {type, inputRef, ...props} = this.props; return
- +
; } } diff --git a/modules/ui/components/controls/NumberControl.jsx b/modules/ui/components/controls/NumberControl.jsx index 02d23145..167b2a46 100644 --- a/modules/ui/components/controls/NumberControl.jsx +++ b/modules/ui/components/controls/NumberControl.jsx @@ -15,12 +15,7 @@ export default class NumberControl extends React.Component { onWheel = (e) => { let {baseStep, round, min, max, onChange, accelerator} = this.props; - let delta = 0; - if ( e.wheelDelta ) { // WebKit / Opera / Explorer 9 - delta = e.wheelDelta; - } else if ( e.detail ) { // Firefox - delta = - e.detail; - } + let delta = e.deltaY; let val = e.target.value; if (!val) val = 0; let step = baseStep * (e.shiftKey ? accelerator : 1); diff --git a/modules/ui/components/controls/TextControl.jsx b/modules/ui/components/controls/TextControl.jsx index 56391cbc..759e1080 100644 --- a/modules/ui/components/controls/TextControl.jsx +++ b/modules/ui/components/controls/TextControl.jsx @@ -11,19 +11,3 @@ export default class TextControl extends React.Component { onChange={e => onChange(e.target.value)} /> } } - -TextControl.propTypes = { - baseStep: PropTypes.number, - round: PropTypes.number, - min: PropTypes.number, - max: PropTypes.number, - accelerator: PropTypes.number, - initValue: PropTypes.number.isRequired, - onChange: PropTypes.func.isRequired -}; - -TextControl.defaultProps = { - baseStep: 1, - round: 0, - accelerator: 100 -}; \ No newline at end of file diff --git a/modules/ui/connect.jsx b/modules/ui/connect.jsx index 456061d9..712fd8c4 100644 --- a/modules/ui/connect.jsx +++ b/modules/ui/connect.jsx @@ -1,15 +1,16 @@ import React from 'react'; import PropTypes from 'prop-types'; +import shallowEqual from "../gems/shallowEqual"; -export default function connect(tokens, WrappedComponent, staticProps, mapper, dispatchMapper) { +export default function connect(WrappedComponent, tokens, {staticProps, mapProps, mapActions}) { if (!Array.isArray(tokens)) { tokens = [tokens]; } - mapper = createMapper(mapper); + mapProps = createMapper(mapProps); - dispatchMapper = dispatchMapper || function(dispatch) { + mapActions = mapActions || function(dispatch) { return dispatch; }; @@ -19,7 +20,7 @@ export default function connect(tokens, WrappedComponent, staticProps, mapper, d super(); this.mounted = false; this.stateProps = {}; - this.dispatchProps = dispatchMapper(this.dispatch); + this.dispatchProps = mapActions(this.dispatch); } componentWillMount() { @@ -37,12 +38,17 @@ export default function connect(tokens, WrappedComponent, staticProps, mapper, d } setExternalState = (state) => { - this.stateProps = mapper(state); + this.stateProps = mapProps(state); if (this.mounted) { this.forceUpdate(); } }; + shouldComponentUpdate(nextProps, nextState) { + return !shallowEqual(this.props, nextProps); + + } + dispatch = (event, data) => { this.context.bus.dispatch(event, data); }; @@ -51,6 +57,9 @@ export default function connect(tokens, WrappedComponent, staticProps, mapper, d return } + componentDidCatch() { + } + static contextTypes = { bus: PropTypes.object }; diff --git a/web/app/cad/actions/operationActions.js b/web/app/cad/actions/operationActions.js index 88fbee07..392e8de3 100644 --- a/web/app/cad/actions/operationActions.js +++ b/web/app/cad/actions/operationActions.js @@ -29,12 +29,6 @@ const OPERATION_ACTIONS = [ }, ...requiresFaceSelection(1) }, - { - id: 'BOX', - appearance: { - info: 'creates new object box' - }, - }, { id: 'PLANE', appearance: { @@ -78,7 +72,7 @@ const OPERATION_ACTIONS = [ function mergeInfo(action) { const op = Operations[action.id]; - action.invoke = app => app.ui.initOperation(action.id); + action.invoke = ({services}) => services.operation.startOperation(action.id); Object.assign(action.appearance, { label: op.label, icon32: op.icon + '32.png', diff --git a/web/app/cad/craft/brep/wizards/preview-wizard.js b/web/app/cad/craft/brep/wizards/preview-wizard.js deleted file mode 100644 index e8ef2fa2..00000000 --- a/web/app/cad/craft/brep/wizards/preview-wizard.js +++ /dev/null @@ -1,136 +0,0 @@ -import {Wizard} from './wizard' -import {ReadSketchFromFace} from '../../sketch/sketch-reader' -import {Loop} from '../../../../brep/topo/loop' - -export class PreviewWizard extends Wizard { - - constructor(app, opearation, metadata, initialState) { - super(app, opearation, metadata, initialState); - this.operation = opearation; - this.previewGroup = new THREE.Object3D(); - this.previewObject = null; - this.app.viewer.workGroup.add(this.previewGroup); - this.updatePreview(); - app.bus.subscribe('refreshSketch', this.onSketchUpdate); - } - - onSketchUpdate = () => { - this.updatePreview(); - }; - - createPreviewObject() {throw 'abstract'}; - - updatePreview() { - this.destroyPreviewObject(); - this.previewObject = this.createPreviewObject(this.app, this.readFormFields()); - if (this.previewObject !== null) { - this.previewGroup.add( this.previewObject ); - } - this.app.viewer.render(); - } - - destroyPreviewObject() { - if (this.previewObject !== null) { - this.previewGroup.remove( this.previewObject ); - this.previewObject.geometry.dispose(); - this.previewObject = null; - } - } - - onUIChange() { - super.onUIChange(); - this.updatePreview(); - } - - dispose() { - this.app.bus.unSubscribe('refreshSketch', this.onSketchUpdate); - this.destroyPreviewObject(); - this.app.viewer.workGroup.remove(this.previewGroup); - this.app.viewer.render(); - super.dispose(); - } -} - -PreviewWizard.createMesh = function(triangles) { - const geometry = new THREE.Geometry(); - - for (let tr of triangles) { - const a = geometry.vertices.length; - const b = a + 1; - const c = a + 2; - const face = new THREE.Face3(a, b, c); - tr.forEach(v => geometry.vertices.push(v.three())); - geometry.faces.push(face); - } - geometry.mergeVertices(); - geometry.computeFaceNormals(); - - return new THREE.Mesh(geometry, IMAGINARY_SURFACE_MATERIAL); -}; - -export class SketchBasedPreviewer { - - constructor() { - //this.fixToCCW = true; - } - - createImpl(app, params, sketch, face) { - throw 'not implemented'; - } - - create(app, params) { - const face = app.findFace(params.face); - if (!face) return null; - const triangles = this.createImpl(app, params, face.sketch.fetchContours(), face); - return PreviewWizard.createMesh(triangles); - } -} - -export class SketchBasedNurbsPreviewer { - - constructor() { - } - - createNurbses(app, params, sketch, face) { - throw 'not implemented'; - } - - createMesh(app, params) { - const face = app.findFace(params.face); - if (!face) return null; - const needSketchRead = !this.sketch || params.face != this.face; - if (needSketchRead) { - this.sketch = ReadSketchFromFace(app, face); - this.face = params.face; - } - const nurbses = this.createNurbses(app, params, this.sketch, face); - const geom = new THREE.Geometry(); - - for (let nurbs of nurbses) { - const off = geom.vertices.length; - const tess = nurbs.tessellate({maxDepth: 3}); - const points = []; - tess.points.forEach(p => geom.vertices.push(new THREE.Vector3().fromArray(p))); - for (let faceIndices of tess.faces) { - let normales = faceIndices.map(function(x) { - var vn = tess.normals[x]; - return new THREE.Vector3( vn[0], vn[1], vn[2] ); - }); - const face = new THREE.Face3(faceIndices[0] + off, faceIndices[1] + off, faceIndices[2] + off, normales); - geom.faces.push(face); - } - } - return new THREE.Mesh(geom, IMAGINARY_SURFACE_MATERIAL); - } -} - -export const IMAGINARY_SURFACE_MATERIAL = new THREE.MeshPhongMaterial({ - vertexColors: THREE.FaceColors, - color: 0xFA8072, - transparent: true, - opacity: 0.5, - shininess: 0, - depthWrite: false, - depthTest: false, - side : THREE.DoubleSide -}); diff --git a/web/app/cad/craft/craftPlugin.js b/web/app/cad/craft/craftPlugin.js new file mode 100644 index 00000000..926d37a1 --- /dev/null +++ b/web/app/cad/craft/craftPlugin.js @@ -0,0 +1,67 @@ +import {BoxWizard} from "./mesh/wizards/box"; + +export function activate({bus, services}) { + + + + + + function createWizard(type, overridingHistory, initParams, face) { + let wizard = null; + if ('CUT' === type) { + wizard = new CutWizard(this.app, initParams); + } else if ('EXTRUDE' === type) { + wizard = new ExtrudeWizard(this.app, initParams); + } else if ('REVOLVE' === type) { + wizard = new RevolveWizard(this.app, face, initParams); + } else if ('PLANE' === type) { + wizard = new PlaneWizard(this.app, initParams); + } else if ('BOX' === type) { + wizard = new BoxWizard(this.app, initParams); + } else if ('SPHERE' === type) { + wizard = new SphereWizard(this.app.viewer, initParams); + } else if ('IMPORT_STL' === type) { + wizard = new ImportWizard(this.app.viewer, initParams); + } else { + console.log('unknown operation'); + } + if (wizard != null) { + this.registerWizard(wizard, overridingHistory); + } + return wizard; + }; + + + function startOperation(id) { + let selection = services.selection.face(); + + if ('CUT' === type) { + wizard = new CutWizard(this.app, initParams); + } else if ('EXTRUDE' === type) { + wizard = new ExtrudeWizard(this.app, initParams); + } else if ('REVOLVE' === type) { + wizard = new RevolveWizard(this.app, face, initParams); + } else if ('PLANE' === type) { + wizard = new PlaneWizard(this.app, initParams); + } else if ('BOX' === type) { + wizard = new BoxWizard(this.app, initParams); + } else if ('SPHERE' === type) { + wizard = new SphereWizard(this.app.viewer, initParams); + } else if ('IMPORT_STL' === type) { + wizard = new ImportWizard(this.app.viewer, initParams); + } else { + console.log('unknown operation'); + } + if (wizard != null) { + this.registerWizard(wizard, overridingHistory); + } + return wizard; + + return this.createWizard(op, false, undefined, selection[0]); + + } + + services.operation = { + startOperation + } +} \ No newline at end of file diff --git a/web/app/cad/craft/operationPlugin.js b/web/app/cad/craft/operationPlugin.js new file mode 100644 index 00000000..b3cc55d8 --- /dev/null +++ b/web/app/cad/craft/operationPlugin.js @@ -0,0 +1,56 @@ +import {createToken} from 'bus'; +import {TOKENS as WIZARD_TOKENS} from './wizard/wizardPlugin' + +export function activate(context) { + let {bus, services} = context; + + let registry = {}; + + function addOperation(descriptor, actions) { + let {id, label, info, icon, actionParams} = descriptor; + + let opAction = { + id: id, + appearance: { + label, + info, + icon32: icon + '32.png', + icon96: icon + '96.png', + }, + invoke: () => bus.dispatch(WIZARD_TOKENS.OPEN, {type: id}), + ...actionParams + }; + actions.push(opAction); + + registry[id] = descriptor; + + bus.subscribe(TOKENS.RUN, ({type, params}) => registry[type].run(params)); + } + + function registerOperations(operations) { + let actions = []; + for (let op of operations) { + addOperation(op, actions); + } + services.action.registerActions(actions); + } + + function get(id) { + let op = registry[id]; + if (!op) { + this `operation ${id} is not registered`; + } + return op; + } + + services.operation = { + registerOperations, + registry, + get + } +} + + +export const TOKENS = { + RUN: createToken('operation', 'run'), +}; \ No newline at end of file diff --git a/web/app/cad/craft/operations.js b/web/app/cad/craft/operations.js index de88447d..e18ec1f4 100644 --- a/web/app/cad/craft/operations.js +++ b/web/app/cad/craft/operations.js @@ -1,9 +1,9 @@ // import {MESH_OPERATIONS} from './mesh/workbench' // import {Extrude, Cut} from './brep/cut-extrude' // import {Revolve} from './brep/revolve' -// import {BREPSceneSolid} from '../scene/wrappers/brepSceneObject' +import {BREPSceneSolid} from '../scene/wrappers/brepSceneObject' // import {PlaneSceneObject} from '../scene/wrappers/planeSceneObject' -// import {box} from '../../brep/brep-primitives' +import {box} from '../../brep/brep-primitives' export const CUT = { icon: 'img/cad/cut', diff --git a/web/app/cad/craft/primitives/boxOperation.js b/web/app/cad/craft/primitives/boxOperation.js new file mode 100644 index 00000000..9a3539c4 --- /dev/null +++ b/web/app/cad/craft/primitives/boxOperation.js @@ -0,0 +1,28 @@ +import {box} from '../../../brep/brep-primitives' +import {BREPSceneSolid} from '../../scene/wrappers/brepSceneObject'; +import {createPreviewer} from "../../preview/scenePreviewer"; +import {createBoxGeometry} from "../../../../../modules/scene/geoms"; + +const METADATA = [ + ['width' , 'number', 500, {min: 0}], + ['height' , 'number', 500, {min: 0}], + ['depth' , 'number', 500, {min: 0}] +]; + +function createBox(solids, {width, height, depth}) { + return { + outdated: [], + created: [new BREPSceneSolid(box(width, height, depth))] + } +} + +export default { + id: 'BOX', + metadata: METADATA, + label: 'Box', + info: 'creates new object box', + paramsInfo: ({width, height, depth}) => `(${width}, ${height}, ${depth})`, + previewer: createPreviewer(({width, height, depth}) => createBoxGeometry(width, height, depth)), + run: createBox +}; + diff --git a/web/app/cad/craft/wizard/wizardPlugin.js b/web/app/cad/craft/wizard/wizardPlugin.js new file mode 100644 index 00000000..51853294 --- /dev/null +++ b/web/app/cad/craft/wizard/wizardPlugin.js @@ -0,0 +1,26 @@ +import {createToken} from 'bus'; + +export function activate({bus, services}) { + + bus.enableState(TOKENS.WIZARDS, []); + bus.subscribe(TOKENS.OPEN, ({type, initialState, overridingHistory}) => { + + let wizard = { + type, + initialState, + overridingHistory, + }; + + bus.updateState(TOKENS.WIZARDS, opened => [...opened, wizard]) + }); + + bus.subscribe(TOKENS.CLOSE, wizard => { + bus.updateState(TOKENS.WIZARDS, opened => opened.filter(w => w === wizard)); + }); +} + +export const TOKENS = { + WIZARDS: createToken('wizards'), + OPEN: createToken('wizards', 'open'), + CLOSE: createToken('wizards', 'close'), +}; \ No newline at end of file diff --git a/web/app/cad/dom/actionInfo/ActionInfo.jsx b/web/app/cad/dom/actionInfo/ActionInfo.jsx index ffb3cf77..5bcb5c4e 100644 --- a/web/app/cad/dom/actionInfo/ActionInfo.jsx +++ b/web/app/cad/dom/actionInfo/ActionInfo.jsx @@ -4,7 +4,7 @@ import ls from './ActionInfo.less'; import AuxWidget from '../../../../../modules/ui/components/AuxWidget'; import connect from '../../../../../modules/ui/connect'; import {TOKENS as ACTION_TOKENS} from '../../actions/actionSystemPlugin'; -import {TOKENS as KeyboardTokens} from "../../keyboard/keyboardPlugin"; +import {TOKENS as KeyboardTokens} from '../../keyboard/keyboardPlugin'; function ActionInfo({actionId, x, y, info, hint, hotKey}) { let visible = !!actionId; @@ -19,6 +19,7 @@ function ActionInfo({actionId, x, y, info, hint, hotKey}) { ; } -export default connect([ACTION_TOKENS.HINT, KeyboardTokens.KEYMAP], ActionInfo, undefined, - ([ hintInfo, keymap ]) => (Object.assign({hotKey: hintInfo && keymap[hintInfo.actionId]}, hintInfo)) ); +export default connect(ActionInfo, [ACTION_TOKENS.HINT, KeyboardTokens.KEYMAP], { + mapProps: ([ hintInfo, keymap ]) => (Object.assign({hotKey: hintInfo && keymap[hintInfo.actionId]}, hintInfo)) +}); diff --git a/web/app/cad/dom/components/PlugableControlBar.jsx b/web/app/cad/dom/components/PlugableControlBar.jsx index 3a0550d0..8839beb5 100644 --- a/web/app/cad/dom/components/PlugableControlBar.jsx +++ b/web/app/cad/dom/components/PlugableControlBar.jsx @@ -15,10 +15,13 @@ export default function PlugableControlBar() { function ButtonGroup({actions}) { return actions.map(actionRef => { let [id, overrides] = toIdAndOverrides(actionRef); - let Comp = connect([ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)], - ActionButton, {actionId: id}, - ([appearance, state]) => Object.assign({}, appearance, state, overrides), - mapActionBehavior(id) + let Comp = connect(ActionButton, + [ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)], + { + staticProps: {actionId: id}, + mapProps: ([appearance, state]) => Object.assign({}, appearance, state, overrides), + mapActions: mapActionBehavior(id) + } ); return ; }); @@ -46,8 +49,12 @@ class ActionButton extends React.Component { } } -const LeftGroup = connect(UI_TOKENS.CONTROL_BAR_LEFT, ButtonGroup, undefined, ([actions]) => ({actions})); -const RightGroup = connect(UI_TOKENS.CONTROL_BAR_RIGHT, ButtonGroup, undefined, ([actions]) => ({actions})); +const BUTTON_CONNECTOR = { + mapProps: ([actions]) => ({actions}) +}; + +const LeftGroup = connect(ButtonGroup, UI_TOKENS.CONTROL_BAR_LEFT, BUTTON_CONNECTOR); +const RightGroup = connect(ButtonGroup, UI_TOKENS.CONTROL_BAR_RIGHT, BUTTON_CONNECTOR); function getMenuData(el) { //TODO: make more generic diff --git a/web/app/cad/dom/components/PlugableToolbar.jsx b/web/app/cad/dom/components/PlugableToolbar.jsx index 095668af..62336064 100644 --- a/web/app/cad/dom/components/PlugableToolbar.jsx +++ b/web/app/cad/dom/components/PlugableToolbar.jsx @@ -15,9 +15,11 @@ function ConfigurableToolbar({actions, small, ...props}) { {actions.map(actionRef => { let [id, overrides] = toIdAndOverrides(actionRef); let Comp = connect( - [ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)], - ActionButton, {small}, - ([appearance, state]) => Object.assign({}, appearance, state, overrides)); + ActionButton, + [ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)], { + staticProps: {small}, + mapProps: ([appearance, state]) => Object.assign({}, appearance, state, overrides) + }); return })} @@ -37,7 +39,10 @@ function ActionButton({label, icon96, cssIcons, small, enabled, visible, onClick } export function createPlugableToolbar(configToken, small) { - return connect(configToken, ConfigurableToolbar, {small}, ([actions]) => ({actions}) ); + return connect(ConfigurableToolbar, configToken, { + staticProps: {small}, + mapProps: ([actions]) => ({actions}) + }); } export const PlugableToolbarLeft = createPlugableToolbar(UI_TOKENS.TOOLBAR_BAR_LEFT); diff --git a/web/app/cad/dom/components/UISystem.jsx b/web/app/cad/dom/components/UISystem.jsx index e6924dfd..5d39c41f 100644 --- a/web/app/cad/dom/components/UISystem.jsx +++ b/web/app/cad/dom/components/UISystem.jsx @@ -1,10 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; -import MenuHolder from "../menu/MenuHolder"; +import MenuHolder from '../menu/MenuHolder'; import {TOKENS as MENU_TOKENS} from '../menu/menuPlugin'; import WindowSystem from 'ui/WindowSystem'; -import ActionInfo from "../actionInfo/ActionInfo"; +import ActionInfo from '../actionInfo/ActionInfo'; export default class UISystem extends React.Component { @@ -17,12 +17,15 @@ export default class UISystem extends React.Component { } + shouldComponentUpdate() { + return false; + } + closeAllUpPopups = () => { let openedMenus = this.context.bus.state[MENU_TOKENS.OPENED]; if (openedMenus && openedMenus.length !== 0) { this.context.bus.dispatch(MENU_TOKENS.CLOSE_ALL); } - }; getChildContext() { diff --git a/web/app/cad/dom/components/View3d.jsx b/web/app/cad/dom/components/View3d.jsx index 6499ee02..480ba076 100644 --- a/web/app/cad/dom/components/View3d.jsx +++ b/web/app/cad/dom/components/View3d.jsx @@ -24,9 +24,15 @@ import ButtonGroup from "ui/components/controls/ButtonGroup"; import Button from "ui/components/controls/Button"; import TextControl from './../../../../../modules/ui/components/controls/TextControl'; import UISystem from './UISystem'; +import WizardManager from './wizard/WizardManager'; -export default class View3d extends React.PureComponent { +export default class View3d extends React.Component { + + shouldComponentUpdate() { + //we don't want the dom to be updated under any circumstances or we loose the WEB-GL container + return false; + } render() { return @@ -43,8 +49,8 @@ export default class View3d extends React.PureComponent { + - diff --git a/web/app/cad/dom/components/WebApplication.jsx b/web/app/cad/dom/components/WebApplication.jsx index af6f7c54..92a5e1b4 100644 --- a/web/app/cad/dom/components/WebApplication.jsx +++ b/web/app/cad/dom/components/WebApplication.jsx @@ -15,9 +15,9 @@ const DEFAULT_VIEW = {id: 'view3d', label: '3D View', Component: View3d}; export default class WebApplication extends React.Component { - constructor({bus}) { + constructor({appContext}) { super(); - this.bus = bus; + this.appContext = appContext; this.views = [DEFAULT_VIEW, {id: 'XXX', label: '3D View2', Component: Fragment}]; this.state = { activeView: DEFAULT_VIEW @@ -49,11 +49,12 @@ export default class WebApplication extends React.Component { } getChildContext() { - return {bus: this.bus}; + return this.appContext; } static childContextTypes = { - bus: PropTypes.object + bus: PropTypes.object, + services: PropTypes.object }; } diff --git a/web/app/cad/dom/components/wizard/Wizard.jsx b/web/app/cad/dom/components/wizard/Wizard.jsx new file mode 100644 index 00000000..44a99d01 --- /dev/null +++ b/web/app/cad/dom/components/wizard/Wizard.jsx @@ -0,0 +1,81 @@ +import React from 'react'; +import Window from 'ui/components/Window'; +import Stack from 'ui/components/Stack'; +import Field from 'ui/components/controls/Field'; +import Label from 'ui/components/controls/Label'; +import camelCaseSplit from 'gems/camelCaseSplit'; +import NumberControl from 'ui/components/controls/NumberControl'; +import TextControl from 'ui/components/controls/TextControl'; +import Button from 'ui/components/controls/Button'; +import ButtonGroup from 'ui/components/controls/ButtonGroup'; + +export default class Wizard extends React.Component { + + constructor({initialState, metadata, previewer}) { + super(); + this.params = {}; + + metadata.forEach(([name,, v]) => this.params[name] = v); + + Object.assign(this.params, initialState); + + this.preview = previewer(this.params); + } + + shouldComponentUpdate() { + // all controls are unmanaged and they should keep their state + // if the wizard manager gets updated when a new wizard appears + return false; + } + + render() { + let {left, title, metadata, onOK, onCancel} = this.props; + let onClose = () => { + this.onClose(); + onCancel(); + }; + return + + {metadata.map(([name, type, , params], index) => { + return + + {this.controlForType(name, type, params)} + + } )} + +