Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

448 строки
14 KiB

  1. var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  2. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  3. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  4. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  5. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  6. // Copyright (c) 2016 - 2017 Uber Technologies, Inc.
  7. //
  8. // Permission is hereby granted, free of charge, to any person obtaining a copy
  9. // of this software and associated documentation files (the "Software"), to deal
  10. // in the Software without restriction, including without limitation the rights
  11. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. // copies of the Software, and to permit persons to whom the Software is
  13. // furnished to do so, subject to the following conditions:
  14. //
  15. // The above copyright notice and this permission notice shall be included in
  16. // all copies or substantial portions of the Software.
  17. //
  18. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. // THE SOFTWARE.
  25. import React, { PureComponent } from 'react';
  26. import PropTypes from 'prop-types';
  27. import { transformValueToString } from '../utils/data-utils';
  28. import { getAttributeFunctor } from '../utils/scales-utils';
  29. /*
  30. * Hint provides two options for placement of hint:
  31. * a) around a data point in one of four quadrants (imagine the point bisected
  32. * by two axes -vertical, horizontal- creating 4 quadrants around a data
  33. * point).
  34. * b) **New** pin to an edge of chart/plot area and position along that edge
  35. * using data point's other dimension value.
  36. *
  37. * To support these two options, deprecate one Hint props (orientation) with two
  38. * new Hint align prop object (horizontal, vertical) with following values:
  39. *
  40. * horizontal: auto, left, right, leftEdge, rightEdge
  41. * vertical: auto, bottom, top, bottomEdge, topEdge
  42. *
  43. * Thus, the following ALIGN constants are the values for horizontal
  44. * and vertical
  45. */
  46. var ALIGN = {
  47. AUTO: 'auto',
  48. LEFT: 'left',
  49. RIGHT: 'right',
  50. LEFT_EDGE: 'leftEdge',
  51. RIGHT_EDGE: 'rightEdge',
  52. BOTTOM: 'bottom',
  53. TOP: 'top',
  54. BOTTOM_EDGE: 'bottomEdge',
  55. TOP_EDGE: 'topEdge'
  56. };
  57. /**
  58. * For backwards support, retain the ORIENTATION prop constants
  59. */
  60. var ORIENTATION = {
  61. BOTTOM_LEFT: 'bottomleft',
  62. BOTTOM_RIGHT: 'bottomright',
  63. TOP_LEFT: 'topleft',
  64. TOP_RIGHT: 'topright'
  65. };
  66. /**
  67. * Default format function for the value.
  68. * @param {Object} value Value.
  69. * @returns {Array} title-value pairs.
  70. */
  71. function defaultFormat(value) {
  72. return Object.keys(value).map(function getProp(key) {
  73. return { title: key, value: transformValueToString(value[key]) };
  74. });
  75. }
  76. var Hint = function (_PureComponent) {
  77. _inherits(Hint, _PureComponent);
  78. function Hint() {
  79. _classCallCheck(this, Hint);
  80. return _possibleConstructorReturn(this, (Hint.__proto__ || Object.getPrototypeOf(Hint)).apply(this, arguments));
  81. }
  82. _createClass(Hint, [{
  83. key: '_getAlign',
  84. /**
  85. * Obtain align object with horizontal and vertical settings
  86. * but convert any AUTO values to the non-auto ALIGN depending on the
  87. * values of x and y.
  88. * @param {number} x X value.
  89. * @param {number} y Y value.
  90. * @returns {Object} Align object w/ horizontal, vertical prop strings.
  91. * @private
  92. */
  93. value: function _getAlign(x, y) {
  94. var _props = this.props,
  95. innerWidth = _props.innerWidth,
  96. innerHeight = _props.innerHeight,
  97. orientation = _props.orientation,
  98. _props$align = _props.align,
  99. horizontal = _props$align.horizontal,
  100. vertical = _props$align.vertical;
  101. var align = orientation ? this._mapOrientationToAlign(orientation) : { horizontal: horizontal, vertical: vertical };
  102. if (horizontal === ALIGN.AUTO) {
  103. align.horizontal = x > innerWidth / 2 ? ALIGN.LEFT : ALIGN.RIGHT;
  104. }
  105. if (vertical === ALIGN.AUTO) {
  106. align.vertical = y > innerHeight / 2 ? ALIGN.TOP : ALIGN.BOTTOM;
  107. }
  108. return align;
  109. }
  110. /**
  111. * Get the class names from align values.
  112. * @param {Object} align object with horizontal and vertical prop strings.
  113. * @returns {string} Class names.
  114. * @private
  115. */
  116. }, {
  117. key: '_getAlignClassNames',
  118. value: function _getAlignClassNames(align) {
  119. var orientation = this.props.orientation;
  120. var orientationClass = orientation ? 'rv-hint--orientation-' + orientation : '';
  121. return orientationClass + ' rv-hint--horizontalAlign-' + align.horizontal + '\n rv-hint--verticalAlign-' + align.vertical;
  122. }
  123. /**
  124. * Get a CSS mixin for a proper positioning of the element.
  125. * @param {Object} align object with horizontal and vertical prop strings.
  126. * @param {number} x X position.
  127. * @param {number} y Y position.
  128. * @returns {Object} Object, that may contain `left` or `right, `top` or
  129. * `bottom` properties.
  130. * @private
  131. */
  132. }, {
  133. key: '_getAlignStyle',
  134. value: function _getAlignStyle(align, x, y) {
  135. return _extends({}, this._getXCSS(align.horizontal, x), this._getYCSS(align.vertical, y));
  136. }
  137. /**
  138. * Get the bottom coordinate of the hint.
  139. * When y undefined or null, edge case, pin bottom.
  140. * @param {number} y Y.
  141. * @returns {{bottom: *}} Mixin.
  142. * @private
  143. */
  144. }, {
  145. key: '_getCSSBottom',
  146. value: function _getCSSBottom(y) {
  147. if (y === undefined || y === null) {
  148. return {
  149. bottom: 0
  150. };
  151. }
  152. var _props2 = this.props,
  153. innerHeight = _props2.innerHeight,
  154. marginBottom = _props2.marginBottom;
  155. return {
  156. bottom: marginBottom + innerHeight - y
  157. };
  158. }
  159. /**
  160. * Get the left coordinate of the hint.
  161. * When x undefined or null, edge case, pin left.
  162. * @param {number} x X.
  163. * @returns {{left: *}} Mixin.
  164. * @private
  165. */
  166. }, {
  167. key: '_getCSSLeft',
  168. value: function _getCSSLeft(x) {
  169. if (x === undefined || x === null) {
  170. return {
  171. left: 0
  172. };
  173. }
  174. var marginLeft = this.props.marginLeft;
  175. return {
  176. left: marginLeft + x
  177. };
  178. }
  179. /**
  180. * Get the right coordinate of the hint.
  181. * When x undefined or null, edge case, pin right.
  182. * @param {number} x X.
  183. * @returns {{right: *}} Mixin.
  184. * @private
  185. */
  186. }, {
  187. key: '_getCSSRight',
  188. value: function _getCSSRight(x) {
  189. if (x === undefined || x === null) {
  190. return {
  191. right: 0
  192. };
  193. }
  194. var _props3 = this.props,
  195. innerWidth = _props3.innerWidth,
  196. marginRight = _props3.marginRight;
  197. return {
  198. right: marginRight + innerWidth - x
  199. };
  200. }
  201. /**
  202. * Get the top coordinate of the hint.
  203. * When y undefined or null, edge case, pin top.
  204. * @param {number} y Y.
  205. * @returns {{top: *}} Mixin.
  206. * @private
  207. */
  208. }, {
  209. key: '_getCSSTop',
  210. value: function _getCSSTop(y) {
  211. if (y === undefined || y === null) {
  212. return {
  213. top: 0
  214. };
  215. }
  216. var marginTop = this.props.marginTop;
  217. return {
  218. top: marginTop + y
  219. };
  220. }
  221. /**
  222. * Get the position for the hint and the appropriate class name.
  223. * @returns {{style: Object, positionClassName: string}} Style and className for the
  224. * hint.
  225. * @private
  226. */
  227. }, {
  228. key: '_getPositionInfo',
  229. value: function _getPositionInfo() {
  230. var _props4 = this.props,
  231. value = _props4.value,
  232. getAlignStyle = _props4.getAlignStyle;
  233. var x = getAttributeFunctor(this.props, 'x')(value);
  234. var y = getAttributeFunctor(this.props, 'y')(value);
  235. var align = this._getAlign(x, y);
  236. return {
  237. position: getAlignStyle ? getAlignStyle(align, x, y) : this._getAlignStyle(align, x, y),
  238. positionClassName: this._getAlignClassNames(align)
  239. };
  240. }
  241. }, {
  242. key: '_getXCSS',
  243. value: function _getXCSS(horizontal, x) {
  244. // obtain xCSS
  245. switch (horizontal) {
  246. case ALIGN.LEFT_EDGE:
  247. // this pins x to left edge
  248. return this._getCSSLeft(null);
  249. case ALIGN.RIGHT_EDGE:
  250. // this pins x to left edge
  251. return this._getCSSRight(null);
  252. case ALIGN.LEFT:
  253. // this places hint text to the left of center, so set its right edge
  254. return this._getCSSRight(x);
  255. case ALIGN.RIGHT:
  256. default:
  257. // this places hint text to the right of center, so set its left edge
  258. // default case should not be possible but if it happens set to RIGHT
  259. return this._getCSSLeft(x);
  260. }
  261. }
  262. }, {
  263. key: '_getYCSS',
  264. value: function _getYCSS(verticalAlign, y) {
  265. // obtain yCSS
  266. switch (verticalAlign) {
  267. case ALIGN.TOP_EDGE:
  268. // this pins x to top edge
  269. return this._getCSSTop(null);
  270. case ALIGN.BOTTOM_EDGE:
  271. // this pins x to bottom edge
  272. return this._getCSSBottom(null);
  273. case ALIGN.BOTTOM:
  274. // this places hint text to the bottom of center, so set its top edge
  275. return this._getCSSTop(y);
  276. case ALIGN.TOP:
  277. default:
  278. // this places hint text to the top of center, so set its bottom edge
  279. // default case should not be possible but if it happens set to BOTTOM
  280. return this._getCSSBottom(y);
  281. }
  282. }
  283. }, {
  284. key: '_mapOrientationToAlign',
  285. value: function _mapOrientationToAlign(orientation) {
  286. // TODO: print warning that this feature is deprecated and support will be
  287. // removed in next major release.
  288. switch (orientation) {
  289. case ORIENTATION.BOTTOM_LEFT:
  290. return {
  291. horizontal: ALIGN.LEFT,
  292. vertical: ALIGN.BOTTOM
  293. };
  294. case ORIENTATION.BOTTOM_RIGHT:
  295. return {
  296. horizontal: ALIGN.RIGHT,
  297. vertical: ALIGN.BOTTOM
  298. };
  299. case ORIENTATION.TOP_LEFT:
  300. return {
  301. horizontal: ALIGN.LEFT,
  302. vertical: ALIGN.TOP
  303. };
  304. case ORIENTATION.TOP_RIGHT:
  305. return {
  306. horizontal: ALIGN.RIGHT,
  307. vertical: ALIGN.TOP
  308. };
  309. default:
  310. // fall back to horizontalAlign, verticalAlign that are either
  311. // provided or defaulted to AUTO. So, don't change things
  312. break;
  313. }
  314. }
  315. }, {
  316. key: 'render',
  317. value: function render() {
  318. var _props5 = this.props,
  319. value = _props5.value,
  320. format = _props5.format,
  321. children = _props5.children,
  322. style = _props5.style,
  323. className = _props5.className;
  324. var _getPositionInfo2 = this._getPositionInfo(),
  325. position = _getPositionInfo2.position,
  326. positionClassName = _getPositionInfo2.positionClassName;
  327. return React.createElement(
  328. 'div',
  329. {
  330. className: 'rv-hint ' + positionClassName + ' ' + className,
  331. style: _extends({}, style, position, {
  332. position: 'absolute'
  333. })
  334. },
  335. children ? children : React.createElement(
  336. 'div',
  337. { className: 'rv-hint__content', style: style.content },
  338. format(value).map(function (formattedProp, i) {
  339. return React.createElement(
  340. 'div',
  341. { key: 'rv-hint' + i, style: style.row },
  342. React.createElement(
  343. 'span',
  344. { className: 'rv-hint__title', style: style.title },
  345. formattedProp.title
  346. ),
  347. ': ',
  348. React.createElement(
  349. 'span',
  350. { className: 'rv-hint__value', style: style.value },
  351. formattedProp.value
  352. )
  353. );
  354. })
  355. )
  356. );
  357. }
  358. }], [{
  359. key: 'defaultProps',
  360. get: function get() {
  361. return {
  362. format: defaultFormat,
  363. align: {
  364. horizontal: ALIGN.AUTO,
  365. vertical: ALIGN.AUTO
  366. },
  367. style: {}
  368. };
  369. }
  370. }, {
  371. key: 'propTypes',
  372. get: function get() {
  373. return {
  374. marginTop: PropTypes.number,
  375. marginLeft: PropTypes.number,
  376. innerWidth: PropTypes.number,
  377. innerHeight: PropTypes.number,
  378. scales: PropTypes.object,
  379. value: PropTypes.object,
  380. format: PropTypes.func,
  381. style: PropTypes.object,
  382. className: PropTypes.string,
  383. align: PropTypes.shape({
  384. horizontal: PropTypes.oneOf([ALIGN.AUTO, ALIGN.LEFT, ALIGN.RIGHT, ALIGN.LEFT_EDGE, ALIGN.RIGHT_EDGE]),
  385. vertical: PropTypes.oneOf([ALIGN.AUTO, ALIGN.BOTTOM, ALIGN.TOP, ALIGN.BOTTOM_EDGE, ALIGN.TOP_EDGE])
  386. }),
  387. getAlignStyle: PropTypes.func,
  388. orientation: PropTypes.oneOf([ORIENTATION.BOTTOM_LEFT, ORIENTATION.BOTTOM_RIGHT, ORIENTATION.TOP_LEFT, ORIENTATION.TOP_RIGHT])
  389. };
  390. }
  391. }]);
  392. return Hint;
  393. }(PureComponent);
  394. Hint.displayName = 'Hint';
  395. Hint.ORIENTATION = ORIENTATION;
  396. Hint.ALIGN = ALIGN;
  397. export default Hint;