Skip to content

Instantly share code, notes, and snippets.

@mika76
Forked from sploders101/MuuriGrid.vue
Created August 18, 2020 08:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mika76/b16befd862ca81a05399a4cfa066876e to your computer and use it in GitHub Desktop.
Save mika76/b16befd862ca81a05399a4cfa066876e to your computer and use it in GitHub Desktop.
Array-based muuri grid
<template>
<div
ref="muuriel"
class="muuri"
>
<div
class="muuri-item"
v-for="field in value"
:muurikey="field[muurikey]"
:key="field[muurikey]"
>
<slot
:field="field"
/>
</div>
</div>
</template>
<script lang="ts">
import {
Component,
Vue,
Prop,
PropSync,
Ref,
Watch,
} from "vue-property-decorator";
import Muuri from "muuri";
import Options from "@/types/modules/muuri/options";
@Component({
})
export default class MuuriGridVue extends Vue {
// ┌─────────────┐
// │ Props │
// └─────────────┘
@Prop() readonly muurikey!: string;
@Prop({ default: () => ({}) }) readonly options!: Options;
// ┌────────────┐
// │ Refs │
// └────────────┘
@Ref() readonly muuriel!: HTMLDivElement;
// ┌────────────────────────────┐
// │ Value Sync (v-model) │
// └────────────────────────────┘
@Prop() readonly value!: any[];
get fields() { return this.value; }
set fields(newVal: any[]) { this.$emit("input", newVal); }
// ┌─────────────┐
// │ State │
// └─────────────┘
grid: Muuri | null = null;
// ┌───────────────────────┐
// │ Lifecycle Hooks │
// └───────────────────────┘
beforeDestroy() {
if(this.grid) {
this.grid.destroy(false);
}
}
// ┌────────────────────┐
// │ Grid Helpers │
// └────────────────────┘
@Watch("options", { immediate: true })
async init() {
if(this.options !== null) {
while(!this.muuriel) await this.$nextTick();
if(this.grid) this.grid.destroy();
this.grid = new Muuri(this.muuriel, this.options);
this.grid.on("move", () => this.updateData());
}
}
layout() {
if(this.grid) {
this.grid.refreshItems();
this.grid.layout();
}
}
private getOrder() {
if(this.grid) {
return this.grid
.getItems()
.map((item) => item.getElement().getAttribute("muurikey"));
} else return [];
}
// ┌─────────────────┐
// │ Data Sync │
// └─────────────────┘
updateData() {
// If there's no grid, there's nothing to model the data after
if(!this.grid) return;
// Get data order by keys
const order = this.getOrder();
// Use keys to sort data
this.fields
.sort((a, b) => order.indexOf(a[this.muurikey]) - order.indexOf(b[this.muurikey]));
}
@Watch("fields", { deep: true })
async updateGrid(newData: any[], oldData: any[]) {
// If there's no grid, we don't need to update it
if(!this.grid) return;
// Map to what vue uses as keys (used to determine what to update/replace/reorder)
const oldKeys = oldData.map((e) => e[this.muurikey]);
const newKeys = newData.map((e) => e[this.muurikey]);
// Get which keys (individual elements) were added/removed
const added = newKeys.filter((key) => oldKeys.indexOf(key) === -1);
const removed = oldKeys.filter((key) => newKeys.indexOf(key) === -1);
// Deal with the removed tiles first
removed.forEach((po) => {
this.grid!.remove(this.getOrder().indexOf(po), {
// Vue will take care of this
removeElements: false,
// We will be adding items too, so not quite yet
layout: false,
});
});
await this.$nextTick();
// Now add in the new ones
this.grid.add(
([ // Wrap NodeList in array for extra methods
...this.muuriel.childNodes,
] as HTMLElement[])
// Filter out anything not in list of added orders
.filter((el) => added.indexOf(el.getAttribute("muurikey")!) !== -1),
{
// We will be re-ordering, so wait a bit longer
layout: false,
});
// Re-order if needed
const items = this.grid.getItems();
const newOrder = this.fields.map((field) => field[this.muurikey]);
this.grid.sort((a, b) => {
return newOrder.indexOf(a.getElement().getAttribute("muurikey")!)
- newOrder.indexOf(b.getElement().getAttribute("muurikey")!);
});
// Now we can start the layout
this.layout();
}
}
</script>
<style lang="scss" scoped>
.muuri {
position: relative;
& > .muuri-item {
display: block;
position: absolute;
&.muuri-item-dragging {
z-index: 3;
}
&.muuri-item-releasing {
z-index: 2;
}
&.muuri-item-hidden {
z-index: 0;
}
}
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment