<commit_before>import React, { Component } from 'react';
import Radium from 'radium';
import _ from 'lodash/object';

//Default style used as defualt prop
const defaultStyle = {
  fieldContainer: {
    width: '300px',
    minHeight: '90px',
    margin: '8px 0px',
  },
  fieldText: {
    textAlign: 'left',
    fontFamily: 'sans-serif',
    lineHeight: 1,
  },
  fieldLabel: {
    fontSize: '14px',
    margin: '0px 0px 4px 0px',
  },
  fieldHintText: {
    margin: '0px 0px 4px 0px',
    fontSize: '12px',
    color: '#7a7a7a',
  },
  fieldInput: {
    base: {
      border: '1px solid #c3c3c3',
      borderRadius: '3px',
      padding: '8px',
      width: '250px',
      fontSize: '16px',
      ':focus': {
        border: '1px solid #494949',
        outline: 0,
      },
      ':disabled': {
        backgroundColor: '#f7f7f7',
      }
    },
    valid: {
      border: '1px solid #009c19',
      ':focus': {
        border: '1px solid #009c19',
        outline: 0,
      }
    },
    invalid: {
      border: '1px solid #c70000',
      ':focus': {
        border: '1px solid #c70000',
        outline: 0,
      }
    }
  },
  fieldMessage: {
    base: {
      margin: '4px 0px 4px 0px',
      fontSize: '12px',
      color: '#7a7a7a',
    },
    valid: {
      color: '#009c19',
    },
    invalid: {
      color: '#c70000',
    },
  }
};

class AgileTextField extends Component {
  constructor(props) {
    super(props);
    console.log(this.props.style);
    //Overwrite defaultStyle if custom has been provided, Without removing untouched defaultStyles
    //Creates a new object based on the defaultStyle and the custom style from prop
    var styles = this.props.style ? _.merge({},defaultStyle, this.props.style) : defaultStyle;
    this.state = {
      state: 'base',
      value: '',
      isValid: true,
      message: '',
      style: styles
    };
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleValidate = this.handleValidate.bind(this);
  }
  handleInputChange(event) {
    this.setState({value: event.target.value});
  }
  handleValidate() {
    //The validator returns an object representing the state
    var result = this.props.validator(this.state.value);
    console.log(result);
    this.setState(result);
  }
  render() {
    return (

{this.props.label}

        {this.props.hintText.length > 0 &&

{this.props.hintText}

        }
          <input style={[this.state.style.fieldInput.base, this.state.style.fieldInput[this.state.state]]}
            type={this.props.type}
            name={this.props.name}
            value={this.state.value}
            onChange={this.handleInputChange}
            onBlur={this.props.validateInput ? this.handleValidate : ''}
            disabled={this.props.disabled}
            />

            {this.state.message}

    );
  }
}

//Default validator
function defaultValidator(input) {
  if(input.length > 0) {
    return {
      isValid: true,
      message: '',
      state: 'valid',
    }
  }else if (input.length === 0) {
    return {
      isValid: false,
      message: 'Empty string not allowed string',
      state: 'invalid',
    }
  }
}

//Specifies the propTypes
AgileTextField.propTypes = {
  type: React.PropTypes.oneOf(['text', 'email', 'password']).isRequired,
  name: React.PropTypes.string,
  label: React.PropTypes.string.isRequired,
  hintText: React.PropTypes.string,
  validator: React.PropTypes.func,
  validateInput: React.PropTypes.bool,
  style: React.PropTypes.object,
  disabled: React.PropTypes.bool,
}
// Specifies the default values for props:
AgileTextField.defaultProps = {
  type: 'text',
  label: 'label',
  hintText: '',
  validator: defaultValidator,
  validateInput: false,
  disabled: false,
};

