"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var react_1 = tslib_1.__importDefault(require("react"));
var Scoped_1 = require("../Scoped");
var factory_1 = require("../factory");
var service_1 = require("../store/service");
var tpl_1 = require("../utils/tpl");
var cx = require("classnames");
var helper_1 = require("../utils/helper");
var api_1 = require("../utils/api");
var Wizard = /** @class */ (function (_super) {
    tslib_1.__extends(Wizard, _super);
    function Wizard(props) {
        var _this = _super.call(this, props) || this;
        _this.state = {
            currentStep: -1,
        };
        _this.handleAction = _this.handleAction.bind(_this);
        _this.handleChange = _this.handleChange.bind(_this);
        _this.handleSubmit = _this.handleSubmit.bind(_this);
        _this.handleDialogConfirm = _this.handleDialogConfirm.bind(_this);
        _this.handleDialogClose = _this.handleDialogClose.bind(_this);
        _this.formRef = _this.formRef.bind(_this);
        _this.domRef = _this.domRef.bind(_this);
        _this.getPopOverContainer = _this.getPopOverContainer.bind(_this);
        return _this;
    }
    Wizard.prototype.componentDidMount = function () {
        var _this = this;
        var _a = this.props, initApi = _a.initApi, initFetch = _a.initFetch, initAsyncApi = _a.initAsyncApi, initFinishedField = _a.initFinishedField, store = _a.store, data = _a.data, _b = _a.messages, fetchSuccess = _b.fetchSuccess, fetchFailed = _b.fetchFailed, onInit = _a.onInit;
        if (initApi && initFetch !== false && (!initApi.sendOn || tpl_1.evalExpression(initApi.sendOn, data))) {
            store
                .fetchInitData(initApi, store.data, {
                successMessage: fetchSuccess,
                errorMessage: fetchFailed,
                onSuccess: function () {
                    if (!initAsyncApi || store.data[initFinishedField || 'finished']) {
                        return;
                    }
                    return helper_1.until(function () { return store.checkRemote(initAsyncApi, store.data); }, function (ret) { return ret && ret[initFinishedField || 'finished']; }, function (cancel) { return (_this.asyncCancel = cancel); });
                },
            })
                .then(function (value) {
                onInit && onInit(store.data);
                var state = {
                    currentStep: 1,
                };
                if (value && value.data
                    && (typeof value.data.step === 'number'
                        || typeof value.data.step === 'string' && /^\d+$/.test(value.data.step))) {
                    state.currentStep = parseInt(value.data.step, 10);
                }
                _this.setState(state, function () {
                    // 如果 initApi 返回的状态是正在提交，则进入轮顺状态。
                    if (value && value.data && (value.data.submiting || value.data.submited)) {
                        _this.checkSubmit();
                    }
                });
                return value;
            });
        }
        else {
            this.setState({
                currentStep: 1,
            }, function () { return onInit && onInit(store.data); });
        }
    };
    Wizard.prototype.componentDidUpdate = function (prevProps) {
        var props = this.props;
        var store = props.store, fetchSuccess = props.fetchSuccess, fetchFailed = props.fetchFailed;
        if (api_1.isApiOutdated(prevProps.initApi, props.initApi, prevProps.data, props.data)) {
            store.fetchData(props.initApi, store.data, {
                successMessage: fetchSuccess,
                errorMessage: fetchFailed,
            });
        }
    };
    Wizard.prototype.componentWillUnmount = function () {
        this.asyncCancel && this.asyncCancel();
    };
    Wizard.prototype.gotoStep = function (index) {
        var steps = this.props.steps || [];
        index = Math.max(Math.min(steps.length, index), 1);
        this.setState({
            currentStep: index,
        });
    };
    Wizard.prototype.formRef = function (ref) {
        if (ref) {
            while (ref && ref.getWrappedInstance) {
                ref = ref.getWrappedInstance();
            }
            this.form = ref;
        }
        else {
            this.form = undefined;
        }
    };
    Wizard.prototype.submitToTarget = function (target, values) {
        throw new Error('Please implements this!');
    };
    Wizard.prototype.reloadTarget = function (target, data) {
        throw new Error('Please implements this!');
    };
    Wizard.prototype.domRef = function (ref) {
        this.dom = ref;
    };
    Wizard.prototype.getPopOverContainer = function () {
        return this.dom;
    };
    // 用来还原异步提交状态。
    Wizard.prototype.checkSubmit = function () {
        var _this = this;
        var _a = this.props, store = _a.store, steps = _a.steps, asyncApi = _a.asyncApi, finishedField = _a.finishedField, target = _a.target, redirect = _a.redirect, reload = _a.reload, env = _a.env, onFinished = _a.onFinished;
        var step = steps[this.state.currentStep - 1];
        var finnalAsyncApi = step && step.asyncApi || this.state.currentStep === steps.length && asyncApi;
        if (!step || !finnalAsyncApi) {
            return;
        }
        store.markSaving(true);
        store.updateData((_b = {},
            _b[finishedField || 'finished'] = false,
            _b));
        helper_1.until(function () { return store.checkRemote(finnalAsyncApi, store.data); }, function (ret) { return ret && ret[finishedField || 'finished']; }, function (cancel) { return (_this.asyncCancel = cancel); })
            .then(function () {
            store.markSaving(false);
            _this.gotoStep(_this.state.currentStep + 1);
        })
            .catch(function (e) {
            env.notify('error', e.message);
            store.markSaving(false);
        });
        var _b;
    };
    Wizard.prototype.handleAction = function (e, action, data, throwErrors) {
        var _a = this.props, onAction = _a.onAction, store = _a.store;
        if (action.actionType === 'next' || action.type === 'submit') {
            this.form.doAction(tslib_1.__assign({}, action, { actionType: 'submit' }), data);
        }
        else if (action.actionType === 'prev') {
            this.gotoStep(this.state.currentStep - 1);
        }
        else if (action.type === 'reset') {
            this.form.reset();
        }
        else if (action.actionType === 'dialog') {
            store.openDialog(data);
        }
        else if (onAction) {
            onAction(e, action, data);
        }
    };
    Wizard.prototype.handleChange = function (values) {
        var store = this.props.store;
        store.updateData(values);
    };
    // 接管里面 form 的提交，不能直接让 form 提交，因为 wizard 自己需要知道进度。
    Wizard.prototype.handleSubmit = function (values, action) {
        var _this = this;
        var _a = this.props, store = _a.store, steps = _a.steps, api = _a.api, asyncApi = _a.asyncApi, finishedField = _a.finishedField, target = _a.target, redirect = _a.redirect, reload = _a.reload, env = _a.env, onFinished = _a.onFinished;
        var step = steps[this.state.currentStep - 1];
        store.updateData(values);
        if (this.state.currentStep < steps.length) {
            var finnalAsyncApi_1 = action.asyncApi || step.asyncApi;
            finnalAsyncApi_1 &&
                store.updateData((_b = {},
                    _b[finishedField || 'finished'] = false,
                    _b));
            if (step.api || action.api) {
                store
                    .saveRemote(action.api || step.api, store.data, {
                    onSuccess: function () {
                        if (!finnalAsyncApi_1 || store.data[finishedField || 'finished']) {
                            return;
                        }
                        return helper_1.until(function () { return store.checkRemote(finnalAsyncApi_1, store.data); }, function (ret) { return ret && ret[finishedField || 'finished']; }, function (cancel) { return (_this.asyncCancel = cancel); });
                    },
                })
                    .then(function () { return _this.gotoStep(_this.state.currentStep + 1); })
                    .catch(function (e) {
                    // do nothing
                });
            }
            else {
                this.gotoStep(this.state.currentStep + 1);
            }
        }
        else {
            // 最后一步
            if (target) {
                this.submitToTarget(target, store.data);
            }
            else if (action.api || step.api || api) {
                var finnalAsyncApi_2 = action.asyncApi || step.asyncApi || asyncApi;
                finnalAsyncApi_2 &&
                    store.updateData((_c = {},
                        _c[finishedField || 'finished'] = false,
                        _c));
                var formStore = this.form ? this.form.props.store : store;
                store.markSaving(true);
                formStore
                    .saveRemote(action.api || step.api || api, store.data, {
                    onSuccess: function () {
                        if (!finnalAsyncApi_2 || store.data[finishedField || 'finished']) {
                            return;
                        }
                        return helper_1.until(function () { return store.checkRemote(finnalAsyncApi_2, store.data); }, function (ret) { return ret && ret[finishedField || 'finished']; }, function (cancel) { return (_this.asyncCancel = cancel); });
                    },
                })
                    .then(function (value) {
                    store.updateData(tslib_1.__assign({}, store.data, value));
                    store.markSaving(false);
                    if (onFinished && onFinished(value, action) === false) {
                        // 如果是 false 后面的操作就不执行
                        return value;
                    }
                    if (redirect) {
                        env.updateLocation(tpl_1.filter(redirect, store.data));
                    }
                    else if (reload) {
                        _this.reloadTarget(reload, store.data);
                    }
                    return value;
                })
                    .catch(function (e) {
                    store.markSaving(false);
                    console.error(e);
                });
            }
        }
        return false;
        var _b, _c;
    };
    Wizard.prototype.handleDialogConfirm = function (values, action, ctx, targets) {
        var store = this.props.store;
        if (action.mergeData && values.length === 1 && values[0] && targets[0].props.type === 'form') {
            store.updateData(values[0]);
        }
        store.closeDialog();
    };
    Wizard.prototype.handleDialogClose = function () {
        var store = this.props.store;
        store.closeDialog();
    };
    Wizard.prototype.renderSteps = function () {
        var _this = this;
        var _a = this.props, steps = _a.steps, store = _a.store, mode = _a.mode, ns = _a.classPrefix;
        var currentStep = this.state.currentStep;
        return (react_1.default.createElement("div", { className: ns + "Wizard-steps clearfix " + ns + "Wizard--" + mode, id: "form-wizard" }, Array.isArray(steps) && steps.length ? (react_1.default.createElement("ul", null, steps.map(function (step, key) {
            var canJump = isJumpable(step, key, currentStep, store.data);
            return (react_1.default.createElement("li", { key: key, className: cx({
                    'is-complete': canJump,
                    'is-active': currentStep === key + 1,
                }), onClick: function () { return (canJump ? _this.gotoStep(key + 1) : null); } },
                react_1.default.createElement("span", { className: cx('Badge', {
                        // 'Badge--success': canJump && currentStep != key + 1,
                        'Badge--info': currentStep === key + 1 || (canJump && currentStep != key + 1),
                    }) }, key + 1),
                step.title || step.label || "\u7B2C " + (key + 1) + " \u6B65"));
        }))) : null));
    };
    Wizard.prototype.renderActions = function () {
        var _this = this;
        var _a = this.props, steps = _a.steps, store = _a.store, readOnly = _a.readOnly, disabled = _a.disabled, actionClassName = _a.actionClassName, actionPrevLabel = _a.actionPrevLabel, actionNextLabel = _a.actionNextLabel, actionNextSaveLabel = _a.actionNextSaveLabel, actionFinishLabel = _a.actionFinishLabel, render = _a.render, ns = _a.classPrefix, cx = _a.classnames;
        if (!Array.isArray(steps)) {
            return null;
        }
        var currentStepIndex = this.state.currentStep;
        var nextStep = steps[currentStepIndex];
        var prevStep = steps[currentStepIndex - 2];
        var waiting = store.loading;
        var step = steps[currentStepIndex - 1];
        if (!step) {
            return null;
        }
        var prevCanJump = prevStep ? isJumpable(prevStep, currentStepIndex - 2, currentStepIndex, store.data) : false;
        if (step.actions && Array.isArray(step.actions)) {
            return step.actions.length ? (react_1.default.createElement("div", { className: cx('Panel-footer') }, step.actions.map(function (action, index) {
                return render("action/" + index, action, {
                    key: index,
                    onAction: _this.handleAction,
                    disabled: action.disabled ||
                        waiting ||
                        disabled ||
                        (action.actionType === 'prev' && !prevCanJump) ||
                        (action.actionType === 'next' && readOnly && (!!step.api || !nextStep)),
                });
            }))) : null;
        }
        return (react_1.default.createElement("div", { className: cx('Panel-footer') },
            render("prev-btn", {
                type: 'button',
                label: actionPrevLabel,
                actionType: 'prev',
                className: actionClassName,
            }, {
                disabled: waiting || !prevCanJump || disabled,
                onAction: this.handleAction,
            }),
            render("next-btn", {
                type: 'button',
                label: !nextStep ? actionFinishLabel : !step.api ? actionNextLabel : actionNextSaveLabel,
                actionType: 'next',
                primary: !nextStep || !!step.api,
                className: actionClassName,
            }, {
                disabled: waiting || disabled || (readOnly && (!!step.api || !nextStep)),
                onAction: this.handleAction,
            })));
    };
    Wizard.prototype.render = function () {
        var _a = this.props, className = _a.className, steps = _a.steps, render = _a.render, store = _a.store, mode = _a.mode, ns = _a.classPrefix;
        var currentStep = this.state.currentStep;
        var step = Array.isArray(steps) ? steps[currentStep - 1] : null;
        return (react_1.default.createElement("div", { ref: this.domRef, className: cx(ns + "Panel " + ns + "Panel--default " + ns + "Wizard", className) },
            this.renderSteps(),
            react_1.default.createElement("div", { className: ns + "Wizard-stepContent clearfix" }, step ? (render('body', tslib_1.__assign({}, step, { type: 'form', wrapWithPanel: false, 
                // 接口相关需要外部来接管
                api: null }), {
                key: this.state.currentStep,
                ref: this.formRef,
                onSubmit: this.handleSubmit,
                onAction: this.handleAction,
                disabled: store.loading,
                popOverContainer: this.getPopOverContainer,
                onChange: this.handleChange,
            })) : currentStep === -1 ? ('初始中。。') : (react_1.default.createElement("p", { className: "text-danger" }, "\u914D\u7F6E\u9519\u8BEF"))),
            render('dialog', tslib_1.__assign({}, (store.action && store.action.dialog), { type: 'dialog' }), {
                key: 'dialog',
                data: store.dialogData,
                onConfirm: this.handleDialogConfirm,
                onClose: this.handleDialogClose,
                show: store.dialogOpen,
            }),
            this.renderActions(),
            store.loading
                ? render('spinner', {
                    type: 'spinner',
                    overlay: true,
                    size: 'lg',
                })
                : null));
    };
    Wizard.defaultProps = {
        mode: 'horizontal',
        readOnly: false,
        messages: {},
        actionClassName: '',
        actionPrevLabel: '上一步',
        actionNextLabel: '下一步',
        actionNextSaveLabel: '保存并下一步',
        actionFinishLabel: '完成',
    };
    Wizard.propsList = [
        'steps',
        'mode',
        'messages',
        'actionClassName',
        'actionPrevLabel',
        'actionNextLabel',
        'actionNextSaveLabel',
        'actionFinishLabel',
        'onFinished',
    ];
    return Wizard;
}(react_1.default.Component));
exports.default = Wizard;
function isJumpable(step, index, currentStep, data) {
    var canJump = false;
    if (step && step.hasOwnProperty('jumpable')) {
        canJump = step.jumpable;
    }
    else if (step && step.jumpableOn) {
        canJump = tpl_1.evalExpression(step.jumpableOn, helper_1.createObject(data, {
            currentStep: currentStep,
        }));
    }
    else {
        canJump = index + 1 < currentStep;
    }
    return canJump;
}
var WizardRenderer = /** @class */ (function (_super) {
    tslib_1.__extends(WizardRenderer, _super);
    function WizardRenderer() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    WizardRenderer.prototype.componentWillMount = function () {
        var scoped = this.context;
        scoped.registerComponent(this);
    };
    WizardRenderer.prototype.componentWillUnmount = function () {
        var scoped = this.context;
        scoped.unRegisterComponent(this);
    };
    WizardRenderer.prototype.doAction = function (action, data, throwErrors) {
        if (throwErrors === void 0) { throwErrors = false; }
        return this.handleAction(undefined, action, data, throwErrors);
    };
    WizardRenderer.prototype.submitToTarget = function (target, values) {
        var scoped = this.context;
        scoped.send(target, values);
    };
    WizardRenderer.prototype.reloadTarget = function (target, data) {
        var scoped = this.context;
        scoped.reload(target, data);
    };
    WizardRenderer.prototype.handleDialogConfirm = function (values, action, ctx, targets) {
        _super.prototype.handleDialogConfirm.call(this, values, action, ctx, targets);
        var store = this.props.store;
        var scoped = this.context;
        if (action.reload) {
            scoped.reload(action.reload, store.data);
        }
        else if (store.action && store.action.reload) {
            scoped.reload(store.action.reload, store.data);
        }
    };
    WizardRenderer.contextType = Scoped_1.ScopedContext;
    WizardRenderer = tslib_1.__decorate([
        factory_1.Renderer({
            test: /(^|\/)wizard$/,
            storeType: service_1.ServiceStore.name,
            name: 'wizard',
            isolateScope: true,
        })
    ], WizardRenderer);
    return WizardRenderer;
}(Wizard));
exports.WizardRenderer = WizardRenderer;
//# sourceMappingURL=./renderers/Wizard.js.map
