Как найти пустые места в сетке vue grid layout
Есть вот такая вот панель с виджетами. Юзается vue grid layout. Все виджеты разных размеров. Количество колонок - 24. Сверху есть кнопки которые добавляют виджеты. Из документации предлагается рассчитывать координаты добавляемого виджета так:
x: (this.selectedWidgets.length * 2) % (this.colNum || 12), y: (this.selectedWidgets.length + (this.colNum || 12)),
Это работает если все добавляемые виджеты одинакового размера, но если разного то не работает и всё располагается очень криво.
И в общем надо формировать виджеты слева направо заполняя пустые места (если виджет туда влезает).
Иначе говоря есть сетка, и нужно как то рассчитать координаты так, чтобы добавляемые виджеты заполняли всю сетку.
<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>
