[WIP] multiplefixes for items / equipment / stable (#8938)

* highlight egg on dragging a potion - hide popover while dragging - flat show more button

* Show EquipGearModal on Item-Click - misc fixes

* disable dragging

* hide egg notes while dragging

* rename headgear to helm

* sort equipment by name & attributes

* set welcome-dialog flag

* show feeding notification

* select animals on click

* add hatchable popover back

* fix stable sortby A-Z

* remove property brackets
This commit is contained in:
negue
2017-08-13 18:18:46 +02:00
committed by GitHub
parent 7b9ebc3465
commit fcea1ecbc2
13 changed files with 338 additions and 51 deletions

View File

@@ -46,6 +46,10 @@
&.highlight {
box-shadow: 0 0 8px 8px rgba($black, 0.16), 0 5px 10px 0 rgba($black, 0.12) !important;
}
&.highlight-border {
border-color: $purple-500;
}
}
.flat .item {

View File

@@ -22,7 +22,6 @@
line-height: 1.2;
text-align: center;
color: $gray-50;
display: inline-block;
}
.text {

View File

@@ -0,0 +1,165 @@
<template lang="pug">
b-modal#equipgear-modal(
:visible="true",
v-if="item != null",
:hide-header="true",
@change="onChange($event)"
)
div.close
span.svg-icon.inline.icon-10(aria-hidden="true", v-html="icons.close", @click="hideDialog()")
div.content(v-if="item != null")
div.inner-content
avatar(
:member="user",
:avatarOnly="true",
:withBackground="true",
:overrideAvatarGear="memberOverrideAvatarGear(item)"
)
h4.title {{ itemText }}
div.text(v-html="itemNotes")
equipmentAttributesGrid.bordered(
:item="item",
v-if="attributesGridVisible"
)
button.btn.btn-primary(@click="equipItem()") {{ $t(isEquipped ? 'unequip' : 'equip') }}
div.clearfix(slot="modal-footer")
</template>
<style lang="scss">
@import '~client/assets/scss/colors.scss';
@import '~client/assets/scss/modal.scss';
#equipgear-modal {
@include centeredModal();
.content {
text-align: center;
}
.item-wrapper {
margin-bottom: 0 !important;
}
.inner-content {
margin: 33px auto auto;
width: 282px;
}
.bordered {
border-radius: 2px;
background-color: #f9f9f9;
margin-bottom: 24px;
padding: 24px 24px 10px;
}
.avatar {
display: inline-block;
cursor: inherit;
}
.content-text {
font-family: 'Roboto', sans-serif;
font-size: 14px;
font-weight: normal;
line-height: 1.43;
width: 400px;
}
button.btn.btn-primary {
margin-top: 24px;
margin-bottom: 24px;
}
}
</style>
<script>
import { mapState } from 'client/libs/store';
import bModal from 'bootstrap-vue/lib/components/modal';
import svgClose from 'assets/svg/close.svg';
import Avatar from 'client/components/avatar';
import EquipmentAttributesGrid from 'client/components/shops/market/equipmentAttributesGrid.vue';
export default {
components: {
bModal,
Avatar,
EquipmentAttributesGrid,
},
data () {
return {
icons: Object.freeze({
close: svgClose,
}),
};
},
computed: {
...mapState({
content: 'content',
user: 'user.data',
}),
itemText () {
if (this.item.text instanceof Function) {
return this.item.text();
} else {
return this.item.text;
}
},
itemNotes () {
if (this.item.notes instanceof Function) {
return this.item.notes();
} else {
return this.item.notes;
}
},
attributesGridVisible () {
if (this.costumeMode) {
return false;
}
return true;
},
},
methods: {
onChange ($event) {
this.$emit('change', $event);
},
equipItem () {
this.$emit('equipItem', this.item);
this.hideDialog();
},
hideDialog () {
this.$root.$emit('hide::modal', 'equipgear-modal');
},
memberOverrideAvatarGear (gear) {
return {
[gear.type]: gear.key,
};
},
},
props: {
item: {
type: Object,
},
priceType: {
type: String,
},
costumeMode: {
type: Boolean,
},
isEquipped: {
type: Boolean,
},
},
};
</script>

View File

@@ -22,10 +22,14 @@
h1.float-left.mb-0.page-header(v-once) {{ $t('equipment') }}
.float-right
span.dropdown-label {{ $t('sortBy') }}
b-dropdown(:text="'Sort 1'", right=true)
b-dropdown-item(href="#") Option 1
b-dropdown-item(href="#") Option 2
b-dropdown-item(href="#") Option 3
b-dropdown(:text="$t(selectedSortGearBy)", right=true)
b-dropdown-item(
v-for="sort in sortGearBy",
@click="selectedSortGearBy = sort",
:active="selectedSortGearBy === sort",
:key="sort"
) {{ $t(sort) }}
span.dropdown-label {{ $t('groupBy2') }}
b-dropdown(:text="$t(groupBy === 'type' ? 'equipmentType' : 'class')", right=true)
b-dropdown-item(@click="groupBy = 'type'", :active="groupBy === 'type'") {{ $t('equipmentType') }}
@@ -88,10 +92,11 @@
)
h2
| {{ group.label }}
|
span.badge.badge-pill.badge-default {{items[group.key].length}}
itemRows(
:items="items[group.key]",
:items="sortItems(items[group.key], selectedSortGearBy)",
:itemWidth=94,
:itemMargin=24,
:noItemsLabel="$t('noGearItemsOfType', { type: group.label })"
@@ -102,28 +107,32 @@
:itemContentClass="'shop_' + context.item.key",
:emptyItem="!context.item || context.item.key.indexOf('_base_0') !== -1",
:key="context.item.key",
@click="openEquipDialog(context.item)"
)
template(slot="itemBadge", scope="context")
starBadge(
:selected="activeItems[context.item.type] === context.item.key",
:show="!costume || user.preferences.costume",
@click="equip(context.item)",
@click="openEquipDialog(context.item)",
)
template(slot="popoverContent", scope="context")
equipmentAttributesPopover(:item="context.item")
</template>
<style lang="scss" scoped>
h2 {
margin-top: 24px;
}
</style>
equipGearModal(
:item="gearToEquip",
@equipItem="equipItem($event)",
@change="changeModalState($event)",
:costumeMode="costume",
:isEquipped="gearToEquip == null ? false : activeItems[gearToEquip.type] === gearToEquip.key"
)
</template>
<script>
import { mapState } from 'client/libs/store';
import each from 'lodash/each';
import map from 'lodash/map';
import throttle from 'lodash/throttle';
import _sortBy from 'lodash/sortBy';
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
@@ -138,6 +147,18 @@ import Drawer from 'client/components/ui/drawer';
import i18n from 'common/script/i18n';
import EquipGearModal from './equipGearModal';
const sortGearTypes = ['sortByName', 'sortByCon', 'sortByPer', 'sortByStr', 'sortByInt'];
const sortGearTypeMap = {
sortByName: (i) => i.text(),
sortByCon: 'con',
sortByStr: 'str',
sortByInt: 'int',
};
export default {
name: 'Equipment',
components: {
@@ -150,6 +171,7 @@ export default {
bDropdownItem,
bPopover,
toggleSwitch,
EquipGearModal,
},
data () {
return {
@@ -178,6 +200,9 @@ export default {
armoire: i18n.t('armoireText'),
}),
viewOptions: {},
gearToEquip: null,
sortGearBy: sortGearTypes,
selectedSortGearBy: 'sortByName',
};
},
watch: {
@@ -186,14 +211,26 @@ export default {
}, 250),
},
methods: {
equip (item) {
openEquipDialog (item) {
this.gearToEquip = item;
},
changeModalState (visible) {
if (!visible) {
this.gearToEquip = null;
}
},
equipItem (item) {
this.$store.dispatch('common:equip', {key: item.key, type: this.costume ? 'costume' : 'equipped'});
this.gearToEquip = null;
},
changeDrawerPreference (newVal) {
this.$store.dispatch('user:set', {
[`preferences.${this.drawerPreference}`]: newVal,
});
},
sortItems (items, sortBy) {
return _sortBy(items, sortGearTypeMap[sortBy]);
},
},
computed: {
...mapState({

View File

@@ -5,15 +5,22 @@ div(v-if="emptyItem")
.item-content
span.item-label(v-if="label") {{ label }}
b-popover(
v-else,
:triggers="[showPopover?'hover':'']",
v-else-if="showPopover",
:triggers="['hover']",
:placement="popoverPosition",
)
span(slot="content")
slot(name="popoverContent", :item="item")
.item-wrapper(@click="click")
.item(:class="{'item-active': active }")
.item(:class="{'item-active': active, 'highlight-border':highlightBorder }")
slot(name="itemBadge", :item="item")
span.item-content(
:class="itemContentClass"
)
span.item-label(v-if="label") {{ label }}
.item-wrapper(@click="click", v-else)
.item(:class="{'item-active': active, 'highlight-border':highlightBorder }")
slot(name="itemBadge", :item="item")
span.item-content(
:class="itemContentClass"
@@ -53,6 +60,9 @@ export default {
active: {
type: Boolean,
},
highlightBorder: {
type: Boolean,
},
},
methods: {
click () {

View File

@@ -47,18 +47,18 @@
:item="context.item",
:key="context.item.key",
:itemContentClass="`${group.classPrefix}${context.item.key}`",
:showPopover="currentDraggingPotion == null",
:highlightBorder="currentDraggingPotion != null",
v-drag.drop.hatch="context.item.key",
@itemDragOver="onDragOver($event, context.item)",
@itemDropped="onDrop($event, context.item)",
@itemDragLeave="onDragLeave()",
@click="onEggClicked($event, context.item)"
@click="onEggClicked($event, context.item)",
)
template(slot="popoverContent", scope="context")
h4.popover-content-title {{ context.item.text() }}
.popover-content-text {{ context.item.notes() }}
.popover-content-text(v-if="currentDraggingPotion == null") {{ context.item.notes() }}
template(slot="itemBadge", scope="context")
span.badge.badge-pill.badge-item.badge-quantity {{ context.item.quantity }}

View File

@@ -99,13 +99,21 @@
:popoverPosition="'top'",
:progress="context.item.progress",
:emptyItem="!context.item.isOwned()",
:showPopover="context.item.isOwned()",
:showPopover="context.item.isOwned() || context.item.isHatchable()",
:highlightBorder="highlightPet == context.item.key",
@click="petClicked(context.item)"
)
span(slot="popoverContent")
div(v-if="context.item.isOwned()")
h4.popover-content-title {{ context.item.name }}
div.hatchablePopover(v-else-if="context.item.isHatchable()")
h4.popover-content-title {{ context.item.name }}
div.popover-content-text(v-html="$t('haveHatchablePet', { potion: context.item.potionName, egg: context.item.eggName })")
div.potionEggGroup
div.potionEggBackground
div(:class="'Pet_HatchingPotion_'+context.item.potionKey")
div.potionEggBackground
div(:class="'Pet_Egg_'+context.item.eggKey")
template(slot="itemBadge", scope="context")
starBadge(
@@ -139,6 +147,7 @@
:popoverPosition="'top'",
:emptyItem="!context.item.isOwned()",
:showPopover="context.item.isOwned()",
@click="selectMount(context.item)"
)
span(slot="popoverContent")
h4.popover-content-title {{ context.item.name }}
@@ -200,7 +209,8 @@
:ok-only="true",
:ok-title="$t('gotIt')",
:visible="!hideDialog",
:hide-header="true"
:hide-header="true",
@hide="hideFlag()"
)
div.content
div.npc_matt
@@ -439,6 +449,33 @@
width: 100px;
}
}
.hatchablePopover {
width: 180px;
.potionEggGroup {
margin: 0 auto;
margin-top: 10px;
}
.potionEggBackground {
display: inline-flex;
align-items: center;
width: 64px;
height: 64px;
border-radius: 2px;
background-color: #4e4a57;
&:first-child {
margin-right: 24px;
}
& div {
margin: 0 auto;
}
}
}
</style>
<script>
@@ -476,6 +513,8 @@
import svgInformation from 'assets/svg/information.svg';
import svgClose from 'assets/svg/close.svg';
import notifications from 'client/mixins/notifications';
// TODO Normalize special pets and mounts
// import Store from 'client/store';
// import deepFreeze from 'client/libs/deepFreeze';
@@ -484,6 +523,7 @@
let lastMouseMoveEvent = {};
export default {
mixins: [notifications],
components: {
PetItem,
Item,
@@ -739,7 +779,7 @@
// 2. Sort
switch (sort) {
case 'AZ':
animals = _sortBy(animals, ['pet']);
animals = _sortBy(animals, ['name']);
break;
case 'sortByColor':
@@ -848,10 +888,10 @@
}
},
onDrop (ev, pet) {
this.$store.dispatch('common:feed', {pet: pet.key, food: ev.draggingKey});
async onDrop (ev, pet) {
this.highlightPet = '';
this.feedAction(pet.key, ev.draggingKey);
},
onDragEnd () {
@@ -866,11 +906,16 @@
petClicked (pet) {
if (this.currentDraggingFood !== null && pet.isAllowedToFeed()) {
// food process
this.$store.dispatch('common:feed', {pet: pet.key, food: this.currentDraggingFood.key});
this.feedAction(pet.key, this.currentDraggingFood.key);
this.currentDraggingFood = null;
this.foodClickMode = false;
} else {
if (pet.isOwned() || !pet.isHatchable()) {
if (pet.isOwned()) {
this.selectPet(pet);
return;
}
if (!pet.isHatchable()) {
return;
}
// opens the hatch dialog
@@ -878,6 +923,14 @@
}
},
async feedAction (petKey, foodKey) {
let result = await this.$store.dispatch('common:feed', {pet: petKey, food: foodKey});
if (result.message) {
this.text(result.message);
}
},
closeHatchPetDialog () {
this.$root.$emit('hide::modal', 'hatching-modal');
},
@@ -913,6 +966,12 @@
lastMouseMoveEvent = $event;
}
},
hideFlag () {
this.$store.dispatch('user:set', {
'flags.tutorial.common.mounts': true,
});
},
},
};
</script>

View File

@@ -92,7 +92,7 @@
}
.drawer-slider {
padding: 12px 0 0 0;
padding: 12px 0 0 8px;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;

View File

@@ -10,7 +10,7 @@
div(v-if="items.length === 0")
p(v-once) {{ noItemsLabel }}
.btn.btn-show-more(
.btn-flat.btn-show-more(
@click="showAll = !showAll",
v-if="items.length > itemsPerRow()"
) {{ showAll ? $t('showLess') : $t('showMore') }}

View File

@@ -1,8 +1,10 @@
/*
import {emit} from './directive.common';
import _keys from 'lodash/keys';
import _without from 'lodash/without';
*/
/**
* DRAG_GROUP is a static custom value
* KEY_OF_ITEM
@@ -11,14 +13,15 @@ import _without from 'lodash/without';
* v-drag.drop.DRAG_GROUP="KEY_OF_ITEM" @itemDropped="callback" @itemDragOver="optional"
*/
/*
const DROPPED_EVENT_NAME = 'itemDropped';
const DRAGSTART_EVENT_NAME = 'itemDragStart';
const DRAGEND_EVENT_NAME = 'itemDragEnd';
const DRAGOVER_EVENT_NAME = 'itemDragOver';
const DRAGLEAVE_EVENT_NAME = 'itemDragLeave';
*/
export default {
bind (el, binding, vnode) {
/* bind (el, binding, vnode) {
el.isDropHandler = binding.modifiers.drop === true;
el.dragGroup = _without(_keys(binding.modifiers), 'drop')[0];
el.key = binding.value;
@@ -32,17 +35,20 @@ export default {
};
emit(vnode, DRAGSTART_EVENT_NAME, dragStartEventData);
};
el.addEventListener('dragstart', el.handleDrag);
if(!el.handleDragEnd) {
el.handleDragEnd = () => {
let dragEndEventData = {};
emit(vnode, DRAGEND_EVENT_NAME, dragEndEventData);
};
el.addEventListener('dragend', el.handleDrag);
// need to add the listener after the drag begin, cause its fired right after start :/
setTimeout(function () {
el.addEventListener('dragend', el.handleDragEnd);
}, 50);
}
};
el.addEventListener('dragstart', el.handleDrag);
} else {
el.handleDragOver = (ev) => {
let dragOverEventData = {
@@ -51,6 +57,7 @@ export default {
event: ev,
};
console.info('dragover');
emit(vnode, DRAGOVER_EVENT_NAME, dragOverEventData);
if (dragOverEventData.dropable) {
@@ -62,10 +69,13 @@ export default {
draggingKey: ev.dataTransfer.getData('KEY'),
};
console.info('dragdrop');
emit(vnode, DROPPED_EVENT_NAME, dropEventData);
};
el.handleDragLeave = () => {
console.info('dragleave');
emit(vnode, DRAGLEAVE_EVENT_NAME, {});
};
@@ -73,7 +83,7 @@ export default {
el.addEventListener('dragleave', el.handleDragLeave);
el.addEventListener('drop', el.handleDrop);
}
},
}, */
unbind (el) {
if (!el.isDropHandler) {

View File

@@ -23,12 +23,11 @@ export function hatch (store, params) {
// .catch((err) => console.error('equip', err));
}
export function feed (store, params) {
export async function feed (store, params) {
const user = store.state.user.data;
feedOp(user, {params});
axios
let response = await axios
.post(`/api/v3/user/feed/${params.pet}/${params.food}`);
// TODO
// .then((res) => console.log('equip', res))
// .catch((err) => console.error('equip', err));
return response.data;
}

View File

@@ -662,8 +662,8 @@
"armorArmoireYellowPartyDressText": "Yellow Party Dress",
"armorArmoireYellowPartyDressNotes": "Yellow Party Dress: You're perceptive, strong, smart, and so fashionable! Increases Perception, Strength, and Intelligence by <%= attrs %> each. Enchanted Armoire: Yellow Hairbow Set (Item 2 of 2).",
"headgear": "headgear",
"headgearCapitalized": "Headgear",
"headgear": "helm",
"headgearCapitalized": "Helm",
"headBase0Text": "No Helm",
"headBase0Notes": "No headgear.",

View File

@@ -274,5 +274,9 @@
"self_improvement": "Self-Improvement",
"spirituality": "Spirituality",
"time_management": "Time-Management + Accountability",
"recovery_support_groups": "Recovery + Support Groups"
"recovery_support_groups": "Recovery + Support Groups",
"equip": "Equip",
"unequip": "Unequip",
"sortByName": "Name",
"haveHatchablePet": "You have a <%= potion %> hatching potion and <%= egg %> egg to hatch this pet! <b>Click</b> the paw print to hatch."
}