export default Radium(AgileTextField);
<commit_msg>Removed default label prop (it's required anyways) Type no longer required. It will default to text<commit_after>import React, { Component } from 'react';
import Radium from 'radium';
import _ from 'lodash/object';

//Default style used as defualt prop
const defaultStyle = {
  fieldContainer: {
    width: '300px',
    minHeight: '90px',
    margin: '8px 0px',
  },
  fieldText: {
    textAlign: 'left',
    fontFamily: 'sans-serif',
    lineHeight: 1,
  },
  fieldLabel: {
    fontSize: '14px',
    margin: '0px 0px 4px 0px',
  },
  fieldHintText: {
    margin: '0px 0px 4px 0px',
    fontSize: '12px',
    color: '#7a7a7a',
  },
  fieldInput: {
    base: {
      border: '1px solid #c3c3c3',
      borderRadius: '3px',
      padding: '8px',
      width: '250px',
      fontSize: '16px',
      ':focus': {
        border: '1px solid #494949',
        outline: 0,
      },
      ':disabled': {
        backgroundColor: '#f7f7f7',
      }
    },
    valid: {
      border: '1px solid #009c19',
      ':focus': {
        border: '1px solid #009c19',
        outline: 0,
      }
    },
    invalid: {
      border: '1px solid #c70000',
      ':focus': {
        border: '1px solid #c70000',
        outline: 0,
      }
    }
  },
  fieldMessage: {
    base: {
      margin: '4px 0px 4px 0px',
      fontSize: '12px',
      color: '#7a7a7a',
    },
    valid: {
      color: '#009c19',
    },
    invalid: {
      color: '#c70000',
    },
  }
};

class AgileTextField extends Component {
  constructor(props) {
    super(props);
    console.log(this.props.style);
    //Overwrite defaultStyle if custom has been provided, Without removing untouched defaultStyles
    //Creates a new object based on the defaultStyle and the custom style from prop
    var styles = this.props.style ? _.merge({},defaultStyle, this.props.style) : defaultStyle;
    this.state = {
      state: 'base',
      value: '',
      isValid: true,
      message: '',
      style: styles
    };
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleValidate = this.handleValidate.bind(this);
  }
  handleInputChange(event) {
    this.setState({value: event.target.value});
  }
  handleValidate() {
    //The validator returns an object representing the state
    var result = this.props.validator(this.state.value);
    console.log(result);
    this.setState(result);
  }
  render() {
    return (

{this.props.label}

        {this.props.hintText.length > 0 &&

{this.props.hintText}

        }
          <input style={[this.state.style.fieldInput.base, this.state.style.fieldInput[this.state.state]]}
            type={this.props.type}
            name={this.props.name}
            value={this.state.value}
            onChange={this.handleInputChange}
            onBlur={this.props.validateInput ? this.handleValidate : ''}
            disabled={this.props.disabled}
            />

            {this.state.message}

    );
  }
}

//Default validator
function defaultValidator(input) {
  if(input.length > 0) {
    return {
      isValid: true,
      message: '',
      state: 'valid',
    }
  }else if (input.length === 0) {
    return {
      isValid: false,
      message: 'Empty string not allowed string',
      state: 'invalid',
    }
  }
}

//Specifies the propTypes
AgileTextField.propTypes = {
  type: React.PropTypes.oneOf(['text', 'email', 'password']),
  name: React.PropTypes.string,
  label: React.PropTypes.string.isRequired,
  hintText: React.PropTypes.string,
  validator: React.PropTypes.func,
  validateInput: React.PropTypes.bool,
  style: React.PropTypes.object,
  disabled: React.PropTypes.bool,
}
// Specifies the default values for props:
AgileTextField.defaultProps = {
  type: 'text',
  hintText: '',
  validator: defaultValidator,
  validateInput: false,
  disabled: false,
};

export default Radium(AgileTextField);
<|endoftext|>


<commit_before>(function () {
	"use strict";

	var chartjs = this,
		helpers = chartjs.helpers;

	var defaultConfig = {

		// LINES

		datasetStroke: true,			// Boolean - Whether to show a stroke for datasets
		datasetStrokeWidth: 2,			// Number - Pixel width of dataset stroke

		bezierCurve: true,				// Boolean - Whether the line is curved between points
		bezierCurveTension: 0.4,		// Number - Tension of the bezier curve between points

		// POINTS
		pointDot: true,					// Boolean - Whether to show a dot for each point
		pointDotStrokeWidth: 1,			// Number - Pixel width of point dot stroke
		pointDotRadius: 4,				// Number - Radius of each point dot in pixels
		pointHitDetectionRadius: 20,	// Number - amount extra to add to the radius to cater for hit detection outside the drawn point

		x: 1

	};

	chartjs.Type.extend({
		name: "AltLineChart",
		defaults: defaultConfig,

		initialize: function (datasets) {

			//this.chart.ctx // The drawing context for this chart
			//this.chart.canvas // the canvas node for this chart

			this.AltPointClass = chartjs.Point.extend({
				radius: this.options.pointDotRadius,
				hitDetectionRadius: this.options.pointHitDetectionRadius,
				strokeWidth: this.options.pointDotStrokeWidth,
				display: this.options.pointDot,
				ctx: this.chart.ctx,
				view: null
			});

			this.datasets = [];

			//Iterate through each of the datasets, and build this into a property of the chart
			helpers.each(datasets, function (dataset) {

				var datasetObject = {
					label: dataset.label || null,
					strokeColor: dataset.strokeColor,
					points: []
				};

				this.datasets.push(datasetObject);

				helpers.each(dataset.data, function (dataPoint) {

					//Add a new point for each piece of data, passing any required data to draw.
					datasetObject.points.push(new this.AltPointClass({
						value: dataPoint,
						label: dataPoint.y,
						datasetLabel: dataset.label,
						strokeColor: 'white',
						fillColor: dataset.strokeColor,
						highlightStroke: dataset.strokeColor,
						highlightFill: 'white'
					}));
				}, this);

			}, this);

			this.render();
		},

		calculateRange: function () {

			var xmin = undefined,
				xmax = undefined,
				ymin = undefined,
				ymax = undefined;

			helpers.each(this.datasets, function(dataset) {

				helpers.each(dataset.points, function (point) {

					var value = point.value;

					// min x
					if (xmin === undefined || value.x < xmin) {
						xmin = value.x;
					}

					// max x
					if (xmax === undefined || value.x > xmax) {
						xmax = value.x;
					}

					// min y
					if (ymin === undefined || value.y < ymin) {
						ymin = value.y;
					}

					// max y
					if (ymax === undefined || value.y > ymax) {
						ymax = value.y;
					}
				});
			});

			return {
				xmin: xmin,
				xmax: xmax,
				ymin: ymin,
				ymax: ymax
			}
		},

		initCalculator: function (ease, options) {

			var easingDecimal = ease || 1,
				range = this.calculateRange(),
				width = this.chart.width,
				height = this.chart.height;

			var api = {
				getElementOrDefault: function (array, index, defaultValue) {

					return index >= 0 && index < array.length
						? array[index]
						: defaultValue;
				},
				calculateControlPoints: function (prev, current, next, tension) {

					var tensionBefore = !!prev ? tension : 0;
					var tensionAfter = !!next ? tension : 0;

					var innerCurrent = current.value;
					var innerPrev = prev ? prev.value : current.value;
					var innerNext = next ? next.value : current.value;

					var a = { xx: innerCurrent.x - innerPrev.x, yy: innerCurrent.y - innerPrev.y }
					var b = { xx: innerNext.x - innerPrev.x, yy: innerNext.y - innerPrev.y }

					var mul = a.xx * b.xx + a.yy * b.yy;
					var mod = Math.sqrt(b.xx * b.xx + b.yy * b.yy);

					var k = Math.min(Math.max(mul / (mod * mod), 0.3), 0.7);

					var result = {
						before: { x: innerCurrent.x - b.xx * k * tensionBefore, y: innerCurrent.y - b.yy * k * tensionBefore },
						after: { x: innerCurrent.x + b.xx * (1 - k) * tensionAfter, y: innerCurrent.y + b.yy * (1 - k) * tensionAfter }
					};

					if (result.after.y > range.ymax) {

						result.after.y = range.ymax;
					} else {

						if (result.after.y < range.ymin) {

							result.after.y = range.ymin;
						}
					}

					// Cap inner bezier handles to the upper/lower scale bounds
					if (result.before.y > range.ymax) {

						result.before.y = range.ymax;
					} else {

						if (result.before.y < range.ymin) {

							result.before.y = range.ymin;
						}
					}

					return result;
				},
				calculateScaleParameters: function (min, max) {

					var range = max - min;
					var step = 0.0000001;
					while (range / step > 20) {

						step *= 10;
					}

					var pos = (Math.trunc(min / step) + 1) * step;
					var end = Math.trunc(max / step) * step;

					var a = [];
					while (pos <= end) {

						a.push(pos);
						pos += step;
					}

					return a;
				}
			};

			return {
				calculateX: function (x) {

					return (x - range.xmin) * width / (range.xmax - range.xmin);
				},
				calculateY: function (y) {

					return height - ((y - range.ymin) * height / (range.ymax - range.ymin)) * easingDecimal;
				},

				updatePointPositions: function (data) {

					for (var i = 0; i < data.length; i++) {

						var current = api.getElementOrDefault(data, i);

						current.x = this.calculateX(current.value.x);
						current.y = this.calculateY(current.value.y);

						if (options.bezierCurve) {

							var view = {};

							var prev = api.getElementOrDefault(data, i - 1);
							var next = api.getElementOrDefault(data, i + 1);

							var obj = api.calculateControlPoints(prev, current, next, options.bezierCurveTension);

							view.x1 = this.calculateX(obj.before.x);
							view.y1 = this.calculateY(obj.before.y);

							view.x2 = this.calculateX(obj.after.x);
							view.y2 = this.calculateY(obj.after.y);

							current.view = view;
						}
					}
				},

				calculateXScaleParameters: function() {

					return api.calculateScaleParameters(range.xmin, range.xmax);
				},
				calculateYScaleParameters: function() {

					return api.calculateScaleParameters(range.ymin, range.ymax);
				}
			};
		},

		// Used to draw something on the canvas
		draw: function (ease) {

			var calc = this.initCalculator(ease, this.options);

			this.clear();

			// axis
			ctx.strokeStyle = 'gray';
			ctx.fillStyle = 'gray';
			ctx.lineWidth = 0.3;

			var scaleX = calc.calculateXScaleParameters();
			for (var i = 0; i <= scaleX.length; i++) {

				var xValue = scaleX[i];
				var xpos1 = calc.calculateX(xValue);
				ctx.beginPath();
				ctx.moveTo(xpos1, 0);
				ctx.lineTo(xpos1, this.chart.height);
				ctx.stroke();

				ctx.textAlign = "center";
				ctx.textBaseline = "top";
				ctx.fillText(xValue, xpos1, 0);
			}

			var scaleY = calc.calculateXScaleParameters();
			for (var j = 0; j <= scaleY.length; j++) {

				var yValue = scaleY[j];
				var ypos1 = calc.calculateY(yValue);

				ctx.beginPath();
				ctx.moveTo(0, ypos1);
				ctx.lineTo(this.chart.width, ypos1);
				ctx.stroke();

				ctx.textAlign = "right";
				ctx.textBaseline = "middle";
				ctx.fillText(yValue, this.chart.width, ypos1);
			}

			helpers.each(this.datasets, function (dataset) {

				// update points view params
				calc.updatePointPositions(dataset.points);

				ctx.lineWidth = this.options.datasetStrokeWidth;
				ctx.strokeStyle = dataset.strokeColor;
				ctx.beginPath();

				var prev = dataset.points[0];

				helpers.each(dataset.points, function (point, index) {

					if (index === 0) {

						ctx.moveTo(point.x, point.y);
					}
					else {

						if (this.options.bezierCurve) {

							ctx.bezierCurveTo(prev.view.x2, prev.view.y2, point.view.x1, point.view.y1, point.x, point.y);
							prev = point;
						}
						else {

							ctx.lineTo(point.x, point.y);
						}
					}

				}, this);

				ctx.stroke();

				// debug
				//ctx.lineWidth = 0.3;

				//if (this.options.bezierCurve) {

				//	helpers.each(dataset.points, function (point) {

				//		ctx.beginPath();
				//		ctx.moveTo(point.view.x1, point.view.y1);
				//		ctx.lineTo(point.view.x2, point.view.y2);
				//		ctx.stroke();
				//	});
				//}

				// draw points
				helpers.each(dataset.points, function (point) {
					point.draw();
				});

			}, this);

		}
	});

}).call(window.Chart);<commit_msg>- show point toottip;<commit_after>(function () {
	"use strict";

	var chartjs = this,
		helpers = chartjs.helpers;

	var defaultConfig = {

		// LINES

		datasetStroke: true,			// Boolean - Whether to show a stroke for datasets
		datasetStrokeWidth: 2,			// Number - Pixel width of dataset stroke

		bezierCurve: true,				// Boolean - Whether the line is curved between points
		bezierCurveTension: 0.4,		// Number - Tension of the bezier curve between points

		// POINTS
		pointDot: true,					// Boolean - Whether to show a dot for each point
		pointDotStrokeWidth: 1,			// Number - Pixel width of point dot stroke
		pointDotRadius: 4,				// Number - Radius of each point dot in pixels
		pointHitDetectionRadius: 5,		// Number - amount extra to add to the radius to cater for hit detection outside the drawn point

		tooltipTemplate: "<%if (datasetLabel){%><%=datasetLabel%>: <%}%><%= value.y %>",

		x: 1

	};

	chartjs.Type.extend({
		name: "AltLineChart",
		defaults: defaultConfig,

		initialize: function (datasets) {

			//this.chart.ctx // The drawing context for this chart
			//this.chart.canvas // the canvas node for this chart

			this.AltPointClass = chartjs.Point.extend({
				radius: this.options.pointDotRadius,
				hitDetectionRadius: this.options.pointHitDetectionRadius,
				strokeWidth: this.options.pointDotStrokeWidth,
				display: this.options.pointDot,
				ctx: this.chart.ctx,
				view: null
			});

			this.datasets = [];

			//Iterate through each of the datasets, and build this into a property of the chart
			helpers.each(datasets, function (dataset) {

				var datasetObject = {
					label: dataset.label || null,
					strokeColor: dataset.strokeColor,
					points: []
				};

				this.datasets.push(datasetObject);

				helpers.each(dataset.data, function (dataPoint) {

					//Add a new point for each piece of data, passing any required data to draw.
					datasetObject.points.push(new this.AltPointClass({
						value: dataPoint,
						label: dataPoint.y,
						datasetLabel: dataset.label,
						strokeColor: 'white',
						fillColor: dataset.strokeColor,
						highlightStroke: dataset.strokeColor,
						highlightFill: 'white'
					}));
				}, this);

			}, this);

			//Set up tooltip events on the chart
			if (this.options.showTooltips) {

				helpers.bindEvents(this, this.options.tooltipEvents, function (evt) {

					var activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];

					helpers.each(this.datasets, function (dataset) {

						helpers.each(dataset.points, function (point) {

							point.restore(['fillColor', 'strokeColor']);
						});
					});

					helpers.each(activePoints, function (activePoint) {
						activePoint.fillColor = activePoint.highlightFill;
						activePoint.strokeColor = activePoint.highlightStroke;
					});

					this.showTooltip(activePoints);
				});
			}

			this.render();
		},

		// helpers
		getPointsAtEvent: function (e) {
			var pointsArray = [],
				eventPosition = helpers.getRelativePosition(e);
			helpers.each(this.datasets, function (dataset) {
				helpers.each(dataset.points, function (point) {
					if (point.inRange(eventPosition.x, eventPosition.y)) pointsArray.push(point);
				});
			}, this);
			return pointsArray;
		},

		showTooltip: function(elements) {

			this.draw();

			if (elements.length > 0) {

				helpers.each(elements, function (element) {

					var tooltipPosition = element.tooltipPosition();

					new chartjs.Tooltip({
						x: Math.round(tooltipPosition.x),
						y: Math.round(tooltipPosition.y),
						xPadding: this.options.tooltipXPadding,
						yPadding: this.options.tooltipYPadding,
						fillColor: this.options.tooltipFillColor,
						textColor: this.options.tooltipFontColor,
						fontFamily: this.options.tooltipFontFamily,
						fontStyle: this.options.tooltipFontStyle,
						fontSize: this.options.tooltipFontSize,
						caretHeight: this.options.tooltipCaretSize,
						cornerRadius: this.options.tooltipCornerRadius,
						text: helpers.template(this.options.tooltipTemplate, element),
						chart: this.chart,
						custom: this.options.customTooltips
					}).draw();
				}, this);

			}
			return this;
		},

		calculateRange: function () {

			var xmin = undefined,
				xmax = undefined,
				ymin = undefined,
				ymax = undefined;

			helpers.each(this.datasets, function(dataset) {

				helpers.each(dataset.points, function (point) {

					var value = point.value;

					// min x
					if (xmin === undefined || value.x < xmin) {
						xmin = value.x;
					}

					// max x
					if (xmax === undefined || value.x > xmax) {
						xmax = value.x;
					}

					// min y
					if (ymin === undefined || value.y < ymin) {
						ymin = value.y;
					}

					// max y
					if (ymax === undefined || value.y > ymax) {
						ymax = value.y;
					}
				});
			});

			return {
				xmin: xmin,
				xmax: xmax,
				ymin: ymin,
				ymax: ymax
			}
		},

		initCalculator: function (ease, options) {

			var easingDecimal = ease || 1,
				range = this.calculateRange(),
				width = this.chart.width,
				height = this.chart.height;

			var api = {
				trunc: function(x) {
					return x < 0 ? Math.ceil(x) : Math.floor(x);
				},
				getElementOrDefault: function (array, index, defaultValue) {

					return index >= 0 && index < array.length
						? array[index]
						: defaultValue;
				},
				calculateControlPoints: function (prev, current, next, tension) {

					var tensionBefore = !!prev ? tension : 0;
					var tensionAfter = !!next ? tension : 0;

					var innerCurrent = current.value;
					var innerPrev = prev ? prev.value : current.value;
					var innerNext = next ? next.value : current.value;

					var a = { xx: innerCurrent.x - innerPrev.x, yy: innerCurrent.y - innerPrev.y }
					var b = { xx: innerNext.x - innerPrev.x, yy: innerNext.y - innerPrev.y }

					var mul = a.xx * b.xx + a.yy * b.yy;
					var mod = Math.sqrt(b.xx * b.xx + b.yy * b.yy);

					var k = Math.min(Math.max(mul / (mod * mod), 0.3), 0.7);

					var result = {
						before: { x: innerCurrent.x - b.xx * k * tensionBefore, y: innerCurrent.y - b.yy * k * tensionBefore },
						after: { x: innerCurrent.x + b.xx * (1 - k) * tensionAfter, y: innerCurrent.y + b.yy * (1 - k) * tensionAfter }
					};

					if (result.after.y > range.ymax) {

						result.after.y = range.ymax;
					} else {

						if (result.after.y < range.ymin) {

							result.after.y = range.ymin;
						}
					}

					// Cap inner bezier handles to the upper/lower scale bounds
					if (result.before.y > range.ymax) {

						result.before.y = range.ymax;
					} else {

						if (result.before.y < range.ymin) {

							result.before.y = range.ymin;
						}
					}

					return result;
				},
				calculateScaleParameters: function (min, max) {

					var range = max - min;
					var step = 0.0000001;
					while (range / step > 20) {

						step *= 10;
					}

					var pos = (api.trunc(min / step) + 1) * step;
					var end = api.trunc(max / step) * step;

					var a = [];
					while (pos <= end) {

						a.push(pos);
						pos += step;
					}

					return a;
				}
			};

			return {
				calculateX: function (x) {

					return (x - range.xmin) * width / (range.xmax - range.xmin);
				},
				calculateY: function (y) {

					return height - ((y - range.ymin) * height / (range.ymax - range.ymin)) * easingDecimal;
				},

				updatePointPositions: function (data) {

					for (var i = 0; i < data.length; i++) {

						var current = api.getElementOrDefault(data, i);

						current.x = this.calculateX(current.value.x);
						current.y = this.calculateY(current.value.y);

						if (options.bezierCurve) {

							var view = {};

							var prev = api.getElementOrDefault(data, i - 1);
							var next = api.getElementOrDefault(data, i + 1);

							var obj = api.calculateControlPoints(prev, current, next, options.bezierCurveTension);

							view.x1 = this.calculateX(obj.before.x);
							view.y1 = this.calculateY(obj.before.y);

							view.x2 = this.calculateX(obj.after.x);
							view.y2 = this.calculateY(obj.after.y);

							current.view = view;
						}
					}
				},

				calculateXScaleParameters: function() {

					return api.calculateScaleParameters(range.xmin, range.xmax);
				},
				calculateYScaleParameters: function() {

					return api.calculateScaleParameters(range.ymin, range.ymax);
				}
			};
		},

		// Used to draw something on the canvas
		draw: function (ease) {

			var calc = this.initCalculator(ease, this.options);

			this.clear();

			// axis
			ctx.strokeStyle = 'gray';
			ctx.fillStyle = 'gray';
			ctx.lineWidth = 0.3;

			var scaleX = calc.calculateXScaleParameters();
			for (var i = 0; i <= scaleX.length; i++) {

				var xValue = scaleX[i];
				var xpos1 = calc.calculateX(xValue);
				ctx.beginPath();
				ctx.moveTo(xpos1, 0);
				ctx.lineTo(xpos1, this.chart.height);
				ctx.stroke();

				ctx.textAlign = "center";
				ctx.textBaseline = "top";
				ctx.fillText(xValue, xpos1, 0);
			}

			var scaleY = calc.calculateXScaleParameters();
			for (var j = 0; j <= scaleY.length; j++) {

				var yValue = scaleY[j];
				var ypos1 = calc.calculateY(yValue);

				ctx.beginPath();
				ctx.moveTo(0, ypos1);
				ctx.lineTo(this.chart.width, ypos1);
				ctx.stroke();

				ctx.textAlign = "right";
				ctx.textBaseline = "middle";
				ctx.fillText(yValue, this.chart.width, ypos1);
			}

			helpers.each(this.datasets, function (dataset) {

				// update points view params
				calc.updatePointPositions(dataset.points);

				ctx.lineWidth = this.options.datasetStrokeWidth;
				ctx.strokeStyle = dataset.strokeColor;
				ctx.beginPath();

				var prev = dataset.points[0];

				helpers.each(dataset.points, function (point, index) {

					if (index === 0) {

						ctx.moveTo(point.x, point.y);
					}
					else {

						if (this.options.bezierCurve) {

							ctx.bezierCurveTo(prev.view.x2, prev.view.y2, point.view.x1, point.view.y1, point.x, point.y);
							prev = point;
						}
						else {

							ctx.lineTo(point.x, point.y);
						}
					}

				}, this);

				ctx.stroke();

				// debug
				//ctx.lineWidth = 0.3;

				//if (this.options.bezierCurve) {

				//	helpers.each(dataset.points, function (point) {

				//		ctx.beginPath();
				//		ctx.moveTo(point.view.x1, point.view.y1);
				//		ctx.lineTo(point.view.x2, point.view.y2);
				//		ctx.stroke();
				//	});
				//}

				// draw points
				helpers.each(dataset.points, function (point) {
					point.draw();
				});

			}, this);

		}
	});

}).call(window.Chart);<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import InputField from './InputField';
import { FormattedMessage, defineMessages } from 'react-intl';
import { defaultBackgroundImage } from '../constants/collectives';
import withIntl from '../lib/withIntl';
import { Button } from 'react-bootstrap';
import { get } from 'lodash';
import CollectiveCategoryPicker from './CollectiveCategoryPicker';
import fetch from 'fetch-jsonp';
import { firstSentence } from '../lib/utils';

