"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MultiStackedBarChartBase = void 0;
var tslib_1 = require("tslib");
var React = require("react");
var Utilities_1 = require("@fluentui/react/lib/Utilities");
var index_1 = require("../Legends/index");
var index_2 = require("./index");
var Callout_1 = require("@fluentui/react/lib/Callout");
var react_focus_1 = require("@fluentui/react-focus");
var locale_util_1 = require("../../utilities/locale-util");
var index_3 = require("../../utilities/index");
var FocusableTooltipText_1 = require("../../utilities/FocusableTooltipText");
var getClassNames = (0, Utilities_1.classNamesFunction)();
var MultiStackedBarChartBase = /** @class */ (function (_super) {
    tslib_1.__extends(MultiStackedBarChartBase, _super);
    function MultiStackedBarChartBase(props) {
        var _this = _super.call(this, props) || this;
        _this._isRTL = (0, Utilities_1.getRTL)();
        _this._adjustProps = function () {
            var _a = _this.props, theme = _a.theme, className = _a.className, styles = _a.styles, width = _a.width, barHeight = _a.barHeight;
            _this._classNames = getClassNames(styles, {
                legendColor: _this.state.color,
                theme: theme,
                width: width,
                className: className,
                barHeight: barHeight,
            });
        };
        _this._getLegendData = function (data, hideRatio, palette) {
            var defaultPalette = [palette.blueLight, palette.blue, palette.blueMid, palette.red, palette.black];
            var actions = [];
            data.forEach(function (singleChartData, index) {
                var validChartData = singleChartData.chartData.filter(function (_) { return !_.placeHolder; });
                if (validChartData.length < 3) {
                    var hideNumber = hideRatio[index] === undefined ? false : hideRatio[index];
                    if (hideNumber) {
                        validChartData.forEach(function (point, pointIndex) {
                            var _a, _b;
                            var color = point.color ? point.color : defaultPalette[Math.floor(Math.random() * 4 + 1)];
                            if (_this.props.enableGradient) {
                                color = ((_a = point.gradient) === null || _a === void 0 ? void 0 : _a[0]) || (0, index_3.getNextGradient)(pointIndex, 0, (_b = _this.props.theme) === null || _b === void 0 ? void 0 : _b.isInverted)[0];
                            }
                            var checkSimilarLegends = actions.filter(function (leg) { return leg.title === point.legend && leg.color === color; });
                            if (checkSimilarLegends.length > 0) {
                                return;
                            }
                            var legend = {
                                title: point.legend,
                                color: color,
                                action: function () {
                                    _this._onClick(point.legend);
                                },
                                hoverAction: function () {
                                    _this._handleChartMouseLeave();
                                    _this._onHover(point.legend);
                                },
                                onMouseOutAction: function () {
                                    _this._onLeave();
                                },
                            };
                            actions.push(legend);
                        });
                    }
                }
                else {
                    validChartData.forEach(function (point, pointIndex) {
                        var _a, _b;
                        var color = point.color ? point.color : defaultPalette[Math.floor(Math.random() * 4 + 1)];
                        if (_this.props.enableGradient) {
                            color = ((_a = point.gradient) === null || _a === void 0 ? void 0 : _a[0]) || (0, index_3.getNextGradient)(pointIndex, 0, (_b = _this.props.theme) === null || _b === void 0 ? void 0 : _b.isInverted)[0];
                        }
                        var checkSimilarLegends = actions.filter(function (leg) { return leg.title === point.legend && leg.color === color; });
                        if (checkSimilarLegends.length > 0) {
                            return;
                        }
                        var legend = {
                            title: point.legend,
                            color: color,
                            action: function () {
                                _this._onClick(point.legend);
                            },
                            hoverAction: function () {
                                _this._handleChartMouseLeave();
                                _this._onHover(point.legend);
                            },
                            onMouseOutAction: function () {
                                _this._onLeave();
                            },
                        };
                        actions.push(legend);
                    });
                }
            });
            return (React.createElement(index_1.Legends, tslib_1.__assign({ legends: actions, overflowProps: _this.props.legendsOverflowProps, focusZonePropsInHoverCard: _this.props.focusZonePropsForLegendsInHoverCard, overflowText: _this.props.legendsOverflowText }, _this.props.legendProps)));
        };
        _this._handleChartMouseLeave = function () {
            _this._calloutAnchorPoint = null;
            _this.setState({
                isCalloutVisible: false,
            });
        };
        _this._closeCallout = function () {
            _this.setState({
                isCalloutVisible: false,
            });
        };
        /**
         * This function checks if the given legend is highlighted or not.
         * A legend can be highlighted in 2 ways:
         * 1. selection: if the user clicks on it
         * 2. hovering: if there is no selected legend and the user hovers over it
         */
        _this._legendHighlighted = function (legendTitle) {
            return (_this.state.selectedLegend === legendTitle ||
                (_this.state.selectedLegend === '' && _this.state.activeLegend === legendTitle));
        };
        /**
         * This function checks if none of the legends is selected or hovered.
         */
        _this._noLegendHighlighted = function () {
            return _this.state.selectedLegend === '' && _this.state.activeLegend === '';
        };
        _this._getAriaLabel = function (point) {
            var _a;
            var legend = point.xAxisCalloutData || point.legend;
            var yValue = point.yAxisCalloutData || point.data || 0;
            return (((_a = point.callOutAccessibilityData) === null || _a === void 0 ? void 0 : _a.ariaLabel) ||
                (legend ? "".concat(legend, ", ") : point.placeHolder ? 'Remaining, ' : '') + "".concat(yValue, "."));
        };
        _this._computeLongestBarTotalValue = function () {
            var longestBarTotalValue = 0;
            _this.props.data.forEach(function (_a) {
                var chartData = _a.chartData;
                var barTotalValue = chartData.reduce(function (acc, point) { return acc + (point.data ? point.data : 0); }, 0);
                longestBarTotalValue = Math.max(longestBarTotalValue, barTotalValue);
            });
            return longestBarTotalValue;
        };
        _this.state = {
            isCalloutVisible: false,
            refArray: [],
            selectedLegend: '',
            activeLegend: '',
            refSelected: null,
            dataForHoverCard: 0,
            color: '',
            xCalloutValue: '',
            yCalloutValue: '',
            calloutLegend: '',
            barSpacingInPercent: 0,
        };
        _this._onLeave = _this._onLeave.bind(_this);
        _this._onBarLeave = _this._onBarLeave.bind(_this);
        _this._calloutId = (0, Utilities_1.getId)('callout');
        _this.barChartSvgRef = React.createRef();
        _this._emptyChartId = (0, Utilities_1.getId)('_MSBC_empty');
        _this._barId = (0, Utilities_1.getId)('_MSBC_rect_');
        _this._barIdPlaceholderPartToWhole = (0, Utilities_1.getId)('_MSBC_rect_partToWhole_');
        _this._barIdEmpty = (0, Utilities_1.getId)('_MSBC_rect_empty');
        return _this;
    }
    MultiStackedBarChartBase.prototype.componentDidMount = function () {
        var _a;
        var svgWidth = ((_a = this.barChartSvgRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().width) || 0;
        var MARGIN_WIDTH_IN_PX = 3;
        if (svgWidth) {
            var currentBarSpacing = (MARGIN_WIDTH_IN_PX / svgWidth) * 100;
            this.setState({ barSpacingInPercent: currentBarSpacing });
        }
    };
    MultiStackedBarChartBase.prototype.render = function () {
        var _this = this;
        if (!this._isChartEmpty()) {
            var _a = this.props, data = _a.data, theme = _a.theme, culture = _a.culture;
            this._adjustProps();
            var palette_1 = theme.palette;
            var legends = this._getLegendData(data, this.props.hideRatio, palette_1);
            var isCalloutVisible = this.state.isCalloutVisible;
            this._classNames = getClassNames(this.props.styles, {
                legendColor: this.state.color,
                theme: theme,
                variant: this.props.variant,
                hideLabels: this.props.hideLabels,
            });
            var legendName = this.state.xCalloutValue ? this.state.xCalloutValue : this.state.calloutLegend;
            var calloutYVal = this.state.yCalloutValue ? this.state.yCalloutValue : this.state.dataForHoverCard;
            this._longestBarTotalValue = this._computeLongestBarTotalValue();
            var bars = data.map(function (singleChartData, index) {
                var singleChartBars = _this._createBarsAndLegends(singleChartData, _this.props.barHeight, palette_1, _this.props.hideRatio[index], _this.props.hideDenominator[index], _this.props.href, index);
                return (React.createElement("div", { key: index, id: "_MSBC_bar-".concat(index) }, singleChartBars));
            });
            return (React.createElement("div", { className: this._classNames.root, onMouseLeave: this._handleChartMouseLeave },
                bars,
                !this.props.hideLegend && React.createElement("div", { className: this._classNames.legendContainer }, legends),
                React.createElement(Callout_1.Callout, tslib_1.__assign({ gapSpace: 15, isBeakVisible: false, target: this.state.refSelected, setInitialFocus: true, hidden: !(!this.props.hideTooltip && isCalloutVisible), directionalHint: Callout_1.DirectionalHint.topAutoEdge, id: this._calloutId, onDismiss: this._closeCallout, 
                    // eslint-disable-next-line @typescript-eslint/no-deprecated
                    preventDismissOnLostFocus: true, 
                    /** Keep the callout updated with details of focused/hovered bar */
                    shouldUpdateWhenHidden: true }, this.props.calloutProps, (0, index_3.getAccessibleDataObject)(this.state.callOutAccessibilityData, 'text', false)),
                    React.createElement(React.Fragment, null, this.props.onRenderCalloutPerDataPoint ? (this.props.onRenderCalloutPerDataPoint(this.state.dataPointCalloutProps)) : (React.createElement(index_3.ChartHoverCard, { Legend: legendName, YValue: calloutYVal, color: this.state.color, culture: culture }))))));
        }
        return (React.createElement("div", { id: this._emptyChartId, role: 'alert', style: { opacity: '0' }, "aria-label": 'Graph has no data to display' }));
    };
    /**
     * The Create bar functions returns an array of <rect> elements, which form the bars
     * For each bar an x value, and a width needs to be specified
     * The computations are done based on percentages
     * Extra margin is also provided, in the x value to provide some spacing
     */
    MultiStackedBarChartBase.prototype._createBarsAndLegends = function (data, barHeight, palette, hideRatio, hideDenominator, href, barNo) {
        var _this = this;
        var _a, _b;
        var noOfBars = ((_a = data.chartData) === null || _a === void 0 ? void 0 : _a.reduce(function (count, point) { return (count += (point.data || 0) > 0 ? 1 : 0); }, 0)) ||
            1;
        var totalMarginPercent = this.state.barSpacingInPercent * (noOfBars - 1);
        var culture = this.props.culture;
        var defaultPalette = [palette.blueLight, palette.blue, palette.blueMid, palette.red, palette.black];
        // calculating starting point of each bar and it's range
        var startingPoint = [];
        var barTotalValue = data.chartData.reduce(function (acc, point) { return acc + (point.data ? point.data : 0); }, 0);
        var total = this.props.variant === index_2.MultiStackedBarChartVariant.AbsoluteScale ? this._longestBarTotalValue : barTotalValue;
        var sumOfPercent = 0;
        data.chartData.map(function (point, index) {
            var pointData = point.data ? point.data : 0;
            var currValue = (pointData / total) * 100;
            var value = currValue ? currValue : 0;
            if (value < 1 && value !== 0) {
                value = 1;
            }
            else if (value > 99 && value !== 100) {
                value = 99;
            }
            sumOfPercent += value;
            return sumOfPercent;
        });
        // Include an imaginary placeholder bar with value equal to
        // the difference between longestBarTotalValue and barTotalValue
        // while calculating sumOfPercent to get correct scalingRatio for absolute-scale variant
        if (this.props.variant === index_2.MultiStackedBarChartVariant.AbsoluteScale) {
            var value_1 = total === 0 ? 0 : ((total - barTotalValue) / total) * 100;
            if (value_1 < 1 && value_1 !== 0) {
                value_1 = 1;
            }
            else if (value_1 > 99 && value_1 !== 100) {
                value_1 = 99;
            }
            sumOfPercent += value_1;
        }
        /**
         * The %age of the space occupied by the margin needs to subtracted
         * while computing the scaling ratio, since the margins are not being
         * scaled down, only the data is being scaled down from a higher percentage to lower percentage
         * Eg: 95% of the space is taken by the bars, 5% by the margins
         * Now if the sumOfPercent is 120% -> This needs to be scaled down to 95%, not 100%
         * since that's only space available to the bars
         */
        var scalingRatio = sumOfPercent !== 0 ? sumOfPercent / (100 - totalMarginPercent) : 1;
        var prevPosition = 0;
        var value = 0;
        var bars = data.chartData.map(function (point, index) {
            var _a, _b, _c, _d;
            var startColor = point.color
                ? point.color
                : point.placeHolder
                    ? palette.neutralLight
                    : defaultPalette[Math.floor(Math.random() * 4 + 1)];
            var endColor = startColor;
            if (_this.props.enableGradient) {
                startColor = ((_a = point.gradient) === null || _a === void 0 ? void 0 : _a[0]) || (0, index_3.getNextGradient)(index, 0, (_b = _this.props.theme) === null || _b === void 0 ? void 0 : _b.isInverted)[0];
                endColor = ((_c = point.gradient) === null || _c === void 0 ? void 0 : _c[1]) || (0, index_3.getNextGradient)(index, 0, (_d = _this.props.theme) === null || _d === void 0 ? void 0 : _d.isInverted)[1];
            }
            var pointData = point.data ? point.data : 0;
            if (index > 0) {
                prevPosition += value;
            }
            value = (pointData / total) * 100 ? (pointData / total) * 100 : 0;
            if (value < 1 && value !== 0) {
                value = 1 / scalingRatio;
            }
            else if (value > 99 && value !== 100) {
                value = 99 / scalingRatio;
            }
            else {
                value = value / scalingRatio;
            }
            startingPoint.push(prevPosition);
            var styles = _this.props.styles;
            var shouldHighlight = _this._legendHighlighted(point.legend) || _this._noLegendHighlighted() ? true : false;
            var hoverCardYColor = point.placeHolder ? _this.props.theme.semanticColors.bodyText : startColor;
            _this._classNames = getClassNames(styles, {
                theme: _this.props.theme,
                shouldHighlight: shouldHighlight,
                href: href,
                variant: _this.props.variant,
                hideLabels: _this.props.hideLabels,
            });
            var gradientId = (0, Utilities_1.getId)('MSBC_Gradient') + "_".concat(barNo, "_").concat(index);
            return (React.createElement("g", { key: index, className: point.placeHolder ? _this._classNames.placeHolderOnHover : _this._classNames.opacityChangeOnHover, ref: function (e) {
                    _this._refCallback(e, point.legend);
                }, "data-is-focusable": !_this.props.hideTooltip && shouldHighlight, onFocus: _this._onBarFocus.bind(_this, pointData, hoverCardYColor, point), onBlur: _this._onBarLeave, role: "img", "aria-label": _this._getAriaLabel(point), onMouseOver: _this._onBarHover.bind(_this, pointData, hoverCardYColor, point), onMouseMove: _this._onBarHover.bind(_this, pointData, hoverCardYColor, point), onMouseLeave: _this._onBarLeave, onClick: href ? (point.placeHolder ? undefined : _this._redirectToUrl.bind(_this, href)) : point.onClick },
                _this.props.enableGradient && (React.createElement("defs", null,
                    React.createElement("linearGradient", { id: gradientId },
                        React.createElement("stop", { offset: "0", stopColor: startColor }),
                        React.createElement("stop", { offset: "100%", stopColor: endColor })))),
                React.createElement("rect", { key: index, id: "".concat(_this._barId, "-").concat(barNo, "-").concat(index), x: "".concat(_this._isRTL
                        ? 100 - startingPoint[index] - value - _this.state.barSpacingInPercent * index
                        : startingPoint[index] + _this.state.barSpacingInPercent * index, "%"), y: 0, width: value + '%', height: barHeight, rx: _this.props.roundCorners ? 3 : 0, fill: _this.props.enableGradient ? "url(#".concat(gradientId, ")") : startColor })));
        });
        if (this.props.variant === index_2.MultiStackedBarChartVariant.AbsoluteScale && !this.props.hideLabels) {
            var showLabel_1 = false;
            var barLabel_1 = 0;
            if (this._noLegendHighlighted()) {
                showLabel_1 = true;
                barLabel_1 = barTotalValue;
            }
            else {
                (_b = data.chartData) === null || _b === void 0 ? void 0 : _b.forEach(function (point) {
                    if (_this._legendHighlighted(point.legend)) {
                        showLabel_1 = true;
                        barLabel_1 += point.data ? point.data : 0;
                    }
                });
            }
            if (showLabel_1) {
                bars.push(React.createElement("text", { key: "text", x: "".concat(this._isRTL
                        ? 100 - (startingPoint[startingPoint.length - 1] || 0) - value - totalMarginPercent
                        : (startingPoint[startingPoint.length - 1] || 0) + value + totalMarginPercent, "%"), textAnchor: 'start', y: barHeight / 2, dominantBaseline: "central", transform: "translate(".concat(this._isRTL ? -4 : 4, ")"), className: this._classNames.barLabel, "aria-label": "Total: ".concat(barLabel_1), role: "img" }, (0, index_3.formatValueWithSIPrefix)(barLabel_1)));
            }
        }
        else {
            // Render placeholder bars only for part-to-whole variant
            if (data.chartData.length === 0) {
                bars.push(React.createElement("g", { key: 0, className: this._classNames.noData, onClick: this._redirectToUrl.bind(this, href) },
                    React.createElement("rect", { key: 0, id: this._barIdPlaceholderPartToWhole, x: '0%', y: 0, width: '100%', height: barHeight, fill: palette.neutralLight, rx: this.props.roundCorners ? 3 : 0 })));
            }
            if (barTotalValue === 0) {
                bars.push(React.createElement("g", { key: 'empty', className: this._classNames.noData, onClick: this._redirectToUrl.bind(this, href) },
                    React.createElement("rect", { key: 0, id: this._barIdEmpty, x: '0%', y: 0, width: '100%', height: barHeight, fill: palette.neutralLight, rx: this.props.roundCorners ? 3 : 0 })));
            }
        }
        var hideNumber = hideRatio === undefined ? false : hideRatio;
        // Hide right side text of chart title for absolute-scale variant
        var showRatio = this.props.variant !== index_2.MultiStackedBarChartVariant.AbsoluteScale && !hideNumber && data.chartData.length === 2;
        var showNumber = this.props.variant !== index_2.MultiStackedBarChartVariant.AbsoluteScale && !hideNumber && data.chartData.length === 1;
        var getChartData = function () { return (0, locale_util_1.convertToLocaleString)(data.chartData[0].data ? data.chartData[0].data : 0, culture); };
        return (React.createElement("div", { className: this._classNames.singleChartRoot },
            React.createElement(react_focus_1.FocusZone, { direction: react_focus_1.FocusZoneDirection.horizontal },
                React.createElement("div", { className: this._classNames.chartTitle },
                    data.chartTitle && (React.createElement(FocusableTooltipText_1.FocusableTooltipText, { className: this._classNames.chartTitleLeft, content: data.chartTitle, accessibilityData: data.chartTitleAccessibilityData })),
                    showRatio && (React.createElement("div", tslib_1.__assign({}, (0, index_3.getAccessibleDataObject)(data.chartDataAccessibilityData, 'text', false)),
                        React.createElement("span", { className: this._classNames.ratioNumerator }, getChartData()),
                        !hideDenominator && (React.createElement("span", { className: this._classNames.ratioDenominator }, ' / ' + (0, locale_util_1.convertToLocaleString)(barTotalValue, culture))))),
                    showNumber && (React.createElement("div", tslib_1.__assign({ className: this._classNames.ratioNumerator }, (0, index_3.getAccessibleDataObject)(data.chartDataAccessibilityData, 'text', false)), getChartData())))),
            React.createElement(react_focus_1.FocusZone, { direction: react_focus_1.FocusZoneDirection.horizontal },
                React.createElement("div", { className: this._classNames.chartWrapper },
                    React.createElement("svg", { ref: this.barChartSvgRef, className: this._classNames.chart, "aria-label": data === null || data === void 0 ? void 0 : data.chartTitle }, bars)))));
    };
    MultiStackedBarChartBase.prototype._onBarFocus = function (pointData, color, point) {
        var _this = this;
        this.state.refArray.forEach(function (obj) {
            if (obj.legendText === point.legend) {
                _this.setState({
                    refSelected: obj.refElement,
                    /** Show the callout if highlighted bar is focused and Hide it if unhighlighted bar is focused */
                    isCalloutVisible: _this.state.selectedLegend === '' || _this.state.selectedLegend === point.legend,
                    calloutLegend: point.legend,
                    dataForHoverCard: pointData,
                    color: color,
                    xCalloutValue: point.xAxisCalloutData,
                    yCalloutValue: point.yAxisCalloutData,
                    dataPointCalloutProps: tslib_1.__assign(tslib_1.__assign({}, point), { color: color }),
                    callOutAccessibilityData: point.callOutAccessibilityData,
                });
            }
        });
    };
    MultiStackedBarChartBase.prototype._refCallback = function (element, legendTitle) {
        this.state.refArray.push({ legendText: legendTitle, refElement: element });
    };
    MultiStackedBarChartBase.prototype._onHover = function (legendTitle) {
        this.setState({
            activeLegend: legendTitle,
        });
    };
    MultiStackedBarChartBase.prototype._onClick = function (legendTitle) {
        if (this.state.selectedLegend === legendTitle) {
            this.setState({
                selectedLegend: '',
            });
        }
        else {
            this.setState({
                selectedLegend: legendTitle,
            });
        }
    };
    MultiStackedBarChartBase.prototype._onLeave = function () {
        this.setState({
            activeLegend: '',
        });
    };
    MultiStackedBarChartBase.prototype._onBarHover = function (pointData, color, point, mouseEvent) {
        mouseEvent.persist();
        if (this._calloutAnchorPoint !== point) {
            this._calloutAnchorPoint = point;
            this.setState({
                refSelected: mouseEvent,
                /** Show the callout if highlighted bar is hovered and Hide it if unhighlighted bar is hovered */
                isCalloutVisible: this.state.selectedLegend === '' || this.state.selectedLegend === point.legend,
                calloutLegend: point.legend,
                dataForHoverCard: pointData,
                color: color,
                xCalloutValue: point.xAxisCalloutData,
                yCalloutValue: point.yAxisCalloutData,
                dataPointCalloutProps: tslib_1.__assign(tslib_1.__assign({}, point), { color: color }),
                callOutAccessibilityData: point.callOutAccessibilityData,
            });
        }
    };
    MultiStackedBarChartBase.prototype._onBarLeave = function () {
        /**/
    };
    MultiStackedBarChartBase.prototype._redirectToUrl = function (href) {
        href ? (window.location.href = href) : '';
    };
    MultiStackedBarChartBase.prototype._isChartEmpty = function () {
        return !(this.props.data &&
            this.props.data.length > 0 &&
            this.props.data.filter(function (item) { return item.chartData && item.chartData.length === 0 && (!item.chartTitle || item.chartTitle === ''); }).length === 0);
    };
    MultiStackedBarChartBase.defaultProps = {
        barHeight: 12,
        hideRatio: [],
        hideDenominator: [],
    };
    return MultiStackedBarChartBase;
}(React.Component));
exports.MultiStackedBarChartBase = MultiStackedBarChartBase;
//# sourceMappingURL=MultiStackedBarChart.base.js.map