Can now move creatures between folders using drag and drop

This commit is contained in:
Stefan Zermatten
2021-06-20 13:32:28 +02:00
parent 69f4bbf360
commit 814e371148
9 changed files with 172 additions and 34 deletions

View File

@@ -5,7 +5,6 @@ let CreatureFolders = new Mongo.Collection('creatureFolders');
let creatureFolderSchema = new SimpleSchema({
name: {
type: String,
defaultValue: 'Folder',
trim: false,
optional: true,
},

View File

@@ -1,3 +1,4 @@
import '/imports/api/creature/creatureFolders/methods.js/insertCreatureFolder.js';
import '/imports/api/creature/creatureFolders/methods.js/updateCreatureFolderName.js';
import '/imports/api/creature/creatureFolders/methods.js/removeCreatureFolder.js';
import '/imports/api/creature/creatureFolders/methods.js/removeCreatureFolder.js';
import '/imports/api/creature/creatureFolders/methods.js/moveCreatureToFolder.js';

View File

@@ -36,6 +36,7 @@ const insertCreatureFolder = new ValidatedMethod({
}
// Insert
return CreatureFolders.insert({
name: 'Folder',
owner: userId,
order,
});

View File

@@ -0,0 +1,45 @@
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
const moveCreatureToFolder = new ValidatedMethod({
name: 'creatureFolders.methods.moveCreatureToFolder',
validate: null,
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({creatureId, folderId}) {
// Ensure logged in
let userId = this.userId;
if (!userId) {
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
'You need to be logged in to remove a folder');
}
// Check that this folder is owned by the user
if (folderId){
let existingFolder = CreatureFolders.findOne(folderId);
if (existingFolder.owner !== userId){
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
'This folder does not belong to you');
}
}
// Remove from other folders
CreatureFolders.update({
owner: userId
}, {
$pull: {creatures: creatureId},
}, {
multi: true,
});
if (folderId){
// Add to this folder
CreatureFolders.update(folderId, {
$addToSet: {creatures: creatureId},
});
}
},
});
export default moveCreatureToFolder;

View File

@@ -0,0 +1,91 @@
<template lang="html">
<v-list>
<draggable
v-model="dataCreatures"
style="min-height: 24px;"
:sort="false"
:group="`creature-list`"
ghost-class="ghost"
draggable=".creature"
handle=".handle"
:animation="200"
@change="change"
>
<creature-list-tile
v-for="creature in dataCreatures"
:key="creature._id"
class="creature"
:to="creature.url"
:model="creature"
/>
</draggable>
</v-list>
</template>
<script lang="js">
import CreatureListTile from '/imports/ui/creature/creatureList/CreatureListTile.vue';
import draggable from 'vuedraggable';
import moveCreatureToFolder from '/imports/api/creature/creatureFolders/methods.js/moveCreatureToFolder.js';
import {snackbar} from '/imports/ui/components/snackbars/SnackbarQueue.js';
export default {
components: {
CreatureListTile,
draggable,
},
props: {
creatures: {
type: Array,
required: true,
},
folderId: {
type: String,
default: null,
},
},
data(){return {
dataCreatures: [],
}},
watch:{
creatures(newValue){
this.dataCreatures = newValue;
},
},
mounted(){
this.dataCreatures = this.creatures;
},
methods: {
change({added, moved}){
let event = added || moved;
if (event){
// If this item is now adjacent to another, set the order accordingly
let order;
let before = this.dataCreatures[event.newIndex - 1];
let after = this.dataCreatures[event.newIndex + 1];
if (before && before._id){
order = before.order + 0.5;
} else if (after && after._id) {
order = after.order - 0.5;
} else {
order = -0.5;
}
let doc = event.element;
console.log({doc, order, newIndex: event.newIndex, before, after});
moveCreatureToFolder.call({
creatureId: doc._id,
folderId: this.folderId
}, error => {
if (!error) return;
console.error(error);
snackbar({
text: error.reason,
});
});
}
},
}
}
</script>
<style lang="css" scoped>
</style>

View File

@@ -21,11 +21,18 @@
{{ model.alignment }} {{ model.gender }} {{ model.race }}
</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action v-if="selection">
<v-list-item-action>
<v-checkbox
v-if="selection"
:input-value="selected && selected.has(model._id)"
@change="$emit('select')"
/>
<v-icon
style="height: 100%; width: 40px; cursor: move;"
class="handle"
>
mdi-drag
</v-icon>
</v-list-item-action>
</v-list-item>
</template>

View File

@@ -4,16 +4,12 @@
style="height: 100%"
>
<v-card :class="{'mb-4': folders && folders.length}">
<v-list v-if="CreaturesWithNoParty.length">
<creature-list-tile
v-for="creature in CreaturesWithNoParty"
:key="creature._id"
:to="creature.url"
:model="creature"
/>
</v-list>
<creature-list :creatures="CreaturesWithNoParty" />
</v-card>
<v-expansion-panels v-if="folders && folders.length">
<v-expansion-panels
v-if="folders && folders.length"
multiple
>
<v-expansion-panel
v-for="folder in folders"
:key="folder._id"
@@ -57,24 +53,22 @@
</template>
</v-expansion-panel-header>
<v-expansion-panel-content>
<v-list v-if="folder.creatures">
<creature-list-tile
v-for="creature in folder.creatures"
:key="creature._id"
:to="creature.url"
:model="creature"
/>
</v-list>
<creature-list
:creatures="folder.creatures"
:folder-id="folder._id"
/>
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
<v-btn
text
:loading="loadingInsertFolder"
@click="insertFolder"
>
add folder
</v-btn>
<div class="layout justify-end mt-2">
<v-btn
text
:loading="loadingInsertFolder"
@click="insertFolder"
>
add folder
</v-btn>
</div>
<v-btn
color="accent"
fab
@@ -93,13 +87,13 @@
import Vue from 'vue';
import Creatures, {insertCreature} from '/imports/api/creature/Creatures.js';
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
import CreatureList from '/imports/ui/creature/creatureList/CreatureList.vue';
import { getUserTier } from '/imports/api/users/patreon/tiers.js';
import CreatureListTile from '/imports/ui/creature/CreatureListTile.vue';
import insertCreatureFolder from '/imports/api/creature/creatureFolders/methods.js/insertCreatureFolder.js';
import updateCreatureFolderName from '/imports/api/creature/creatureFolders/methods.js/updateCreatureFolderName.js';
import removeCreatureFolder from '/imports/api/creature/creatureFolders/methods.js/removeCreatureFolder.js';
import {snackbar} from '/imports/ui/components/snackbars/SnackbarQueue.js';
const characterTransform = function(char){
char.url = `/character/${char._id}/${char.urlName || '-'}`;
char.initial = char.name && char.name[0] || '?';
@@ -107,7 +101,7 @@
};
export default {
components: {
CreatureListTile,
CreatureList,
},
data(){ return{
fab: false,
@@ -183,9 +177,9 @@
}
},
insertFolder(){
loadingInsertFolder = true;
this.loadingInsertFolder = true;
insertCreatureFolder.call(error => {
loadingInsertFolder = false;
this.loadingInsertFolder = false;
if (!error) return;
console.error(error);
snackbar({

View File

@@ -32,7 +32,7 @@
<script lang="js">
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
import Creatures from '/imports/api/creature/Creatures.js';
import CreatureListTile from '/imports/ui/creature/CreatureListTile.vue';
import CreatureListTile from '/imports/ui/creature/creatureList/CreatureListTile.vue';
export default {
components: {

View File

@@ -92,7 +92,7 @@
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
import Creatures from '/imports/api/creature/Creatures.js';
import Libraries from '/imports/api/library/Libraries.js';
import CreatureListTile from '/imports/ui/creature/CreatureListTile.vue';
import CreatureListTile from '/imports/ui/creature/creatureList/CreatureListTile.vue';
export default {
components: {