<!--
    A simple position:sticky container that adds stuck / unstuck events and classes as the item
    becomes stuck / unstuck.
-->
<template>
    <div
        :class="[bem({ disabled: !enabled }), stuck ? stuckClass : unstuckClass]"
        :style="elementStyle"
    >
        <div v-show="enabled" ref="sentinel" :class="bem('sentinel')" :style="sentinelStyle"></div>
        <slot :stuck="stuck" />
    </div>
</template>

<script>
export default {
    name: 'px-sticky',
    props: {
        enabled: { type: Boolean, default: true },
        mode: { type: String, default: 'top' },
        offset: { type: Number, default: 0 },
        overlap: { type: Number, default: 1 },
        z: { type: Number, default: 1 },
        stuckClass: { type: String, default: '' },
        unstuckClass: { type: String, default: '' },
    },
    data() {
        return {
            stuck: false,
            stuckScrollY: null,
        };
    },
    computed: {
        elementStyle() {
            if (this.enabled) {
                const style = { position: 'sticky', zIndex: this.z };
                style[this.mode] = this.cssPx(this.offset);
                return style;
            }

            return null;
        },
        sentinelStyle() {
            if (this.enabled) {
                const style = { height: this.cssPx(this.offset + this.overlap) };
                style[this.mode] = this.cssPx(-(this.offset + this.overlap));
                return style;
            }
            return null;
        },
    },
    watch: {
        enabled() {
            this.updateConnections();
        },
        stuck(nv) {
            if (nv) {
                this.$emit('stuck');
            } else {
                this.$emit('unstuck');
            }
        },
    },
    mounted() {
        this.updateConnections();
    },
    beforeUnmount() {
        this.tryDisconnect();
    },
    methods: {
        updateConnections() {
            if (this.enabled) {
                this.tryConnect();
            } else {
                this.tryDisconnect();
            }
        },
        tryConnect() {
            if (!this.observer) {
                try {
                    this.observer = new IntersectionObserver(
                        ([e]) => {
                            this.stuck = e.intersectionRatio < 1;
                        },
                        { threshold: [1] },
                    );
                    this.observer.observe(this.$refs.sentinel);
                } catch (e) {
                    console.warn('Cound not create css sticky detector.');
                    console.warn(e);
                }
            }
        },
        tryDisconnect() {
            this.stuck = false;
            if (this.observer) {
                this.observer.disconnect();
                this.observer = null;
            }
        },
    },
};
</script>

<style lang="scss">
.px-sticky {
    position: relative;

    &:not(.px-sticky--disabled) {
        position: sticky !important;
    }

    &__sentinel {
        position: absolute;
        left: 0;
        right: 0; /* needs dimensions */
        visibility: hidden;
        opacity: 0;

        // DEBUG
        // visibility: visible;
        // opacity: 0.9;
        // background-color: lime;
        // z-index: 10000000;
    }
}
</style>
