Rift.IO OSM R1 Initial Submission
Signed-off-by: Jeremy Mordkoff <jeremy.mordkoff@riftio.com>
diff --git a/skyquake/framework/widgets/input-range-slider/input-range-slider.jsx b/skyquake/framework/widgets/input-range-slider/input-range-slider.jsx
new file mode 100644
index 0000000..d9e0544
--- /dev/null
+++ b/skyquake/framework/widgets/input-range-slider/input-range-slider.jsx
@@ -0,0 +1,72 @@
+
+/*
+ *
+ * Copyright 2016 RIFT.IO Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+import React from 'react';
+// import Slider from 'react-rangeslider';
+// import Slider from './react-rangeslider.jsx';
+import './input-range-slider.scss';
+
+
+class RWslider extends React.Component {
+ constructor(props, context) {
+ super(props, context);
+ this.state = {...props}
+ }
+
+ handleChange = (value) => {
+ this.props.handleInputUpdate(value);
+ this.setState({
+ value: value
+ });
+ };
+
+ render() {
+ let state = this.state;
+ var className = "input-range-slider_" + this.props.orientation;
+ return (
+ <div className={className}>
+ <div className={className + '-info'}>
+ {state["min-value"]}
+ </div>
+ <Slider
+ displayValue={true}
+ value={state.value}
+ max={state["max-value"]}
+ min={state["min-value"]}
+ step={state["step-value"]}
+ onChange={this.handleChange}
+ className={className + '-info'} />
+ <div className={className + '-info'}>
+ {state["max-value"]}
+ </div>
+ </div>
+ );
+ }
+}
+
+RWslider.defaultProps = {
+ value: 10,
+ "min-value": 0,
+ "max-value":100,
+ "step-value":1,
+ "units": "%",
+ orientation: "horizontal"
+}
+
+export default RWslider
+
diff --git a/skyquake/framework/widgets/input-range-slider/input-range-slider.scss b/skyquake/framework/widgets/input-range-slider/input-range-slider.scss
new file mode 100644
index 0000000..803bd8b
--- /dev/null
+++ b/skyquake/framework/widgets/input-range-slider/input-range-slider.scss
@@ -0,0 +1,104 @@
+
+/*
+ *
+ * Copyright 2016 RIFT.IO Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+@import '../../style/_colors.scss';
+
+ .input-range-slider_horizontal{
+ display:flex;
+ align-items:baseline;
+ &-info {
+ margin:0 0.5rem;
+ }
+ }
+
+
+.rangeslider {
+ margin: 20px 0;
+ position: relative;
+ background: #e6e6e6;
+ display:flex;
+ flex:1;
+ .rangeslider__fill, .rangeslider__handle {
+ position: absolute;
+ }
+ &, .rangeslider__fill {
+ display: block;
+ box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.3);
+ }
+ .rangeslider__handle {
+ background: #fff;
+ border: 1px solid #ccc;
+ cursor: pointer;
+ display: inline-block;
+ position: absolute;
+ &:active {
+ background: #999;
+ }
+ }
+}
+
+/**
+ * Rangeslider - Horizontal slider
+ */
+.rangeslider-horizontal {
+ height: 1rem;
+ margin-bottom: 2.5rem;
+ .rangeslider__fill {
+ height: 100%;
+ background: $light-blue;
+ top: 0;
+ }
+ .rangeslider__handle {
+ width: 0.5rem;
+ height: 2rem;
+ top: -0.5rem;
+ left:-10px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ >div{
+ width: 5rem;
+ margin-top: 2rem;
+ text-align: center;
+ }
+ }
+}
+
+/**
+ * Rangeslider - Vertical slider
+ */
+.rangeslider-vertical {
+ margin: 20px auto;
+ height: 150px;
+ max-width: 10px;
+ background: none;
+ .rangeslider__fill {
+ width: 100%;
+ background: $light-blue;
+ box-shadow: none;
+ bottom: 0;
+ }
+ .rangeslider__handle {
+ width: 30px;
+ height: 10px;
+ left: -10px;
+ &:active {
+ box-shadow: none;
+ }
+ }
+}
diff --git a/skyquake/framework/widgets/input-range-slider/react-rangeslider.jsx b/skyquake/framework/widgets/input-range-slider/react-rangeslider.jsx
new file mode 100644
index 0000000..feff1eb
--- /dev/null
+++ b/skyquake/framework/widgets/input-range-slider/react-rangeslider.jsx
@@ -0,0 +1,251 @@
+
+/*
+ *
+ * Copyright 2016 RIFT.IO Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+/**
+ * https://github.com/larsmaultsby/react-rangeslider
+ *
+ *
+ * Forked from: https://github.com/whoisandie/react-rangeslider
+ *
+ *
+ The MIT License (MIT)
+
+ Copyright (c) 2015 Bhargav Anand
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ *
+ */
+
+import React, { PropTypes, Component, findDOMNode } from 'react';
+import joinClasses from 'react/lib/joinClasses';
+
+function capitalize(str) {
+ return str.charAt(0).toUpperCase() + str.substr(1);
+}
+
+function maxmin(pos, min, max) {
+ if (pos < min) { return min; }
+ if (pos > max) { return max; }
+ return pos;
+}
+
+const constants = {
+ orientation: {
+ horizontal: {
+ dimension: 'width',
+ direction: 'left',
+ coordinate: 'x',
+ },
+ vertical: {
+ dimension: 'height',
+ direction: 'top',
+ coordinate: 'y',
+ }
+ }
+};
+
+class Slider extends Component {
+ static propTypes = {
+ min: PropTypes.number,
+ max: PropTypes.number,
+ step: PropTypes.number,
+ value: PropTypes.number,
+ orientation: PropTypes.string,
+ onChange: PropTypes.func,
+ className: PropTypes.string,
+ }
+
+ static defaultProps = {
+ min: 0,
+ max: 100,
+ step: 1,
+ value: 0,
+ orientation: 'horizontal',
+ }
+
+ state = {
+ limit: 0,
+ grab: 0
+ }
+
+ // Add window resize event listener here
+ componentDidMount() {
+ this.calculateDimensions();
+ window.addEventListener('resize', this.calculateDimensions);
+ }
+
+ componentWillUnmount() {
+ window.removeEventListener('resize', this.calculateDimensions);
+ }
+
+ handleSliderMouseDown = (e) => {
+ let value, { onChange } = this.props;
+ if (!onChange) return;
+ value = this.position(e);
+ onChange && onChange(value);
+ }
+
+ handleKnobMouseDown = () => {
+ document.addEventListener('mousemove', this.handleDrag);
+ document.addEventListener('mouseup', this.handleDragEnd);
+ }
+
+ handleDrag = (e) => {
+ let value, { onChange } = this.props;
+ if (!onChange) return;
+ value = this.position(e);
+ onChange && onChange(value);
+ }
+
+ handleDragEnd = () => {
+ document.removeEventListener('mousemove', this.handleDrag);
+ document.removeEventListener('mouseup', this.handleDragEnd);
+ }
+
+ handleNoop = (e) => {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ calculateDimensions = () => {
+ let { orientation } = this.props;
+ let dimension = capitalize(constants.orientation[orientation].dimension);
+ const sliderPos = findDOMNode(this.refs.slider)['offset' + dimension];
+ const handlePos = findDOMNode(this.refs.handle)['offset' + dimension]
+ this.setState({
+ limit: sliderPos - handlePos,
+ grab: handlePos / 2,
+ });
+ }
+ getPositionFromValue = (value) => {
+ let percentage, pos;
+ let { limit } = this.state;
+ let { min, max } = this.props;
+ percentage = (value - min) / (max - min);
+ pos = Math.round(percentage * limit);
+
+ return pos;
+ }
+
+ getValueFromPosition = (pos) => {
+ let percentage, value;
+ let { limit } = this.state;
+ let { orientation, min, max, step } = this.props;
+ percentage = (maxmin(pos, 0, limit) / (limit || 1));
+
+ if (orientation === 'horizontal') {
+ value = step * Math.round(percentage * (max - min) / step) + min;
+ } else {
+ value = max - (step * Math.round(percentage * (max - min) / step) + min);
+ }
+
+ return value;
+ }
+
+ position = (e) => {
+ let pos, value, { grab } = this.state;
+ let { orientation } = this.props;
+ const node = findDOMNode(this.refs.slider);
+ const coordinateStyle = constants.orientation[orientation].coordinate;
+ const directionStyle = constants.orientation[orientation].direction;
+ const coordinate = e['client' + capitalize(coordinateStyle)];
+ const direction = node.getBoundingClientRect()[directionStyle];
+
+ pos = coordinate - direction - grab;
+ value = this.getValueFromPosition(pos);
+
+ return value;
+ }
+
+ coordinates = (pos) => {
+ let value, fillPos, handlePos;
+ let { limit, grab } = this.state;
+ let { orientation } = this.props;
+ value = this.getValueFromPosition(pos);
+ handlePos = this.getPositionFromValue(value);
+
+ if (orientation === 'horizontal') {
+ fillPos = handlePos + grab;
+ } else {
+ fillPos = limit - handlePos + grab;
+ }
+
+ return {
+ fill: fillPos,
+ handle: handlePos,
+ };
+ }
+
+ render() {
+ let dimension, direction, position, coords, fillStyle, handleStyle, displayValue;
+ let { value, orientation, className } = this.props;
+
+ dimension = constants.orientation[orientation].dimension;
+ direction = constants.orientation[orientation].direction;
+
+ position = this.getPositionFromValue(value);
+ coords = this.coordinates(position);
+
+ fillStyle = {[dimension]: `${coords.fill}px`};
+ handleStyle = {[direction]: `${coords.handle}px`};
+
+ if(this.props.displayValue) {
+ displayValue = <div>{this.props.value}</div>;
+ }
+
+ return (
+ <div
+ ref="slider"
+ className={joinClasses('rangeslider ', 'rangeslider-' + orientation, className)}
+ onMouseDown={this.handleSliderMouseDown}
+ onClick={this.handleNoop}
+ style={{display:'flex'}}>
+ <div
+ ref="fill"
+ className="rangeslider__fill"
+ style={fillStyle} />
+ <div
+ ref="handle"
+ className="rangeslider__handle"
+ onMouseDown={this.handleKnobMouseDown}
+ onClick={this.handleNoop}
+ style={handleStyle}>
+ {displayValue}
+ </div>
+ </div>
+ );
+ }
+}
+
+export default Slider;