Merge branch 'develop' into attachSvgRenderer
This commit is contained in:
commit
bee4ffb8a5
17 files changed
+408
-5896
No files matched your search
@@ -6,5 +6,5 @@
|
||||
["react-intl", {
|
||||
"messagesDir": "./translations/messages/"
|
||||
}]],
|
||||
"presets": ["es2015", "react"]
|
||||
"presets": [["env", {"targets": {browsers: ["last 3 versions", "Safari >= 8", "iOS >= 8"]}}], "react"]
|
||||
}
|
||||
+13
-11
@@ -27,6 +27,7 @@
|
||||
"react-dom": "^16.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"arraybuffer-loader": "^1.0.3",
|
||||
"autoprefixer": "^8.1.0",
|
||||
"babel-core": "^6.23.1",
|
||||
"babel-eslint": "^8.0.1",
|
||||
@@ -34,13 +35,12 @@
|
||||
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
||||
"babel-plugin-transform-async-to-generator": "^6.24.1",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.22.0",
|
||||
"babel-preset-es2015": "^6.22.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"babel-preset-react": "^6.22.0",
|
||||
"buffer-loader": "0.0.1",
|
||||
"chromedriver": "2.38.2",
|
||||
"classnames": "2.2.5",
|
||||
"copy-webpack-plugin": "^4.3.0",
|
||||
"css-loader": "^0.28.7",
|
||||
"copy-webpack-plugin": "^4.5.1",
|
||||
"css-loader": "^0.28.11",
|
||||
"enzyme": "^3.1.0",
|
||||
"enzyme-adapter-react-16": "1.1.1",
|
||||
"es6-object-assign": "1.1.0",
|
||||
@@ -52,7 +52,7 @@
|
||||
"get-float-time-domain-data": "0.1.0",
|
||||
"get-user-media-promise": "1.1.1",
|
||||
"gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder",
|
||||
"html-webpack-plugin": "^3.0.5",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"immutable": "3.8.2",
|
||||
"jest": "^21.0.0",
|
||||
"lodash.bindall": "4.4.0",
|
||||
@@ -65,11 +65,11 @@
|
||||
"mkdirp": "^0.5.1",
|
||||
"platform": "1.3.5",
|
||||
"postcss-import": "^11.0.0",
|
||||
"postcss-loader": "^2.0.5",
|
||||
"postcss-loader": "^2.1.4",
|
||||
"postcss-simple-vars": "^4.0.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"raf": "^3.4.0",
|
||||
"raw-loader": "0.5.1",
|
||||
"raw-loader": "^0.5.1",
|
||||
"react": "16.2.0",
|
||||
"react-contextmenu": "2.9.2",
|
||||
"react-dom": "16.2.0",
|
||||
@@ -94,17 +94,19 @@
|
||||
"scratch-paint": "0.2.0-prerelease.20180510175214",
|
||||
"scratch-render": "0.1.0-prerelease.20180510210730",
|
||||
"scratch-storage": "0.4.1",
|
||||
"scratch-svg-renderer": "0.1.0-prerelease.20180510171850",
|
||||
"scratch-svg-renderer": "0.1.0-prerelease.20180510181711",
|
||||
"scratch-vm": "0.1.0-prerelease.1525975472",
|
||||
"selenium-webdriver": "3.6.0",
|
||||
"startaudiocontext": "1.2.1",
|
||||
"style-loader": "^0.20.0",
|
||||
"style-loader": "^0.21.0",
|
||||
"svg-to-image": "1.1.3",
|
||||
"text-encoding": "0.6.4",
|
||||
"uglifyjs-webpack-plugin": "^1.2.5",
|
||||
"wav-encoder": "1.3.0",
|
||||
"web-audio-test-api": "^0.5.2",
|
||||
"webpack": "^3.6.0",
|
||||
"webpack-dev-server": "^2.9.7",
|
||||
"webpack": "^4.6.0",
|
||||
"webpack-cli": "^2.0.15",
|
||||
"webpack-dev-server": "^3.1.3",
|
||||
"xhr": "2.4.1"
|
||||
},
|
||||
"jest": {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
|
Before Width: | Height: | Size: 425 KiB After Width: | Height: | Size: 5.1 KiB |
@@ -11,6 +11,10 @@
|
||||
background-color: $ui-modal-overlay;
|
||||
}
|
||||
|
||||
.modal-content * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
margin: 100px auto;
|
||||
outline: none;
|
||||
|
||||
@@ -12,17 +12,25 @@ const MonitorList = props => (
|
||||
// Use static `monitor-overlay` class for bounds of draggables
|
||||
className={classNames(styles.monitorList, 'monitor-overlay')}
|
||||
>
|
||||
{props.monitors.valueSeq().map(monitorData => (
|
||||
<Monitor
|
||||
id={monitorData.id}
|
||||
key={monitorData.id}
|
||||
opcode={monitorData.opcode}
|
||||
params={monitorData.params}
|
||||
spriteName={monitorData.spriteName}
|
||||
value={monitorData.value}
|
||||
onDragEnd={props.onMonitorChange}
|
||||
/>
|
||||
))}
|
||||
{props.monitors.valueSeq().filter(m => m.visible)
|
||||
.map(monitorData => (
|
||||
<Monitor
|
||||
height={monitorData.height}
|
||||
id={monitorData.id}
|
||||
key={monitorData.id}
|
||||
max={monitorData.sliderMax}
|
||||
min={monitorData.sliderMin}
|
||||
mode={monitorData.mode}
|
||||
opcode={monitorData.opcode}
|
||||
params={monitorData.params}
|
||||
spriteName={monitorData.spriteName}
|
||||
value={monitorData.value}
|
||||
width={monitorData.width}
|
||||
x={monitorData.x}
|
||||
y={monitorData.y}
|
||||
onDragEnd={props.onMonitorChange}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
|
||||
|
||||
@@ -4,14 +4,16 @@ import styles from './monitor.css';
|
||||
|
||||
const DefaultMonitor = ({categoryColor, label, value}) => (
|
||||
<div className={styles.defaultMonitor}>
|
||||
<div className={styles.label}>
|
||||
{label}
|
||||
</div>
|
||||
<div
|
||||
className={styles.value}
|
||||
style={{background: categoryColor}}
|
||||
>
|
||||
{value}
|
||||
<div className={styles.row}>
|
||||
<div className={styles.label}>
|
||||
{label}
|
||||
</div>
|
||||
<div
|
||||
className={styles.value}
|
||||
style={{background: categoryColor}}
|
||||
>
|
||||
{value}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styles from './monitor.css';
|
||||
|
||||
const ListMonitor = ({categoryColor, label, width, height, value}) => (
|
||||
<div
|
||||
className={styles.listMonitor}
|
||||
style={{
|
||||
width: `${width || 80}px`,
|
||||
height: `${height || 200}px`
|
||||
}}
|
||||
>
|
||||
<div className={styles.listHeader}>
|
||||
{label}
|
||||
</div>
|
||||
<div className={styles.listBody}>
|
||||
{!value || value.length === 0 ? (
|
||||
<div className={styles.listEmpty}>
|
||||
{'(empty)' /* @todo not translating, awaiting design */}
|
||||
</div>
|
||||
) : value.map((v, i) => (
|
||||
<div
|
||||
className={styles.listRow}
|
||||
key={`label-${i}`}
|
||||
>
|
||||
<div className={styles.listIndex}>{i + 1 /* one indexed */}</div>
|
||||
<div
|
||||
className={styles.listValue}
|
||||
style={{background: categoryColor}}
|
||||
>
|
||||
<div className={styles.valueInner}>{v}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className={styles.listFooter}>
|
||||
<div className={styles.footerButton}>
|
||||
{/* @todo add button here */}
|
||||
</div>
|
||||
<div className={styles.footerLength}>
|
||||
<span className={styles.lengthNumber}>
|
||||
{value.length}
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.resizeHandle}>
|
||||
{/* @todo resize handle */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
ListMonitor.propTypes = {
|
||||
categoryColor: PropTypes.string.isRequired,
|
||||
height: PropTypes.number,
|
||||
label: PropTypes.string.isRequired,
|
||||
value: PropTypes.arrayOf(PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number
|
||||
])),
|
||||
width: PropTypes.number
|
||||
};
|
||||
|
||||
ListMonitor.defaultProps = {
|
||||
width: 80,
|
||||
height: 200
|
||||
};
|
||||
|
||||
export default ListMonitor;
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
.default-monitor {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
@@ -45,11 +46,94 @@
|
||||
}
|
||||
|
||||
.large-value {
|
||||
min-height: 1.5rem;
|
||||
min-height: 1.4rem;
|
||||
min-width: 3rem;
|
||||
padding: 0.25rem;
|
||||
padding: 0.1rem 0.25rem;
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
transform: translateZ(0); /* Fixes flickering in Safari */
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.list-monitor {
|
||||
min-width: 80px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.list-header {
|
||||
background: $ui-white;
|
||||
border-bottom: 1px solid $ui-black-transparent;
|
||||
text-align: center;
|
||||
padding: 3px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: bold;
|
||||
color: $text-primary;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.list-body {
|
||||
background: $ui-primary;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: scroll;
|
||||
height: calc(100% - 44px);
|
||||
}
|
||||
|
||||
.list-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
padding: 2px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.list-index {
|
||||
font-weight: bold;
|
||||
color: $text-primary;
|
||||
margin: 0 3px;
|
||||
}
|
||||
|
||||
.list-value {
|
||||
min-width: 40px;
|
||||
text-align: left;
|
||||
color: white;
|
||||
margin: 0 3px;
|
||||
border-radius: calc($space / 2);
|
||||
border: 1px solid $ui-black-transparent;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.list-footer {
|
||||
background: $ui-white;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding: 3px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: bold;
|
||||
color: $text-primary;
|
||||
}
|
||||
|
||||
.list-empty {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.value-inner {
|
||||
padding: 3px 5px;
|
||||
min-height: 22px;
|
||||
}
|
||||
@@ -7,6 +7,8 @@ import {ContextMenu, MenuItem} from '../context-menu/context-menu.jsx';
|
||||
import Box from '../box/box.jsx';
|
||||
import DefaultMonitor from './default-monitor.jsx';
|
||||
import LargeMonitor from './large-monitor.jsx';
|
||||
import SliderMonitor from './slider-monitor.jsx';
|
||||
import ListMonitor from './list-monitor.jsx';
|
||||
|
||||
import styles from './monitor.css';
|
||||
|
||||
@@ -19,74 +21,102 @@ const categories = {
|
||||
list: '#FC662C'
|
||||
};
|
||||
|
||||
const types = {
|
||||
const modes = {
|
||||
default: DefaultMonitor,
|
||||
large: LargeMonitor
|
||||
large: LargeMonitor,
|
||||
slider: SliderMonitor,
|
||||
list: ListMonitor
|
||||
};
|
||||
|
||||
const MonitorComponent = props => (
|
||||
<ContextMenuTrigger id={`monitor-${props.label}`}>
|
||||
<Draggable
|
||||
bounds=".monitor-overlay" // Class for monitor container
|
||||
cancel=".no-drag" // Class used for slider input to prevent drag
|
||||
defaultClassNameDragging={styles.dragging}
|
||||
onStop={props.onDragEnd}
|
||||
>
|
||||
<Box
|
||||
className={styles.monitorContainer}
|
||||
componentRef={props.componentRef}
|
||||
onDoubleClick={props.onNextType}
|
||||
onDoubleClick={props.mode === 'list' ? null : props.onNextMode}
|
||||
>
|
||||
{types[props.type]({
|
||||
{(modes[props.mode] || modes.default)({ // Use default until other modes arrive
|
||||
categoryColor: categories[props.category],
|
||||
label: props.label,
|
||||
value: props.value
|
||||
value: props.value,
|
||||
width: props.width,
|
||||
height: props.height,
|
||||
min: props.min,
|
||||
max: props.max
|
||||
})}
|
||||
</Box>
|
||||
</Draggable>
|
||||
<ContextMenu id={`monitor-${props.label}`}>
|
||||
<MenuItem onClick={props.onSetTypeToDefault}>
|
||||
<FormattedMessage
|
||||
defaultMessage="normal readout"
|
||||
description="Menu item to switch to the default monitor"
|
||||
id="gui.monitor.contextMenu.default"
|
||||
/>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={props.onSetTypeToLarge}>
|
||||
<FormattedMessage
|
||||
defaultMessage="large readout"
|
||||
description="Menu item to switch to the large monitor"
|
||||
id="gui.monitor.contextMenu.large"
|
||||
/>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
{props.mode === 'list' ? null : (
|
||||
<ContextMenu id={`monitor-${props.label}`}>
|
||||
<MenuItem onClick={props.onSetModeToDefault}>
|
||||
<FormattedMessage
|
||||
defaultMessage="normal readout"
|
||||
description="Menu item to switch to the default monitor"
|
||||
id="gui.monitor.contextMenu.default"
|
||||
/>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={props.onSetModeToLarge}>
|
||||
<FormattedMessage
|
||||
defaultMessage="large readout"
|
||||
description="Menu item to switch to the large monitor"
|
||||
id="gui.monitor.contextMenu.large"
|
||||
/>
|
||||
</MenuItem>
|
||||
{props.onSetModeToSlider ? (
|
||||
<MenuItem onClick={props.onSetModeToSlider}>
|
||||
<FormattedMessage
|
||||
defaultMessage="slider"
|
||||
description="Menu item to switch to the slider monitor"
|
||||
id="gui.monitor.contextMenu.slider"
|
||||
/>
|
||||
</MenuItem>
|
||||
) : null}
|
||||
</ContextMenu>
|
||||
)}
|
||||
</ContextMenuTrigger>
|
||||
|
||||
);
|
||||
|
||||
MonitorComponent.categories = categories;
|
||||
|
||||
const monitorTypes = Object.keys(types);
|
||||
const monitorModes = Object.keys(modes);
|
||||
|
||||
MonitorComponent.propTypes = {
|
||||
category: PropTypes.oneOf(Object.keys(categories)),
|
||||
componentRef: PropTypes.func.isRequired,
|
||||
height: PropTypes.number,
|
||||
label: PropTypes.string.isRequired,
|
||||
max: PropTypes.number,
|
||||
min: PropTypes.number,
|
||||
mode: PropTypes.oneOf(monitorModes),
|
||||
onDragEnd: PropTypes.func.isRequired,
|
||||
onNextType: PropTypes.func.isRequired,
|
||||
onSetTypeToDefault: PropTypes.func.isRequired,
|
||||
onSetTypeToLarge: PropTypes.func.isRequired,
|
||||
type: PropTypes.oneOf(monitorTypes),
|
||||
onNextMode: PropTypes.func.isRequired,
|
||||
onSetModeToDefault: PropTypes.func.isRequired,
|
||||
onSetModeToLarge: PropTypes.func.isRequired,
|
||||
onSetModeToSlider: PropTypes.func,
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number])
|
||||
PropTypes.number,
|
||||
PropTypes.arrayOf(PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number
|
||||
]))
|
||||
]),
|
||||
width: PropTypes.number
|
||||
};
|
||||
|
||||
MonitorComponent.defaultProps = {
|
||||
category: 'data',
|
||||
type: 'default'
|
||||
mode: 'default'
|
||||
};
|
||||
|
||||
export {
|
||||
MonitorComponent as default,
|
||||
monitorTypes
|
||||
monitorModes
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styles from './monitor.css';
|
||||
|
||||
const SliderMonitor = ({categoryColor, label, min, max, value}) => (
|
||||
<div className={styles.defaultMonitor}>
|
||||
<div className={styles.row}>
|
||||
<div className={styles.label}>
|
||||
{label}
|
||||
</div>
|
||||
<div
|
||||
className={styles.value}
|
||||
style={{background: categoryColor}}
|
||||
>
|
||||
{value}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.row}>
|
||||
<input
|
||||
className="no-drag" // Class used on parent Draggable to prevent drags
|
||||
max={max}
|
||||
min={min}
|
||||
type="range"
|
||||
value={value}
|
||||
// @todo onChange callback
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
|
||||
SliderMonitor.propTypes = {
|
||||
categoryColor: PropTypes.string.isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
max: PropTypes.number,
|
||||
min: PropTypes.number,
|
||||
// @todo callback for change events
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number
|
||||
])
|
||||
};
|
||||
|
||||
SliderMonitor.defaultProps = {
|
||||
min: 0,
|
||||
max: 100
|
||||
};
|
||||
|
||||
export default SliderMonitor;
|
||||
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import VM from 'scratch-vm';
|
||||
import {connect} from 'react-redux';
|
||||
import ReactModal from 'react-modal';
|
||||
|
||||
import {openExtensionLibrary} from '../reducers/modals';
|
||||
import {
|
||||
@@ -120,4 +121,7 @@ const ConnectedGUI = connect(
|
||||
mapDispatchToProps,
|
||||
)(GUI);
|
||||
|
||||
export default ProjectLoaderHOC(AppStateHOC(vmListenerHOC(ConnectedGUI)));
|
||||
const WrappedGui = ProjectLoaderHOC(AppStateHOC(vmListenerHOC(ConnectedGUI)));
|
||||
|
||||
WrappedGui.setAppElement = ReactModal.setAppElement;
|
||||
export default WrappedGui;
|
||||
+52
-20
@@ -3,25 +3,37 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import monitorAdapter from '../lib/monitor-adapter.js';
|
||||
import MonitorComponent, {monitorTypes} from '../components/monitor/monitor.jsx';
|
||||
import MonitorComponent, {monitorModes} from '../components/monitor/monitor.jsx';
|
||||
import {addMonitorRect, getInitialPosition, resizeMonitorRect, removeMonitorRect} from '../reducers/monitor-layout';
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
const availableModes = opcode => (
|
||||
monitorModes.filter(t => {
|
||||
if (opcode === 'data_variable') {
|
||||
return t !== 'list';
|
||||
} else if (opcode === 'data_listcontents') {
|
||||
return t === 'list';
|
||||
}
|
||||
return t !== 'slider' && t !== 'list';
|
||||
})
|
||||
);
|
||||
|
||||
class Monitor extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'handleDragEnd',
|
||||
'handleNextType',
|
||||
'handleSetTypeToDefault',
|
||||
'handleSetTypeToLarge',
|
||||
'handleNextMode',
|
||||
'handleSetModeToDefault',
|
||||
'handleSetModeToLarge',
|
||||
'handleSetModeToSlider',
|
||||
'setElement'
|
||||
]);
|
||||
|
||||
// @todo this eventually will be stored in the VM
|
||||
// @todo consume from VM, but need to store until there are APIs to update vm
|
||||
this.state = {
|
||||
type: 'default'
|
||||
mode: props.mode || 'default'
|
||||
};
|
||||
}
|
||||
componentDidMount () {
|
||||
@@ -67,32 +79,40 @@ class Monitor extends React.Component {
|
||||
parseInt(this.element.style.top, 10) + y
|
||||
);
|
||||
}
|
||||
handleNextType () {
|
||||
// @todo the type list needs to be filtered for current available types
|
||||
// i.e. no sliders for read-only monitors, only list type for list vars.
|
||||
const typeIndex = monitorTypes.indexOf(this.state.type);
|
||||
this.setState({type: monitorTypes[(typeIndex + 1) % monitorTypes.length]});
|
||||
handleNextMode () {
|
||||
const modes = availableModes(this.props.opcode);
|
||||
const modeIndex = modes.indexOf(this.state.mode);
|
||||
this.setState({mode: modes[(modeIndex + 1) % modes.length]});
|
||||
}
|
||||
handleSetTypeToDefault () {
|
||||
this.setState({type: 'default'});
|
||||
handleSetModeToDefault () {
|
||||
this.setState({mode: 'default'});
|
||||
}
|
||||
handleSetTypeToLarge () {
|
||||
this.setState({type: 'large'});
|
||||
handleSetModeToLarge () {
|
||||
this.setState({mode: 'large'});
|
||||
}
|
||||
handleSetModeToSlider () {
|
||||
this.setState({mode: 'slider'});
|
||||
}
|
||||
setElement (monitorElt) {
|
||||
this.element = monitorElt;
|
||||
}
|
||||
render () {
|
||||
const monitorProps = monitorAdapter(this.props);
|
||||
const showSliderOption = availableModes(this.props.opcode).indexOf('slider') !== -1;
|
||||
return (
|
||||
<MonitorComponent
|
||||
componentRef={this.setElement}
|
||||
{...monitorProps}
|
||||
type={this.state.type}
|
||||
height={this.props.height}
|
||||
max={this.props.max}
|
||||
min={this.props.min}
|
||||
mode={this.state.mode}
|
||||
width={this.props.width}
|
||||
onDragEnd={this.handleDragEnd}
|
||||
onNextType={this.handleNextType}
|
||||
onSetTypeToDefault={this.handleSetTypeToDefault}
|
||||
onSetTypeToLarge={this.handleSetTypeToLarge}
|
||||
onNextMode={this.handleNextMode}
|
||||
onSetModeToDefault={this.handleSetModeToDefault}
|
||||
onSetModeToLarge={this.handleSetModeToLarge}
|
||||
onSetModeToSlider={showSliderOption ? this.handleSetModeToSlider : null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -100,7 +120,11 @@ class Monitor extends React.Component {
|
||||
|
||||
Monitor.propTypes = {
|
||||
addMonitorRect: PropTypes.func.isRequired,
|
||||
height: PropTypes.number,
|
||||
id: PropTypes.string.isRequired,
|
||||
max: PropTypes.number,
|
||||
min: PropTypes.number,
|
||||
mode: PropTypes.oneOf(['default', 'slider', 'large', 'list']),
|
||||
monitorLayout: PropTypes.shape({
|
||||
monitors: PropTypes.object,
|
||||
savedMonitorPositions: PropTypes.object
|
||||
@@ -111,7 +135,15 @@ Monitor.propTypes = {
|
||||
removeMonitorRect: PropTypes.func.isRequired,
|
||||
resizeMonitorRect: PropTypes.func.isRequired,
|
||||
spriteName: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
|
||||
value: PropTypes.string.isRequired, // eslint-disable-line react/no-unused-prop-types
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
PropTypes.arrayOf(PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number
|
||||
]))
|
||||
]), // eslint-disable-line react/no-unused-prop-types
|
||||
width: PropTypes.number,
|
||||
x: PropTypes.number,
|
||||
y: PropTypes.number
|
||||
};
|
||||
|
||||
+3
-1
@@ -64,7 +64,9 @@ export default function (vm) {
|
||||
const backdropsMenu = function () {
|
||||
if (vm.runtime.targets[0] && vm.runtime.targets[0].getCostumes().length > 0) {
|
||||
return vm.runtime.targets[0].getCostumes().map(costume => [costume.name, costume.name])
|
||||
.concat([['next backdrop', 'next backdrop'], ['previous backdrop', 'previous backdrop']]);
|
||||
.concat([['next backdrop', 'next backdrop'],
|
||||
['previous backdrop', 'previous backdrop'],
|
||||
['random backdrop', 'random backdrop']]);
|
||||
}
|
||||
return [['', '']];
|
||||
};
|
||||
|
||||
@@ -2,9 +2,9 @@ import {TextEncoder} from 'text-encoding';
|
||||
import projectJson from './project.json';
|
||||
|
||||
/* eslint-disable import/no-unresolved */
|
||||
import popWav from '!buffer-loader!./83a9787d4cb6f3b7632b4ddfebf74367.wav';
|
||||
import meowWav from '!buffer-loader!./83c36d806dc92327b9e7049a565c6bff.wav';
|
||||
import backdrop from '!buffer-loader!./739b5e2a2435f6e1ec2993791b423146.png';
|
||||
import popWav from '!arraybuffer-loader!./83a9787d4cb6f3b7632b4ddfebf74367.wav';
|
||||
import meowWav from '!arraybuffer-loader!./83c36d806dc92327b9e7049a565c6bff.wav';
|
||||
import backdrop from '!arraybuffer-loader!./739b5e2a2435f6e1ec2993791b423146.png';
|
||||
import costume1 from '!raw-loader!./09dc888b0b7df19f70d81588ae73420e.svg';
|
||||
import costume2 from '!raw-loader!./3696356a03a8d938318876a593572843.svg';
|
||||
/* eslint-enable import/no-unresolved */
|
||||
@@ -19,17 +19,17 @@ export default [{
|
||||
id: '83a9787d4cb6f3b7632b4ddfebf74367',
|
||||
assetType: 'Sound',
|
||||
dataFormat: 'WAV',
|
||||
data: popWav
|
||||
data: new Uint8Array(popWav)
|
||||
}, {
|
||||
id: '83c36d806dc92327b9e7049a565c6bff',
|
||||
assetType: 'Sound',
|
||||
dataFormat: 'WAV',
|
||||
data: meowWav
|
||||
data: new Uint8Array(meowWav)
|
||||
}, {
|
||||
id: '739b5e2a2435f6e1ec2993791b423146',
|
||||
assetType: 'ImageBitmap',
|
||||
dataFormat: 'PNG',
|
||||
data: backdrop
|
||||
data: new Uint8Array(backdrop)
|
||||
}, {
|
||||
id: '09dc888b0b7df19f70d81588ae73420e',
|
||||
assetType: 'ImageVector',
|
||||
|
||||
@@ -10,7 +10,7 @@ const isUndefined = a => typeof a === 'undefined';
|
||||
* with given target ID. The name of the target sprite when the monitor was created
|
||||
* @param {string} block.opcode - The opcode of the monitor
|
||||
* @param {object} block.params - Extra params to the monitor block
|
||||
* @param {string} block.value - The monitor value
|
||||
* @param {string|number|Array} block.value - The monitor value
|
||||
* @return {object} The adapted monitor with label and category
|
||||
*/
|
||||
export default function ({id, spriteName, opcode, params, value}) {
|
||||
@@ -25,8 +25,8 @@ export default function ({id, spriteName, opcode, params, value}) {
|
||||
}
|
||||
|
||||
// If value is a number, round it to six decimal places
|
||||
if (typeof value === 'number' || (typeof value === 'string' && String(parseFloat(value)) === value)) {
|
||||
value = Number(Number(value).toFixed(6));
|
||||
if (typeof value === 'number') {
|
||||
value = Number(value.toFixed(6));
|
||||
}
|
||||
|
||||
return {id, label, category, value};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'es6-object-assign/auto';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import analytics from '../lib/analytics';
|
||||
import GUI from '../containers/gui.jsx';
|
||||
@@ -20,6 +19,6 @@ const appTarget = document.createElement('div');
|
||||
appTarget.className = styles.app;
|
||||
document.body.appendChild(appTarget);
|
||||
|
||||
Modal.setAppElement(appTarget);
|
||||
GUI.setAppElement(appTarget);
|
||||
|
||||
ReactDOM.render(<GUI />, appTarget);
|
||||
+28
-19
@@ -5,6 +5,7 @@ var webpack = require('webpack');
|
||||
// Plugins
|
||||
var CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
var HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
var UglifyJsPlugin = require('uglifyjs-webpack-plugin');
|
||||
|
||||
// PostCss
|
||||
var autoprefixer = require('autoprefixer');
|
||||
@@ -12,6 +13,7 @@ var postcssVars = require('postcss-simple-vars');
|
||||
var postcssImport = require('postcss-import');
|
||||
|
||||
const base = {
|
||||
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
|
||||
devtool: 'cheap-module-source-map',
|
||||
devServer: {
|
||||
contentBase: path.resolve(__dirname, 'build'),
|
||||
@@ -61,23 +63,25 @@ const base = {
|
||||
}]
|
||||
}]
|
||||
},
|
||||
plugins: [].concat(process.env.NODE_ENV === 'production' ? [
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
include: /\.min\.js$/,
|
||||
minimize: true
|
||||
})
|
||||
] : [])
|
||||
optimization: {
|
||||
minimizer: [
|
||||
new UglifyJsPlugin({
|
||||
include: /\.min\.js$/
|
||||
})
|
||||
]
|
||||
},
|
||||
plugins: []
|
||||
};
|
||||
|
||||
module.exports = [
|
||||
// to run editor examples
|
||||
defaultsDeep({}, base, {
|
||||
entry: {
|
||||
lib: ['react', 'react-dom'],
|
||||
gui: './src/playground/index.jsx',
|
||||
blocksonly: './src/playground/blocks-only.jsx',
|
||||
compatibilitytesting: './src/playground/compatibility-testing.jsx',
|
||||
player: './src/playground/player.jsx'
|
||||
'lib.min': ['react', 'react-dom'],
|
||||
'gui': './src/playground/index.jsx',
|
||||
'blocksonly': './src/playground/blocks-only.jsx',
|
||||
'compatibilitytesting': './src/playground/compatibility-testing.jsx',
|
||||
'player': './src/playground/player.jsx'
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'build'),
|
||||
@@ -98,36 +102,41 @@ module.exports = [
|
||||
}
|
||||
])
|
||||
},
|
||||
optimization: {
|
||||
splitChunks: {
|
||||
chunks: 'all',
|
||||
name: 'lib.min'
|
||||
},
|
||||
runtimeChunk: {
|
||||
name: 'lib.min'
|
||||
}
|
||||
},
|
||||
plugins: base.plugins.concat([
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': '"' + process.env.NODE_ENV + '"',
|
||||
'process.env.DEBUG': Boolean(process.env.DEBUG),
|
||||
'process.env.GA_ID': '"' + (process.env.GA_ID || 'UA-000000-01') + '"'
|
||||
}),
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'lib',
|
||||
filename: 'lib.min.js'
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
chunks: ['lib', 'gui'],
|
||||
chunks: ['lib.min', 'gui'],
|
||||
template: 'src/playground/index.ejs',
|
||||
title: 'Scratch 3.0 GUI',
|
||||
sentryConfig: process.env.SENTRY_CONFIG ? '"' + process.env.SENTRY_CONFIG + '"' : null
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
chunks: ['lib', 'blocksonly'],
|
||||
chunks: ['lib.min', 'blocksonly'],
|
||||
template: 'src/playground/index.ejs',
|
||||
filename: 'blocks-only.html',
|
||||
title: 'Scratch 3.0 GUI: Blocks Only Example'
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
chunks: ['lib', 'compatibilitytesting'],
|
||||
chunks: ['lib.min', 'compatibilitytesting'],
|
||||
template: 'src/playground/index.ejs',
|
||||
filename: 'compatibility-testing.html',
|
||||
title: 'Scratch 3.0 GUI: Compatibility Testing'
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
chunks: ['lib', 'player'],
|
||||
chunks: ['lib.min', 'player'],
|
||||
template: 'src/playground/index.ejs',
|
||||
filename: 'player.html',
|
||||
title: 'Scratch 3.0 GUI: Player Example'
|
||||
|
||||
Reference in new issue
Block a user