UNPKG

44.2 kBJavaScriptView Raw
1/**
2 * Category axis module
3 */
4import { __extends } from "tslib";
5/**
6 * ============================================================================
7 * IMPORTS
8 * ============================================================================
9 * @hidden
10 */
11import { Axis, AxisDataItem } from "./Axis";
12import { AxisRendererX } from "./AxisRendererX";
13import { AxisRendererY } from "./AxisRendererY";
14import { registry } from "../../core/Registry";
15import { Dictionary } from "../../core/utils/Dictionary";
16import { CategoryAxisBreak } from "./CategoryAxisBreak";
17import * as $math from "../../core/utils/Math";
18import * as $type from "../../core/utils/Type";
19import * as $iter from "../../core/utils/Iterator";
20/**
21 * ============================================================================
22 * DATA ITEM
23 * ============================================================================
24 * @hidden
25 */
26/**
27 * Defines a [[DataItem]] for [[CategoryAxis]].
28 *
29 * @see {@link DataItem}
30 */
31var CategoryAxisDataItem = /** @class */ (function (_super) {
32 __extends(CategoryAxisDataItem, _super);
33 /**
34 * Constructor
35 */
36 function CategoryAxisDataItem() {
37 var _this = _super.call(this) || this;
38 _this.seriesDataItems = {};
39 _this.className = "CategoryAxisDataItem";
40 _this.text = "{category}";
41 _this.locations.category = 0;
42 _this.locations.endCategory = 1;
43 _this.deltaPosition = 0;
44 _this.applyTheme();
45 return _this;
46 }
47 Object.defineProperty(CategoryAxisDataItem.prototype, "category", {
48 /**
49 * @return Category
50 */
51 get: function () {
52 if (this._adapterO) {
53 if (this._adapterO.isEnabled("category")) {
54 return this._adapterO.apply("category", this.properties.category);
55 }
56 }
57 return this.properties.category;
58 },
59 /**
60 * Category.
61 *
62 * @param value Category
63 */
64 set: function (value) {
65 var oldCategory = this.properties.category;
66 this.setProperty("category", value);
67 if ($type.hasValue(oldCategory) && oldCategory != value) {
68 if (this.component) {
69 this.component.validateDataElement(this);
70 }
71 }
72 },
73 enumerable: true,
74 configurable: true
75 });
76 Object.defineProperty(CategoryAxisDataItem.prototype, "endCategory", {
77 /**
78 * @return End category
79 */
80 get: function () {
81 return this.properties.endCategory;
82 },
83 /**
84 * End category.
85 *
86 * Used for items that span several categories, like [[CategoryAxisBreak]].
87 *
88 * @param value End category
89 */
90 set: function (value) {
91 this.setProperty("endCategory", value);
92 },
93 enumerable: true,
94 configurable: true
95 });
96 Object.defineProperty(CategoryAxisDataItem.prototype, "deltaPosition", {
97 get: function () {
98 return this.properties.deltaCoordinate;
99 },
100 set: function (value) {
101 if (value != this.properties.deltaCoordinate) {
102 this.setProperty("deltaCoordinate", value);
103 if (this.component) {
104 this.component.invalidateDataItems();
105 this.component.invalidateSeries();
106 }
107 }
108 },
109 enumerable: true,
110 configurable: true
111 });
112 return CategoryAxisDataItem;
113}(AxisDataItem));
114export { CategoryAxisDataItem };
115/**
116 * ============================================================================
117 * MAIN CLASS
118 * ============================================================================
119 * @hidden
120 */
121/**
122 * Used to create a category-based axis for the chart.
123 *
124 * ```TypeScript
125 * // Create the axis
126 * let xAxis = chart.xAxes.push(new am4charts.CategoryAxis());
127 *
128 * // Set settings
129 * xAxis.title.text = "Clients";
130 * ```
131 * ```JavaScript
132 * // Create the axis
133 * var valueAxis = chart.xAxes.push(new am4charts.CategoryAxis());
134 *
135 * // Set settings
136 * valueAxis.title.text = "Clients";
137 * ```
138 * ```JSON
139 * "xAxes": [{
140 * "type": "CategoryAxis",
141 * "title": {
142 * "text": "Clients"
143 * }
144 * }]
145 * ```
146 *
147 * @see {@link ICategoryAxisEvents} for a list of available Events
148 * @see {@link ICategoryAxisAdapters} for a list of available Adapters
149 * @important
150 */
151var CategoryAxis = /** @class */ (function (_super) {
152 __extends(CategoryAxis, _super);
153 /**
154 * Constructor
155 */
156 function CategoryAxis() {
157 var _this =
158 // Init
159 _super.call(this) || this;
160 /**
161 * A collection that holds Axis' data items sorted by each category.
162 */
163 _this.dataItemsByCategory = new Dictionary();
164 _this.className = "CategoryAxis";
165 // Set field name
166 _this.axisFieldName = "category";
167 _this._lastDataItem = _this.createDataItem();
168 _this._lastDataItem.component = _this;
169 _this._disposers.push(_this._lastDataItem);
170 // Apply theme
171 _this.applyTheme();
172 var dataItemsByCategory = _this.dataItemsByCategory;
173 _this.addDisposer(_this.mainDataSet.events.on("removed", function (event) {
174 dataItemsByCategory.removeKey(event.oldValue.category);
175 }));
176 return _this;
177 }
178 /**
179 * Returns a new/empty [[DataItem]] of the type appropriate for this object.
180 *
181 * @see {@link DataItem}
182 * @return Data Item
183 */
184 CategoryAxis.prototype.createDataItem = function () {
185 return new CategoryAxisDataItem();
186 };
187 /**
188 * Returns a new/empty [[AxisBreak]] of the appropriate type.
189 *
190 * @return Axis break
191 */
192 CategoryAxis.prototype.createAxisBreak = function () {
193 return new CategoryAxisBreak();
194 };
195 /**
196 * Processes a related series' data item.
197 *
198 * @ignore Exclude from docs
199 * @todo Description
200 * @param dataItem Data item
201 */
202 CategoryAxis.prototype.processSeriesDataItem = function (dataItem, axisLetter) {
203 _super.prototype.processSeriesDataItem.call(this, dataItem, axisLetter);
204 var category = dataItem["category" + this.axisLetter];
205 if ($type.hasValue(category)) {
206 var categoryAxisDataItem = this.dataItemsByCategory.getKey(category);
207 if (categoryAxisDataItem) {
208 var seriesId = dataItem.component.uid;
209 var seriesDataItems = categoryAxisDataItem.seriesDataItems[seriesId];
210 if (!seriesDataItems) {
211 seriesDataItems = [];
212 categoryAxisDataItem.seriesDataItems[seriesId] = seriesDataItems;
213 }
214 seriesDataItems.push(dataItem);
215 }
216 }
217 else {
218 dataItem.component.dataItems.remove(dataItem);
219 }
220 };
221 /**
222 * Validates the data range.
223 *
224 * @ignore Exclude from docs
225 * @todo Description (review)
226 */
227 CategoryAxis.prototype.validateDataRange = function () {
228 var _this = this;
229 _super.prototype.validateDataRange.call(this);
230 $iter.each(this._series.iterator(), function (series) {
231 if ((series.xAxis instanceof CategoryAxis) && (series.yAxis instanceof CategoryAxis)) {
232 series.invalidateDataRange();
233 }
234 else {
235 var startIndex = _this.positionToIndex(_this.start);
236 var endIndex = _this.positionToIndex(_this.end);
237 if (endIndex >= _this.dataItems.length) {
238 endIndex--;
239 }
240 var seriesId = series.uid;
241 var minIndex = void 0;
242 var maxIndex = void 0;
243 for (var i = startIndex; i <= endIndex; i++) {
244 var axisDataItem = _this.dataItems.getIndex(i);
245 if (axisDataItem) {
246 var seriesDataItems = axisDataItem.seriesDataItems[seriesId];
247 if (seriesDataItems) {
248 for (var i_1 = 0; i_1 < seriesDataItems.length; i_1++) {
249 var seriesDataItem = seriesDataItems[i_1];
250 if (seriesDataItem) {
251 var index = seriesDataItem.index;
252 if (!$type.isNumber(minIndex) || index < minIndex) {
253 minIndex = index;
254 }
255 if (!$type.isNumber(maxIndex) || index > maxIndex) {
256 maxIndex = index;
257 }
258 }
259 }
260 }
261 }
262 }
263 if ($type.isNumber(minIndex)) {
264 series.startIndex = minIndex;
265 }
266 else {
267 series.start = _this.start;
268 }
269 if ($type.isNumber(maxIndex)) {
270 series.endIndex = maxIndex + 1;
271 }
272 else {
273 series.end = _this.end;
274 }
275 // range might not change, but axis breaks might.
276 if (_this._axisBreaks && _this._axisBreaks.length > 0) {
277 series.invalidateDataRange();
278 }
279 }
280 });
281 };
282 /**
283 * Validates the whole axis. Causes it to redraw.
284 *
285 * @ignore Exclude from docs
286 * @todo Description (review)
287 */
288 CategoryAxis.prototype.validate = function () {
289 var _this = this;
290 _super.prototype.validate.call(this);
291 var dataCount = this.dataItems.length;
292 var startIndex = $math.fitToRange(Math.floor(this.start * dataCount - 1), 0, dataCount);
293 var endIndex = $math.fitToRange(Math.ceil(this.end * dataCount), 0, dataCount);
294 if (this.renderer.invalid) {
295 this.renderer.validate();
296 }
297 // find frequency at which we'll show items
298 var maxCount = this.renderer.axisLength / Math.max(this.renderer.minGridDistance, 1 / Number.MAX_SAFE_INTEGER);
299 var frequency = Math.min(this.dataItems.length, Math.ceil((endIndex - startIndex) / maxCount));
300 this._startIndex = Math.floor(startIndex / frequency) * frequency;
301 this._endIndex = Math.ceil(this.end * dataCount);
302 this.fixAxisBreaks();
303 if (this._startIndex == this._endIndex) {
304 this._endIndex++;
305 }
306 this._frequency = frequency;
307 if (this.axisLength <= 0) {
308 return;
309 }
310 this.maxZoomFactor = this.dataItems.length;
311 if (this.dataItems.length <= 0) {
312 this.maxZoomFactor = 1;
313 }
314 this.resetIterators();
315 // it's important to use protected variables here, as getters will return 0 - length
316 // TODO use iterator instead
317 // @ todo: not solved cat axis item fading
318 startIndex = $math.max(0, this._startIndex - this._frequency);
319 endIndex = $math.min(this.dataItems.length, this._endIndex + this._frequency);
320 var itemIndex = 0;
321 for (var i = 0; i < startIndex; i++) {
322 var dataItem = this.dataItems.getIndex(i);
323 dataItem.__disabled = true;
324 }
325 for (var i = endIndex, len = this.dataItems.length; i < len; i++) {
326 var dataItem = this.dataItems.getIndex(i);
327 dataItem.__disabled = true;
328 }
329 for (var i = startIndex; i < endIndex; i++) {
330 if (i < this.dataItems.length) {
331 var dataItem = this.dataItems.getIndex(i);
332 if (i / this._frequency == Math.round(i / this._frequency)) {
333 var axisBreak = this.isInBreak(i);
334 if (!axisBreak) {
335 this.appendDataItem(dataItem);
336 this.validateDataElement(dataItem, itemIndex);
337 }
338 itemIndex++;
339 }
340 else {
341 //previously we disabled all before, but this is better for cpu
342 //this.validateDataElement(dataItem, itemIndex); // helps to solve shrinking // not good - creates all items
343 dataItem.__disabled = true;
344 }
345 }
346 }
347 this.appendDataItem(this._lastDataItem);
348 this.validateDataElement(this._lastDataItem, itemIndex + 1, this.dataItems.length);
349 if (this._axisBreaks) {
350 var axisBreaks = this._axisBreaks;
351 axisBreaks.each(function (axisBreak) {
352 var adjustedStartValue = axisBreak.adjustedStartValue;
353 var adjustedEndValue = axisBreak.adjustedEndValue;
354 if ($math.intersect({ start: adjustedStartValue, end: adjustedEndValue }, { start: _this._startIndex, end: _this._endIndex })) {
355 for (var b = adjustedStartValue; b <= adjustedEndValue; b++) {
356 var dataItem = _this.dataItems.getIndex(b);
357 dataItem.__disabled = true;
358 }
359 var frequency_1 = $math.fitToRange(Math.ceil(_this._frequency / axisBreak.breakSize), 1, adjustedEndValue - adjustedStartValue);
360 var itemIndex_1 = 0;
361 if (axisBreak.breakSize > 0) {
362 // TODO use iterator instead
363 for (var b = adjustedStartValue; b <= adjustedEndValue; b = b + frequency_1) {
364 var dataItem = _this.dataItems.getIndex(b);
365 dataItem.__disabled = false;
366 _this.appendDataItem(dataItem);
367 _this.validateDataElement(dataItem, itemIndex_1);
368 itemIndex_1++;
369 }
370 }
371 }
372 });
373 }
374 this.validateBreaks();
375 this.validateAxisRanges();
376 this.ghostLabel.invalidate(); // solves font issue
377 this.renderer.invalidateLayout();
378 };
379 /**
380 * [validateDataElement description]
381 *
382 * @ignore Exclude from docs
383 * @todo Description
384 * @param dataItem [description]
385 * @param itemIndex [description]
386 */
387 CategoryAxis.prototype.validateDataElement = function (dataItem, itemIndex, index) {
388 _super.prototype.validateDataElement.call(this, dataItem);
389 dataItem.itemIndex = this._axisItemCount;
390 this._axisItemCount++;
391 //dataItem.__disabled = false;
392 var renderer = this.renderer;
393 if (!$type.isNumber(index)) {
394 index = this.categoryToIndex(dataItem.category);
395 }
396 var endIndex = this.categoryToIndex(dataItem.endCategory);
397 if (!$type.isNumber(endIndex)) {
398 endIndex = index;
399 }
400 var position = this.indexToPosition(index, dataItem.locations.category);
401 var endPosition = this.indexToPosition(endIndex, dataItem.locations.endCategory);
402 dataItem.position = position;
403 var fillEndIndex;
404 var fillPosition;
405 var fillEndPosition;
406 if (dataItem.isRange) {
407 fillEndIndex = endIndex;
408 fillPosition = this.indexToPosition(index, dataItem.locations.category);
409 fillEndPosition = this.indexToPosition(fillEndIndex, dataItem.locations.endCategory);
410 }
411 dataItem.point = renderer.positionToPoint(position);
412 var tick = dataItem.tick;
413 if (tick && !tick.disabled) {
414 renderer.updateTickElement(tick, position, endPosition);
415 }
416 var grid = dataItem.grid;
417 if (grid && !grid.disabled) {
418 renderer.updateGridElement(grid, position, endPosition);
419 }
420 var label = dataItem.label;
421 if (label && !label.disabled) {
422 // theorethically this might result problems if category text changes, the range text won't change. But otherwise range.label.text = "custom text" won't work, which is not intuitive.
423 if (!dataItem.isRange || label.text == undefined) {
424 dataItem.text = dataItem.text;
425 }
426 renderer.updateLabelElement(label, position, endPosition);
427 if ((renderer instanceof AxisRendererY && dataItem.label.measuredWidth > this.ghostLabel.measuredWidth) || (renderer instanceof AxisRendererX && dataItem.label.measuredHeight > this.ghostLabel.measuredHeight)) {
428 if (dataItem.label.html) {
429 this.ghostLabel.html = dataItem.label.currentText;
430 }
431 else {
432 this.ghostLabel.text = dataItem.label.currentText;
433 }
434 }
435 }
436 var fill = dataItem.axisFill;
437 if (fill && !fill.disabled) {
438 if (!dataItem.isRange) {
439 fillEndIndex = index + this._frequency;
440 fillPosition = this.indexToPosition(index, fill.location);
441 fillEndPosition = this.indexToPosition(fillEndIndex, fill.location);
442 }
443 renderer.updateFillElement(fill, fillPosition, fillEndPosition);
444 if (!dataItem.isRange) {
445 this.fillRule(dataItem, itemIndex);
446 }
447 }
448 if (dataItem.bullet) {
449 renderer.updateBullet(dataItem.bullet, position, endPosition);
450 }
451 var mask = dataItem.mask;
452 if (mask) {
453 renderer.updateFillElement(mask, fillPosition, fillEndPosition);
454 }
455 };
456 /**
457 * @ignore
458 */
459 CategoryAxis.prototype.disposeData = function () {
460 this.dataItemsByCategory.clear();
461 _super.prototype.disposeData.call(this);
462 };
463 /**
464 * Processes the axis data item.
465 *
466 * @ignore Exclude from docs
467 * @param dataItem Data item
468 * @param dataContext The raw data that corresponds to this data item
469 */
470 CategoryAxis.prototype.processDataItem = function (dataItem, dataContext) {
471 if (dataItem) {
472 // creat a collection for fast access
473 _super.prototype.processDataItem.call(this, dataItem, dataContext);
474 // check if such category already exists
475 //let existingDataItem: CategoryAxisDataItem = this.dataItemsByCategory.getKey(dataItem.category);
476 //if (existingDataItem && existingDataItem != dataItem) {
477 // this.dataItems.remove(existingDataItem);
478 //}
479 if ($type.hasValue(dataItem.category)) {
480 this.dataItemsByCategory.setKey(dataItem.category, dataItem);
481 }
482 }
483 };
484 CategoryAxis.prototype.getDataItem = function (dataContext) {
485 var category = (dataContext[this.dataFields.category]);
486 if ($type.hasValue(category)) {
487 var dataItem = this.dataItemsByCategory.getKey(category);
488 if (dataItem) {
489 return dataItem;
490 }
491 else {
492 return this.dataItems.create();
493 }
494 }
495 };
496 /**
497 * Converts a category index to an actual screen coordinate on the axis.
498 *
499 * `location` identifies relative location within category. 0 - beginning,
500 * 0.5 - middle, 1 - end, and anything inbetween.
501 *
502 * @param index Index
503 * @param location Location (0-1)
504 * @return Position (px)
505 */
506 CategoryAxis.prototype.indexToPosition = function (index, location) {
507 if (!$type.isNumber(location)) {
508 location = 0.5;
509 }
510 var startIndex = this.startIndex;
511 var endIndex = this.endIndex;
512 var difference = this.adjustDifference(startIndex, endIndex);
513 var startLocation = this.startLocation;
514 var endLocation = this.endLocation;
515 difference -= startLocation;
516 difference -= (1 - endLocation);
517 if (this._axisBreaks) {
518 var axisBreaks = this._axisBreaks;
519 $iter.eachContinue(axisBreaks.iterator(), function (axisBreak) {
520 var breakStartIndex = axisBreak.adjustedStartValue;
521 var breakEndIndex = axisBreak.adjustedEndValue;
522 if (index < startIndex || !$type.isNumber(breakStartIndex) || !$type.isNumber(breakEndIndex)) {
523 return false;
524 }
525 if ($math.intersect({ start: breakStartIndex, end: breakEndIndex }, { start: startIndex, end: endIndex })) {
526 breakStartIndex = Math.max(startIndex, breakStartIndex);
527 breakEndIndex = Math.min(endIndex, breakEndIndex);
528 var breakSize = axisBreak.breakSize;
529 // value to the right of break end
530 if (index > breakEndIndex) {
531 startIndex += (breakEndIndex - breakStartIndex) * (1 - breakSize);
532 }
533 // value to the left of break start
534 else if (index < breakStartIndex) {
535 }
536 // value within break
537 else {
538 index = breakStartIndex + (index - breakStartIndex) * breakSize;
539 }
540 }
541 return true;
542 });
543 }
544 var deltaPosition = 0;
545 var dataItem = this.dataItems.getIndex(index);
546 if (dataItem) {
547 deltaPosition = dataItem.deltaPosition;
548 }
549 return $math.round(deltaPosition + (index + location - startLocation - startIndex) / difference, 5);
550 };
551 /**
552 * Converts a string category name to relative position on axis.
553 *
554 * `location` identifies relative location within category. 0 - beginning,
555 * 0.5 - middle, 1 - end, and anything inbetween.
556 *
557 * @param category Category name
558 * @param location Location (0-1)
559 * @return Position
560 */
561 CategoryAxis.prototype.categoryToPosition = function (category, location) {
562 var index = this.categoryToIndex(category);
563 return this.indexToPosition(index, location);
564 };
565 /**
566 * Converts a string category name to a orientation point (x, y, angle) on axis
567 *
568 * `location` identifies relative location within category. 0 - beginning,
569 * 0.5 - middle, 1 - end, and anything inbetween.
570 * @param category Category name
571 * @param location Location (0-1)
572 * @return Orientation point
573 */
574 CategoryAxis.prototype.categoryToPoint = function (category, location) {
575 var position = this.categoryToPosition(category, location);
576 var point = this.renderer.positionToPoint(position);
577 var angle = this.renderer.positionToAngle(position);
578 return { x: point.x, y: point.y, angle: angle };
579 };
580 /**
581 * Converts a string category name to a orientation point (x, y, angle) on axis
582 *
583 * `location` identifies relative location within category. 0 - beginning,
584 * 0.5 - middle, 1 - end, and anything inbetween.
585 * @param category Category name
586 * @param location Location (0-1)
587 * @return Orientation point
588 */
589 CategoryAxis.prototype.anyToPoint = function (category, location) {
590 return this.categoryToPoint(category, location);
591 };
592 /**
593 * Converts a string category name to relative position on axis.
594 *
595 * An alias to `categoryToPosition()`.
596 *
597 * @param category Category name
598 * @param location Location (0-1)
599 * @return Relative position
600 */
601 CategoryAxis.prototype.anyToPosition = function (category, location) {
602 return this.categoryToPosition(category, location);
603 };
604 /**
605 * Converts named category to an index of data item it corresponds to.
606 *
607 * @param category Category
608 * @return Data item index
609 */
610 CategoryAxis.prototype.categoryToIndex = function (category) {
611 if ($type.hasValue(category)) {
612 var dataItem = this.dataItemsByCategory.getKey(category);
613 if (dataItem) {
614 return dataItem.index;
615 }
616 }
617 };
618 /**
619 * Zooms the axis to specific named ctaegories.
620 *
621 * @param startCategory Start category
622 * @param endCategory End category
623 */
624 CategoryAxis.prototype.zoomToCategories = function (startCategory, endCategory) {
625 this.zoomToIndexes(this.categoryToIndex(startCategory), this.categoryToIndex(endCategory) + 1);
626 };
627 /**
628 * [getAnyRangePath description]
629 *
630 * @ignore Exclude from docs
631 * @todo Description
632 * @param start [description]
633 * @param end [description]
634 * @param startLocation [description]
635 * @param endLocation [description]
636 * @return [description]
637 */
638 CategoryAxis.prototype.getAnyRangePath = function (start, end, startLocation, endLocation) {
639 var startPos = this.categoryToPosition(start, startLocation);
640 var endPos = this.categoryToPosition(end, endLocation);
641 return this.getPositionRangePath(startPos, endPos); // Base class (Axis) gets range shape from AxisRenderer
642 };
643 /**
644 * Takes an absolute position (px) within axis and adjust it to a specific
645 * `location` within category it corresponds to.
646 *
647 * @param position Source position (px)
648 * @param location Location within category (0-1)
649 * @return Adjusted position (px)
650 */
651 CategoryAxis.prototype.roundPosition = function (position, location) {
652 var index = this.positionToIndex(position);
653 return this.indexToPosition(index, location);
654 };
655 /**
656 * Finds and returns first series data item with specific category
657 * @param series Target series
658 * @param category Category
659 * @return XYSeriesDataItem data item
660 */
661 CategoryAxis.prototype.getFirstSeriesDataItem = function (series, category) {
662 for (var i = 0; i < series.dataItems.length; i++) {
663 var dataItem = series.dataItems.getIndex(i);
664 if (series.xAxis == this) {
665 if (dataItem.categoryX == category) {
666 return dataItem;
667 }
668 }
669 if (series.yAxis == this) {
670 if (dataItem.categoryY == category) {
671 return dataItem;
672 }
673 }
674 }
675 };
676 /**
677 * Finds and returns last series data item with specific category.
678 * @param series Target series
679 * @param category Category
680 * @return XYSeriesDataItem data item
681 */
682 CategoryAxis.prototype.getLastSeriesDataItem = function (series, category) {
683 for (var i = series.dataItems.length - 1; i >= 0; i--) {
684 var dataItem = series.dataItems.getIndex(i);
685 if (series.xAxis == this) {
686 if (dataItem.categoryX == category) {
687 return dataItem;
688 }
689 }
690 if (series.yAxis == this) {
691 if (dataItem.categoryY == category) {
692 return dataItem;
693 }
694 }
695 }
696 };
697 // todo: optimize
698 CategoryAxis.prototype.getSeriesDataItemByCategory = function (category, series) {
699 var _this = this;
700 var seriesDataItem;
701 series.dataItems.each(function (dataItem) {
702 if (series.xAxis == _this) {
703 if (dataItem.categoryX == category) {
704 seriesDataItem = dataItem;
705 }
706 }
707 else if (series.yAxis == _this) {
708 if (dataItem.categoryY == category) {
709 seriesDataItem = dataItem;
710 }
711 }
712 });
713 return seriesDataItem;
714 };
715 /**
716 * Returns a data item from Series that corresponds to a specific absolute
717 * position on the Axis.
718 *
719 * @param series Target series
720 * @param position Position (px)
721 * @return XYSeriesDataItem data item
722 */
723 CategoryAxis.prototype.getSeriesDataItem = function (series, position, findNearest) {
724 var _this = this;
725 if ($type.isNumber(position)) {
726 var index_1 = this.positionToIndex(position);
727 if (index_1 >= this.dataItems.length) {
728 index_1--;
729 }
730 var dataItem = this.dataItems.getIndex(index_1);
731 if (dataItem) {
732 var category_1 = dataItem.category;
733 var sdi_1;
734 var seriesDataItem = series.dataItems.getIndex(index_1);
735 if (seriesDataItem) {
736 if (series.xAxis == this) {
737 if (seriesDataItem.categoryX == category_1) {
738 return seriesDataItem;
739 }
740 }
741 if (series.yAxis == this) {
742 if (seriesDataItem.categoryY == category_1) {
743 return seriesDataItem;
744 }
745 }
746 }
747 series.dataItems.each(function (dataItem) {
748 if (series.xAxis == _this) {
749 if (dataItem.categoryX == category_1) {
750 if (!sdi_1) {
751 sdi_1 = dataItem;
752 }
753 if (Math.abs(index_1 - sdi_1.index) > Math.abs(index_1 - dataItem.index)) {
754 sdi_1 = dataItem;
755 }
756 }
757 }
758 if (series.yAxis == _this) {
759 if (dataItem.categoryY == category_1) {
760 if (!sdi_1) {
761 sdi_1 = dataItem;
762 }
763 if (Math.abs(index_1 - sdi_1.index) > Math.abs(index_1 - dataItem.index)) {
764 sdi_1 = dataItem;
765 }
766 }
767 }
768 });
769 //@todo
770 if (findNearest) {
771 }
772 return sdi_1;
773 }
774 }
775 };
776 /**
777 * Returns the X coordinate for series' data item.
778 *
779 * @ignore Exclude from docs
780 * @todo Description (review)
781 * @param dataItem Data item
782 * @param key Category
783 * @param location Location (0-1)
784 * @return X coordinate (px)
785 */
786 CategoryAxis.prototype.getX = function (dataItem, key, location, stackKey, range) {
787 var position = this.getPositionX(dataItem, key, location, stackKey, range);
788 if ($type.isNaN(position)) {
789 return this.basePoint.x;
790 }
791 else {
792 return this.renderer.positionToPoint(position).x;
793 }
794 };
795 /**
796 * Returns relative position on axis for series' data item.
797 *
798 * @since 4.5.14
799 * @param dataItem Data item
800 * @param key Category
801 * @param location Location (0-1)
802 * @return Relative position
803 */
804 CategoryAxis.prototype.getPositionX = function (dataItem, key, location, stackKey, range) {
805 var position;
806 if ($type.hasValue(key)) {
807 position = this.categoryToPosition(dataItem.categories[key], location);
808 }
809 if (range) {
810 position = $math.fitToRange(position, range.start, range.end);
811 }
812 return position;
813 };
814 /**
815 * Returns the Y coordinate for series' data item.
816 *
817 * @ignore Exclude from docs
818 * @todo Description (review)
819 * @param dataItem Data item
820 * @param key Category
821 * @param location Location (0-1)
822 * @return Y coordinate (px)
823 */
824 CategoryAxis.prototype.getY = function (dataItem, key, location, stackKey, range) {
825 var position = this.getPositionY(dataItem, key, location, stackKey, range);
826 if ($type.isNaN(position)) {
827 return this.basePoint.y;
828 }
829 else {
830 return this.renderer.positionToPoint(position).y;
831 }
832 };
833 /**
834 * Returns relative position on axis for series' data item.
835 *
836 * @since 4.5.14
837 * @param dataItem Data item
838 * @param key Category
839 * @param location Location (0-1)
840 * @return Relative position
841 */
842 CategoryAxis.prototype.getPositionY = function (dataItem, key, location, stackKey, range) {
843 var position;
844 if ($type.hasValue(key)) {
845 position = this.categoryToPosition(dataItem.categories[key], location);
846 }
847 if (range) {
848 position = $math.fitToRange(position, range.start, range.end);
849 }
850 return position;
851 };
852 /**
853 * Returns an angle for series data item.
854 *
855 * @ignore Exclude from docs
856 * @todo Description (review)
857 * @param dataItem Data item
858 * @param key Category
859 * @param location Location (0-1)
860 * @param stackKey Stack key (?)
861 * @param range Range to fit in
862 * @return Angle
863 */
864 CategoryAxis.prototype.getAngle = function (dataItem, key, location, stackKey, range) {
865 var position = this.categoryToPosition(dataItem.categories[key], location);
866 if (range) {
867 position = $math.fitToRange(position, range.start, range.end);
868 }
869 return this.positionToAngle(position);
870 };
871 /**
872 * Returns an absolute pixel coordinate of the start of the cell (category),
873 * that specific position value falls into.
874 *
875 * @ignore Exclude from docs
876 * @todo Description (review)
877 * @param position Position (px)
878 * @return Cell start position (px)
879 */
880 CategoryAxis.prototype.getCellStartPosition = function (position) {
881 return this.roundPosition(position, 0);
882 };
883 /**
884 * Returns an absolute pixel coordinate of the end of the cell (category),
885 * that specific position value falls into.
886 *
887 * @ignore Exclude from docs
888 * @todo Description (review)
889 * @param position Position (px)
890 * @return Cell end position (px)
891 */
892 CategoryAxis.prototype.getCellEndPosition = function (position) {
893 return this.roundPosition(position, 1);
894 };
895 /**
896 * Returns text to show in a category tooltip, based on specific position
897 * within axis.
898 *
899 * @ignore Exclude from docs
900 * @param position Position (px)
901 * @return Label (category)
902 */
903 CategoryAxis.prototype.getTooltipText = function (position) {
904 var dataItem = this.dataItems.getIndex(this.positionToIndex(position));
905 if (dataItem) {
906 this.tooltipDataItem = dataItem;
907 this.tooltip.dataItem = dataItem;
908 if (this.tooltipText) {
909 return this.tooltipText;
910 }
911 if (!this._adapterO) {
912 return dataItem.category;
913 }
914 else {
915 return this._adapterO.apply("getTooltipText", dataItem.category);
916 }
917 }
918 };
919 /**
920 * Returns an index of the category that corresponds to specific pixel
921 * position within axis.
922 *
923 * @param position Position (px)
924 * @return Category index
925 */
926 CategoryAxis.prototype.positionToIndex = function (position) {
927 position = $math.round(position, 10);
928 if (position < 0) {
929 position = 0;
930 }
931 if (position > 1) {
932 position = 1;
933 }
934 var startIndex = this.startIndex;
935 var endIndex = this.endIndex;
936 var difference = endIndex - startIndex - this.startLocation - (1 - this.endLocation);
937 position += 1 / difference * this.startLocation;
938 var index = null;
939 if (this._axisBreaks) {
940 var axisBreaks = this._axisBreaks;
941 // in case we have some axis breaks
942 $iter.eachContinue(axisBreaks.iterator(), function (axisBreak) {
943 var breakStartPosition = axisBreak.startPosition;
944 var breakEndPosition = axisBreak.endPosition;
945 var breakStartIndex = axisBreak.adjustedStartValue;
946 var breakEndIndex = axisBreak.adjustedEndValue;
947 breakStartIndex = $math.max(breakStartIndex, startIndex);
948 breakEndIndex = $math.min(breakEndIndex, endIndex);
949 var breakSize = axisBreak.breakSize;
950 difference -= (breakEndIndex - breakStartIndex) * (1 - breakSize);
951 // position to the right of break end
952 if (position > breakEndPosition) {
953 startIndex += (breakEndIndex - breakStartIndex) * (1 - breakSize);
954 }
955 // position to the left of break start
956 else if (position < breakStartPosition) {
957 }
958 // value within break
959 else {
960 var breakPosition = (position - breakStartPosition) / (breakEndPosition - breakStartPosition);
961 index = breakStartIndex + Math.round(breakPosition * (breakEndIndex - breakStartIndex));
962 return false;
963 }
964 return true;
965 });
966 }
967 if (!$type.isNumber(index)) {
968 index = Math.floor(position * difference + startIndex);
969 }
970 if (index >= this.dataItems.length) {
971 index = this.dataItems.length - 1;
972 }
973 // not good, when panning out of bounds, each time one less item gets selected
974 //if (index >= endIndex) {
975 // index--;
976 //}
977 return index;
978 };
979 /**
980 * Returns category based on position.
981 *
982 * Please note that `position` represents position within axis which may be
983 * zoomed and not correspond to Cursor's `position`.
984 *
985 * To convert Cursor's `position` to Axis' `position` use `toAxisPosition()` method.
986 *
987 * This is a synonim of `getPositionLabel()` implemented here for consistentcy.
988 *
989 * @since 4.3.8
990 * @see {@link https://www.amcharts.com/docs/v4/tutorials/tracking-cursors-position-via-api/#Tracking_Cursor_s_position} For more information about cursor tracking.
991 * @param position Relative position on axis (0-1)
992 * @return Position label
993 */
994 CategoryAxis.prototype.positionToCategory = function (position) {
995 return this.getPositionLabel(position);
996 };
997 /**
998 * Returns category based on position.
999 *
1000 * Please note that `position` represents position within axis which may be
1001 * zoomed and not correspond to Cursor's `position`.
1002 *
1003 * To convert Cursor's `position` to Axis' `position` use `toAxisPosition()` method.
1004 *
1005 * @see {@link https://www.amcharts.com/docs/v4/tutorials/tracking-cursors-position-via-api/#Tracking_Cursor_s_position} For more information about cursor tracking.
1006 * @param position Relative position on axis (0-1)
1007 * @return Position label
1008 */
1009 CategoryAxis.prototype.getPositionLabel = function (position) {
1010 var dataItem = this.dataItems.getIndex(this.positionToIndex(position));
1011 if (dataItem) {
1012 return dataItem.category;
1013 }
1014 };
1015 Object.defineProperty(CategoryAxis.prototype, "basePoint", {
1016 /**
1017 * Coordinates of the actual axis start.
1018 *
1019 * @ignore Exclude from docs
1020 * @return Base point
1021 */
1022 get: function () {
1023 // This makes base grid to be drawn at the end of the axis and adds extra
1024 // grid which we need to nicely close the chart.
1025 return this.renderer.positionToPoint(1);
1026 },
1027 enumerable: true,
1028 configurable: true
1029 });
1030 /**
1031 * Initializes Axis' renderer.
1032 *
1033 * @ignore Exclude from docs
1034 */
1035 CategoryAxis.prototype.initRenderer = function () {
1036 _super.prototype.initRenderer.call(this);
1037 var renderer = this.renderer;
1038 renderer.baseGrid.disabled = true;
1039 };
1040 Object.defineProperty(CategoryAxis.prototype, "frequency", {
1041 /**
1042 * Current frequency of labels of the axis.
1043 *
1044 * Normally it would be 1, but when labels start to be hidden due
1045 * to `minGridDistance` this read-only property will increase.
1046 *
1047 * @readonly
1048 * @since 4.2.0
1049 * @return Label frequency
1050 */
1051 get: function () {
1052 return this._frequency;
1053 },
1054 enumerable: true,
1055 configurable: true
1056 });
1057 Object.defineProperty(CategoryAxis.prototype, "sortBySeries", {
1058 /**
1059 * @return Sort categories?
1060 */
1061 get: function () {
1062 return this.getPropertyValue("sortBySeries");
1063 },
1064 /**
1065 * If set to a reference of [[ColumnSeries]] the categories will be sorted
1066 * by actual values.
1067 *
1068 * The categories are ordered in descending order (from highest values to
1069 * lowest). To reverse the order, use axis renderer's `inversed` setting.
1070 * E.g.:
1071 *
1072 * ```TypeScript
1073 * categoryAxis.sortBySeries = series;
1074 * categoryAxis.renderer.inversed = true;
1075 * ```
1076 * ```JavaScript
1077 * categoryAxis.sortBySeries = series;
1078 * categoryAxis.renderer.inversed = true;
1079 * ```
1080 * ```JSON
1081 * {
1082 * // ...
1083 * "xAxes": [{
1084 * // ...
1085 * "sortBySeries": "s1",
1086 * "renderer": {
1087 * // ...
1088 * "inversed": true
1089 * }
1090 * }]
1091 * }
1092 * ```
1093 *
1094 * @since 4.8.7
1095 * @param value Sort categories?
1096 */
1097 set: function (value) {
1098 this.setPropertyValue("sortBySeries", value, true);
1099 },
1100 enumerable: true,
1101 configurable: true
1102 });
1103 /**
1104 * Processes JSON-based config before it is applied to the object.
1105 *
1106 * @ignore Exclude from docs
1107 * @param config Config
1108 */
1109 CategoryAxis.prototype.processConfig = function (config) {
1110 if (config) {
1111 if ($type.hasValue(config.sortBySeries) && $type.isString(config.sortBySeries)) {
1112 if (this.map.hasKey(config.sortBySeries)) {
1113 config.sortBySeries = this.map.getKey(config.sortBySeries);
1114 }
1115 else {
1116 this.addDelayedMap("sortBySeries", config.sortBySeries);
1117 delete config.sortBySeries;
1118 }
1119 }
1120 }
1121 _super.prototype.processConfig.call(this, config);
1122 };
1123 return CategoryAxis;
1124}(Axis));
1125export { CategoryAxis };
1126/**
1127 * Register class, so that it can be instantiated using its name from
1128 * anywhere.
1129 *
1130 * @ignore
1131 */
1132registry.registeredClasses["CategoryAxis"] = CategoryAxis;
1133registry.registeredClasses["CategoryAxisDataItem"] = CategoryAxisDataItem;
1134//# sourceMappingURL=CategoryAxis.js.map
\No newline at end of file