var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
import * as classnames from "classnames";
import * as React from "react";
import * as PropTypes from "prop-types";
import { Sticky, StickyContainer } from "react-sticky";
import * as _ from "lodash";
import WizardStep from "./WizardStep";
import { Button, ProgressBar } from "../";
import { classNameFor } from "../utils";
import "./Wizard.less";
// this is a function so that we get a new array each time
var getInitialState = function () { return ({
    currentStep: 0,
    percentComplete: 0,
    stepsVisited: [0],
}); };
var propTypes = {
    className: PropTypes.string,
    style: PropTypes.object,
    title: PropTypes.string.isRequired,
    description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired,
    help: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
    wizardButtons: PropTypes.arrayOf(PropTypes.shape({
        handler: PropTypes.func.isRequired,
        buttonValue: PropTypes.node.isRequired,
        buttonClassName: PropTypes.string,
    })),
    initialWizardData: PropTypes.object,
    onComplete: PropTypes.func.isRequired,
    steps: PropTypes.arrayOf(PropTypes.shape({
        title: PropTypes.string.isRequired,
        description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
        component: PropTypes.oneOfType([PropTypes.func, PropTypes.instanceOf(React.Component)])
            .isRequired,
        nextButtonValue: PropTypes.oneOfType([PropTypes.string, PropTypes.node, PropTypes.func]),
        validate: PropTypes.func.isRequired,
        canContinue: PropTypes.func,
        help: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
        props: PropTypes.object,
        onStepComplete: PropTypes.func,
    })).isRequired,
    initialStep: PropTypes.number,
    nextButtonValue: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
    prevButtonValue: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
    seekable: PropTypes.bool,
    hideProgressBar: PropTypes.bool,
    stickySidebar: PropTypes.bool,
    stepNumberInTitle: PropTypes.bool,
};
var defaultProps = {
    stepNumberInTitle: true,
};
var Wizard = /** @class */ (function (_super) {
    __extends(Wizard, _super);
    function Wizard(props) {
        var _this = _super.call(this, props) || this;
        _this.state = Object.assign({}, getInitialState(), {
            data: props.initialWizardData || {},
        });
        _this.reset = _this.reset.bind(_this);
        _this.prevStepHandler = _this.prevStepHandler.bind(_this);
        _this.nextStepHandler = _this.nextStepHandler.bind(_this);
        _this.calculatePercentComplete = _this.calculatePercentComplete.bind(_this);
        // Launch the wizard at a specific step (e.g. to resume at furthest step)
        if (_this.props.initialStep) {
            var idx = _this.props.initialStep;
            Object.assign(_this.state, {
                currentStep: idx,
                stepsVisited: _.range(idx + 1),
                percentComplete: _this.calculatePercentComplete(),
            });
        }
        return _this;
    }
    Wizard.prototype.reset = function () {
        this.setState(Object.assign({}, getInitialState(), { data: {} }));
    };
    Wizard.prototype.jumpToStep = function (idx) {
        this.setState({
            currentStep: idx,
            stepsVisited: _.union(this.state.stepsVisited, [idx]),
            percentComplete: this.calculatePercentComplete(),
        });
    };
    Wizard.prototype.prevStepHandler = function () {
        var steps = this.props.steps;
        var prevStep = Math.max(0, this.state.currentStep - 1);
        if (steps[prevStep].shouldSkipStep && steps[prevStep].shouldSkipStep(this.state.data)) {
            prevStep = Math.max(0, prevStep - 1);
        }
        this.jumpToStep(prevStep);
    };
    Wizard.prototype.nextStepHandler = function () {
        var _this = this;
        var steps = this.props.steps;
        var _a = this.state, currentStep = _a.currentStep, data = _a.data;
        // If an onStepComplete handler is provided for the current step, we will only proceed to the
        // next step if the promise returned by the handler resolves to a truthy value.
        if (steps[currentStep].onStepComplete) {
            steps[currentStep].onStepComplete(data).then(function (success) { return success && _this.goToNextStep(); });
            return;
        }
        this.goToNextStep();
    };
    Wizard.prototype.goToNextStep = function () {
        var _a = this.props, onComplete = _a.onComplete, steps = _a.steps;
        var _b = this.state, currentStep = _b.currentStep, data = _b.data;
        var nextStep = currentStep + 1;
        while (nextStep < steps.length &&
            steps[nextStep].shouldSkipStep &&
            steps[nextStep].shouldSkipStep(this.state.data)) {
            nextStep++;
        }
        if (nextStep === steps.length) {
            onComplete(data);
            return;
        }
        this.jumpToStep(nextStep);
    };
    Wizard.prototype.calculatePercentComplete = function (data) {
        if (data === void 0) { data = this.state.data; }
        var validSteps = this.props.steps.filter(function (step) { return step.validate(data); });
        return validSteps.length / this.props.steps.length;
    };
    Wizard.prototype._renderSidebar = function (style) {
        var _this = this;
        var _a = this.props, title = _a.title, description = _a.description, wizardButtons = _a.wizardButtons, steps = _a.steps, seekable = _a.seekable, hideProgressBar = _a.hideProgressBar, className = _a.className;
        var baseClasses = ["Wizard", className];
        return (React.createElement("div", { style: style, className: classNameFor(baseClasses, "sidebarContent") },
            React.createElement("h2", null, title),
            _.isString(description) ? (React.createElement("p", { className: classNameFor(baseClasses, "description") }, description)) : (React.createElement("div", { className: classNameFor(baseClasses, "description") }, description)),
            !hideProgressBar && React.createElement(ProgressBar, { percentage: this.state.percentComplete }),
            React.createElement("ul", { className: classNameFor(baseClasses, "stepsDisplay") }, steps.map(function (step, idx) {
                var stepValid = step.validate(_this.state.data);
                var stepVisited = _.includes(_this.state.stepsVisited, idx);
                var stepClassName = classnames(classNameFor(baseClasses, ["stepsDisplay", "step"]), idx === _this.state.currentStep &&
                    classNameFor(baseClasses, ["stepsDisplay", "currentStep"]), stepValid && classNameFor(baseClasses, ["stepsDisplay", "valid"]), stepVisited && classNameFor(baseClasses, ["stepsDisplay", "visited"]), seekable && classNameFor(baseClasses, ["stepsDisplay", "stepLink"]));
                var listValue = (React.createElement("span", { className: stepClassName },
                    React.createElement("span", { className: classNameFor(baseClasses, ["stepsDisplay", "icon"]) }),
                    React.createElement("span", { className: classNameFor(baseClasses, ["stepsDisplay", "stepTitle"]) }, step.title)));
                return (React.createElement("li", { key: idx }, seekable ? (React.createElement(Button, { className: classNameFor(baseClasses, ["stepsDisplay", "stepButton"]), type: "link", onClick: function () { return _this.jumpToStep(idx); }, value: listValue })) : (listValue)));
            })),
            wizardButtons && (React.createElement("div", { className: classNameFor(baseClasses, "controls") }, wizardButtons.map(function (btnSpec, idx) { return (React.createElement(Button, { key: idx, onClick: function () { return btnSpec.handler(_this.state.data, { resetWizard: _this.reset }); }, value: btnSpec.buttonValue, className: classNameFor(baseClasses, ["controls", "control"]) })); })))));
    };
    Wizard.prototype.render = function () {
        var _this = this;
        var _a = this.props, className = _a.className, style = _a.style, help = _a.help, steps = _a.steps, nextButtonValue = _a.nextButtonValue, prevButtonValue = _a.prevButtonValue, stickySidebar = _a.stickySidebar, stepNumberInTitle = _a.stepNumberInTitle;
        var baseClasses = ["Wizard", className].filter(function (c) { return !!c; });
        var curStep = steps[this.state.currentStep];
        var validSteps = steps.filter(function (step) { return step.validate(_this.state.data); });
        // If on the last step, cannot click next (i.e. complete) unless the whole form is valid; for
        // all other steps, only the current step needs to be valid.
        var nextDisabled;
        if (curStep.canContinue != null) {
            nextDisabled = !curStep.canContinue(this.state.data);
        }
        else {
            nextDisabled =
                this.state.currentStep === steps.length - 1
                    ? validSteps.length !== steps.length
                    : !curStep.validate(this.state.data);
        }
        return (React.createElement(StickyContainer, { className: classnames(baseClasses), style: style },
            React.createElement("div", { className: classNameFor(baseClasses, "sidebar") }, !stickySidebar ? (this._renderSidebar()) : (React.createElement(Sticky, { topOffset: 24 }, function (_a) {
                var sidebarStyle = _a.style;
                return _this._renderSidebar(sidebarStyle);
            }))),
            React.createElement("div", { className: classNameFor(baseClasses, "step") },
                React.createElement(WizardStep, { className: className, Component: steps[this.state.currentStep].component, componentProps: steps[this.state.currentStep].props, stepNumberInTitle: stepNumberInTitle, setWizardState: function (changes) {
                        var newState = Object.assign(_this.state.data, changes);
                        _this.setState({ data: newState });
                        return newState;
                    }, wizardState: this.state.data, updatePercentComplete: function (wizardState) {
                        return _this.setState({
                            percentComplete: _this.calculatePercentComplete(wizardState),
                        });
                    }, calculatePercentComplete: this.calculatePercentComplete, percentComplete: this.state.percentComplete, totalSteps: steps.length, title: curStep.title, description: curStep.description, currentStep: this.state.currentStep, help: curStep.help ? curStep.help : help, validate: curStep.validate }),
                React.createElement("div", { className: classnames(classNameFor(baseClasses, "contentGroup"), classNameFor(baseClasses, "navButtons")) },
                    this.state.currentStep !== 0 && (React.createElement(Button, { className: classNameFor(baseClasses, "prevButton"), type: "link", onClick: this.prevStepHandler, value: curStep.prevButtonValue || prevButtonValue || "Back" })),
                    React.createElement(Button, { className: classNameFor(baseClasses, "nextButton"), onClick: this.nextStepHandler, disabled: nextDisabled, type: "primary", value: typeof curStep.nextButtonValue === "function"
                            ? curStep.nextButtonValue(this.state.data)
                            : curStep.nextButtonValue || nextButtonValue || "Next" })))));
    };
    Wizard.propTypes = propTypes;
    Wizard.defaultProps = defaultProps;
    return Wizard;
}(React.Component));
export { Wizard };
