diff --git a/modules/gems/iterables.js b/modules/gems/iterables.js
index 4443f544..b6b55ef2 100644
--- a/modules/gems/iterables.js
+++ b/modules/gems/iterables.js
@@ -41,4 +41,10 @@ export function flatten(arr, result = [], depth, _currLevel) {
return result;
}
+export function indexArray(array, getKey, getValue = v => v) {
+ let obj = {};
+ array.forEach(item => obj[getKey(item)] = getValue(item))
+ return obj;
+}
+
export const EMPTY_ARRAY = Object.freeze([]);
diff --git a/modules/ui/bind.js b/modules/ui/bind.js
new file mode 100644
index 00000000..c02bc846
--- /dev/null
+++ b/modules/ui/bind.js
@@ -0,0 +1,41 @@
+import React from 'react';
+import context from 'context';
+
+export default function bind(streamProvider) {
+ return function (Component) {
+ return class Connected extends React.Component {
+
+ state = {hasError: false, value: null};
+
+ onChange = value => streamProvider(context.streams, this.props).next(value);
+
+ componentWillMount() {
+ this.stream = streamProvider(context.streams, this.props);
+ this.detacher = this.stream.attach(value => {
+ this.setState({
+ hasError: false,
+ value
+ });
+ });
+ }
+
+ componentWillUnmount() {
+ this.detacher();
+ }
+
+ render() {
+ if (this.state.hasError) {
+ return null;
+ }
+ return ;
+
+ }
+
+ componentDidCatch() {
+ this.setState({hasError: true});
+ }
+ };
+ }
+}
diff --git a/modules/ui/components/Folder.jsx b/modules/ui/components/Folder.jsx
index 7fbcb922..8dba4e03 100644
--- a/modules/ui/components/Folder.jsx
+++ b/modules/ui/components/Folder.jsx
@@ -2,6 +2,7 @@ import React from 'react';
import ls from './Folder.less'
import Fa from "./Fa";
+import cx from 'classnames';
export default class Folder extends React.Component{
@@ -23,11 +24,11 @@ export default class Folder extends React.Component{
};
render() {
- let {title, closable, children} = this.props;
- return
+ let {title, closable, className, children} = this.props;
+ return
- {title}
+ {' '}{title}
{!this.isClosed() && children}
diff --git a/modules/ui/components/Row.jsx b/modules/ui/components/Row.jsx
index e69de29b..7ee45324 100644
--- a/modules/ui/components/Row.jsx
+++ b/modules/ui/components/Row.jsx
@@ -0,0 +1,7 @@
+import React from 'react';
+import ls from './Row.less';
+import cx from 'classnames';
+
+export default function Row({className, props, children}) {
+ return
{children}
;
+}
\ No newline at end of file
diff --git a/modules/ui/components/Row.less b/modules/ui/components/Row.less
new file mode 100644
index 00000000..9a2935d5
--- /dev/null
+++ b/modules/ui/components/Row.less
@@ -0,0 +1,9 @@
+.root {
+ line-height: 1px;
+}
+
+.root > * {
+ line-height: 1px;
+ margin: 3px;
+ padding: 5px 3px;
+}
\ No newline at end of file
diff --git a/modules/ui/components/ToolButton.jsx b/modules/ui/components/ToolButton.jsx
new file mode 100644
index 00000000..96fb9461
--- /dev/null
+++ b/modules/ui/components/ToolButton.jsx
@@ -0,0 +1,7 @@
+import React from 'react';
+import ls from './ToolButton.less';
+import cx from 'classnames';
+
+export default function ToolButton({pressed, type, className, ...props}) {
+ return ;
+}
diff --git a/modules/ui/components/ToolButton.less b/modules/ui/components/ToolButton.less
new file mode 100644
index 00000000..8fde30d8
--- /dev/null
+++ b/modules/ui/components/ToolButton.less
@@ -0,0 +1,38 @@
+@import "../styles/theme.less";
+
+.root {
+
+ border: 1px solid @bg-color;
+ margin: 3px;
+ border-radius: 3px;
+
+ padding: 5px 2px;
+ cursor: pointer;
+ background-color: @bg-color;
+
+ outline: none;
+
+ &:hover {
+ background-color: #9c9c9c !important;
+ }
+ &:active {
+ transition: 200ms;
+ background-color: #BFBFBF !important;
+ }
+ &.pressed {
+ background-color: #7d7d7d;
+ }
+}
+
+.accent {
+ background-color: @color-accent;
+}
+
+.highlight {
+ background-color: @color-highlight;
+}
+
+.danger {
+ background-color: @color-danger;
+}
+
diff --git a/modules/ui/components/controls/Button.less b/modules/ui/components/controls/Button.less
index 8c764b6a..8fab0f2b 100644
--- a/modules/ui/components/controls/Button.less
+++ b/modules/ui/components/controls/Button.less
@@ -17,6 +17,10 @@
.button-behavior(@color-accent)
}
+.highlihgt {
+ .button-behavior(@color-highlight)
+}
+
.danger {
.button-behavior(@color-danger)
}
diff --git a/modules/ui/components/controls/NumberControl.jsx b/modules/ui/components/controls/NumberControl.jsx
index 4bfede66..e244879f 100644
--- a/modules/ui/components/controls/NumberControl.jsx
+++ b/modules/ui/components/controls/NumberControl.jsx
@@ -8,30 +8,22 @@ export default class NumberControl extends React.Component {
let {onChange, value} = this.props;
return this.input = input} />
}
onChange = e => {
- let val;
- try {
- val = parseFloat(e.target.value)
- } catch (ignore) {
- return;
- }
- if (!isNaN(val)) {
- this.props.onChange(val);
- }
+ this.props.onChange(e.target.value);
};
onWheel = (e) => {
let {baseStep, round, min, max, onChange, accelerator} = this.props;
let delta = e.deltaY;
- let val = e.target.value;
- if (!val) val = 0;
let step = baseStep * (e.shiftKey ? accelerator : 1);
- val = parseFloat(val) + (delta < 0 ? -step : step);
+ let val = parseFloat(e.target.value);
+ if (isNaN(val)) val = 0;
+ val = val + (delta < 0 ? -step : step);
if (min !== undefined && val < min) {
val = min;
}
diff --git a/modules/ui/styles/common.less b/modules/ui/styles/common.less
new file mode 100644
index 00000000..0809554e
--- /dev/null
+++ b/modules/ui/styles/common.less
@@ -0,0 +1,26 @@
+@import "./theme.less";
+@import "./table.less";
+
+*.autoMarginLeft {
+ margin-left: auto !important;
+}
+
+*.floatRight {
+ float: right;
+}
+
+.fullWidth {
+ width: 100%;
+}
+
+.dangerColor {
+ color: @color-danger;
+}
+
+.dangerBg {
+ background-color: @color-danger;
+}
+
+.inlineBlock {
+ display: inline-block;
+}
diff --git a/modules/ui/styles/init/main.less b/modules/ui/styles/init/main.less
index c9b187d9..72fcca75 100644
--- a/modules/ui/styles/init/main.less
+++ b/modules/ui/styles/init/main.less
@@ -1,8 +1,10 @@
@import "../theme.less";
@import "../mixins.less";
+@fontSize: 11px;
+
html, pre {
- font: 11px 'Lucida Grande', sans-serif;
+ font: @fontSize 'Lucida Grande', sans-serif;
}
body {
@@ -31,3 +33,7 @@ button {
pre {
line-height: 1.5;
}
+
+table {
+ font-size: @fontSize;
+}
\ No newline at end of file
diff --git a/modules/ui/styles/table.less b/modules/ui/styles/table.less
new file mode 100644
index 00000000..5693501a
--- /dev/null
+++ b/modules/ui/styles/table.less
@@ -0,0 +1,37 @@
+@import "./theme.less";
+
+.stripedTable {
+ & tr:nth-child(even), & th {
+ background-color: @bg-color
+ }
+ & tr:nth-child(odd) {
+ background-color: @bg-color-alt
+ }
+ & tr:hover {
+ background-color: @color-highlight;
+ }
+}
+
+.delineatedTable {
+ border-collapse: collapse;
+ border-spacing: 0;
+ & td, & th {
+ padding: 5px 2px;
+ border: 1px solid @border-color;
+ }
+
+ & tr:first-child th {
+ border-top: 0;
+ }
+ //& tr:last-child td {
+ // border-bottom: 0;
+ //}
+ & tr td:first-child,
+ & tr th:first-child {
+ border-left: 0;
+ }
+ & tr td:last-child,
+ & tr th:last-child {
+ border-right: 0;
+ }
+}
\ No newline at end of file
diff --git a/modules/ui/styles/theme.less b/modules/ui/styles/theme.less
index 9901d6e8..85e684e2 100644
--- a/modules/ui/styles/theme.less
+++ b/modules/ui/styles/theme.less
@@ -22,6 +22,7 @@
@color-danger: #b00;
@color-accent: #2B7D2B;
@color-neutral: #66727d;
+@color-highlight: #003f5d;
//@work-area-toolbar-bg-color: ;
//@work-area-toolbar-font-color: ;
diff --git a/web/app/cad/actions/actionDecorators.jsx b/web/app/cad/actions/actionDecorators.jsx
new file mode 100644
index 00000000..e1e00a22
--- /dev/null
+++ b/web/app/cad/actions/actionDecorators.jsx
@@ -0,0 +1,12 @@
+import React, {Fragment} from 'react';
+
+import {mapActionBehavior} from './actionButtonBehavior';
+
+export function actionDecorator(actionId) {
+ let actionBehavior = mapActionBehavior(actionId);
+ return function (Component) {
+ return function ActionDecorator(props) {
+ return ;
+ }
+ }
+}
\ No newline at end of file
diff --git a/web/app/cad/craft/craftPlugin.js b/web/app/cad/craft/craftPlugin.js
index d3b4eb5f..d44aaaa1 100644
--- a/web/app/cad/craft/craftPlugin.js
+++ b/web/app/cad/craft/craftPlugin.js
@@ -2,6 +2,8 @@ import {addModification, stepOverriding} from './craftHistoryUtils';
import {state, stream} from 'lstream';
import {MShell} from '../model/mshell';
import {MDatum} from '../model/mdatum';
+import materializeParams from './materializeParams';
+import CadError from '../../utils/errors';
export function activate({streams, services}) {
@@ -54,7 +56,17 @@ export function activate({streams, services}) {
throw(`unknown operation ${request.type}`);
}
- return op.run(request.params, services);
+ let params = {};
+ let errors = [];
+ materializeParams(services, request.params, op.schema, params, errors);
+ if (errors.length) {
+ throw new CadError({
+ kind: CadError.KIND.INVALID_PARAMS,
+ userMessage: errors.map(err => `${err.path.join('.')}: ${err.message}`).join('\n')
+ });
+ }
+
+ return op.run(params, services);
}
function runOrGetPreRunResults(request) {
@@ -92,6 +104,7 @@ export function activate({streams, services}) {
streams.craft.models.next(Array.from(models).sort(m => m.id));
} catch(e) {
console.error(e);
+ //TODO: need to find a way to propagate the error to the wizard.
setTimeout(() => streams.craft.modifications.next({
...curr,
pointer: i-1
diff --git a/web/app/cad/craft/intializeBySchema.js b/web/app/cad/craft/intializeBySchema.js
index 8abbc11c..9a5acfe5 100644
--- a/web/app/cad/craft/intializeBySchema.js
+++ b/web/app/cad/craft/intializeBySchema.js
@@ -25,6 +25,8 @@ export default function initializeBySchema(schema, context) {
val = context.streams.selection[md.type].value[0];
} else if (md.type === 'object') {
val = initializeBySchema(md.schema, context);
+ } else if (md.type === 'number') {
+ val = md.defaultValue + '';
} else {
val = md.defaultValue;
}
diff --git a/web/app/cad/craft/validateParams.js b/web/app/cad/craft/materializeParams.js
similarity index 64%
rename from web/app/cad/craft/validateParams.js
rename to web/app/cad/craft/materializeParams.js
index da4fe402..1e081c50 100644
--- a/web/app/cad/craft/validateParams.js
+++ b/web/app/cad/craft/materializeParams.js
@@ -1,8 +1,7 @@
import {ENTITIES} from '../scene/entites';
-export default function validateParams(services, params, schema, errors, parentPath) {
+export default function materializeParams(services, params, schema, result, errors, parentPath) {
- errors = errors || [];
parentPath = parentPath || ROOT_PATH;
for (let field of Object.keys(schema)) {
@@ -17,18 +16,20 @@ export default function validateParams(services, params, schema, errors, parentP
}
} else {
if (md.type === 'number') {
- if (typeof value !== 'number') {
- errors.push({path: [...parentPath, field], message: 'not a number type'});
- } else {
- if (md.min !== undefined ) {
- if (value < md.min) {
- errors.push({path: [...parentPath, field], message: 'less than allowed'});
- }
+ try {
+ value = services.expressions.evaluateExpression(value);
+ } catch (e) {
+ errors.push({path: [...parentPath, field], message: 'unable to evaluate expression'});
+ }
+
+ if (md.min !== undefined ) {
+ if (value < md.min) {
+ errors.push({path: [...parentPath, field], message: 'less than allowed'});
}
- if (md.max !== undefined ) {
- if (value > md.max) {
- errors.push({path: [...parentPath, field], message: 'greater than allowed'});
- }
+ }
+ if (md.max !== undefined ) {
+ if (value > md.max) {
+ errors.push({path: [...parentPath, field], message: 'greater than allowed'});
}
}
} else if (md.type === 'string') {
@@ -55,12 +56,14 @@ export default function validateParams(services, params, schema, errors, parentP
if (!Array.isArray(value)) {
errors.push({path: [...parentPath, field], message: 'not an array type'});
}
- value.forEach((item , i) => {
- validateParams(services, item, md.schema, errors, [...parentPath, i]);
+ value = value.map((item , i) => {
+ let itemResult = {};
+ materializeParams(services, item, md.schema, item, errors, [...parentPath, i]);
+ return itemResult;
});
}
+ result[field] = value;
}
- return errors;
}
}
diff --git a/web/app/cad/craft/wizard/components/Wizard.jsx b/web/app/cad/craft/wizard/components/Wizard.jsx
index 9a828cff..40a9b2b6 100644
--- a/web/app/cad/craft/wizard/components/Wizard.jsx
+++ b/web/app/cad/craft/wizard/components/Wizard.jsx
@@ -23,7 +23,6 @@ export default class Wizard extends React.Component {
state = {
hasError: false,
- validationErrors: [],
};
@@ -51,8 +50,7 @@ export default class Wizard extends React.Component {
let formContext = {
data: params,
- validationErrors: this.state.validationErrors,
- updateParam,
+ updateParam
};
let Form = operation.form;
@@ -72,15 +70,13 @@ export default class Wizard extends React.Component {
{this.state.hasError &&
- performing operation with current parameters leads to an invalid object
- (self-intersecting / zero-thickness / complete degeneration or unsupported cases)
+ {this.state.algorithmError &&
+ performing operation with current parameters leads to an invalid object
+ (self-intersecting / zero-thickness / complete degeneration or unsupported cases)
+ }
{this.state.code &&
{this.state.code}
}
{this.state.userMessage &&
{this.state.userMessage}
}
}
- {this.state.validationErrors.length !== 0 &&
- {this.state.validationErrors.map((err, i) =>
{err.path.join(' ')} {err.message}
)}
-
}
-
;
}
@@ -110,17 +106,6 @@ export default class Wizard extends React.Component {
onOK = () => {
try {
- let {type, params, resolveOperation, validator} = this.props;
- if (!type) {
- return null;
- }
-
- let operation = resolveOperation(type);
- let validationErrors = validator(params, operation.schema);
- if (validationErrors.length !== 0) {
- this.setState({validationErrors});
- return;
- }
this.props.onOK();
} catch (error) {
this.handleError(error);
@@ -135,6 +120,9 @@ export default class Wizard extends React.Component {
if (error.TYPE === CadError) {
let {code, userMessage, kind} = error;
printError = !code;
+ if (CadError.ALGORITMTHM_ERROR_KINDS.includes(kind)) {
+ stateUpdate.algorithmError = true
+ }
if (code && kind === CadError.KIND.INTERNAL_ERROR) {
console.warn('Operation Error Code: ' + code);
}
diff --git a/web/app/cad/craft/wizard/components/Wizard.less b/web/app/cad/craft/wizard/components/Wizard.less
index a16baad4..2d532a39 100644
--- a/web/app/cad/craft/wizard/components/Wizard.less
+++ b/web/app/cad/craft/wizard/components/Wizard.less
@@ -1,15 +1,9 @@
.errorMessage {
color: lightgoldenrodyellow;
-}
-
-.userErrorMessage, .errorCode {
- font-size: 9px;
-}
-
-.userErrorMessage {
- color: white;
+ white-space: pre-line;
}
.errorCode {
+ font-size: 9px;
font-style: italic;
-}
\ No newline at end of file
+}
diff --git a/web/app/cad/craft/wizard/components/WizardManager.jsx b/web/app/cad/craft/wizard/components/WizardManager.jsx
index f6aa720a..43bba870 100644
--- a/web/app/cad/craft/wizard/components/WizardManager.jsx
+++ b/web/app/cad/craft/wizard/components/WizardManager.jsx
@@ -3,17 +3,13 @@ import Wizard from './Wizard';
import connect from 'ui/connect';
import decoratorChain from 'ui/decoratorChain';
import mapContext from 'ui/mapContext';
-import {finishHistoryEditing, stepOverriding} from '../../craftHistoryUtils';
-import validateParams from '../../validateParams';
-import {NOOP} from 'gems/func';
-import {clone} from 'gems/objects';
+import {finishHistoryEditing} from '../../craftHistoryUtils';
-function WizardManager({type, changingHistory, resolve, cancel, stepHistory, insertOperation, cancelHistoryEdit, applyWorkingRequest, validator}) {
+function WizardManager({type, changingHistory, resolve, cancel, stepHistory, insertOperation, cancelHistoryEdit, applyWorkingRequest}) {
if (!type) {
return null;
}
return
}
@@ -22,7 +18,6 @@ export default decoratorChain(
connect(streams => streams.wizard.effectiveOperation),
mapContext((ctx, props) => ({
cancel: ctx.services.wizard.cancel,
- validator: (params, schema) => validateParams(ctx.services, params, schema),
resolve: type => ctx.services.operation.get(type),
cancelHistoryEdit: () => ctx.streams.craft.modifications.update(modifications => finishHistoryEditing(modifications)),
applyWorkingRequest: ctx.services.wizard.applyWorkingRequest
diff --git a/web/app/cad/dom/components/FloatView.jsx b/web/app/cad/dom/components/FloatView.jsx
index c07baf1c..d738e533 100644
--- a/web/app/cad/dom/components/FloatView.jsx
+++ b/web/app/cad/dom/components/FloatView.jsx
@@ -1,11 +1,15 @@
import React from 'react';
-import ObjectExplorer from '../../craft/ui/ObjectExplorer';
-import OperationHistory from '../../craft/ui/OperationHistory';
import Folder from 'ui/components/Folder';
-import Fa from '../../../../../modules/ui/components/Fa';
import ls from './FloatView.less';
-import cx from 'classnames';
+import connect from 'ui/connect';
+import mapContext from 'ui/mapContext';
+import Fa from 'ui/components/Fa';
+import ToolButton from 'ui/components/ToolButton';
+@connect(state => state.ui.floatViews.map(views => ({views})))
+@mapContext(ctx => ({
+ getDescriptor: ctx.services.ui.getFloatView
+}))
export default class FloatView extends React.Component {
state = {
@@ -13,33 +17,32 @@ export default class FloatView extends React.Component {
};
render() {
+ let {views, getDescriptor} = this.props;
+
+ function view(id) {
+ let {title, icon, Component} = getDescriptor(id);
+ return {title}}>
+
+ ;
+ }
+
+ function icon(id) {
+ let {Icon} = getDescriptor(id);
+ return
+ }
+
return