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

897 строки
28 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 _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
  3. var _SCALE_FUNCTIONS;
  4. function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
  5. function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
  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 { scaleLinear, scalePoint, scaleOrdinal, scaleLog, scaleTime, scaleUtc } from 'd3-scale';
  26. import { extent } from 'd3-array';
  27. import { set } from 'd3-collection';
  28. import { hsl } from 'd3-color';
  29. import PropTypes from 'prop-types';
  30. import { warning } from './react-utils';
  31. import { getUniquePropertyValues, addValueToArray } from './data-utils';
  32. /**
  33. * Linear scale name.
  34. * @type {string}
  35. * @const
  36. */
  37. var LINEAR_SCALE_TYPE = 'linear';
  38. /**
  39. * Ordinal scale name.
  40. * @type {string}
  41. * @const
  42. */
  43. var ORDINAL_SCALE_TYPE = 'ordinal';
  44. /**
  45. * Category scale.
  46. * @type {string}
  47. * @const
  48. */
  49. var CATEGORY_SCALE_TYPE = 'category';
  50. /**
  51. * Literal scale.
  52. * Differs slightly from d3's identity scale in that it does not coerce value
  53. * into numbers, it simply returns exactly what you give it
  54. * @type {string}
  55. * @const
  56. */
  57. var LITERAL_SCALE_TYPE = 'literal';
  58. /**
  59. * Log scale name.
  60. * @type {string}
  61. * @const
  62. */
  63. var LOG_SCALE_TYPE = 'log';
  64. /**
  65. * Time scale name.
  66. * @type {string}
  67. * @const
  68. */
  69. var TIME_SCALE_TYPE = 'time';
  70. /**
  71. * Time UTC scale name.
  72. * @type {string}
  73. * @const
  74. */
  75. var TIME_UTC_SCALE_TYPE = 'time-utc';
  76. /**
  77. * Scale functions that are supported in the library.
  78. * @type {Object}
  79. * @const
  80. */
  81. var SCALE_FUNCTIONS = (_SCALE_FUNCTIONS = {}, _defineProperty(_SCALE_FUNCTIONS, LINEAR_SCALE_TYPE, scaleLinear), _defineProperty(_SCALE_FUNCTIONS, ORDINAL_SCALE_TYPE, scalePoint), _defineProperty(_SCALE_FUNCTIONS, CATEGORY_SCALE_TYPE, scaleOrdinal), _defineProperty(_SCALE_FUNCTIONS, LITERAL_SCALE_TYPE, literalScale), _defineProperty(_SCALE_FUNCTIONS, LOG_SCALE_TYPE, scaleLog), _defineProperty(_SCALE_FUNCTIONS, TIME_SCALE_TYPE, scaleTime), _defineProperty(_SCALE_FUNCTIONS, TIME_UTC_SCALE_TYPE, scaleUtc), _SCALE_FUNCTIONS);
  82. /**
  83. * Attrs for which a scale can be set up at XYPlot level
  84. * @type {Array}
  85. * @const
  86. */
  87. var XYPLOT_ATTR = ['color', 'fill', 'opacity', 'stroke'];
  88. /**
  89. * Title case a given string
  90. * @param {String} str Array of values.
  91. * @returns {String} titlecased string
  92. */
  93. function toTitleCase(str) {
  94. return '' + str[0].toUpperCase() + str.slice(1);
  95. }
  96. /**
  97. * Find the smallest distance between the values on a given scale and return
  98. * the index of the element, where the smallest distance was found.
  99. * It returns the first occurrence of i where
  100. * `scale(value[i]) - scale(value[i - 1])` is minimal
  101. * @param {Array} values Array of values.
  102. * @param {Object} scaleObject Scale object.
  103. * @returns {number} Index of an element where the smallest distance was found.
  104. * @private
  105. */
  106. export function _getSmallestDistanceIndex(values, scaleObject) {
  107. var scaleFn = getScaleFnFromScaleObject(scaleObject);
  108. var result = 0;
  109. if (scaleFn) {
  110. var nextValue = void 0;
  111. var currentValue = scaleFn(values[0]);
  112. var distance = Infinity;
  113. var nextDistance = void 0;
  114. for (var i = 1; i < values.length; i++) {
  115. nextValue = scaleFn(values[i]);
  116. nextDistance = Math.abs(nextValue - currentValue);
  117. if (nextDistance < distance) {
  118. distance = nextDistance;
  119. result = i;
  120. }
  121. currentValue = nextValue;
  122. }
  123. }
  124. return result;
  125. }
  126. /**
  127. * This is a workaround for issue that ordinal scale
  128. * does not have invert method implemented in d3-scale.
  129. * @param {Object} Ordinal d3-scale object.
  130. * @returns {void}
  131. * @private
  132. */
  133. function addInvertFunctionToOrdinalScaleObject(scale) {
  134. if (scale.invert) {
  135. return;
  136. }
  137. scale.invert = function invert(value) {
  138. var _scale$range = scale.range(),
  139. _scale$range2 = _slicedToArray(_scale$range, 2),
  140. lower = _scale$range2[0],
  141. upper = _scale$range2[1];
  142. var start = Math.min(lower, upper);
  143. var stop = Math.max(lower, upper);
  144. if (value < start + scale.padding() * scale.step()) {
  145. return scale.domain()[0];
  146. }
  147. if (value > stop - scale.padding() * scale.step()) {
  148. return scale.domain()[scale.domain().length - 1];
  149. }
  150. var index = Math.floor((value - start - scale.padding() * scale.step()) / scale.step());
  151. return scale.domain()[index];
  152. };
  153. }
  154. /**
  155. * Crate a scale function from the scale object.
  156. * @param {Object} scaleObject Scale object.
  157. - scaleObject.domain {Array}
  158. - scaleObject.range {Array}
  159. - scaleObject.type {string}
  160. - scaleObject.attr {string}
  161. * @returns {*} Scale function.
  162. * @private
  163. */
  164. export function getScaleFnFromScaleObject(scaleObject) {
  165. if (!scaleObject) {
  166. return null;
  167. }
  168. var type = scaleObject.type,
  169. domain = scaleObject.domain,
  170. range = scaleObject.range;
  171. var modDomain = domain[0] === domain[1] ? domain[0] === 0 ? [-1, 0] : [-domain[0], domain[0]] : domain;
  172. if (type === LITERAL_SCALE_TYPE) {
  173. return literalScale(range[0]);
  174. }
  175. var scale = SCALE_FUNCTIONS[type]().domain(modDomain).range(range);
  176. if (type === ORDINAL_SCALE_TYPE) {
  177. scale.padding(0.5);
  178. addInvertFunctionToOrdinalScaleObject(scale);
  179. }
  180. return scale;
  181. }
  182. /**
  183. * Get the domain from the array of data.
  184. * @param {Array} allData All data.
  185. * @param {function} accessor - accessor for main value.
  186. * @param {function} accessor0 - accessor for the naught value.
  187. * @param {string} type Scale type.
  188. * @returns {Array} Domain.
  189. * @private
  190. */
  191. export function getDomainByAccessor(allData, accessor, accessor0, type) {
  192. var domain = void 0;
  193. // Collect both attr and available attr0 values from the array of data.
  194. var values = allData.reduce(function (data, d) {
  195. var value = accessor(d);
  196. var value0 = accessor0(d);
  197. if (_isDefined(value)) {
  198. data.push(value);
  199. }
  200. if (_isDefined(value0)) {
  201. data.push(value0);
  202. }
  203. return data;
  204. }, []);
  205. if (!values.length) {
  206. return [];
  207. }
  208. // Create proper domain depending on the type of the scale.
  209. if (type !== ORDINAL_SCALE_TYPE && type !== CATEGORY_SCALE_TYPE) {
  210. domain = extent(values);
  211. } else {
  212. domain = set(values).values();
  213. }
  214. return domain;
  215. }
  216. /**
  217. * Create custom scale object from the value. When the scale is created from
  218. * this object, it should return the same value all time.
  219. * @param {string} attr Attribute.
  220. * @param {*} value Value.
  221. * @param {string} type - the type of scale being used
  222. * @param {function} accessor - the accessor function
  223. * @param {function} accessor0 - the accessor function for the potential naught value
  224. * @returns {Object} Custom scale object.
  225. * @private
  226. */
  227. function _createScaleObjectForValue(attr, value, type, accessor, accessor0) {
  228. if (type === LITERAL_SCALE_TYPE) {
  229. return {
  230. type: LITERAL_SCALE_TYPE,
  231. domain: [],
  232. range: [value],
  233. distance: 0,
  234. attr: attr,
  235. baseValue: undefined,
  236. isValue: true,
  237. accessor: accessor,
  238. accessor0: accessor0
  239. };
  240. }
  241. if (typeof value === 'undefined') {
  242. return null;
  243. }
  244. return {
  245. type: CATEGORY_SCALE_TYPE,
  246. range: [value],
  247. domain: [],
  248. distance: 0,
  249. attr: attr,
  250. baseValue: undefined,
  251. isValue: true,
  252. accessor: accessor,
  253. accessor0: accessor0
  254. };
  255. }
  256. /**
  257. * Create a regular scale object for a further use from the existing parameters.
  258. * @param {Array} domain - Domain.
  259. * @param {Array} range - Range.
  260. * @param {string} type - Type.
  261. * @param {number} distance - Distance.
  262. * @param {string} attr - Attribute.
  263. * @param {number} baseValue - Base value.
  264. * @param {function} accessor - Attribute accesor
  265. * @param {function} accessor0 - Attribute accesor for potential naught value
  266. * @returns {Object} Scale object.
  267. * @private
  268. */
  269. function _createScaleObjectForFunction(_ref) {
  270. var domain = _ref.domain,
  271. range = _ref.range,
  272. type = _ref.type,
  273. distance = _ref.distance,
  274. attr = _ref.attr,
  275. baseValue = _ref.baseValue,
  276. accessor = _ref.accessor,
  277. accessor0 = _ref.accessor0;
  278. return {
  279. domain: domain,
  280. range: range,
  281. type: type,
  282. distance: distance,
  283. attr: attr,
  284. baseValue: baseValue,
  285. isValue: false,
  286. accessor: accessor,
  287. accessor0: accessor0
  288. };
  289. }
  290. /**
  291. * Get scale object from props. E. g. object like {xRange, xDomain, xDistance,
  292. * xType} is transformed into {range, domain, distance, type}.
  293. * @param {Object} props Props.
  294. * @param {string} attr Attribute.
  295. * @returns {*} Null or an object with the scale.
  296. * @private
  297. */
  298. function _collectScaleObjectFromProps(props, attr) {
  299. var value = props[attr],
  300. fallbackValue = props['_' + attr + 'Value'],
  301. range = props[attr + 'Range'],
  302. _props$ = props[attr + 'Distance'],
  303. distance = _props$ === undefined ? 0 : _props$,
  304. baseValue = props[attr + 'BaseValue'],
  305. _props$2 = props[attr + 'Type'],
  306. type = _props$2 === undefined ? LINEAR_SCALE_TYPE : _props$2,
  307. noFallBack = props[attr + 'NoFallBack'],
  308. _props$3 = props['get' + toTitleCase(attr)],
  309. accessor = _props$3 === undefined ? function (d) {
  310. return d[attr];
  311. } : _props$3,
  312. _props$4 = props['get' + toTitleCase(attr) + '0'],
  313. accessor0 = _props$4 === undefined ? function (d) {
  314. return d[attr + '0'];
  315. } : _props$4;
  316. var domain = props[attr + 'Domain'];
  317. // Return value-based scale if the value is assigned.
  318. if (!noFallBack && typeof value !== 'undefined') {
  319. return _createScaleObjectForValue(attr, value, props[attr + 'Type'], accessor, accessor0);
  320. }
  321. // Pick up the domain from the properties and create a new one if it's not
  322. // available.
  323. if (typeof baseValue !== 'undefined') {
  324. domain = addValueToArray(domain, baseValue);
  325. }
  326. // Make sure that the minimum necessary properties exist.
  327. if (!range || !domain || !domain.length) {
  328. // Try to use the fallback value if it is available.
  329. return _createScaleObjectForValue(attr, fallbackValue, props[attr + 'Type'], accessor, accessor0);
  330. }
  331. return _createScaleObjectForFunction({
  332. domain: domain,
  333. range: range,
  334. type: type,
  335. distance: distance,
  336. attr: attr,
  337. baseValue: baseValue,
  338. accessor: accessor,
  339. accessor0: accessor0
  340. });
  341. }
  342. /**
  343. * Compute left domain adjustment for the given values.
  344. * @param {Array} values Array of values.
  345. * @returns {number} Domain adjustment.
  346. * @private
  347. */
  348. function _computeLeftDomainAdjustment(values) {
  349. if (values.length > 1) {
  350. return (values[1] - values[0]) / 2;
  351. }
  352. if (values.length === 1) {
  353. return values[0] - 0.5;
  354. }
  355. return 0;
  356. }
  357. /**
  358. * Compute right domain adjustment for the given values.
  359. * @param {Array} values Array of values.
  360. * @returns {number} Domain adjustment.
  361. * @private
  362. */
  363. function _computeRightDomainAdjustment(values) {
  364. if (values.length > 1) {
  365. return (values[values.length - 1] - values[values.length - 2]) / 2;
  366. }
  367. if (values.length === 1) {
  368. return values[0] - 0.5;
  369. }
  370. return 0;
  371. }
  372. /**
  373. * Compute distance for the given values.
  374. * @param {Array} values Array of values.
  375. * @param {Array} domain Domain.
  376. * @param {number} bestDistIndex Index of a best distance found.
  377. * @param {function} scaleFn Scale function.
  378. * @returns {number} Domain adjustment.
  379. * @private
  380. */
  381. function _computeScaleDistance(values, domain, bestDistIndex, scaleFn) {
  382. if (values.length > 1) {
  383. // Avoid zero indexes.
  384. var i = Math.max(bestDistIndex, 1);
  385. return Math.abs(scaleFn(values[i]) - scaleFn(values[i - 1]));
  386. }
  387. if (values.length === 1) {
  388. return Math.abs(scaleFn(domain[1]) - scaleFn(domain[0]));
  389. }
  390. return 0;
  391. }
  392. /**
  393. * Normilize array of values with a single value.
  394. * @param {Array} arr Array of data.
  395. * @param {Array} values Array of values.
  396. * @param {string} attr Attribute.
  397. * @param {string} type Type.
  398. * @private
  399. */
  400. function _normalizeValues(data, values, accessor0, type) {
  401. if (type === TIME_SCALE_TYPE && values.length === 1) {
  402. var attr0 = accessor0(data[0]);
  403. return [attr0].concat(_toConsumableArray(values));
  404. }
  405. return values;
  406. }
  407. /**
  408. * Get the distance, the smallest and the largest value of the domain.
  409. * @param {Array} data Array of data for the single series.
  410. * @param {Object} scaleObject Scale object.
  411. * @returns {{domain0: number, domainN: number, distance: number}} Result.
  412. * @private
  413. */
  414. export function _getScaleDistanceAndAdjustedDomain(data, scaleObject) {
  415. var domain = scaleObject.domain,
  416. type = scaleObject.type,
  417. accessor = scaleObject.accessor,
  418. accessor0 = scaleObject.accessor0;
  419. var uniqueValues = getUniquePropertyValues(data, accessor);
  420. // Fix time scale if a data has only one value.
  421. var values = _normalizeValues(data, uniqueValues, accessor0, type);
  422. var index = _getSmallestDistanceIndex(values, scaleObject);
  423. var adjustedDomain = [].concat(domain);
  424. adjustedDomain[0] -= _computeLeftDomainAdjustment(values);
  425. adjustedDomain[domain.length - 1] += _computeRightDomainAdjustment(values);
  426. // Fix log scale if it's too small.
  427. if (type === LOG_SCALE_TYPE && domain[0] <= 0) {
  428. adjustedDomain[0] = Math.min(domain[1] / 10, 1);
  429. }
  430. var adjustedScaleFn = getScaleFnFromScaleObject(_extends({}, scaleObject, {
  431. domain: adjustedDomain
  432. }));
  433. var distance = _computeScaleDistance(values, adjustedDomain, index, adjustedScaleFn);
  434. return {
  435. domain0: adjustedDomain[0],
  436. domainN: adjustedDomain[adjustedDomain.length - 1],
  437. distance: distance
  438. };
  439. }
  440. /**
  441. * Returns true if scale adjustments are possible for a given scale.
  442. * @param {Object} props Props.
  443. * @param {Object} scaleObject Scale object.
  444. * @returns {boolean} True if scale adjustments possible.
  445. * @private
  446. */
  447. function _isScaleAdjustmentPossible(props, scaleObject) {
  448. var attr = scaleObject.attr;
  449. var _props$_adjustBy = props._adjustBy,
  450. adjustBy = _props$_adjustBy === undefined ? [] : _props$_adjustBy,
  451. _props$_adjustWhat = props._adjustWhat,
  452. adjustWhat = _props$_adjustWhat === undefined ? [] : _props$_adjustWhat;
  453. // The scale cannot be adjusted if there's no attributes to adjust, no
  454. // suitable values
  455. return adjustWhat.length && adjustBy.length && adjustBy.indexOf(attr) !== -1;
  456. }
  457. /**
  458. * Adjust continuous scales (e.g. 'linear', 'log' and 'time') by adding the
  459. * space from the left and right of them and by computing the best distance.
  460. * @param {Object} props Props.
  461. * @param {Object} scaleObject Scale object.
  462. * @returns {*} Scale object with adjustments.
  463. * @private
  464. */
  465. function _adjustContinuousScale(props, scaleObject) {
  466. var allSeriesData = props._allData,
  467. _props$_adjustWhat2 = props._adjustWhat,
  468. adjustWhat = _props$_adjustWhat2 === undefined ? [] : _props$_adjustWhat2;
  469. // Assign the initial values.
  470. var domainLength = scaleObject.domain.length;
  471. var domain = scaleObject.domain;
  472. var scaleDomain0 = domain[0];
  473. var scaleDomainN = domain[domainLength - 1];
  474. var scaleDistance = scaleObject.distance;
  475. // Find the smallest left position of the domain, the largest right position
  476. // of the domain and the best distance for them.
  477. allSeriesData.forEach(function (data, index) {
  478. if (adjustWhat.indexOf(index) === -1) {
  479. return;
  480. }
  481. if (data && data.length) {
  482. var _getScaleDistanceAndA = _getScaleDistanceAndAdjustedDomain(data, scaleObject),
  483. domain0 = _getScaleDistanceAndA.domain0,
  484. domainN = _getScaleDistanceAndA.domainN,
  485. distance = _getScaleDistanceAndA.distance;
  486. scaleDomain0 = Math.min(scaleDomain0, domain0);
  487. scaleDomainN = Math.max(scaleDomainN, domainN);
  488. scaleDistance = Math.max(scaleDistance, distance);
  489. }
  490. });
  491. scaleObject.domain = [scaleDomain0].concat(_toConsumableArray(domain.slice(1, -1)), [scaleDomainN]);
  492. scaleObject.distance = scaleDistance;
  493. return scaleObject;
  494. }
  495. /**
  496. * Get an adjusted scale. Suitable for 'category' and 'ordinal' scales.
  497. * @param {Object} scaleObject Scale object.
  498. * @returns {*} Scale object with adjustments.
  499. * @private
  500. */
  501. export function _adjustCategoricalScale(scaleObject) {
  502. var scaleFn = getScaleFnFromScaleObject(scaleObject);
  503. var domain = scaleObject.domain,
  504. range = scaleObject.range;
  505. if (domain.length > 1) {
  506. scaleObject.distance = Math.abs(scaleFn(domain[1]) - scaleFn(domain[0]));
  507. } else {
  508. scaleObject.distance = Math.abs(range[1] - range[0]);
  509. }
  510. return scaleObject;
  511. }
  512. /**
  513. * Retrieve a scale object or a value from the properties passed.
  514. * @param {Object} props Object of props.
  515. * @param {string} attr Attribute.
  516. * @returns {*} Scale object, value or null.
  517. */
  518. export function getScaleObjectFromProps(props, attr) {
  519. // Create the initial scale object.
  520. var scaleObject = _collectScaleObjectFromProps(props, attr);
  521. if (!scaleObject) {
  522. return null;
  523. }
  524. // Make sure if it's possible to add space to the scale object. If not,
  525. // return the object immediately.
  526. if (!_isScaleAdjustmentPossible(props, scaleObject)) {
  527. return scaleObject;
  528. }
  529. var type = scaleObject.type;
  530. // Depending on what type the scale is, apply different adjustments. Distances
  531. // for the ordinal and category scales are even, equal domains cannot be
  532. // adjusted.
  533. if (type === ORDINAL_SCALE_TYPE || type === CATEGORY_SCALE_TYPE) {
  534. return _adjustCategoricalScale(scaleObject);
  535. }
  536. return _adjustContinuousScale(props, scaleObject);
  537. }
  538. /**
  539. * Get d3 scale for a given prop.
  540. * @param {Object} props Props.
  541. * @param {string} attr Attribute.
  542. * @returns {function} d3 scale function.
  543. */
  544. export function getAttributeScale(props, attr) {
  545. var scaleObject = getScaleObjectFromProps(props, attr);
  546. return getScaleFnFromScaleObject(scaleObject);
  547. }
  548. /**
  549. * Get the value of `attr` from the object.
  550. * @param {Object} d - data Object.
  551. * @param {Function} accessor - accessor function.
  552. * @returns {*} Value of the point.
  553. * @private
  554. */
  555. function _getAttrValue(d, accessor) {
  556. return accessor(d.data ? d.data : d);
  557. }
  558. function _isDefined(value) {
  559. return typeof value !== 'undefined';
  560. }
  561. /*
  562. * Adds a percentage of padding to a given domain
  563. * @param {Array} domain X or Y domain to pad.
  564. * @param {Number} padding Percentage of padding to add to domain
  565. * @returns {Array} Padded Domain
  566. */
  567. function _padDomain(domain, padding) {
  568. if (!domain) {
  569. return domain;
  570. }
  571. if (isNaN(parseFloat(domain[0])) || isNaN(parseFloat(domain[1]))) {
  572. return domain;
  573. }
  574. var _domain = _slicedToArray(domain, 2),
  575. min = _domain[0],
  576. max = _domain[1];
  577. var domainPadding = (max - min) * (padding * 0.01);
  578. return [min - domainPadding, max + domainPadding];
  579. }
  580. /**
  581. * Get prop functor (either a value or a function) for a given attribute.
  582. * @param {Object} props Series props.
  583. * @param {Function} accessor - Property accessor.
  584. * @returns {*} Function or value.
  585. */
  586. export function getAttributeFunctor(props, attr) {
  587. var scaleObject = getScaleObjectFromProps(props, attr);
  588. if (scaleObject) {
  589. var scaleFn = getScaleFnFromScaleObject(scaleObject);
  590. return function (d) {
  591. return scaleFn(_getAttrValue(d, scaleObject.accessor));
  592. };
  593. }
  594. return null;
  595. }
  596. /**
  597. * Get the functor which extracts value form [attr]0 property. Use baseValue if
  598. * no attr0 property for a given object is defined. Fall back to domain[0] if no
  599. * base value is available.
  600. * @param {Object} props Object of props.
  601. * @param {string} attr Attribute name.
  602. * @returns {*} Function which returns value or null if no values available.
  603. */
  604. export function getAttr0Functor(props, attr) {
  605. var scaleObject = getScaleObjectFromProps(props, attr);
  606. if (scaleObject) {
  607. var domain = scaleObject.domain;
  608. var _scaleObject$baseValu = scaleObject.baseValue,
  609. baseValue = _scaleObject$baseValu === undefined ? domain[0] : _scaleObject$baseValu;
  610. var scaleFn = getScaleFnFromScaleObject(scaleObject);
  611. return function (d) {
  612. var value = _getAttrValue(d, scaleObject.accessor0);
  613. return scaleFn(_isDefined(value) ? value : baseValue);
  614. };
  615. }
  616. return null;
  617. }
  618. /**
  619. * Tries to get the string|number value of the attr and falls back to
  620. * a fallback property in case if the value is a scale.
  621. * @param {Object} props Series props.
  622. * @param {string} attr Property name.
  623. * @returns {*} Function or value.
  624. */
  625. export function getAttributeValue(props, attr) {
  626. var scaleObject = getScaleObjectFromProps(props, attr);
  627. if (scaleObject) {
  628. if (!scaleObject.isValue && props['_' + attr + 'Value'] === undefined) {
  629. warning('[React-vis] Cannot use data defined ' + attr + ' for this ' + 'series type. Using fallback value instead.');
  630. }
  631. return props['_' + attr + 'Value'] || scaleObject.range[0];
  632. }
  633. return null;
  634. }
  635. /**
  636. * Get prop types by the attribute.
  637. * @param {string} attr Attribute.
  638. * @returns {Object} Object of xDomain, xRange, xType, xDistance and _xValue,
  639. * where x is an attribute passed to the function.
  640. */
  641. export function getScalePropTypesByAttribute(attr) {
  642. var _ref2;
  643. return _ref2 = {}, _defineProperty(_ref2, '_' + attr + 'Value', PropTypes.any), _defineProperty(_ref2, attr + 'Domain', PropTypes.array), _defineProperty(_ref2, 'get' + toTitleCase(attr), PropTypes.func), _defineProperty(_ref2, 'get' + toTitleCase(attr) + '0', PropTypes.func), _defineProperty(_ref2, attr + 'Range', PropTypes.array), _defineProperty(_ref2, attr + 'Type', PropTypes.oneOf(Object.keys(SCALE_FUNCTIONS))), _defineProperty(_ref2, attr + 'Distance', PropTypes.number), _defineProperty(_ref2, attr + 'BaseValue', PropTypes.any), _ref2;
  644. }
  645. /**
  646. * Extract the list of scale properties from the entire props object.
  647. * @param {Object} props Props.
  648. * @param {Array<String>} attributes Array of attributes for the given
  649. * components (for instance, `['x', 'y', 'color']`).
  650. * @returns {Object} Collected props.
  651. */
  652. export function extractScalePropsFromProps(props, attributes) {
  653. var result = {};
  654. Object.keys(props).forEach(function (key) {
  655. // this filtering is critical for extracting the correct accessors!
  656. var attr = attributes.find(function (a) {
  657. // width
  658. var isPlainSet = key.indexOf(a) === 0;
  659. // Ex: _data
  660. var isUnderscoreSet = key.indexOf('_' + a) === 0;
  661. // EX: getX
  662. var usesGet = key.indexOf('get' + toTitleCase(a)) === 0;
  663. return isPlainSet || isUnderscoreSet || usesGet;
  664. });
  665. if (!attr) {
  666. return;
  667. }
  668. result[key] = props[key];
  669. });
  670. return result;
  671. }
  672. /**
  673. * Extract the missing scale props from the given data and return them as
  674. * an object.
  675. * @param {Object} props Props.
  676. * @param {Array} data Array of all data.
  677. * @param {Array<String>} attributes Array of attributes for the given
  678. * components (for instance, `['x', 'y', 'color']`).
  679. * @returns {Object} Collected props.
  680. */
  681. export function getMissingScaleProps(props, data, attributes) {
  682. var result = {};
  683. // Make sure that the domain is set pad it if specified
  684. attributes.forEach(function (attr) {
  685. if (!props['get' + toTitleCase(attr)]) {
  686. result['get' + toTitleCase(attr)] = function (d) {
  687. return d[attr];
  688. };
  689. }
  690. if (!props['get' + toTitleCase(attr) + '0']) {
  691. result['get' + toTitleCase(attr) + '0'] = function (d) {
  692. return d[attr + '0'];
  693. };
  694. }
  695. if (!props[attr + 'Domain']) {
  696. result[attr + 'Domain'] = getDomainByAccessor(data, props['get' + toTitleCase(attr)] || result['get' + toTitleCase(attr)], props['get' + toTitleCase(attr) + '0'] || result['get' + toTitleCase(attr) + '0'], props[attr + 'Type']);
  697. if (props[attr + 'Padding']) {
  698. result[attr + 'Domain'] = _padDomain(result[attr + 'Domain'], props[attr + 'Padding']);
  699. }
  700. }
  701. });
  702. return result;
  703. }
  704. /**
  705. * Return a d3 scale that returns the literal value that was given to it
  706. * @returns {function} literal scale.
  707. */
  708. export function literalScale(defaultValue) {
  709. function scale(d) {
  710. if (d === undefined) {
  711. return defaultValue;
  712. }
  713. return d;
  714. }
  715. function response() {
  716. return scale;
  717. }
  718. scale.domain = response;
  719. scale.range = response;
  720. scale.unknown = response;
  721. scale.copy = response;
  722. return scale;
  723. }
  724. export function getFontColorFromBackground(background) {
  725. if (background) {
  726. return hsl(background).l > 0.57 ? '#222' : '#fff';
  727. }
  728. return null;
  729. }
  730. /**
  731. * Creates fallback values for series from scales defined at XYPlot level.
  732. * @param {Object} props Props of the XYPlot object.
  733. * @param {Array<Object>} children Array of components, children of XYPlot
  734. * @returns {Array<Object>} Collected props.
  735. */
  736. export function getXYPlotValues(props, children) {
  737. var XYPlotScales = XYPLOT_ATTR.reduce(function (prev, attr) {
  738. var domain = props[attr + 'Domain'],
  739. range = props[attr + 'Range'],
  740. type = props[attr + 'Type'];
  741. if (domain && range && type) {
  742. return _extends({}, prev, _defineProperty({}, attr, SCALE_FUNCTIONS[type]().domain(domain).range(range)));
  743. }
  744. return prev;
  745. }, {});
  746. return children.map(function (child) {
  747. return XYPLOT_ATTR.reduce(function (prev, attr) {
  748. if (child.props && child.props[attr] !== undefined) {
  749. var scaleInput = child.props[attr];
  750. var scale = XYPlotScales[attr];
  751. var fallbackValue = scale ? scale(scaleInput) : scaleInput;
  752. return _extends({}, prev, _defineProperty({}, '_' + attr + 'Value', fallbackValue));
  753. }
  754. return prev;
  755. }, {});
  756. });
  757. }
  758. var OPTIONAL_SCALE_PROPS = ['Padding'];
  759. var OPTIONAL_SCALE_PROPS_REGS = OPTIONAL_SCALE_PROPS.map(function (str) {
  760. return new RegExp(str + '$', 'i');
  761. });
  762. /**
  763. * Get the list of optional scale-related settings for XYPlot
  764. * mostly just used to find padding properties
  765. * @param {Object} props Object of props.
  766. * @returns {Object} Optional Props.
  767. * @private
  768. */
  769. export function getOptionalScaleProps(props) {
  770. return Object.keys(props).reduce(function (acc, prop) {
  771. var propIsNotOptional = OPTIONAL_SCALE_PROPS_REGS.every(function (reg) {
  772. return !prop.match(reg);
  773. });
  774. if (propIsNotOptional) {
  775. return acc;
  776. }
  777. acc[prop] = props[prop];
  778. return acc;
  779. }, {});
  780. }
  781. export default {
  782. extractScalePropsFromProps: extractScalePropsFromProps,
  783. getAttributeScale: getAttributeScale,
  784. getAttributeFunctor: getAttributeFunctor,
  785. getAttr0Functor: getAttr0Functor,
  786. getAttributeValue: getAttributeValue,
  787. getDomainByAccessor: getDomainByAccessor,
  788. getFontColorFromBackground: getFontColorFromBackground,
  789. getMissingScaleProps: getMissingScaleProps,
  790. getOptionalScaleProps: getOptionalScaleProps,
  791. getScaleObjectFromProps: getScaleObjectFromProps,
  792. getScalePropTypesByAttribute: getScalePropTypesByAttribute,
  793. getXYPlotValues: getXYPlotValues,
  794. literalScale: literalScale
  795. };