class CreateCollectiveForm extends React.Component {

  static propTypes = {
    host: PropTypes.object,
    collective: PropTypes.object,
    loading: PropTypes.bool,
    onSubmit: PropTypes.func
  };

  constructor(props) {
    super(props);
    this.defineFields = this.defineFields.bind(this);
    this.addLabels = this.addLabels.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.handleObjectChange = this.handleObjectChange.bind(this);

    const collective = { ... props.collective || {} };
    collective.slug = collective.slug ? collective.slug.replace(/.*\//, '') : '';

    this.messages = defineMessages({
      'slug.label': { id: 'collective.slug.label', defaultMessage: 'url' },
      'type.label': { id: 'collective.type.label', defaultMessage: 'type' },
      'name.label': { id: 'collective.name.label', defaultMessage: 'name' },
      'company.label': { id: 'collective.company.label', defaultMessage: 'company' },
      'company.description': { id: 'collective.company.description', defaultMessage: 'Start with a @ to reference an organization (e.g. @airbnb)' },
      'amount.label': { id: 'collective.amount.label', defaultMessage: 'amount' },
      'description.label': { id: 'collective.description.label', defaultMessage: 'Short description' },
      'longDescription.label': { id: 'collective.longDescription.label', defaultMessage: 'Long description' },
      'startsAt.label': { id: 'collective.startsAt.label', defaultMessage: 'start date and time' },
      'image.label': { id: 'collective.image.label', defaultMessage: 'Avatar' },
      'backgroundImage.label': { id: 'collective.backgroundImage.label', defaultMessage: 'Cover image' },
      'twitterHandle.label': { id: 'collective.twitterHandle.label', defaultMessage: 'Twitter' },
      'website.label': { id: 'collective.website.label', defaultMessage: 'Website' },
      'location.label': { id: 'collective.location.label', defaultMessage: 'City' },
      'meetup.label': { id: 'collective.meetup.label', defaultMessage: 'Meetup URL' },
      'members.label': { id: 'collective.members.label', defaultMessage: 'Number of members' },
      'tags.label': { id: 'collective.tags.label', defaultMessage: 'Tags' },
      'tags.description': { id: 'collective.tags.description', defaultMessage: 'Tags helps people discover your collective. Separate them with a comma.' },
      'tos.label': { id: 'collective.tos.label', defaultMessage: 'Terms of Service' }
    });

    collective.backgroundImage = collective.backgroundImage || defaultBackgroundImage[collective.type];

    this.masterKey = '';
    this.state = {
      modified: false,
      section: 'info',
      collective
    };

    this.categories = get(props.host, 'settings.categories') || [];
    if (this.categories.length === 1) {
      this.state.collective.category = this.categories[0];
    }

    this.defineFields(this.state.collective.category);
    this.addLabels();

    window.OC = { collective, state: this.state };

  }

  componentDidMount() {
    const hash = window.location.hash;
    if (hash) {
      this.setState({ section: hash.substr(1) });
    }
  }

  UNSAFE_componentWillReceiveProps(nextProps) {
    if (nextProps.collective && (!this.props.collective || get(nextProps, 'collective.name') != get(this.props, 'collective.name'))) {
      this.setState({ collective: nextProps.collective, tiers: nextProps.collective.tiers });
    }
  }

  defineFields(category) {

    this.fields = {
      info: [
        {
          name: 'name',
          placeholder: '',
          maxLength: 255
        },
        {
          name: 'company',
          placeholder: '',
          maxLength: 255,
          when: () => get(this.state, 'collective.type') === 'USER'
        },
        {
          name: 'description',
          type: 'text',
          maxLength: 255,
          placeholder: ''
        },
        {
          name: 'twitterHandle',
          type: 'text',
          pre: 'https://twitter.com/',
          maxLength: 255,
          placeholder: ''
        },
        {
          name: 'website',
          type: 'text',
          maxLength: 255,
          placeholder: ''
        },
        {
          name: 'location',
          placeholder: 'Search cities',
          type: 'location',
          options: {
            types: ['(cities)']
          }
        },
        {
          name: 'longDescription',
          type: 'textarea',
          placeholder: '',
          help: 'Protip: you can use markdown'
        }
      ]
    }

    if (category === 'meetup') {
      this.fields.info.splice(2, 0, {
        name: 'members',
        type: 'number'
      });
      this.fields.info.unshift({
        name: 'meetup',
        type: 'text',
        pre: 'https://meetup.com/',
        maxLength: 255,
        placeholder: ''
      });
      this.fields.info.push({
        name: 'tags',
        type: 'text',
        maxLength: 255,
        placeholder: 'meetup, nyc, bitcoin'
      });
    }

    this.addLabels();
  }

  addLabels() {
    const { intl } = this.props;
    Object.keys(this.fields).map(key => {
      this.fields[key] = this.fields[key].map(field => {
      if (this.messages[`${field.name}.label`]) {
        field.label = intl.formatMessage(this.messages[`${field.name}.label`]);
      }
      if (this.messages[`${field.name}.description`]) {
        field.description = intl.formatMessage(this.messages[`${field.name}.description`]);
      }
      this.state.collective[field.name] = this.state.collective[field.name] || field.defaultValue;
      return field;
      });
    });
  }

  handleChange(fieldname, value) {
    if (fieldname === 'category' && value === 'opensource') {
      return window.location = '/opensource/apply';
    }

    if (fieldname === 'category') {
      this.defineFields(value);
    }

    const collective = {};
    if (fieldname === 'meetup') {
      fetch(`https://api.meetup.com/${value}?fields=plain_text_description,topics`)
        .then(response => {
          if (response.ok) {
            return response.json();
          }
        })
        .then(json => {
          if (!json) return;
          const { city, localized_location, lat, lon, members, plain_text_description, name, timezone, key_photo, link, created, topics } = json.data;
          Object.assign(collective, { name, timezone })
          collective.description = firstSentence(plain_text_description, 255);
          collective.longDescription = plain_text_description;
          collective.location = { name: city, address: localized_location, lat, long: lon };
          collective.image = get(key_photo, 'highres_link');
          collective.tags = topics && topics.map(t => t.urlkey).join(', ');
          collective.website = link;
          collective.members = members;
          collective.data = {
            created: new Date(created)
          }
          this.masterKey = value; // making sure we recreate the form with new prefilled values
          this.setState( { modified: true, collective: Object.assign({}, this.state.collective, collective) });
          window.state = this.state;
        })
    }
    collective[fieldname] = value;
    this.setState( { modified: true, collective: Object.assign({}, this.state.collective, collective) });
    window.state = this.state;
  }

  handleObjectChange(obj) {
    this.setState({ ...obj, modified: true });
    window.state = this.state;
  }

  async handleSubmit() {
    this.props.onSubmit(this.state.collective);
    this.setState({ modified: false })
  }

  render() {

    const { host, collective, loading, intl } = this.props;

    const showForm = Boolean(this.categories.length === 0 || this.state.collective.category);
    const defaultStartsAt = new Date;
    defaultStartsAt.setHours(19);
    defaultStartsAt.setMinutes(0);

    return (

        { this.categories.length > 1 && <CollectiveCategoryPicker categories={this.categories} onChange={(value) => this.handleChange("category", value)} /> }

        { showForm &&

            { Object.keys(this.fields).map(key =>
              (

                {this.fields[key].map((field) => (!field.when || field.when()) && <InputField
                  key={`${this.masterKey}-${field.name}`}
                  value={this.state.collective[field.name]}
                  className={field.className}
                  defaultValue={this.state.collective[field.name] || field.defaultValue}
                  validate={field.validate}
                  ref={field.name}
                  name={field.name}
                  label={field.label}
                  description={field.description}
                  options={field.options}
                  placeholder={field.placeholder}
                  type={field.type}
                  help={field.help}
                  pre={field.pre}
                  context={this.state.collective}
                  onChange={(value) => this.handleChange(field.name, value)}
                  />)}

)
            )}

              {intl.formatMessage(this.messages['tos.label'])}

                <input type="checkbox" name="tos" onChange={(value) => this.handleChange("tos", value)} />
                I agree with the terms of service of Open Collective

              { (get(host, 'settings.tos')) &&

                  <input type="checkbox" name="hostTos" onChange={(value) => this.handleChange("hostTos", value)} />
                  I agree with the terms of service of the host (<a href={`/${host.slug}`} target="_blank">{host.name}) that will collect money on behalf of our collective

              }

              <Button bsStyle="primary" type="submit" onClick={this.handleSubmit} disabled={loading || !this.state.modified} >
                { loading && <FormattedMessage id="loading" defaultMessage="loading" /> }
                { !loading && collective.type === 'COLLECTIVE' && <FormattedMessage id="host.apply.btn" defaultMessage="Apply to create a collective" /> }
                { !loading && collective.type === 'ORGANIZATION' && <FormattedMessage id="organization.create" defaultMessage="Create organization" /> }
              </Button>

        }

    );
  }

}

export default withIntl(CreateCollectiveForm);
<commit_msg>simplify /create form<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import InputField from './InputField';
import { FormattedMessage, defineMessages } from 'react-intl';
import { defaultBackgroundImage } from '../constants/collectives';
import withIntl from '../lib/withIntl';
import { Button } from 'react-bootstrap';
import { get } from 'lodash';
import CollectiveCategoryPicker from './CollectiveCategoryPicker';
import fetch from 'fetch-jsonp';
import { firstSentence } from '../lib/utils';

class CreateCollectiveForm extends React.Component {

