Как найти пустые места в сетке vue grid layout

Есть вот такая вот панель с виджетами. Юзается vue grid layout. Все виджеты разных размеров. Количество колонок - 24. Сверху есть кнопки которые добавляют виджеты. Из документации предлагается рассчитывать координаты добавляемого виджета так:

x: (this.selectedWidgets.length * 2) % (this.colNum || 12), y: (this.selectedWidgets.length + (this.colNum || 12)),

Это работает если все добавляемые виджеты одинакового размера, но если разного то не работает и всё располагается очень криво.

И в общем надо формировать виджеты слева направо заполняя пустые места (если виджет туда влезает).

Иначе говоря есть сетка, и нужно как то рассчитать координаты так, чтобы добавляемые виджеты заполняли всю сетку.

dashboard с виджетами Код:

<template>
    <div>
        <div>
            <el-button
                v-for="(widget, index) in availableWidgets"
                :key="index"
                @click="addWidget(widget)"
                @drag="drag(widget)"
                @dragend="dragend(widget)"
                draggable="true"
            >
                {{ widget.title }}
            </el-button>
        </div>

        <div id="dashboard">
            <grid-layout
                ref="gridlayout"
                :layout.sync="selectedWidgets"
                :col-num="colNum"
                :row-height="rowHeight"
                :is-responsive="true"
                :is-draggable="draggable"
                :is-resizable="resizable"
                :vertical-compact="true"
                :use-css-transforms="true"
            >
                <grid-item
                    v-for="(item, index) in selectedWidgets"
                    :key="item.i"
                    :x="item.x"
                    :y="item.y"
                    :w="item.w"
                    :h="item.h"
                    :i="item.i"
                >
                    <dashboard-widget-wrapper
                        @remove-widget="removeWidget"
                        :index="item.i"
                        :widget="item.settings.widget">
                        widget content
                    </dashboard-widget-wrapper>
                </grid-item>
            </grid-layout>
        </div>

    </div>
</template>

<script>
import {GridLayout, GridItem} from "vue-grid-layout"
import DashboardWidgetWrapper from '@/views/Dashboard/DashboardWidgetWrapper'

let mouseXY = {"x": null, "y": null};
let DragPos = {"x": null, "y": null, "w": 1, "h": 1, "i": null};

export default {
    components: {
        GridLayout,
        GridItem,
        DashboardWidgetWrapper
    },
    data() {
        return {
            availableWidgets: [
                {
                    title: 'Widget one',
                    component: 'DashboardWidgetOne',
                    w: 5,
                    h: 2,
                    settings: {
                        test1: {
                            type: 'bool',
                            value: true,
                        },
                        test2: {
                            type: 'bool',
                            value: false
                        },
                        test3: {
                            type: 'datetime',
                            value: false
                        },
                        test4: {
                            type: 'select',
                            value: 0,
                            options: [
                                {
                                    label: 'Lorem',
                                    value: 0
                                },
                                {
                                    label: 'Ipsum',
                                    value: 1
                                }
                            ]
                        }
                    }
                },
                {
                    title: 'Widget two',
                    component: 'DashboardWidgetTwo',
                    w: 6,
                    h: 7,
                },
                {
                    title: 'Widget three',
                    component: 'DashboardWidgetThree',
                    w: 5,
                    h: 6,
                    settings: {
                        type: {
                            type: 'select',
                            value: 1,
                            options: [
                                {
                                    label: 'Line 1',
                                    value: 1
                                },
                                {
                                    label: 'Line 2',
                                    value: 2
                                },
                                {
                                    label: 'Pie',
                                    value: 3
                                }
                            ]
                        }
                    }
                },
                {
                    title: 'Widget four',
                    component: 'DashboardWidgetFour',
                    w: 7,
                    h: 7,
                },
            ],
            selectedWidgets: [],
            draggable: true,
            resizable: true,
            colNum: 24,
            rowHeight: 40,
            index: 0
        }
    },
    mounted() {

        this.loadWidgets()
    },
    methods: {
        saveWidgets() {
            localStorage.dashboardWidgets = JSON.stringify(this.selectedWidgets)
        },
        loadWidgets() {
            if (localStorage.dashboardWidgets) {
                this.selectedWidgets = JSON.parse(localStorage.dashboardWidgets)

                let lastIndex = 0

                this.selectedWidgets.forEach((selectedWidget) => {
                    if (selectedWidget.i > lastIndex)
                        lastIndex = selectedWidget.i
                })

                this.index = lastIndex + 1
            }
        },
        addWidget(widget) {
            this.selectedWidgets.push({
                x: (this.selectedWidgets.length * widget.w) % (this.colNum || 12),
                y: (this.selectedWidgets.length + (this.colNum || 12)),
                w: widget.w,
                h: widget.h,
                i: this.index,
                settings: {
                    widget: widget,
                }
            })
            this.index++;
        },
        removeWidget(index) {
            const selectedWidgetsIndex = this.selectedWidgets.map(item => item.i).indexOf(index);
            this.selectedWidgets.splice(selectedWidgetsIndex, 1);
        },
        resetGridLayout() {
            this.$refs.gridLayout.lastBreakpoint = null
            this.$refs.gridLayout.layouts = {}
        },
    },
    watch: {
        selectedWidgets: {
            handler() {
                this.saveWidgets()
            },
            deep: true
        }
    }
}
</script>

Ответы (0 шт):