"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = exports.starPathData = void 0;

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));

var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));

var _getPrototypeOf3 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));

var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));

var _react = _interopRequireWildcard(require("react"));

var _propTypes = require("prop-types");

var _d3Array = require("d3-array");

var _d3Axis = require("d3-axis");

var _d3Scale = require("d3-scale");

var _d3Selection = require("d3-selection");

var _lib = _interopRequireDefault(require("@instructure/ui-themeable/lib"));

var _formatMessage = _interopRequireDefault(require("@instructure/quiz-common/lib/format-message"));

var _theme = _interopRequireDefault(require("./theme"));

var _ScreenReaderContent = _interopRequireDefault(require("@instructure/ui-a11y/lib/components/ScreenReaderContent"));

var _PresentationContent = _interopRequireDefault(require("@instructure/ui-a11y/lib/components/PresentationContent"));

var _d3Transition = require("d3-transition");

var _flow = _interopRequireDefault(require("lodash/flow"));

var _dec,
    _class,
    _class2,
    _temp,
    _default_da_number = Intl.NumberFormat("da", { style: "decimal" }).format,
    _percent_da_number = Intl.NumberFormat("da", { style: "percent" }).format;

var styles = {
  componentId: 'owVFv',
  template: function template(theme) {
    return "\n\n.bIUJn_fKwc{fill:".concat(theme.barFillColor || 'inherit', "}\n\n.bIUJn_erik{fill:").concat(theme.barStarFillColor || 'inherit', "}\n\n.bIUJn_eIKC{color:").concat(theme.xAxisColor || 'inherit', "}\n\n.bIUJn_cKHw{color:").concat(theme.barLabelColor || 'inherit', ";font-family:").concat(theme.barLabelFontFamily || 'inherit', ";font-size:").concat(theme.barLabelFontSize || 'inherit', ";text-anchor:middle}");
  },
  'bar': 'bIUJn_fKwc',
  'barStar': 'bIUJn_erik',
  'xAxis': 'bIUJn_eIKC',
  'barLabel': 'bIUJn_cKHw'
};
// Star path copied from instructure-icons/svg/Solid/star.svg
// eslint-disable-next-line max-len
var starPathData = 'M1915.918 737.475c-10.955-33.543-42.014-56.131-77.364-56.131h-612.029l-189.063-582.1v-.112C1026.394 65.588 995.335 43 959.984 43c-35.237 0-66.41 22.588-77.365 56.245L693.443 681.344H81.415c-35.35 0-66.41 22.588-77.365 56.131-10.955 33.544.79 70.137 29.478 91.03l495.247 359.831-189.177 582.212c-10.955 33.657 1.13 70.25 29.817 90.918 14.23 10.278 30.946 15.473 47.66 15.473 16.716 0 33.432-5.195 47.775-15.586l495.134-359.718 495.021 359.718c28.574 20.781 67.087 20.781 95.662.113 28.687-20.668 40.658-57.261 29.703-91.03l-189.176-582.1 495.36-359.83c28.574-20.894 40.433-57.487 29.364-91.03';
exports.starPathData = starPathData;
var MARGIN_T = 0;
var MARGIN_B = 28;
/**
 *   A component to render an a11y friendly distribution of scores across users.
 *   Underneath the chart is two accessible features:
 *     1. A keyboard friendly set of min(10, bucketCounts) buckets {0-10%, 10-20%, ..., 90-100%}
 *     with the number of users in that slice
 *     2. A screenreader-only block of content providing a table version of all the data
 *
 *     ```jsx_example
 *      <Distribution
 *        summaryDistribution={Array.from({length: 10}, () => Math.floor(Math.random() * 5))}
 *        visualDistribution={Array.from({length: 101}, () => Math.floor(Math.random() * 5))}
 *      />
 *      ```
 *  **/
// TODO: Less inheritance, more composition...