  static propTypes = {
    host: PropTypes.object,
    collective: PropTypes.object,
    loading: PropTypes.bool,
    onSubmit: PropTypes.func
  };

  constructor(props) {
    super(props);
    this.defineFields = this.defineFields.bind(this);
    this.addLabels = this.addLabels.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.handleObjectChange = this.handleObjectChange.bind(this);

    const collective = { ... props.collective || {} };
    collective.slug = collective.slug ? collective.slug.replace(/.*\//, '') : '';

    this.messages = defineMessages({
      'slug.label': { id: 'collective.slug.label', defaultMessage: 'url' },
      'type.label': { id: 'collective.type.label', defaultMessage: 'type' },
      'name.label': { id: 'collective.name.label', defaultMessage: 'name' },
      'company.label': { id: 'collective.company.label', defaultMessage: 'company' },
      'company.description': { id: 'collective.company.description', defaultMessage: 'Start with a @ to reference an organization (e.g. @airbnb)' },
      'amount.label': { id: 'collective.amount.label', defaultMessage: 'amount' },
      'description.label': { id: 'collective.description.label', defaultMessage: 'Short description' },
      'startsAt.label': { id: 'collective.startsAt.label', defaultMessage: 'start date and time' },
      'image.label': { id: 'collective.image.label', defaultMessage: 'Avatar' },
      'backgroundImage.label': { id: 'collective.backgroundImage.label', defaultMessage: 'Cover image' },
      'website.label': { id: 'collective.website.label', defaultMessage: 'Website' },
      'website.description': { id: 'collective.website.description', defaultMessage: 'Enter the URL of your website or Facebook Page' },
      'location.label': { id: 'collective.location.label', defaultMessage: 'City' },
      'meetup.label': { id: 'collective.meetup.label', defaultMessage: 'Meetup URL' },
      'members.label': { id: 'collective.members.label', defaultMessage: 'Number of members' },
      'tags.label': { id: 'collective.tags.label', defaultMessage: 'Tags' },
      'tags.description': { id: 'collective.tags.description', defaultMessage: 'Tags helps people discover your collective. Separate them with a comma.' },
      'tos.label': { id: 'collective.tos.label', defaultMessage: 'Terms of Service' }
    });

    collective.backgroundImage = collective.backgroundImage || defaultBackgroundImage[collective.type];

    this.masterKey = '';
    this.state = {
      modified: false,
      section: 'info',
      collective
    };

    this.categories = get(props.host, 'settings.apply.categories') || [];
    if (this.categories.length === 1) {
      this.state.collective.category = this.categories[0];
    }

    if (get(props.host, 'settings.apply.defaultValues')) {
      this.state.collective = {
        ...this.state.collective,
        ... props.host.settings.apply.defaultValues
      };
    }

    this.defineFields(this.state.collective.category);
    this.addLabels();

    window.OC = { collective, state: this.state };

  }

  componentDidMount() {
    const hash = window.location.hash;
    if (hash) {
      this.setState({ section: hash.substr(1) });
    }
  }

  UNSAFE_componentWillReceiveProps(nextProps) {
    if (nextProps.collective && (!this.props.collective || get(nextProps, 'collective.name') != get(this.props, 'collective.name'))) {
      this.setState({ collective: nextProps.collective, tiers: nextProps.collective.tiers });
    }
  }

  defineFields(category) {

    this.fields = {
      info: [
        {
          name: 'name',
          placeholder: '',
          maxLength: 255
        },
        {
          name: 'company',
          placeholder: '',
          maxLength: 255,
          when: () => get(this.state, 'collective.type') === 'USER'
        },
        {
          name: 'description',
          type: 'text',
          maxLength: 255,
          placeholder: ''
        },
        {
          name: 'website',
          type: 'text',
          maxLength: 255,
          placeholder: ''
        },
        {
          name: 'location',
          placeholder: 'Search cities',
          type: 'location',
          options: {
            types: ['(cities)']
          }
        }
      ]
    }

    if (category === 'meetup') {
      this.fields.info.splice(2, 0, {
        name: 'members',
        type: 'number'
      });
      this.fields.info.unshift({
        name: 'meetup',
        type: 'text',
        pre: 'https://meetup.com/',
        maxLength: 255,
        placeholder: ''
      });
      this.fields.info.push({
        name: 'tags',
        type: 'text',
        maxLength: 255,
        placeholder: 'meetup, nyc, bitcoin'
      });
    }

    this.addLabels();
  }

  addLabels() {
    const { intl } = this.props;
    Object.keys(this.fields).map(key => {
      this.fields[key] = this.fields[key].map(field => {
      if (this.messages[`${field.name}.label`]) {
        field.label = intl.formatMessage(this.messages[`${field.name}.label`]);
      }
      if (this.messages[`${field.name}.description`]) {
        field.description = intl.formatMessage(this.messages[`${field.name}.description`]);
      }
      this.state.collective[field.name] = this.state.collective[field.name] || field.defaultValue;
      return field;
      });
    });
  }

  handleChange(fieldname, value) {
    if (fieldname === 'category' && value === 'opensource') {
      return window.location = '/opensource/apply';
    }

    if (fieldname === 'category') {
      this.defineFields(value);
    }

    const collective = {};
    if (fieldname === 'meetup') {
      fetch(`https://api.meetup.com/${value}?fields=plain_text_description,topics`)
        .then(response => {
          if (response.ok) {
            return response.json();
          }
        })
        .then(json => {
          if (!json) return;
          const { city, localized_location, lat, lon, members, plain_text_description, name, timezone, key_photo, link, created, topics } = json.data;
          Object.assign(collective, { name, timezone })
          collective.description = firstSentence(plain_text_description, 255);
          collective.longDescription = plain_text_description;
          collective.location = { name: city, address: localized_location, lat, long: lon };
          collective.image = get(key_photo, 'highres_link');
          collective.tags = topics && topics.map(t => t.urlkey).join(', ');
          collective.website = link;
          collective.members = members;
          collective.data = {
            created: new Date(created)
          }
          this.masterKey = value; // making sure we recreate the form with new prefilled values
          this.setState( { modified: true, collective: Object.assign({}, this.state.collective, collective) });
          window.state = this.state;
        })
    }
    collective[fieldname] = value;
    this.setState( { modified: true, collective: Object.assign({}, this.state.collective, collective) });
    window.state = this.state;
  }

  handleObjectChange(obj) {
    this.setState({ ...obj, modified: true });
    window.state = this.state;
  }

  async handleSubmit() {
    this.props.onSubmit(this.state.collective);
    this.setState({ modified: false })
  }

  render() {

    const { host, collective, loading, intl } = this.props;

    const showForm = Boolean(this.categories.length === 0 || this.state.collective.category);
    const defaultStartsAt = new Date;
    defaultStartsAt.setHours(19);
    defaultStartsAt.setMinutes(0);

    return (

        { this.categories.length > 1 && <CollectiveCategoryPicker categories={this.categories} onChange={(value) => this.handleChange("category", value)} /> }

        { showForm &&

            { Object.keys(this.fields).map(key =>
              (

                {this.fields[key].map((field) => (!field.when || field.when()) && <InputField
                  key={`${this.masterKey}-${field.name}`}
                  value={this.state.collective[field.name]}
                  className={field.className}
                  defaultValue={this.state.collective[field.name] || field.defaultValue}
                  validate={field.validate}
                  ref={field.name}
                  name={field.name}
                  label={field.label}
                  description={field.description}
                  options={field.options}
                  placeholder={field.placeholder}
                  type={field.type}
                  help={field.help}
                  pre={field.pre}
                  context={this.state.collective}
                  onChange={(value) => this.handleChange(field.name, value)}
                  />)}

)
            )}

              {intl.formatMessage(this.messages['tos.label'])}

                <input type="checkbox" name="tos" onChange={(value) => this.handleChange("tos", value)} />
                I agree with the terms of service of Open Collective

              { (get(host, 'settings.tos')) &&

                  <input type="checkbox" name="hostTos" onChange={(value) => this.handleChange("hostTos", value)} />
                  I agree with the terms of service of the host (<a href={`/${host.slug}`} target="_blank">{host.name}) that will collect money on behalf of our collective

              }

              <Button bsStyle="primary" type="submit" onClick={this.handleSubmit} disabled={loading || !this.state.modified} >
                { loading && <FormattedMessage id="loading" defaultMessage="loading" /> }
                { !loading && collective.type === 'COLLECTIVE' && <FormattedMessage id="host.apply.btn" defaultMessage="Apply to create a collective" /> }
                { !loading && collective.type === 'ORGANIZATION' && <FormattedMessage id="organization.create" defaultMessage="Create organization" /> }
              </Button>

        }

    );
  }

}

export default withIntl(CreateCollectiveForm);
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
import { connect } from 'react-redux'
import {
  ArrowIcon,
  BrowseIcon,
  MoneyIconCircle,
  ReplyAllIcon,
  XIconLG,
} from '../assets/Icons'
import CategoryPostSelector from './CategoryPostSelector'
import { openModal, closeModal } from '../../actions/modals'
import { updateBuyLink, updateCategoryId, clearCategoryId } from '../../actions/editor'
import BuyLinkDialog from '../dialogs/BuyLinkDialog'
import {
  selectSubscribedCategories,
  selectUnsubscribedCategories,
  selectFeaturedInCategories,
  selectPostSelectedCategories,
} from '../../selectors/categories'
import { css, disabled, hover, media, modifier, parent, select } from '../../styles/jss'
import * as s from '../../styles/jso'

const wrapperStyle = css(
  s.flex,
  s.justifySpaceBetween,
  s.hv40,
  s.lh40,
  s.mt10,
)

const leftStyle = css(
  select('& button', s.mr10),
  select('& button:last-child', s.mr0),
  select('& .forCancel.text', s.displayNone),
  select('.PostGrid & .forCancel.text', s.displayNone),
  media(s.minBreak2, select('& .forCancel.text', s.inlineBlock, s.mr0, s.p0)),
  select('& .forCancel.button', s.inlineBlock),
  select('.PostGrid & .forCancel.button', s.inlineBlock),
  media(s.minBreak2, select('& .forCancel.button', s.displayNone)),
)

const rightStyle = css(
  select('& button + button', s.ml10),
  select('& button:first-child', s.ml0),
)

const buttonStyle = css(
  s.bgcBlack,
  s.colorWhite,
  s.hv40,
  s.wv40,
  s.lh40,
  s.nowrap,
  {
    borderRadius: 5,
    transition: `background-color 0.2s ease, color 0.2s ease, width 0.2s ${s.ease}`,
  },
  disabled(s.pointerNone, s.bgcA),
  hover({ backgroundColor: '#6a6a6a' }),
  media(
    s.minBreak2,
    { width: 'auto' },
    select('&.forSubmit',
      { minWidth: 120 },
    ),
  ),
  modifier('.isBuyLinked', s.bgcGreen),
  modifier(
    '.forComment',
    parent(
      '.isComment',
      s.wv40,
      disabled(s.bgcA),
    ),
    parent(
      '.PostGrid .isComment',
      s.wv40,
    ),
  ),
  modifier('.forSubmit', s.bgcGreen, disabled(s.bgcA), hover({ backgroundColor: '#02b302' }), { width: 'auto' }),
  parent('.isComment', s.wv40, media(s.minBreak2, s.wv40)),
  parent('.PostGrid', s.wv40, media(s.minBreak2, s.wv40)),
)

const buttonContentsStyle = css(
  s.inlineFlex,
  s.itemsCenter,
  s.contentCenter,
  s.justifySpaceBetween,
  s.fullWidth,
  s.fullHeight,
  s.pl20,
  s.pr20,
)

const cancelTextButtonStyle = css(
  s.colorA,
  s.px10,
  { width: 'auto' },
  hover({ color: '#6a6a6a' }),
)

const labelStyle = css(
  s.displayNone,
  { marginRight: 10 },
  media(
    s.minBreak2,
    s.inlineBlock,
    select('& + .SVGIcon', { marginLeft: 10 }),
    parent(
      '.PostDetail .forComment',
      // select('& + .SVGIcon', { marginRight: 11, marginLeft: 11 }),
    ),
  ),
  // parent('.forSubmit', s.inlineBlock, select('& + .SVGIcon', { marginRight: 11 })),
  parent('.isComment', s.displayNone, select('& + .SVGIcon', s.mr0)),
  parent('.PostGrid', s.displayNone, select('& + .SVGIcon', s.mr0)),
  parent('.PostGrid .isComment .forComment', s.displayNone, select('& + .SVGIcon', s.mr0)),
  parent('.forSubmit',
    s.inlineBlock,
    select('& + .SVGIcon', { transform: 'scale(1.2)' }),
  ),
)

const hide = css(s.hide)

function mapStateToProps(state, props) {
  return {
    subscribedCategories: selectSubscribedCategories(state, props),
    featuredInCategories: selectFeaturedInCategories(state, props),
    unsubscribedCategories: selectUnsubscribedCategories(state, props),
    selectedCategories: selectPostSelectedCategories(state, props),
  }
}

class PostActionBar extends Component {

