Fixed item drag drop re-ordering

This commit is contained in:
ThaumRystra
2024-11-09 13:28:15 +02:00
parent 2c431293e0
commit 6e5a335a39
11 changed files with 143 additions and 134 deletions

View File

@@ -26,6 +26,7 @@
"thumbhash",
"uncomputed",
"untarget",
"vuedraggable",
"vuetify",
"Vuex",
"walkdown"

View File

@@ -5,7 +5,7 @@ import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
import { organizeDoc } from '/imports/api/parenting/organizeMethods';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS';
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag';
import getParentRefByTag from './getParentByTag';
// Equipping or unequipping an item will also change its parent
const equipItem = new ValidatedMethod({

View File

@@ -1,8 +1,8 @@
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import { getFilter } from '/imports/api/parenting/parentingFunctions';
export default function getParentRefByTag(creatureId, tag) {
let prop = CreatureProperties.findOne({
export default function getParentByTag(creatureId, tag) {
return CreatureProperties.findOne({
...getFilter.descendantsOfRoot(creatureId),
removed: { $ne: true },
inactive: { $ne: true },
@@ -10,5 +10,4 @@ export default function getParentRefByTag(creatureId, tag) {
}, {
sort: { left: 1 },
});
return prop && { id: prop._id, collection: 'creatureProperties' };
}

View File

@@ -5,7 +5,7 @@ import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/ge
import SimpleSchema from 'simpl-schema';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
import { fetchDocByRef, rebuildNestedSets } from '/imports/api/parenting/parentingFunctions';
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag';
import getParentRefByTag from './getParentByTag';
import { RefSchema } from '/imports/api/parenting/ChildSchema';
const insertProperty = new ValidatedMethod({

View File

@@ -68,8 +68,8 @@
<v-card-text class="px-0">
<item-list
equipment
:items="equippedItems"
:parent-ref="equipmentParentRef"
:item-ids="equippedItemIds"
:parent="equipmentParent"
/>
</v-card-text>
</toolbar-card>
@@ -81,8 +81,8 @@
</v-toolbar-title>
<v-card-text class="px-0">
<item-list
:items="carriedItems"
:parent-ref="carriedParentRef"
:item-ids="carriedItemIds"
:parent="carriedParent"
/>
</v-card-text>
</toolbar-card>
@@ -112,7 +112,7 @@ import ColumnLayout from '/imports/client/ui/components/ColumnLayout.vue';
import ContainerCard from '/imports/client/ui/properties/components/inventory/ContainerCard.vue';
import ToolbarCard from '/imports/client/ui/components/ToolbarCard.vue';
import ItemList from '/imports/client/ui/properties/components/inventory/ItemList.vue';
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag';
import getParentByTag from '/imports/api/creature/creatureProperties/methods/getParentByTag';
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS';
import CoinValue from '/imports/client/ui/components/CoinValue.vue';
import stripFloatingPointOddities from '/imports/api/engine/computation/utility/stripFloatingPointOddities';
@@ -141,7 +141,6 @@ export default {
tabName: 'inventory',
};
},
// @ts-ignore Meteor isn't defined on vue
meteor: {
folderIds() {
return CreatureProperties.find({
@@ -191,7 +190,7 @@ export default {
sort: { left: 1 },
});
},
carriedItems() {
carriedItemIds() {
return CreatureProperties.find({
...getFilter.descendantsOfRoot(this.creatureId),
$nor: [getFilter.descendantsOfAll(this.containers)],
@@ -205,9 +204,10 @@ export default {
deactivatedByToggle: { $ne: true },
}, {
sort: { left: 1 },
});
fields: { _id: 1 },
}).map(prop => prop._id);
},
equippedItems() {
equippedItemIds() {
return CreatureProperties.find({
...getFilter.descendantsOfRoot(this.creatureId),
type: 'item',
@@ -216,27 +216,22 @@ export default {
inactive: { $ne: true },
}, {
sort: { left: 1 },
});
fields: { _id: 1 },
}).map(prop => prop._id);
},
equipmentParentRef() {
return getParentRefByTag(
equipmentParent() {
return getParentByTag(
this.creatureId, BUILT_IN_TAGS.equipment
) || getParentRefByTag(
) || getParentByTag(
this.creatureId, BUILT_IN_TAGS.inventory
) || {
id: this.creatureId,
collection: 'creatures'
};
);
},
carriedParentRef() {
return getParentRefByTag(
carriedParent() {
return getParentByTag(
this.creatureId, BUILT_IN_TAGS.carried
) || getParentRefByTag(
) || getParentByTag(
this.creatureId, BUILT_IN_TAGS.inventory
) || {
id: this.creatureId,
collection: 'creatures'
};
);
},
},
computed: {

View File

@@ -75,7 +75,7 @@
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import Creatures from '/imports/api/creature/creatures/Creatures';
import ColumnLayout from '/imports/client/ui/components/ColumnLayout.vue';
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag';
import getParentRefByTag from '../../../../../api/creature/creatureProperties/methods/getParentByTag';
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS';
import CoinValue from '/imports/client/ui/components/CoinValue.vue';
import stripFloatingPointOddities from '/imports/api/engine/computation/utility/stripFloatingPointOddities';

View File

@@ -7,7 +7,6 @@
ghost-class="ghost"
draggable=".creature"
handle=".handle"
:animation="200"
@change="draggableChange"
>
<creature-list-tile
@@ -71,19 +70,6 @@
draggableChange({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;
moveCreatureToFolder.call({
creatureId: doc._id,

View File

@@ -5,7 +5,6 @@
<draggable
v-model="dataItems"
:group="'item-list'"
:animation="200"
:sort="false"
ghost-class="item-to-creature-ghost"
draggable=".no-real-items"
@@ -67,7 +66,7 @@
<script lang="js">
import SharedIcon from '/imports/client/ui/components/SharedIcon.vue';
import draggable from 'vuedraggable';
import { organizeDoc } from '/imports/api/parenting/organizeMethods';
import { moveBetweenRoots } from '/imports/api/parenting/organizeMethods';
import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue';
export default {
@@ -91,44 +90,44 @@ export default {
};
},
methods: {
dropItem({ added }) {
async dropItem({ added }) {
const item = added?.element;
if (!item?._id) return;
const docRef = { collection: 'creatureProperties', id: item._id };
// Create the undo function
const oldOrder = item.order;
const oldParent = item.parent;
// TODO organize doc needs to be replaced with organize between roots
const undo = () => organizeDoc.callAsync({
const oldRoot = item.root;
const oldOrder = item.left;
const undo = async () => {
try {
await moveBetweenRoots.callAsync({
docRef,
parentRef: oldParent,
order: (oldOrder || 0) - 0.5,
skipClient: true, // The client no longer has the doc subscribed, so we can't simulate
}, (error) => {
if (error) {
console.error(error);
snackbar({ text: error.reason || error.message || error.toString() });
}
newRootRef: oldRoot,
newPosition: (oldOrder || 1) - 0.5,
skipClient: true, // The client will no longer have the doc subscribed, so we can't simulate
});
} catch (e) {
console.error(e);
snackbar({ text: e.reason || e.message || e.toString() });
}
}
// TODO organize doc needs to be replaced with organize between roots
organizeDoc.callAsync({
try {
await moveBetweenRoots.callAsync({
docRef,
parentRef: { collection: 'creatures', id: this.model._id },
order: -0.5,
}, (error) => {
if (error) {
console.error(error);
snackbar({ text: error.reason || error.message || error.toString() });
} else {
newRootRef: { collection: 'creatures', id: this.model._id },
newPosition: 0.5,
});
snackbar({
text: `Moved ${item.name || 'item'} to ${this.model.name || 'another character'}`,
callbackName: 'undo',
callback: undo,
});
} catch (e) {
console.error(e);
snackbar({ text: e.reason || e.message || e.toString() });
}
});
},
}
}

View File

@@ -35,8 +35,8 @@
</template>
<v-card-text class="px-0">
<item-list
:items="items"
:parent-ref="{id: model._id, collection: 'creatureProperties'}"
:item-ids="itemIds"
:parent="model"
/>
</v-card-text>
</toolbar-card>
@@ -92,7 +92,7 @@ export default {
},
},
meteor: {
items() {
itemIds() {
return CreatureProperties.find({
'parentId': this.model._id,
type: { $in: ['item', 'container'] },
@@ -102,7 +102,8 @@ export default {
deactivatedByToggle: { $ne: true },
}, {
sort: { left: 1 },
});
fields: { _id: 1 }
}).map(prop => prop._id);
},
}
};

View File

@@ -11,16 +11,16 @@
ghost-class="ghost"
draggable=".item"
handle=".handle"
:animation="200"
:revert-on-spill="true"
@change="change"
>
<item-list-tile
v-for="item in dataItems"
:key="item._id"
v-for="itemId in dataItems"
:key="itemId"
class="item"
:data-id="item._id"
:model="item"
@click="clickProperty(item._id)"
:data-id="itemId"
:item-id="itemId"
@click="clickProperty(itemId)"
/>
</draggable>
</v-list>
@@ -29,8 +29,10 @@
<script lang="js">
import draggable from 'vuedraggable';
import ItemListTile from '/imports/client/ui/properties/components/inventory/ItemListTile.vue';
import { organizeDoc } from '/imports/api/parenting/organizeMethods';
import { moveWithinRoot } from '/imports/api/parenting/organizeMethods';
import updateCreatureProperty from '/imports/api/creature/creatureProperties/methods/updateCreatureProperty';
import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
export default {
components: {
@@ -41,13 +43,13 @@ export default {
context: { default: {} }
},
props: {
items: {
itemIds: {
type: Array,
default: () => [],
},
parentRef: {
parent: {
type: Object,
required: true,
default: () => undefined,
},
preparingSpells: Boolean,
equipment: Boolean,
@@ -57,20 +59,13 @@ export default {
dataItems: [],
}
},
computed: {
levels() {
let levels = new Set();
this.items.forEach(item => levels.add(item.level));
return levels;
},
},
watch: {
items(value) {
itemIds(value) {
this.dataItems = value;
}
},
},
mounted() {
this.dataItems = this.items;
this.dataItems = this.itemIds;
},
methods: {
clickProperty(_id) {
@@ -82,37 +77,58 @@ export default {
},
change({ added, moved }) {
let event = added || moved;
if (event) {
if (! event) return;
// If this item is now adjacent to another, set the order accordingly
let order;
let before = this.dataItems[event.newIndex - 1];
let after = this.dataItems[event.newIndex + 1];
if (before && before._id) {
order = before.order + 0.5;
} else if (after && after._id) {
order = after.order - 0.5;
const beforeId = this.dataItems[event.newIndex - 1]
const afterId = this.dataItems[event.newIndex + 1]
const before = beforeId && CreatureProperties.findOne(beforeId);
const after = afterId && CreatureProperties.findOne(afterId);
if (before) {
order = before.right + 0.5;
} else if (after) {
order = after.left - 0.5;
} else if (this.parent) {
order = this.parent.left + 0.5;
} else {
order = -0.5;
order = 0.5;
}
let doc = event.element;
organizeDoc.callAsync({
let docId = event.element;
const doc = CreatureProperties.findOne(docId);
if (!doc) return;
moveWithinRoot.callAsync({
docRef: {
id: doc._id,
id: docId,
collection: 'creatureProperties',
},
parentRef: this.parentRef,
order,
newPosition: order,
}, (e) => {
if (e) {
console.error(e);
snackbar({ text: e.reason || e.message || e.toString() });
}
});
if (doc.type === 'item' && doc.equipped != this.equipment) {
if (doc.type === 'item' && doc.equipped !== this.equipment) {
updateCreatureProperty.call({
_id: doc._id,
_id: docId,
path: ['equipped'],
value: !!this.equipment,
}, (e) => {
if (e) {
this.dataItems = this.itemIds
console.error(e);
snackbar({ text: e.reason || e.message || e.toString() });
}
});
}
}
setTimeout(() => this.dataItems = this.items, 0);
},
}
}
</script>
<style lang="css" scoped>
.ghost {
opacity: 0.1;
}
</style>

View File

@@ -42,21 +42,28 @@
</template>
<script lang="js">
import treeNodeViewMixin from '/imports/client/ui/properties/treeNodeViews/treeNodeViewMixin';
import PROPERTIES from '/imports/constants/PROPERTIES';
import adjustQuantity from '/imports/api/creature/creatureProperties/methods/adjustQuantity';
import IncrementButton from '/imports/client/ui/components/IncrementButton.vue';
import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue';
import PropertyIcon from '/imports/client/ui/properties/shared/PropertyIcon.vue';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
export default {
components: {
IncrementButton,
PropertyIcon,
},
mixins: [treeNodeViewMixin],
inject: {
context: { default: {} }
},
props: {
itemId: {
type: String,
required: true,
},
selected: Boolean,
hideIcon: Boolean,
preparingSpells: Boolean,
},
data() {
@@ -103,6 +110,11 @@ export default {
});
}
},
meteor: {
model() {
return CreatureProperties.findOne(this.itemId);
}
}
}
</script>