var Distribution = (_dec = (0, _lib.default)(_theme.default, styles), _dec(_class = (_temp = _class2 =
/*#__PURE__*/
function (_Component) {
  (0, _inherits2.default)(Distribution, _Component);

  function Distribution() {
    var _getPrototypeOf2;

    var _this;

    (0, _classCallCheck2.default)(this, Distribution);

    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    _this = (0, _possibleConstructorReturn2.default)(this, (_getPrototypeOf2 = (0, _getPrototypeOf3.default)(Distribution)).call.apply(_getPrototypeOf2, [this].concat(args)));

    _this._isStarred = function (d, i) {
      return typeof _this.props.starIndex === 'number' && i === _this.props.starIndex;
    };

    _this._renderStar = function (svg) {
      if (typeof _this.props.starIndex !== 'number') return;
      var star = svg.append('path').attr('d', starPathData).classed(styles.barStar, true);
      var starIndex = _this.props.starIndex;
      var starX = _this.x(starIndex) + _this.x.bandwidth() / 2;
      var starY = _this.y(_this.props.visualDistribution[starIndex]) - 30; // scale the star down and position it on the x axis - y axis will be applied by animation

      var xTransform = "translate(".concat(starX, " 0) scale(0.011) translate(-960 0)");

      _this.applyTransition(star, function (rects) {
        return rects.attr('opacity', 0).attr('transform', "translate(0 ".concat(starY + 20, ") ").concat(xTransform));
      }, function (rects) {
        return rects.attr('opacity', 1).attr('transform', "translate(0 ".concat(starY, ") ").concat(xTransform));
      }, function (transition) {
        return transition.delay(_this.props.animateDuration).duration(600);
      });
    };

    _this.applyTransition = function (selection, initialFunc, finalFunc, transitionFunc) {
      var identity = function identity(s) {
        return s;
      };

      var transition = function transition(s) {
        return s.transition(_this.getTransition());
      };

      var applicators = [];

      if (_this.isAnimated()) {
        applicators.push(initialFunc || identity, transition, transitionFunc || identity);
      }

      applicators.push(finalFunc || identity);
      return (0, _flow.default)(applicators)(selection);
    };

    return _this;
  }

  (0, _createClass2.default)(Distribution, [{
    key: "componentDidMount",
    value: function componentDidMount() {
      this.__svg = this.createChart(this.chart, this.props);
      this.updateChart(this.__svg, this.props);
    }
  }, {
    key: "shouldComponentUpdate",
    value: function shouldComponentUpdate(nextProps) {
      this.updateChart(this.__svg, nextProps);
      return false;
    }
  }, {
    key: "componentWillUnmount",
    value: function componentWillUnmount() {
      this.removeChart();
    }
  }, {
    key: "isAnimated",
    value: function isAnimated() {
      return this.props.animateDelay || this.props.animateDuration;
    }
  }, {
    key: "calculateAxis",
    value: function calculateAxis(props) {
      // the x scale is static since it will always represent the BucketCount
      // percentiles, so we can avoid recalculating it on every update:
      this.x = (0, _d3Scale.scaleBand)().rangeRound([0, this.width]).paddingInner(props.barPaddingInner).paddingOuter(props.barPaddingOuter).domain((0, _d3Array.range)(props.visualDistribution.length));
      var highest = (0, _d3Array.max)(props.visualDistribution);
      this.y = (0, _d3Scale.scaleLinear)().rangeRound([this.height, props.barPaddingTop]).domain([0, highest]);
      this.xAxis = (0, _d3Axis.axisBottom)(this.x).tickSize([0]).tickValues((0, _d3Array.range)(props.visualDistribution.length));
      this.props.calculateAxis(this.x, this.y, this.xAxis, this.height);
    }
  }, {
    key: "weightedAvg",
    value: function weightedAvg(data) {
      var partitions = data.length - 1;
      var totalStudents = (0, _d3Array.sum)(data);
      var avg = data.reduce(function (total, count, index) {
        // Assumes even weight of scores across distribution
        var score = index / partitions;
        return total + score * count;
      }, 0.0) / totalStudents;
      var avgPartition = Math.floor(avg * partitions);
      return {
        avg: avg,
        aboveAvg: (0, _d3Array.sum)(data.slice(avgPartition)),
        belowAvg: (0, _d3Array.sum)(data.slice(0, avgPartition))
      };
    }
  }, {
    key: "summarizeChart",
    value: function summarizeChart() {
      var data = this.props.summaryDistribution || this.props.visualDistribution;

      if (data.length <= 1) {
        return '';
      }

      var avgInfo = this.weightedAvg(data);

      if (avgInfo.belowAvg + avgInfo.aboveAvg < 1) {
        return '';
      }

      var textForScreenreaders = avgInfo.aboveAvg + " studerende klarede sig over eller p\xE5 gennemsnittet, og " + avgInfo.belowAvg + " under. ";
      data.forEach(function (datum, i) {
        if (datum > 0) {
          var _s, _n, _t, _i;

          textForScreenreaders += (_s = datum, _n = +_s, (_t = +('' + _s).replace(/^[^.]*.?|0+$/g, ''), _i = Math.floor(Math.abs(+_s)), _n === 1 || _t !== 0 && (_i === 0 || _i === 1) ? _default_da_number(datum) + " studerende fik mellem " + _percent_da_number(i / data.length) + " og " + _percent_da_number((i + 1) / data.length) + ". " : _default_da_number(datum) + " studerende fik mellem " + _percent_da_number(i / data.length) + " og " + _percent_da_number((i + 1) / data.length) + ". "));
        }
      });
      return this.props.summarizeChart(textForScreenreaders);
    }
  }, {
    key: "createChart",
    value: function createChart(node, props) {
      // parentNode for IE11
      var parentWidth = (node.parentElement || node.parentNode || {}).offsetWidth;
      this.width = props.chartWidth || parentWidth;
      this.height = props.chartHeight;
      this.viewportHeight = this.height + MARGIN_T + MARGIN_B;
      this.calculateAxis(props);
      var svg = (0, _d3Selection.select)(node).attr('role', 'document').attr('aria-role', 'document').attr('width', '100%').attr('height', '100%').attr('viewBox', "0 0 ".concat(this.width, " ").concat(this.viewportHeight)).attr('preserveAspectRatio', 'xMidYMax').append('g');
      this.title = Distribution.addTitle(svg, "Oversigt over procentvist resultat");
      svg.append('g').attr('class', styles.xAxis).attr('aria-hidden', true).attr('transform', "translate(0,".concat(this.height, ")")).call(this.xAxis).selectAll('text').attr('class', styles.barLabel).attr('y', 9);
      var clipKey = "clipPath".concat(this.width, "x").concat(this.height);
      var clipPath = svg.append('clipPath').attr('id', clipKey);
      clipPath.append('rect').attr('x', 0).attr('y', 0).attr('width', this.width).attr('height', this.height);
      this.barContainer = svg.append('g').attr('class', styles.barContainer).attr('clip-path', "url(#".concat(clipKey, ")"));
      this.props.createChart(svg, this.width);
      return svg;
    }
  }, {
    key: "updateChart",
    value: function updateChart(svg, props) {
      this.renderBars(this.barContainer, props);

      this._renderStar(this.barContainer);

      this.props.updateChart(svg, this.x, this.y, this.applyTransition, this.barContainer, this.height);
    }
  }, {
    key: "removeChart",
    value: function removeChart() {
      if (this.__svg) {
        this.__svg.remove();

        delete this.__svg;
      }
    }
  }, {
    key: "renderBars",
    value: function renderBars(node, props) {
      this.chartData = props.visualDistribution;
      var height = this.height;
      var x = this.x;
      var y = this.y;
      var bars = node.selectAll('rect').data(this.chartData);
      var rects = bars.enter().append('rect').classed(styles.bar, true).classed(styles.barStar, this._isStarred).attr('x', function (d, i) {
        return x(i);
      }).attr('width', x.bandwidth());
      this.applyTransition(rects, function (rects) {
        return rects.attr('y', height).attr('height', 0);
      }, function (rects) {
        return rects.attr('y', y).attr('height', function (d) {
          return height - y(d);
        });
      });
      bars.exit().remove();
    }
  }, {
    key: "getTransition",
    value: function getTransition() {
      this.transition = this.transition || (0, _d3Transition.transition)().delay(this.props.animateDelay || 0).duration(this.props.animateDuration || 0);
      return this.transition;
    }
  }, {
    key: "renderSummary",
    value: function renderSummary() {
      return _react.default.createElement(_ScreenReaderContent.default, null, this.summarizeChart());
    }
  }, {
    key: "render",
    value: function render() {
      var _this2 = this;

      var _this$props = this.props,
          chartWidth = _this$props.chartWidth,
          chartHeight = _this$props.chartHeight;
      var viewportHeight = chartHeight + MARGIN_T + MARGIN_B; // IE11 sizes svg elements incorrectly, so we render a canvas element to
      // layout space for the chart, then position the svg absolute on top of it.

      return _react.default.createElement("div", null, _react.default.createElement(_PresentationContent.default, {
        as: "div"
      }, _react.default.createElement("div", {
        style: {
          position: 'relative'
        }
      }, _react.default.createElement("canvas", {
        width: chartWidth,
        height: viewportHeight,
        style: {
          display: 'block',
          visibility: 'hidden'
        }
      }), _react.default.createElement("svg", {
        ref: function ref(_ref) {
          _this2.chart = _ref;
        },
        style: {
          position: 'absolute',
          top: 0,
          left: 0
        }
      }))), this.renderSummary(), this.props.renderTable());
    }
  }], [{
    key: "addTitle",
    value: function addTitle(svg, title) {
      return svg.append('title').text(title);
    }
  }]);
  Distribution.displayName = "Distribution";
  return Distribution;
}(_react.Component), _class2.displayName = 'Distribution', _class2.propTypes = {
  animateDelay: _propTypes.PropTypes.number,
  animateDuration: _propTypes.PropTypes.number,
  barPaddingInner: _propTypes.PropTypes.number,
  // Spacing between bars, so each is visible, could be accomplished by borders
  barPaddingOuter: _propTypes.PropTypes.number,
  // Spacing between bars, so each is visible, could be accomplished by borders
  barPaddingTop: _propTypes.PropTypes.number,
  chartHeight: _propTypes.PropTypes.number.isRequired,
  // Height in pixels
  chartWidth: _propTypes.PropTypes.number,

  /* *
   * * a fallback minimal distribution for tab-able, keyboard navigable buckets
   * * typically this will be ~ 10 buckets
   * */
  summaryDistribution: _propTypes.PropTypes.arrayOf(_propTypes.PropTypes.node),

  /* *
   * * A list of score values for each bucket.  Size of this array determines the number of buckets
   * */
  visualDistribution: _propTypes.PropTypes.arrayOf(_propTypes.PropTypes.node).isRequired,
  calculateAxis: _propTypes.PropTypes.func,
  createChart: _propTypes.PropTypes.func,
  renderTable: _propTypes.PropTypes.func,
  summarizeChart: _propTypes.PropTypes.func,
  updateChart: _propTypes.PropTypes.func,
  starIndex: _propTypes.PropTypes.number
}, _class2.defaultProps = {
  animateDelay: 200,
  animateDuration: 900,
  barPaddingInner: 0.25,
  // Spacing between bars, so each is visible, could be accomplished by borders
  barPaddingOuter: 0,
  // Spacing between bars, so each is visible, could be accomplished by borders
  barPaddingTop: 0,
  // Spacing between highest bar and top of chart, pixels
  chartHeight: 200,
  // Height in pixels
  chartWidth: null,
  visualDistribution: [],
  calculateAxis: Function.prototype,
  createChart: Function.prototype,
  renderTable: Function.prototype,
  summarizeChart: function summarizeChart(textForScreenreaders) {
    return textForScreenreaders;
  },
  updateChart: Function.prototype,
  starIndex: null
}, _temp)) || _class);
exports.default = Distribution;