  static propTypes = {
    isComment: PropTypes.bool.isRequired,
    buyLink: PropTypes.string,
    cancelAction: PropTypes.func.isRequired,
    dispatch: PropTypes.func.isRequired,
    disableSubmitAction: PropTypes.bool.isRequired,
    editorId: PropTypes.string.isRequired,
    handleFileAction: PropTypes.func.isRequired,
    hasMedia: PropTypes.bool,
    replyAllAction: PropTypes.func,
    submitAction: PropTypes.func.isRequired,
    submitText: PropTypes.string.isRequired,
    postIntoCategory: PropTypes.bool.isRequired,
    subscribedCategories: PropTypes.array.isRequired,
    selectedCategories: PropTypes.array.isRequired,
    featuredInCategories: PropTypes.array.isRequired,
    unsubscribedCategories: PropTypes.array.isRequired,
  }

  static defaultProps = {
    buyLink: null,
    hasMedia: false,
    replyAllAction: null,
  }

  constructor(props) {
    super(props)
    this.state = {
      resetCategorySelection: false,
    }
  }

  componentDidUpdate(prevProps, prevState) {
    // reset the resets
    if (!prevState.resetCategorySelection && this.state.resetCategorySelection) {
      this.refreshResets()
    }
  }

  onAddBuyLink = ({ value }) => {
    const { dispatch, editorId } = this.props
    dispatch(updateBuyLink(editorId, value))
    this.onCloseModal()
  }

  onCloseModal = () => {
    const { dispatch } = this.props
    dispatch(closeModal())
  }

  onSelectCategory = (category) => {
    const { dispatch, editorId } = this.props
    dispatch(updateCategoryId(editorId, category.get('id')))

    this.setState({
      resetCategorySelection: true,
    })
  }

  onClearCategory = () => {
    const { dispatch, editorId } = this.props
    dispatch(clearCategoryId(editorId))
  }

  submitted = () => {
    const { submitAction } = this.props
    submitAction()
  }

  handleFileBrowser = (e) => {
    const { handleFileAction } = this.props
    handleFileAction(e)
    this.fileBrowser.value = ''
  }

  browse = () => {
    this.browseButton.blur()
    this.fileBrowser.click()
  }

  cancel = () => {
    this.props.cancelAction()
  }

  money = () => {
    const { dispatch, buyLink, submitText } = this.props
    dispatch(openModal(
      <BuyLinkDialog
        dispatch={dispatch}
        onConfirm={this.onAddBuyLink}
        onDismiss={this.onCloseModal}
        text={buyLink}
        editorType={submitText}
      />))
  }

  refreshResets = () => {
    this.setState({
      resetCategorySelection: false,
    })
  }

  render() {
    const {
      disableSubmitAction,
      hasMedia,
      replyAllAction,
      submitText,
      editorId,
      postIntoCategory,
      subscribedCategories,
      selectedCategories,
      featuredInCategories,
      unsubscribedCategories,
      isComment,
    } = this.props
    const { resetCategorySelection } = this.state
    const isBuyLinked = this.props.buyLink && this.props.buyLink.length
    return (

          <button className={`PostActionButton forCancel text ${cancelTextButtonStyle}`} onClick={this.cancel}>
            Cancel
          </button>
          {replyAllAction &&
            <button className={`PostActionButton forReplyAll ${buttonStyle}`} onClick={replyAllAction}>

                Reply All
                <ReplyAllIcon />

            </button>
          }
          <button
            className={`PostActionButton forCancel button ${buttonStyle}`}
            onClick={this.cancel}
            disabled={disableSubmitAction}
          >

              Cancel
              <XIconLG />

          </button>

          <button
            className={`PostActionButton forUpload ${buttonStyle}`}
            onClick={this.browse}
            ref={(comp) => { this.browseButton = comp }}
          >

              Upload
              <BrowseIcon />

          </button>
          {!isComment &&
            <button
              className={classNames('PostActionButton forMoney', { isBuyLinked }, `${buttonStyle}`)}
              disabled={!hasMedia}
              onClick={this.money}
            >

                Sell
                <MoneyIconCircle />

            </button>
          }
          {postIntoCategory &&
            <CategoryPostSelector
              onSelect={this.onSelectCategory}
              onClear={this.onClearCategory}
              subscribedCategories={subscribedCategories}
              featuredInCategories={featuredInCategories}
              unsubscribedCategories={unsubscribedCategories}
              selectedCategories={selectedCategories}
              resetSelection={resetCategorySelection}
            />
          }
          <button
            className={`PostActionButton forSubmit for${submitText} ${buttonStyle}`}
            disabled={disableSubmitAction}
            ref={(comp) => { this.submitButton = comp }}
            onClick={this.submitted}
          >

              {submitText}
              <ArrowIcon />

          </button>

        <input
          className={hide}
          onChange={this.handleFileBrowser}
          ref={(comp) => { this.fileBrowser = comp }}
          type="file"
          accept="image/*"
          multiple
        />

    )
  }
}

export default connect(mapStateToProps)(PostActionBar)

<commit_msg>editor - fix comment actions - WIP<commit_after>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
import { connect } from 'react-redux'
import {
  ArrowIcon,
  BrowseIcon,
  MoneyIconCircle,
  ReplyAllIcon,
  XIconLG,
} from '../assets/Icons'
import CategoryPostSelector from './CategoryPostSelector'
import { openModal, closeModal } from '../../actions/modals'
import { updateBuyLink, updateCategoryId, clearCategoryId } from '../../actions/editor'
import BuyLinkDialog from '../dialogs/BuyLinkDialog'
import {
  selectSubscribedCategories,
  selectUnsubscribedCategories,
  selectFeaturedInCategories,
  selectPostSelectedCategories,
} from '../../selectors/categories'
import { css, disabled, hover, media, modifier, parent, select } from '../../styles/jss'
import * as s from '../../styles/jso'

const wrapperStyle = css(
  s.flex,
  s.justifySpaceBetween,
  s.hv40,
  s.lh40,
  s.mt10,
)

const leftStyle = css(
  select('& button', s.mr10),
  select('& button:last-child', s.mr0),
  select('& .forCancel.text', s.displayNone),
  select('.PostGrid & .forCancel.text', s.displayNone),
  media(s.minBreak2, select('& .forCancel.text', s.inlineBlock, s.mr0, s.p0)),
  select('& .forCancel.button', s.inlineBlock),
  select('.PostGrid & .forCancel.button', s.inlineBlock),
  media(s.minBreak2, select('& .forCancel.button', s.displayNone)),
)

const rightStyle = css(
  select('& button + button', s.ml10),
  select('& button:first-child', s.ml0),
)

const buttonStyle = css(
  s.bgcBlack,
  s.colorWhite,
  s.hv40,
  s.wv40,
  s.lh40,
  s.nowrap,
  {
    borderRadius: 5,
    transition: `background-color 0.2s ease, color 0.2s ease, width 0.2s ${s.ease}`,
  },
  disabled(s.pointerNone, s.bgcA),
  hover({ backgroundColor: '#6a6a6a' }),
  media(
    s.minBreak2,
    { width: 'auto' },
    select('&.forSubmit',
      { minWidth: 120 },
    ),
  ),
  modifier('.isBuyLinked', s.bgcGreen),
  modifier(
    '.forComment',
    parent(
      '.isComment',
      s.wv40,
      disabled(s.bgcA),
    ),
    parent(
      '.PostGrid .isComment',
      s.wv40,
    ),
  ),
  modifier('.forSubmit', s.bgcGreen, disabled(s.bgcA), hover({ backgroundColor: '#02b302' }), { width: 'auto' }),
  parent('.isComment', s.wv40, media(s.minBreak2, s.wv40)),
  parent('.PostGrid', s.wv40, media(s.minBreak2, s.wv40)),
)

const buttonContentsStyle = css(
  s.inlineFlex,
  s.itemsCenter,
  s.contentCenter,
  s.justifySpaceBetween,
  s.fullWidth,
  s.fullHeight,
  s.pl20,
  s.pr20,
)

const cancelTextButtonStyle = css(
  s.colorA,
  s.px10,
  { width: 'auto' },
  hover({ color: '#6a6a6a' }),
)

const labelStyle = css(
  s.displayNone,
  { marginRight: 10 },
  media(
    s.minBreak2,
    s.inlineBlock,
    select('& + .SVGIcon', { marginLeft: 10 }),
    parent(
      '.PostDetail .forComment',
      // select('& + .SVGIcon', { marginRight: 11, marginLeft: 11 }),
    ),
  ),
  // parent('.forSubmit', s.inlineBlock, select('& + .SVGIcon', { marginRight: 11 })),
  parent('.isComment', s.displayNone, select('& + .SVGIcon', s.mr0)),
  parent('.PostGrid', s.displayNone, select('& + .SVGIcon', s.mr0)),
  parent('.PostGrid .isComment .forComment', s.displayNone, select('& + .SVGIcon', s.mr0)),
  parent('.forSubmit',
    s.inlineBlock,
    select('& + .SVGIcon', { transform: 'scale(1.2)' }),
  ),
)

const hide = css(s.hide)

function mapStateToProps(state, props) {
  return {
    subscribedCategories: selectSubscribedCategories(state, props),
    featuredInCategories: selectFeaturedInCategories(state, props),
    unsubscribedCategories: selectUnsubscribedCategories(state, props),
    selectedCategories: selectPostSelectedCategories(state, props),
  }
}

class PostActionBar extends Component {

  static propTypes = {
    isComment: PropTypes.bool.isRequired,
    buyLink: PropTypes.string,
    cancelAction: PropTypes.func.isRequired,
    dispatch: PropTypes.func.isRequired,
    disableSubmitAction: PropTypes.bool.isRequired,
    editorId: PropTypes.string.isRequired,
    handleFileAction: PropTypes.func.isRequired,
    hasMedia: PropTypes.bool,
    replyAllAction: PropTypes.func,
    submitAction: PropTypes.func.isRequired,
    submitText: PropTypes.string.isRequired,
    postIntoCategory: PropTypes.bool.isRequired,
    subscribedCategories: PropTypes.array.isRequired,
    selectedCategories: PropTypes.array.isRequired,
    featuredInCategories: PropTypes.array.isRequired,
    unsubscribedCategories: PropTypes.array.isRequired,
  }

  static defaultProps = {
    buyLink: null,
    hasMedia: false,
    replyAllAction: null,
  }

  constructor(props) {
    super(props)
    this.state = {
      resetCategorySelection: false,
    }
  }

  componentDidUpdate(prevProps, prevState) {
    // reset the resets
    if (!prevState.resetCategorySelection && this.state.resetCategorySelection) {
      this.refreshResets()
    }
  }

  onAddBuyLink = ({ value }) => {
    const { dispatch, editorId } = this.props
    dispatch(updateBuyLink(editorId, value))
    this.onCloseModal()
  }

  onCloseModal = () => {
    const { dispatch } = this.props
    dispatch(closeModal())
  }

  onSelectCategory = (category) => {
    const { dispatch, editorId } = this.props
    dispatch(updateCategoryId(editorId, category.get('id')))

    this.setState({
      resetCategorySelection: true,
    })
  }

  onClearCategory = () => {
    const { dispatch, editorId } = this.props
    dispatch(clearCategoryId(editorId))
  }

  submitted = () => {
    const { submitAction } = this.props
    submitAction()
  }

  handleFileBrowser = (e) => {
    const { handleFileAction } = this.props
    handleFileAction(e)
    this.fileBrowser.value = ''
  }

  browse = () => {
    this.browseButton.blur()
    this.fileBrowser.click()
  }

  cancel = () => {
    this.props.cancelAction()
  }

  money = () => {
    const { dispatch, buyLink, submitText } = this.props
    dispatch(openModal(
      <BuyLinkDialog
        dispatch={dispatch}
        onConfirm={this.onAddBuyLink}
        onDismiss={this.onCloseModal}
        text={buyLink}
        editorType={submitText}
      />))
  }

  refreshResets = () => {
    this.setState({
      resetCategorySelection: false,
    })
  }

