Source: pdf/mixins/resizable.js

/**
 * @desc A mixin object containing methods for resizing a component.
 * @module ResizableMixin
 */

// const DEBUG = false;
const DEBUG = true;

export const ResizableMixin = {
    data() {
        return {
            activeHandlerName: null,
            startX: 0,
            startY: 0,
            startLeft: 0,
            startTop: 0,
            startWidth: 0,
            startHeight: 0,
            // lastDifference: null,
            lastPosition: null,
            lastSize: null,
            startClientRect: null,
        };
    },
    computed: {
        // TODO: check if it is enough to use
        // these computed properties as data properties
        // props are updated from parent component
        usingResizeGrid() {
            const [gridX, gridY] = this.getAbsoluteResizeGrid();
            return gridX > 0 || gridY > 0;
        },
        minimumWidthOnGrid() {
            const gridX = this.getAbsoluteResizeGrid()[0];
            const minimumWidth = this.getMinimumWidth();
            if (gridX === 0) {
                return minimumWidth;
            }
            return Math.ceil(minimumWidth / gridX) * gridX;
        },
        minimumHeightOnGrid() {
            const gridY = this.getAbsoluteResizeGrid()[1];
            const minimumHeight = this.getMinimumHeight();
            if (gridY === 0) {
                return minimumHeight;
            }
            return Math.ceil(minimumHeight / gridY) * gridY;
        },
    },
    methods: {
        // getClientPositionInsideContainer(clientX, clientY) {
        //     const containerClientRect = this.getContainerClientRect();
        //     const clientXInsideContainer = Math.max(
        //         containerClientRect.left,
        //         Math.min(containerClientRect.right, clientX)
        //     );
        //     const clientYInsideContainer = Math.max(
        //         containerClientRect.top,
        //         Math.min(containerClientRect.bottom, clientY)
        //     );
        //     return [clientXInsideContainer, clientYInsideContainer];
        // },

        getSizeWithRatio({ width, height }, fitMethod = 'fill') {
            if (this.startWidth === 0 || this.startHeight === 0) {
                return { width, height };
            }
            const currentRation = width / height;
            const requiredRatio = this.startWidth / this.startHeight;
            if (fitMethod === 'fill') {
                if (currentRation > requiredRatio) {
                    height = width / requiredRatio;
                } else {
                    width = height * requiredRatio;
                }
            } else if (fitMethod === 'contain') {
                if (currentRation < requiredRatio) {
                    height = width / requiredRatio;
                } else {
                    width = height * requiredRatio;
                }
            }
            return { width, height };
        },

        getSizeComponentOnGrid(sizeComponent, grid, roundMethod) {
            const precision = this.getInternalRoundingPrecision();
            const quotient = this.roundTo(sizeComponent / grid, precision);
            if (roundMethod === 'round') {
                return Math.round(quotient) * grid;
            }
            if (roundMethod === 'floor') {
                return Math.floor(quotient) * grid;
            }
            if (roundMethod === 'ceil') {
                return Math.ceil(quotient) * grid;
            }
        },

        getSizeOnGrid({ width, height }, { roundMethod = 'round' } = {}) {
            const [gridX, gridY] = this.getAbsoluteResizeGrid();
            if (gridX > 0) {
                // width = Math.round(width / gridX) * gridX;
                width = this.getSizeComponentOnGrid(width, gridX, roundMethod);
            }
            if (gridY > 0) {
                // height = Math.round(height / gridY) * gridY;
                height = this.getSizeComponentOnGrid(
                    height,
                    gridY,
                    roundMethod,
                );
            }
            return { width, height };
        },

        getSizeInsideContainer({ width, height }) {
            const [gridX, gridY] = this.getAbsoluteResizeGrid();
            // const containerClientRect = this.getContainerClientRect();
            const containerSize = this.getContainerSize();
            if (
                containerSize === null ||
                (containerSize.width === 0 && containerSize.height === 0)
            ) {
                return { width, height };
            }
            // let heightMaximum = containerClientRect.height;
            let heightMaximum = containerSize.height;
            if (this.activeHandlerName === null) {
                heightMaximum = containerSize.height - this.startTop;
            } else if (this.activeHandlerName.startsWith('t')) {
                // heightMaximum =
                //     this.startClientRect.bottom - containerClientRect.top;
                heightMaximum = this.startTop + this.startHeight;
            } else if (this.activeHandlerName.startsWith('b')) {
                // heightMaximum =
                //     containerClientRect.bottom - this.startClientRect.top;
                heightMaximum = containerSize.height - this.startTop;
            }
            if (gridY > 0) {
                heightMaximum = Math.floor(heightMaximum / gridY) * gridY;
            }
            heightMaximum = Math.max(heightMaximum, this.minimumHeightOnGrid);
            height = Math.min(height, heightMaximum);
            // let widthMaximum = containerClientRect.width;
            let widthMaximum = containerSize.width;
            if (this.activeHandlerName === null) {
                widthMaximum = containerSize.width - this.startLeft;
            } else if (this.activeHandlerName.endsWith('l')) {
                // widthMaximum =
                //     this.startClientRect.right - containerClientRect.left;
                widthMaximum = this.startLeft + this.startWidth;
            } else if (this.activeHandlerName.endsWith('r')) {
                // widthMaximum =
                //     containerClientRect.right - this.startClientRect.left;
                widthMaximum = containerSize.width - this.startLeft;
            }
            if (gridX > 0) {
                widthMaximum = Math.floor(widthMaximum / gridX) * gridX;
            }
            widthMaximum = Math.max(widthMaximum, this.minimumWidthOnGrid);
            width = Math.min(width, widthMaximum);
            return { width, height };
        },

        getCheckedSize(
            width,
            height,
            { roundMethod = 'round', ignoreResizeGrid = false } = {},
        ) {
            width = Math.max(width, this.minimumWidthOnGrid);
            height = Math.max(height, this.minimumHeightOnGrid);
            let size = { width, height };
            if (this.getKeepRatio()) {
                size = this.getSizeWithRatio(size, 'fill');
            }
            if (this.usingResizeGrid && !ignoreResizeGrid) {
                size = this.getSizeOnGrid(size, { roundMethod });
            }
            if (this.isForcedInsideContainer()) {
                size = this.getSizeInsideContainer(size);
            }
            if (this.getKeepRatio()) {
                size = this.getSizeWithRatio(size, 'contain');
            }
            return size;
        },

        getSizeFromDifference(differenceX, differenceY) {
            let resizeDifferenceX, resizeDifferenceY;
            if (this.activeHandlerName.startsWith('t')) {
                resizeDifferenceY = -differenceY;
            } else if (this.activeHandlerName.startsWith('b')) {
                resizeDifferenceY = differenceY;
            } else {
                resizeDifferenceY = 0;
            }
            if (this.activeHandlerName.endsWith('l')) {
                resizeDifferenceX = -differenceX;
            } else if (this.activeHandlerName.endsWith('r')) {
                resizeDifferenceX = differenceX;
            } else {
                resizeDifferenceX = 0;
            }
            const width = this.startWidth + resizeDifferenceX;
            const height = this.startHeight + resizeDifferenceY;
            const size = this.getCheckedSize(width, height);
            return size;
        },

        // // getDifference(width, height, totalDifferenceX, totalDifferenceY) {
        // getDifference(width, height) {
        //     let moveDifferenceY = 0;
        //     if (this.activeHandlerName.startsWith('t')) {
        //         moveDifferenceY = this.startHeight - height;
        //     }
        //     let moveDifferenceX = 0;
        //     if (this.activeHandlerName.endsWith('l')) {
        //         moveDifferenceX = this.startWidth - width;
        //     }
        //     const difference = {
        //         x: moveDifferenceX,
        //         y: moveDifferenceY,
        //         // totalX: totalDifferenceX + moveDifferenceX,
        //         // totalY: totalDifferenceY + moveDifferenceY,
        //     };
        //     return difference;
        // },

        getPosition({ width, height }) {
            let moveDifferenceY = 0;
            if (this.activeHandlerName.startsWith('t')) {
                moveDifferenceY = this.startHeight - height;
            }
            let moveDifferenceX = 0;
            if (this.activeHandlerName.endsWith('l')) {
                moveDifferenceX = this.startWidth - width;
            }
            const left = this.startLeft + moveDifferenceX;
            const top = this.startTop + moveDifferenceY;
            const position = { left, top };
            return position;
        },

        processResizableSetup(
            width,
            height,
            { roundMethod = 'round', ignoreResizeGrid = false } = {},
        ) {
            const size = this.getCheckedSize(width, height, {
                roundMethod,
                ignoreResizeGrid,
            });
            return size;
        },

        initiateResize(
            handlerName,
            clientX,
            clientY,
            left,
            top,
            width,
            height,
        ) {
            this.activeHandlerName = handlerName;
            this.startX = clientX;
            this.startY = clientY;
            this.startLeft = left;
            this.startTop = top;
            this.startWidth = width;
            this.startHeight = height;
            // if (this.isForcedInsideContainer() || this.usingResizeGrid) {
            //     this.startClientRect = this.getElementClientRect();
            // }
        },

        // processResize(clientX, clientY, totalDifferenceX, totalDifferenceY) {
        processResize(clientX, clientY) {
            // if (this.isForcedInsideContainer()) {
            //     [clientX, clientY] = this.getClientPositionInsideContainer(
            //         clientX,
            //         clientY
            //     );
            // }
            const differenceX = clientX - this.startX;
            const differenceY = clientY - this.startY;
            let size = this.getSizeFromDifference(differenceX, differenceY);

            // const difference = this.getDifference(
            //     size.width,
            //     size.height,
            //     // totalDifferenceX,
            //     // totalDifferenceY
            // );
            const position = this.getPosition(size);
            // this.lastDifference = difference;
            this.lastPosition = position;
            this.lastSize = size;
            return [size, position];
        },

        completeResize() {
            // return this.lastDifference;
            this.activeHandlerName = null;
            return [this.lastPosition, this.lastSize];
        },

        // getContainerClientRect() {
        //     if (DEBUG) {
        //         console.warn(
        //             'The method getContainerClientRect is not implemented.'
        //         );
        //     }
        // },

        isForcedInsideContainer() {
            if (DEBUG) {
                console.warn(
                    'The method isForcedInsideContainer is not implemented.',
                );
            }
        },

        getAbsoluteResizeGrid() {
            if (DEBUG) {
                console.warn(
                    'The method getAbsoluteResizeGrid is not implemented.',
                );
            }
        },

        // getElementClientRect() {
        //     if (DEBUG) {
        //         console.warn(
        //             'The method getElementClientRect is not implemented.'
        //         );
        //     }
        // },

        getContainerSize() {
            if (DEBUG) {
                console.warn('The method getContainerSize is not implemented.');
            }
        },

        getKeepRatio() {
            if (DEBUG) {
                console.warn('The method getKeepRatio is not implemented.');
            }
        },

        getMinimumHeight() {
            if (DEBUG) {
                console.warn('The method getMinimumHeight is not implemented.');
            }
        },

        getMinimumWidth() {
            if (DEBUG) {
                console.warn('The method getMinimumWidth is not implemented.');
            }
        },

        getInternalRoundingPrecision() {
            if (DEBUG) {
                console.warn(
                    'The method getInternalRoundingPrecision ' +
                        'is not implemented.',
                );
            }
        },
    },
};