Skip to content

Instantly share code, notes, and snippets.

@sploders101
Last active August 18, 2020 08:45
Show Gist options
  • Save sploders101/e80c244310e1e7d8b996f7a98faee9be to your computer and use it in GitHub Desktop.
Save sploders101/e80c244310e1e7d8b996f7a98faee9be to your computer and use it in GitHub Desktop.

Revisions

  1. sploders101 revised this gist Jun 24, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion MuuriGrid.vue
    Original file line number Diff line number Diff line change
    @@ -117,7 +117,7 @@
    const order = this.getOrder();
    // Use keys to sort data
    const fields = this.fields
    this.fields
    .sort((a, b) => order.indexOf(a[this.muurikey]) - order.indexOf(b[this.muurikey]));
    }
  2. sploders101 created this gist Jun 24, 2020.
    194 changes: 194 additions & 0 deletions MuuriGrid.vue
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,194 @@
    <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
    const fields = 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>