  render() {
    const {
      disableSubmitAction,
      hasMedia,
      replyAllAction,
      submitText,
      editorId,
      postIntoCategory,
      subscribedCategories,
      selectedCategories,
      featuredInCategories,
      unsubscribedCategories,
      isComment,
    } = this.props
    const { resetCategorySelection } = this.state
    const isBuyLinked = this.props.buyLink && this.props.buyLink.length
    return (

          {!isComment &&
            <button className={`PostActionButton forCancel text ${cancelTextButtonStyle}`} onClick={this.cancel}>
              Cancel
            </button>
          }
          {isComment &&
            <button
              className={`PostActionButton forUpload ${buttonStyle}`}
              onClick={this.browse}
              ref={(comp) => { this.browseButton = comp }}
            >

                Upload
                <BrowseIcon />

            </button>
          }
          {isComment && replyAllAction &&
            <button className={`PostActionButton forReplyAll ${buttonStyle}`} onClick={replyAllAction}>

                Reply All
                <ReplyAllIcon />

            </button>
          }

          {!isComment &&
            <button
              className={`PostActionButton forUpload ${buttonStyle}`}
              onClick={this.browse}
              ref={(comp) => { this.browseButton = comp }}
            >

                Upload
                <BrowseIcon />

            </button>
          }
          {!isComment &&
            <button
              className={classNames('PostActionButton forMoney', { isBuyLinked }, `${buttonStyle}`)}
              disabled={!hasMedia}
              onClick={this.money}
            >

                Sell
                <MoneyIconCircle />

            </button>
          }
          {postIntoCategory &&
            <CategoryPostSelector
              onSelect={this.onSelectCategory}
              onClear={this.onClearCategory}
              subscribedCategories={subscribedCategories}
              featuredInCategories={featuredInCategories}
              unsubscribedCategories={unsubscribedCategories}
              selectedCategories={selectedCategories}
              resetSelection={resetCategorySelection}
            />
          }
          {isComment &&
            <button className={`PostActionButton forCancel text ${cancelTextButtonStyle}`} onClick={this.cancel}>
              Cancel
            </button>
          }
          <button
            className={`PostActionButton forSubmit for${submitText} ${buttonStyle}`}
            disabled={disableSubmitAction}
            ref={(comp) => { this.submitButton = comp }}
            onClick={this.submitted}
          >

              {submitText}
              <ArrowIcon />

          </button>

        <input
          className={hide}
          onChange={this.handleFileBrowser}
          ref={(comp) => { this.fileBrowser = comp }}
          type="file"
          accept="image/*"
          multiple
        />

    )
  }
}

export default connect(mapStateToProps)(PostActionBar)

<|endoftext|>


<commit_before>import TouchPan from '../../directives/touch-pan'
import { css, cssTransform } from '../../utils/dom'
import { between } from '../../utils/format'
import { QResizeObservable } from '../observables'
import ModelToggleMixin from '../../mixins/model-toggle'

const
  bodyClassBelow = 'with-layout-drawer-opened',
  bodyClassAbove = 'with-layout-drawer-opened-above',
  duration = 150

export default {
  name: 'QLayoutDrawer',
  inject: {
    layout: {
      default () {
        console.error('QLayoutDrawer needs to be child of QLayout')
      }
    }
  },
  mixins: [ModelToggleMixin],
  directives: {
    TouchPan
  },
  props: {
    overlay: Boolean,
    side: {
      type: String,
      default: 'left',
      validator: v => ['left', 'right'].includes(v)
    },
    breakpoint: {
      type: Number,
      default: 992
    },
    behavior: {
      type: String,
      validator: v => ['default', 'desktop', 'mobile'].includes(v),
      default: 'default'
    },
    contentStyle: Object,
    contentClass: [String, Object, Array],
    noHideOnRouteChange: Boolean,
    noSwipeOpen: Boolean,
    noSwipeClose: Boolean
  },
  data () {
    const
      largeScreenState = this.value !== void 0 ? this.value : true,
      showing = this.behavior !== 'mobile' && this.breakpoint < this.layout.width && !this.overlay
        ? largeScreenState
        : false

    if (this.value !== void 0 && this.value !== showing) {
      this.$emit('input', showing)
    }

    return {
      showing,
      belowBreakpoint: (
        this.behavior === 'mobile' ||
        (this.behavior !== 'desktop' && this.breakpoint >= this.layout.width)
      ),
      largeScreenState,
      mobileOpened: false,

      size: 300
    }
  },
  watch: {
    belowBreakpoint (val, old) {
      if (this.mobileOpened) {
        return
      }

      if (val) { // from lg to xs
        if (!this.overlay) {
          this.largeScreenState = this.showing
        }
        // ensure we close it for small screen
        this.hide()
      }
      else if (!this.overlay) { // from xs to lg
        this[this.largeScreenState ? 'show' : 'hide']()
      }
    },
    behavior (val) {
      this.__updateLocal('belowBreakpoint', (
        val === 'mobile' ||
        (val !== 'desktop' && this.breakpoint >= this.layout.width)
      ))
    },
    breakpoint (val) {
      this.__updateLocal('belowBreakpoint', (
        this.behavior === 'mobile' ||
        (this.behavior !== 'desktop' && val >= this.layout.width)
      ))
    },
    'layout.width' (val) {
      this.__updateLocal('belowBreakpoint', (
        this.behavior === 'mobile' ||
        (this.behavior !== 'desktop' && this.breakpoint >= val)
      ))
    },
    offset (val) {
      this.__update('offset', val)
    },
    onLayout (val) {
      this.__update('space', val)
      this.layout.__animate()
    },
    $route () {
      if (this.noHideOnRouteChange) {
        return
      }

      if (this.mobileOpened || this.onScreenOverlay) {
        this.hide()
      }
    },
    rightSide () {
      this.applyPosition()
    },
    size () {
      this.applyPosition()
    },
    '$q.i18n.rtl' () {
      this.applyPosition()
    }
  },
  computed: {
    rightSide () {
      return this.side === 'right'
    },
    offset () {
      return this.showing && !this.mobileOpened && !this.overlay
        ? this.size
        : 0
    },
    fixed () {
      return this.overlay || this.layout.view.indexOf(this.rightSide ? 'R' : 'L') > -1
    },
    onLayout () {
      return this.showing && !this.mobileView && !this.overlay
    },
    onScreenOverlay () {
      return this.showing && !this.mobileView && this.overlay
    },
    backdropClass () {
      return {
        'no-pointer-events': !this.showing
      }
    },
    mobileView () {
      return this.belowBreakpoint || this.mobileOpened
    },
    headerSlot () {
      return this.overlay
        ? false
        : (this.rightSide
          ? this.layout.rows.top[2] === 'r'
          : this.layout.rows.top[0] === 'l'
        )
    },
    footerSlot () {
      return this.overlay
        ? false
        : (this.rightSide
          ? this.layout.rows.bottom[2] === 'r'
          : this.layout.rows.bottom[0] === 'l'
        )
    },
    belowClass () {
      return {
        'fixed': true,
        'on-top': true,
        'q-layout-drawer-delimiter': this.fixed && this.showing,
        'top-padding': true
      }
    },
    aboveClass () {
      return {
        'fixed': this.fixed || !this.onLayout,
        'q-layout-drawer-delimiter': this.fixed && this.showing,
        'top-padding': this.headerSlot
      }
    },
    aboveStyle () {
      const css = {}

      if (this.layout.header.space && !this.headerSlot) {
        if (this.fixed) {
          css.top = `${this.layout.header.offset}px`
        }
        else if (this.layout.header.space) {
          css.top = `${this.layout.header.size}px`
        }
      }

      if (this.layout.footer.space && !this.footerSlot) {
        if (this.fixed) {
          css.bottom = `${this.layout.footer.offset}px`
        }
        else if (this.layout.footer.space) {
          css.bottom = `${this.layout.footer.size}px`
        }
      }

      return css
    },
    computedStyle () {
      return [this.contentStyle, this.mobileView ? '' : this.aboveStyle]
    },
    computedClass () {
      return [this.contentClass, this.mobileView ? this.belowClass : this.aboveClass]
    },
    stateDirection () {
      return (this.$q.i18n.rtl ? -1 : 1) * (this.rightSide ? 1 : -1)
    }
  },
  render (h) {
    const child = []

    if (this.mobileView) {
      if (!this.noSwipeOpen) {
        child.push(h('div', {
          staticClass: `q-layout-drawer-opener fixed-${this.side}`,
          directives: [{
            name: 'touch-pan',
            modifiers: { horizontal: true },
            value: this.__openByTouch
          }]
        }))
      }
      child.push(h('div', {
        ref: 'backdrop',
        staticClass: 'fullscreen q-layout-backdrop',
        'class': this.backdropClass,
        on: { click: this.hide },
        directives: [{
          name: 'touch-pan',
          modifiers: { horizontal: true },
          value: this.__closeByTouch
        }]
      }))
    }

    return h('div', { staticClass: 'q-drawer-container' }, child.concat([
      h('aside', {
        ref: 'content',
        staticClass: `q-layout-drawer q-layout-transition q-layout-drawer-${this.side} scroll`,
        'class': this.computedClass,
        style: this.computedStyle,
        attrs: this.$attrs,
        listeners: this.$listeners,
        directives: this.mobileView && !this.noSwipeClose ? [{
          name: 'touch-pan',
          modifiers: { horizontal: true },
          value: this.__closeByTouch
        }] : null
      }, [
        h(QResizeObservable, {
          props: { debounce: 0 },
          on: { resize: this.__onResize }
        }),
        this.$slots.default
      ])
    ]))
  },
  created () {
    this.layout.instances[this.side] = this
    this.__update('space', this.onLayout)
    this.__update('offset', this.offset)

    this.$nextTick(() => {
      this.animateOverlay = true
    })
  },
  mounted () {
    if (this.showing) {
      this.applyPosition(0)
    }
  },
  beforeDestroy () {
    clearTimeout(this.timer)
    if (this.layout.instances[this.side] === this) {
      this.layout.instances[this.side] = null
      this.__update('size', 0)
      this.__update('offset', 0)
      this.__update('space', false)
    }
  },
  methods: {
    applyPosition (position) {
      if (position === void 0) {
        this.$nextTick(() => {
          position = this.showing
            ? 0
            : (this.$q.i18n.rtl ? -1 : 1) * (this.rightSide ? 1 : -1) * this.size

          this.applyPosition(position)
        })
        return
      }
      css(this.$refs.content, cssTransform(`translateX(${position}px)`))
    },
    applyBackdrop (x) {
      this.$refs.backdrop && css(this.$refs.backdrop, { backgroundColor: `rgba(0,0,0,${x * 0.4})` })
    },
    __openByTouch (evt) {
      if (!this.belowBreakpoint) {
        return
      }
      const
        width = this.size,
        position = between(evt.distance.x, 0, width)

      if (evt.isFinal) {
        const
          el = this.$refs.content,
          opened = position >= Math.min(75, width)

        el.classList.remove('no-transition')
        this.layout.__animate()
        this.$nextTick(() => {
          if (opened) {
            this.show()
          }
          else {
            this.applyBackdrop(0)
            this.applyPosition(this.stateDirection * width)
            el.classList.remove('q-layout-drawer-delimiter')
          }
        })
        return
      }

      this.applyPosition(
        (this.$q.i18n.rtl ? !this.rightSide : this.rightSide)
          ? Math.max(width - position, 0)
          : Math.min(0, position - width)
      )
      this.applyBackdrop(
        between(position / width, 0, 1)
      )

      if (evt.isFirst) {
        const el = this.$refs.content
        document.body.classList.add(bodyClassBelow)
        el.classList.add('no-transition')
        el.classList.add('q-layout-drawer-delimiter')
      }
    },
    __closeByTouch (evt) {
      if (!this.mobileOpened) {
        return
      }

      const
        width = this.size,
        dir = evt.direction === this.side,
        position = (this.$q.i18n.rtl ? !dir : dir)
          ? between(evt.distance.x, 0, width)
          : 0

      if (evt.isFinal) {
        const opened = Math.abs(position) < Math.min(75, width)
        this.$refs.content.classList.remove('no-transition')
        this.layout.__animate()
        this.$nextTick(() => {
          if (opened) {
            this.applyBackdrop(1)
            this.applyPosition(0)
          }
          else {
            this.hide()
          }
        })
        return
      }

      this.applyPosition(this.stateDirection * position)
      this.applyBackdrop(between(1 - position / width, 0, 1))

      if (evt.isFirst) {
        this.$refs.content.classList.add('no-transition')
      }
    },
    __show () {
      this.layout.__animate()

      if (this.belowBreakpoint) {
        const otherSide = this.layout.instances[this.rightSide ? 'left' : 'right']
        if (otherSide && otherSide.mobileOpened) {
          otherSide.hide()
        }
        this.mobileOpened = true
        this.applyBackdrop(1)
      }

      this.applyPosition(0)
      document.body.classList.add(this.belowBreakpoint ? bodyClassBelow : bodyClassAbove)

      clearTimeout(this.timer)
      this.timer = setTimeout(() => {
        if (this.showPromise) {
          this.showPromise.then(() => {
            document.body.classList.remove(bodyClassAbove)
          })
          this.showPromiseResolve()
        }
      }, duration)
    },
    __hide () {
      this.layout.__animate()
      clearTimeout(this.timer)

      this.mobileOpened = false
      this.applyPosition((this.$q.i18n.rtl ? -1 : 1) * (this.rightSide ? 1 : -1) * this.size)
      this.applyBackdrop(0)

      document.body.classList.remove(bodyClassAbove)
      document.body.classList.remove(bodyClassBelow)

      this.timer = setTimeout(() => {
        this.hidePromise && this.hidePromiseResolve()
      }, duration)
    },

    __onResize ({ width }) {
      this.__update('size', width)
      this.__updateLocal('size', width)
    },
    __update (prop, val) {
      if (this.layout[this.side][prop] !== val) {
        this.layout[this.side][prop] = val
      }
    },
    __updateLocal (prop, val) {
      if (this[prop] !== val) {
        this[prop] = val
      }
    }
  }
}
<commit_msg>fix(QLayoutDrawer): overflow:hidden lingers after closing a drawer by dragging #1809<commit_after>import TouchPan from '../../directives/touch-pan'
import { css, cssTransform } from '../../utils/dom'
import { between } from '../../utils/format'
import { QResizeObservable } from '../observables'
import ModelToggleMixin from '../../mixins/model-toggle'

const
  bodyClassBelow = 'with-layout-drawer-opened',
  bodyClassAbove = 'with-layout-drawer-opened-above',
  duration = 150

export default {
  name: 'QLayoutDrawer',
  inject: {
    layout: {
      default () {
        console.error('QLayoutDrawer needs to be child of QLayout')
      }
    }
  },
  mixins: [ModelToggleMixin],
  directives: {
    TouchPan
  },
  props: {
    overlay: Boolean,
    side: {
      type: String,
      default: 'left',
      validator: v => ['left', 'right'].includes(v)
    },
    breakpoint: {
      type: Number,
      default: 992
    },
    behavior: {
      type: String,
      validator: v => ['default', 'desktop', 'mobile'].includes(v),
      default: 'default'
    },
    contentStyle: Object,
    contentClass: [String, Object, Array],
    noHideOnRouteChange: Boolean,
    noSwipeOpen: Boolean,
    noSwipeClose: Boolean
  },
  data () {
    const
      largeScreenState = this.value !== void 0 ? this.value : true,
      showing = this.behavior !== 'mobile' && this.breakpoint < this.layout.width && !this.overlay
        ? largeScreenState
        : false

    if (this.value !== void 0 && this.value !== showing) {
      this.$emit('input', showing)
    }

    return {
      showing,
      belowBreakpoint: (
        this.behavior === 'mobile' ||
        (this.behavior !== 'desktop' && this.breakpoint >= this.layout.width)
      ),
      largeScreenState,
      mobileOpened: false,

      size: 300
    }
  },
  watch: {
    belowBreakpoint (val, old) {
      if (this.mobileOpened) {
        return
      }

      if (val) { // from lg to xs
        if (!this.overlay) {
          this.largeScreenState = this.showing
        }
        // ensure we close it for small screen
        this.hide()
      }
      else if (!this.overlay) { // from xs to lg
        this[this.largeScreenState ? 'show' : 'hide']()
      }
    },
    behavior (val) {
      this.__updateLocal('belowBreakpoint', (
        val === 'mobile' ||
        (val !== 'desktop' && this.breakpoint >= this.layout.width)
      ))
    },
    breakpoint (val) {
      this.__updateLocal('belowBreakpoint', (
        this.behavior === 'mobile' ||
        (this.behavior !== 'desktop' && val >= this.layout.width)
      ))
    },
    'layout.width' (val) {
      this.__updateLocal('belowBreakpoint', (
        this.behavior === 'mobile' ||
        (this.behavior !== 'desktop' && this.breakpoint >= val)
      ))
    },
    offset (val) {
      this.__update('offset', val)
    },
    onLayout (val) {
      this.__update('space', val)
      this.layout.__animate()
    },
    $route () {
      if (this.noHideOnRouteChange) {
        return
      }

      if (this.mobileOpened || this.onScreenOverlay) {
        this.hide()
      }
    },
    rightSide () {
      this.applyPosition()
    },
    size () {
      this.applyPosition()
    },
    '$q.i18n.rtl' () {
      this.applyPosition()
    }
  },
  computed: {
    rightSide () {
      return this.side === 'right'
    },
    offset () {
      return this.showing && !this.mobileOpened && !this.overlay
        ? this.size
        : 0
    },
    fixed () {
      return this.overlay || this.layout.view.indexOf(this.rightSide ? 'R' : 'L') > -1
    },
    onLayout () {
      return this.showing && !this.mobileView && !this.overlay
    },
    onScreenOverlay () {
      return this.showing && !this.mobileView && this.overlay
    },
    backdropClass () {
      return {
        'no-pointer-events': !this.showing
      }
    },
    mobileView () {
      return this.belowBreakpoint || this.mobileOpened
    },
    headerSlot () {
      return this.overlay
        ? false
        : (this.rightSide
          ? this.layout.rows.top[2] === 'r'
          : this.layout.rows.top[0] === 'l'
        )
    },
    footerSlot () {
      return this.overlay
        ? false
        : (this.rightSide
          ? this.layout.rows.bottom[2] === 'r'
          : this.layout.rows.bottom[0] === 'l'
        )
    },
    belowClass () {
      return {
        'fixed': true,
        'on-top': true,
        'q-layout-drawer-delimiter': this.fixed && this.showing,
        'top-padding': true
      }
    },
    aboveClass () {
      return {
        'fixed': this.fixed || !this.onLayout,
        'q-layout-drawer-delimiter': this.fixed && this.showing,
        'top-padding': this.headerSlot
      }
    },
    aboveStyle () {
      const css = {}

      if (this.layout.header.space && !this.headerSlot) {
        if (this.fixed) {
          css.top = `${this.layout.header.offset}px`
        }
        else if (this.layout.header.space) {
          css.top = `${this.layout.header.size}px`
        }
      }

      if (this.layout.footer.space && !this.footerSlot) {
        if (this.fixed) {
          css.bottom = `${this.layout.footer.offset}px`
        }
        else if (this.layout.footer.space) {
          css.bottom = `${this.layout.footer.size}px`
        }
      }

      return css
    },
    computedStyle () {
      return [this.contentStyle, this.mobileView ? '' : this.aboveStyle]
    },
    computedClass () {
      return [this.contentClass, this.mobileView ? this.belowClass : this.aboveClass]
    },
    stateDirection () {
      return (this.$q.i18n.rtl ? -1 : 1) * (this.rightSide ? 1 : -1)
    }
  },
  render (h) {
    const child = []

    if (this.mobileView) {
      if (!this.noSwipeOpen) {
        child.push(h('div', {
          staticClass: `q-layout-drawer-opener fixed-${this.side}`,
          directives: [{
            name: 'touch-pan',
            modifiers: { horizontal: true },
            value: this.__openByTouch
          }]
        }))
      }
      child.push(h('div', {
        ref: 'backdrop',
        staticClass: 'fullscreen q-layout-backdrop',
        'class': this.backdropClass,
        on: { click: this.hide },
        directives: [{
          name: 'touch-pan',
          modifiers: { horizontal: true },
          value: this.__closeByTouch
        }]
      }))
    }

    return h('div', { staticClass: 'q-drawer-container' }, child.concat([
      h('aside', {
        ref: 'content',
        staticClass: `q-layout-drawer q-layout-transition q-layout-drawer-${this.side} scroll`,
        'class': this.computedClass,
        style: this.computedStyle,
        attrs: this.$attrs,
        listeners: this.$listeners,
        directives: this.mobileView && !this.noSwipeClose ? [{
          name: 'touch-pan',
          modifiers: { horizontal: true },
          value: this.__closeByTouch
        }] : null
      }, [
        h(QResizeObservable, {
          props: { debounce: 0 },
          on: { resize: this.__onResize }
        }),
        this.$slots.default
      ])
    ]))
  },
  created () {
    this.layout.instances[this.side] = this
    this.__update('space', this.onLayout)
    this.__update('offset', this.offset)

    this.$nextTick(() => {
      this.animateOverlay = true
    })
  },
  mounted () {
    if (this.showing) {
      this.applyPosition(0)
    }
  },
  beforeDestroy () {
    clearTimeout(this.timer)
    if (this.layout.instances[this.side] === this) {
      this.layout.instances[this.side] = null
      this.__update('size', 0)
      this.__update('offset', 0)
      this.__update('space', false)
    }
  },
  methods: {
    applyPosition (position) {
      if (position === void 0) {
        this.$nextTick(() => {
          position = this.showing
            ? 0
            : (this.$q.i18n.rtl ? -1 : 1) * (this.rightSide ? 1 : -1) * this.size

          this.applyPosition(position)
        })
        return
      }
      css(this.$refs.content, cssTransform(`translateX(${position}px)`))
    },
    applyBackdrop (x) {
      this.$refs.backdrop && css(this.$refs.backdrop, { backgroundColor: `rgba(0,0,0,${x * 0.4})` })
    },
    __openByTouch (evt) {
      if (!this.belowBreakpoint) {
        return
      }
      const
        width = this.size,
        position = between(evt.distance.x, 0, width)

      if (evt.isFinal) {
        const
          el = this.$refs.content,
          opened = position >= Math.min(75, width)

        el.classList.remove('no-transition')
        this.layout.__animate()
        this.$nextTick(() => {
          if (opened) {
            this.show()
          }
          else {
            this.applyBackdrop(0)
            this.applyPosition(this.stateDirection * width)
            el.classList.remove('q-layout-drawer-delimiter')
            document.body.classList.remove(bodyClassBelow)
          }
        })
        return
      }

      this.applyPosition(
        (this.$q.i18n.rtl ? !this.rightSide : this.rightSide)
          ? Math.max(width - position, 0)
          : Math.min(0, position - width)
      )
      this.applyBackdrop(
        between(position / width, 0, 1)
      )

      if (evt.isFirst) {
        const el = this.$refs.content
        document.body.classList.add(bodyClassBelow)
        el.classList.add('no-transition')
        el.classList.add('q-layout-drawer-delimiter')
      }
    },
    __closeByTouch (evt) {
      if (!this.mobileOpened) {
        return
      }

      const
        width = this.size,
        dir = evt.direction === this.side,
        position = (this.$q.i18n.rtl ? !dir : dir)
          ? between(evt.distance.x, 0, width)
          : 0

      if (evt.isFinal) {
        const opened = Math.abs(position) < Math.min(75, width)
        this.$refs.content.classList.remove('no-transition')
        this.layout.__animate()
        this.$nextTick(() => {
          if (opened) {
            this.applyBackdrop(1)
            this.applyPosition(0)
          }
          else {
            this.hide()
          }
        })
        return
      }

      this.applyPosition(this.stateDirection * position)
      this.applyBackdrop(between(1 - position / width, 0, 1))

      if (evt.isFirst) {
        this.$refs.content.classList.add('no-transition')
      }
    },
    __show () {
      this.layout.__animate()

      if (this.belowBreakpoint) {
        const otherSide = this.layout.instances[this.rightSide ? 'left' : 'right']
        if (otherSide && otherSide.mobileOpened) {
          otherSide.hide()
        }
        this.mobileOpened = true
        this.applyBackdrop(1)
      }

      this.applyPosition(0)
      document.body.classList.add(this.belowBreakpoint ? bodyClassBelow : bodyClassAbove)

      clearTimeout(this.timer)
      this.timer = setTimeout(() => {
        if (this.showPromise) {
          this.showPromise.then(() => {
            document.body.classList.remove(bodyClassAbove)
          })
          this.showPromiseResolve()
        }
      }, duration)
    },
    __hide () {
      this.layout.__animate()
      clearTimeout(this.timer)

      this.mobileOpened = false
      this.applyPosition((this.$q.i18n.rtl ? -1 : 1) * (this.rightSide ? 1 : -1) * this.size)
      this.applyBackdrop(0)

      document.body.classList.remove(bodyClassAbove)
      document.body.classList.remove(bodyClassBelow)

      this.timer = setTimeout(() => {
        this.hidePromise && this.hidePromiseResolve()
      }, duration)
    },

    __onResize ({ width }) {
      this.__update('size', width)
      this.__updateLocal('size', width)
    },
    __update (prop, val) {
      if (this.layout[this.side][prop] !== val) {
        this.layout[this.side][prop] = val
      }
    },
    __updateLocal (prop, val) {
      if (this[prop] !== val) {
        this[prop] = val
      }
    }
  }
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';

const containerStyle = {
  width: "35em",
  margin: "0 auto",
};

const style = {
  width: "100%",
  height: "1.3em",
  margin: "5px auto",
  fontSize: "1.2em",
  textAlign: "center",
  backgroundColor: "#ddd",
  borderRadius: 2,
};

const styleBefore = {
  height: "3em",
  padding: 4,
  fontSize: "1.2em",
  textAlign: "center",
  backgroundColor: "#ddd",
  borderRadius: 2,
};

const styleAfter = {
  height: "3em",
  padding: 4,
  fontSize: "1.3em",
  textAlign: "center",
  backgroundColor: "#ddd",
  borderRadius: 2,
};

const styleCurrentWord = {
  color: "white",
};

const wordStyle = {
  color: "#333",
};

class TypingTestContents extends Component {
  render() {
    // Break the current line into an array of words
    const currentLine = this.props.text[this.props.line].split(" ");
    const lines = this.props.text.map(line => line);
    const currentWord = this.props.currentWord;
    return (

before text saljkd flkdsf lkjdsf

          {
            currentLine.map(function(word, i) {
              let style = wordStyle;
              if (i === currentWord)
                style = styleCurrentWord;
              return ({word} );
            })
          }

after lasdl kjasdlkjasd lasdlkasdj lkj

    );
  }
}

TypingTestContents.propTypes = {
  text: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
  line: React.PropTypes.number.isRequired,
  currentWord: React.PropTypes.number.isRequired,
}

export default TypingTestContents;
<commit_msg>Cleanup<commit_after>import React, { Component } from 'react';

const containerStyle = {
  width: "35em",
  margin: "0 auto",
};

const style = {
  width: "100%",
  height: "1.3em",
  margin: "5px auto",
  fontSize: "1.2em",
  textAlign: "center",
  backgroundColor: "#ddd",
  borderRadius: 2,
};

const styleBefore = {
  height: "3em",
  padding: 4,
  fontSize: "1.2em",
  textAlign: "center",
  backgroundColor: "#ddd",
  borderRadius: 2,
};

const styleAfter = {
  height: "3em",
  padding: 4,
  fontSize: "1.3em",
  textAlign: "center",
  backgroundColor: "#ddd",
  borderRadius: 2,
};

const styleCurrentWord = {
  color: "white",
};

const wordStyle = {
  color: "#333",
};

class TypingTestContents extends Component {
  render() {
    // Break the current line into an array of words
    const currentLine = this.props.text[this.props.line].split(" ");
    //const lines = this.props.text;
    const currentWord = this.props.currentWord;
    return (

before text saljkd flkdsf lkjdsf

          {
            currentLine.map((word, i) => {
              let style = wordStyle;
              if (i === currentWord)
                style = styleCurrentWord;
              return ({word} );
            })
          }

after lasdl kjasdlkjasd lasdlkasdj lkj

    );
  }
}

TypingTestContents.propTypes = {
  text: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
  line: React.PropTypes.number.isRequired,
  currentWord: React.PropTypes.number.isRequired,
}

export default TypingTestContents;
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var browserify = require('browserify');
var path = require.resolve('../');

/**
 * Module exports.
 */

module.exports = build;

/**
 * Make the build.
 *
 * @api public
 */

function build(fn){
  var opts = {};
  opts.entries = [path];
  opts.builtins = false;
  browserify(opts).bundle({ standalone: 'io', insertGlobalVars: ['global'] }, fn);
}
<commit_msg>support: fix browserify<commit_after>
/**
 * Module dependencies.
 */

var browserify = require('browserify');
var path = require.resolve('../');

/**
 * Module exports.
 */

module.exports = build;

/**
 * Make the build.
 *
 * @api public
 */

function build(fn){
  var opts = {};
  opts.builtins = false;
  opts.entries = [path];
  var bundle = {};
  bundle.standalone = 'io';
  bundle.insertGlobalVars = ['global'];
  browserify(opts).bundle(bundle, fn);
}
<|endoftext|>


<commit_before>// ─────────────────────────────────────────────────────────────────────────────
// import
// ─────────────────────────────────────────────────────────────────────────────

import React, { PureComponent } from 'react';
import { Radio } from '../components';

// ─────────────────────────────────────────────────────────────────────────────
// component
// ─────────────────────────────────────────────────────────────────────────────

export default class ColourThemeContainer extends PureComponent {
  state = {
    currentTheme:
      (typeof window !== 'undefined' && window.localStorage.getItem('currentTheme')) || 'basic',
    emoji: String.fromCodePoint(Math.floor(Math.random() * (128591 - 128513)) + 128513),
  };

  componentDidMount = () => {
    this.handleChangeColorTheme(this.state.currentTheme);
  };

  handleChangeColorTheme = (type = 'basic') => {
    const seed = Math.floor(Math.random() * 360);
    const emojiSeed = Math.floor(Math.random() * (128591 - 128513)) + 128513;

    const themes = {
      basic: {
        color:           '200, 5%, 45%',
        backgroundColor: '0, 100%, 100%',
      },
      crazy: {
        color:           `${seed}, 100%, 50%`,
        backgroundColor: `${seed - 180}, 100%, 50%`,
      },
      dark: {
        color:           '0, 0%, 60%',
        backgroundColor: '0, 0%, 5%',
      },
    };

    this.setState(
      {
        currentTheme: type,
        emoji:        String.fromCodePoint(emojiSeed),
      },
      () => {
        document.documentElement.style.setProperty('--hsl-text', themes[type].color);
        document.documentElement.style.setProperty('--hsl-bg', themes[type].backgroundColor);
        window.localStorage.setItem('currentTheme', type);
      },
    );
  };

  render() {
    return (

        {[
          {
            name:  'basic',
            label: 'basic',
          },
          {
            name:  'crazy',
            label: `🥑 CRAZY!!1! ${this.state.emoji}`,
          },
          {
            name:  'dark',
            label: '🌙',
          },
        ].map(radio => (
          <Radio
            key={radio.name}
            {...radio}
            checked={this.state.currentTheme === radio.name}
            onChange={() => this.handleChangeColorTheme(radio.name)}
          />
        ))}

    );
  }
}
<commit_msg>update colour theme inputs<commit_after>// ─────────────────────────────────────────────────────────────────────────────
// import
// ─────────────────────────────────────────────────────────────────────────────

import React, { PureComponent } from 'react';
import { Radio } from '../components';

// ─────────────────────────────────────────────────────────────────────────────
// component
// ─────────────────────────────────────────────────────────────────────────────

export default class ColourThemeContainer extends PureComponent {
  inputs = [
    {
      name:  'basic',
      label: 'Basic',
    },
    {
      name:  'dark',
      label: 'Dark',
    },
    {
      name:  'crazy',
      label: 'Crazy',
    },
  ];

  state = {
    currentTheme:
      (typeof window !== 'undefined' && window.localStorage.getItem('currentTheme')) || 'basic',
  };

  componentDidMount = () => {
    this.handleChangeColorTheme(this.state.currentTheme);
  };

  handleChangeColorTheme = (type = 'basic') => {
    const seed = Math.floor(Math.random() * 360);

    const themes = {
      basic: {
        color:           '200, 5%, 45%',
        backgroundColor: '0, 100%, 100%',
      },
      crazy: {
        color:           `${seed}, 100%, 50%`,
        backgroundColor: `${seed - 180}, 100%, 50%`,
      },
      dark: {
        color:           '0, 0%, 60%',
        backgroundColor: '0, 0%, 5%',
      },
    };

    this.setState({ currentTheme: type }, () => {
      document.documentElement.style.setProperty('--hsl-text', themes[type].color);
      document.documentElement.style.setProperty('--hsl-bg', themes[type].backgroundColor);
      window.localStorage.setItem('currentTheme', type);
    });
  };

  render() {
    return (

        {this.inputs.map(radio => (
          <Radio
            key={radio.name}
            {...radio}
            checked={this.state.currentTheme === radio.name}
            onChange={() => this.handleChangeColorTheme(radio.name)}
          />
        ))}

    );
  }
}
<|endoftext|>