var _a;
import { __assign } from "tslib";
import { withThemeableProps } from '../../useThemeable';
import { classNamesFunction, css, elementContains } from '@uifabric-vue/utilities';
import { AnimationClassNames } from '@uifabric/styling';
import Vue from 'vue';
import { DirectionalHint } from '../../common/DirectionalHint';
import { getBoundsFromTargetWindow, getMaxHeight, positionCallout, RectangleEdge } from '../../utilities/positioning';
import { Popup } from '../Popup';
var ANIMATIONS = (_a = {},
    _a[RectangleEdge.top] = AnimationClassNames.slideUpIn10,
    _a[RectangleEdge.bottom] = AnimationClassNames.slideDownIn10,
    _a[RectangleEdge.left] = AnimationClassNames.slideLeftIn10,
    _a[RectangleEdge.right] = AnimationClassNames.slideRightIn10,
    _a);
var BEAK_ORIGIN_POSITION = { top: 0, left: 0 };
var OFF_SCREEN_STYLE = {
    opacity: 0,
    filter: 'opacity(0)',
    pointerEvents: 'none',
};
var getClassNames = classNamesFunction({
    disableCaching: true, // disabling caching because stylesProp.position mutates often
});
export var CalloutContentBase = Vue.extend({
    name: 'CalloutContentBase',
    inheritAttrs: false,
    props: __assign(__assign({}, withThemeableProps()), { target: { type: [HTMLElement, Object], required: true }, calloutWidth: { type: Number, default: null }, beakWidth: { type: Number, default: 16 }, minPagePadding: { type: Number, default: 8 }, isBeakVisible: { type: Boolean, default: true }, coverTarget: { type: Boolean, default: false }, gapSpace: { type: Number, default: 0 }, finalHeight: { type: Number, default: undefined }, calloutMaxWidth: { type: Number, default: undefined }, calloutMinWidth: { type: Number, default: undefined }, calloutMaxHeight: { type: Number, default: undefined }, backgroundColor: { type: String, default: undefined }, directionalHint: { type: Number, default: DirectionalHint.bottomAutoEdge }, directionalHintFixed: { type: Boolean, default: false }, overflowYHidden: { type: Boolean, default: false }, doNotLayer: { type: Boolean, default: false }, preventDismissOnScroll: { type: Boolean, default: false }, preventDismissOnResize: { type: Boolean, default: false }, preventDismissOnLostFocus: { type: Boolean, default: false }, dismissOnTargetClick: { type: Boolean, default: false }, repositionOnChildrenUpdated: { type: Boolean, default: false } }),
    data: function () {
        return {
            positions: undefined,
            maxHeight: 0,
            blockResetHeight: false,
            heightOffset: 0,
            positionAttempts: 0,
        };
    },
    computed: {
        beakVisible: function () {
            return this.isBeakVisible && !!this.target;
        },
        targetRef: function () {
            return this.target instanceof HTMLElement ? this.target : this.target.$el;
        },
        classNames: function () {
            var _a = this, theme = _a.theme, className = _a.className, calloutMaxWidth = _a.calloutMaxWidth, calloutMinWidth = _a.calloutMinWidth, backgroundColor = _a.backgroundColor, positions = _a.positions, styles = _a.styles, calloutWidth = _a.calloutWidth, beakWidth = _a.beakWidth, doNotLayer = _a.doNotLayer, overflowYHidden = _a.overflowYHidden;
            return getClassNames(styles, {
                theme: theme,
                className: className,
                overflowYHidden: overflowYHidden,
                calloutWidth: calloutWidth,
                positions: positions,
                beakWidth: beakWidth,
                backgroundColor: backgroundColor,
                calloutMaxWidth: calloutMaxWidth,
                calloutMinWidth: calloutMinWidth,
                doNotLayer: doNotLayer,
            });
        },
        overflowStyle: function () {
            return __assign(__assign({ maxHeight: this.calloutMaxHeight || '100%' }, this.style), (this.overflowYHidden && { overflowY: 'hidden' }));
        },
        visibilityStyle: function () {
            return this.hidden ? { visibility: 'hidden' } : undefined;
        },
        actualBeakWidth: function () {
            return Math.sqrt(this.beakWidth * this.beakWidth * 2);
        },
        positionCss: function () {
            if (!this.positions)
                return null;
            // TODO remove JSON parse/stringify deep clone hack
            var positionCss = JSON.parse(JSON.stringify(this.positions));
            for (var key in positionCss.elementPosition) {
                positionCss.elementPosition[key] = "".concat(positionCss.elementPosition[key], "px");
            }
            for (var key in positionCss.beakPosition.elementPosition) {
                positionCss.beakPosition.elementPosition[key] = "".concat(positionCss.beakPosition.elementPosition[key], "px");
            }
            return positionCss;
        },
        bounds: function () {
            var currentBounds = getBoundsFromTargetWindow(this.targetRef, window);
            return {
                top: currentBounds.top + this.minPagePadding,
                left: currentBounds.left + this.minPagePadding,
                right: currentBounds.right - this.minPagePadding,
                bottom: currentBounds.bottom - this.minPagePadding,
                width: currentBounds.width - this.minPagePadding * 2,
                height: currentBounds.height - this.minPagePadding * 2,
            };
        },
        beakPosition: function () {
            var _a, _b, _c, _d;
            var beakPositionStyle = __assign(__assign({}, (_b = (_a = this.positionCss) === null || _a === void 0 ? void 0 : _a.beakPosition) === null || _b === void 0 ? void 0 : _b.elementPosition), { display: ((_d = (_c = this.positions) === null || _c === void 0 ? void 0 : _c.beakPosition) === null || _d === void 0 ? void 0 : _d.hideBeak) ? 'none' : undefined });
            if (!beakPositionStyle.top && !beakPositionStyle.bottom && !beakPositionStyle.left && !beakPositionStyle.right) {
                beakPositionStyle.left = BEAK_ORIGIN_POSITION.left;
                beakPositionStyle.top = BEAK_ORIGIN_POSITION.top;
            }
            return beakPositionStyle;
        },
        positionsExists: function () {
            return !!this.positions;
        },
    },
    created: function () {
        window.addEventListener('click', this.dismissOnClickOrScroll, true);
        window.addEventListener('scroll', this.dismissOnScroll, true);
    },
    beforeDestroy: function () {
        window.removeEventListener('click', this.dismissOnClickOrScroll, true);
        window.removeEventListener('scroll', this.dismissOnScroll, true);
    },
    mounted: function () {
        var _this = this;
        this.updatePosition();
        if (this.repositionOnChildrenUpdated && this.$refs.calloutMain) {
            var currentHeight_1 = this.$refs.calloutMain.clientHeight;
            var observer = new MutationObserver(function () {
                if (_this.$refs.calloutMain.clientHeight === currentHeight_1)
                    return;
                currentHeight_1 = _this.$refs.calloutMain.clientHeight;
                _this.updatePosition();
            });
            observer.observe(this.$refs.calloutMain, { attributes: true, childList: true, subtree: true });
        }
    },
    methods: {
        updatePosition: function () {
            var positions = this.positions;
            var currentProps = __assign({}, this.$props);
            currentProps.bounds = this.bounds;
            currentProps.target = this.targetRef;
            var newPositions = positionCallout(currentProps, this.$refs.rootRef, this.$refs.calloutElement, this.positions);
            if ((!positions && newPositions) ||
                (positions && newPositions && !arePositionsEqual(positions, newPositions) && this.positionAttempts < 5)) {
                this.positionAttempts++;
                this.positions = newPositions;
            }
            else if (this.positionAttempts > 0) {
                this.positionAttempts = 0;
                this.$emit('positioned', this.positions);
            }
        },
        dismissOnScroll: function (ev) {
            if (this.positionsExists && !this.preventDismissOnScroll) {
                this.dismissOnClickOrScroll(ev);
            }
        },
        dismissOnResize: function (ev) {
            if (!this.preventDismissOnResize && !(this.preventDismissOnEvent && this.preventDismissOnEvent(ev))) {
                this.$emit('dismiss', ev);
            }
        },
        dismissOnLostFocus: function (ev) {
            if (!this.preventDismissOnLostFocus) {
                this.dismissOnClickOrScroll(ev);
            }
        },
        dismissOnClickOrScroll: function (ev) {
            var eventPaths = ev.composedPath ? ev.composedPath() : [];
            var target = eventPaths.length > 0 ? eventPaths[0] : ev.target;
            var isEventTargetOutsideCallout = this.$refs.rootRef && !elementContains(this.$refs.rootRef, target);
            if ((!this.targetRef && isEventTargetOutsideCallout) ||
                (isEventTargetOutsideCallout &&
                    (!this.targetRef ||
                        (target !== this.targetRef && !elementContains(this.targetRef, target))))) {
                this.$emit('dismiss', ev);
            }
        },
        // Max height should remain as synchronous as possible, which is why it is not done using set state.
        // It needs to be synchronous since it will impact the ultimate position of the callout.
        getMaxHeight: function () {
            var _this = this;
            if (!this.maxHeight) {
                if (this.directionalHintFixed && this.target) {
                    var beakWidth = this.isBeakVisible ? this.beakWidth : 0;
                    var gapSpace = this.gapSpace ? this.gapSpace : 0;
                    // Since the callout cannot measure it's border size it must be taken into account here. Otherwise it will
                    // overlap with the target.
                    var totalGap_1 = gapSpace + beakWidth;
                    this._async.requestAnimationFrame(function () {
                        if (_this.target) {
                            _this.maxHeight = getMaxHeight(_this.targetRef, _this.directionalHint, totalGap_1, _this.bounds, _this.coverTarget);
                            _this.blockResetHeight = true;
                            _this.$forceUpdate();
                        }
                    }, this.targetRef);
                }
                else {
                    this.maxHeight = this.bounds.height;
                }
            }
            return this.maxHeight;
        },
    },
    render: function (h) {
        var _this = this;
        var _a = this, ariaLabel = _a.ariaLabel, ariaDescribedBy = _a.ariaDescribedBy, ariaLabelledBy = _a.ariaLabelledBy, role = _a.role, ariaRoledescription = _a.ariaRoledescription, classNames = _a.classNames, positionCss = _a.positionCss, positions = _a.positions;
        return h('div', {
            ref: 'rootRef',
            class: classNames.container,
            style: this.visibilityStyle,
        }, [
            h('div', {
                ref: 'calloutElement',
                class: css(classNames.root, positions && positions.targetEdge && ANIMATIONS[positions.targetEdge]),
                style: positions ? positionCss.elementPosition : OFF_SCREEN_STYLE,
                attrs: {
                    tabIndex: -1,
                },
            }, [
                this.beakVisible && h('div', { class: classNames.beak, style: this.beakPosition }),
                this.beakVisible && h('div', { class: classNames.beakCurtain }),
                h(Popup, {
                    ref: 'calloutMain',
                    class: classNames.calloutMain,
                    props: {
                        role: role,
                        ariaRoledescription: ariaRoledescription,
                        ariaDescribedBy: ariaDescribedBy,
                        ariaLabel: ariaLabel,
                        ariaLabelledBy: ariaLabelledBy,
                        propStyle: this.overflowStyle,
                        forwardRef: this.$refs.calloutMain,
                    },
                    on: {
                        dismiss: function (ev) { return _this.$emit('dismiss', ev); },
                    },
                }, this.$slots.default),
            ]),
        ]);
    },
});
/**
 * (Utility) used to compare two different elementPositions to determine whether they are equal.
 *
 * @param prevElementPositions
 * @param newElementPosition
 */
function arePositionsEqual(prevElementPositions, newElementPosition) {
    return (comparePositions(prevElementPositions.elementPosition, newElementPosition.elementPosition) &&
        comparePositions(prevElementPositions.beakPosition.elementPosition, newElementPosition.beakPosition.elementPosition));
}
/**
 * (Utility) used in **arePositionsEqual** to compare two different elementPositions.
 *
 * @param prevElementPositions
 * @param newElementPositions
 */
function comparePositions(prevElementPositions, newElementPositions) {
    for (var key in newElementPositions) {
        if (Object.prototype.hasOwnProperty.call(newElementPositions, key)) {
            var oldPositionEdge = prevElementPositions[key];
            var newPositionEdge = newElementPositions[key];
            if (oldPositionEdge !== undefined && newPositionEdge !== undefined) {
                if (oldPositionEdge.toFixed(2) !== newPositionEdge.toFixed(2)) {
                    return false;
                }
            }
            else {
                return false;
            }
        }
    }
    return true;
}
//# sourceMappingURL=CalloutContent.base.js.map