mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
Attributes UI Refactoring (#9887)
* include class bonus in sorting * wip - show more information in the attributes grid * attributes tooltip + dialog redesign * fix stat calculation * fix spacings * show class in equip-gear-modal * fix buy-modal attributes-grid, clean up css * show attributes popover in profile-stats overview * add class / purchase type colors to colors.scss - replace colors by variable - clean up * translate strings
This commit is contained in:
@@ -61,3 +61,12 @@ $green-100: #5AEAB2;
|
|||||||
$green-500: #A6FFDF;
|
$green-500: #A6FFDF;
|
||||||
|
|
||||||
$suggested-item-color: #D5C8FF;
|
$suggested-item-color: #D5C8FF;
|
||||||
|
|
||||||
|
$healer-color: #cf8229;
|
||||||
|
$rogue-color: #4F2A93;
|
||||||
|
$warrior-color: #B01515;
|
||||||
|
$wizard-color: #1f6ea2;
|
||||||
|
|
||||||
|
$gems-color: #24CC8F;
|
||||||
|
$gold-color: #FFA623;
|
||||||
|
$hourglass-color: #2995CD;
|
||||||
|
|||||||
@@ -28,6 +28,12 @@
|
|||||||
height: 12px;
|
height: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-24 {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.icon-10 {
|
.icon-10 {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
|
|||||||
@@ -36,17 +36,3 @@
|
|||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popover-content-attr {
|
|
||||||
width: 50%;
|
|
||||||
display: inline-block;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
|
|
||||||
&-key {
|
|
||||||
color: $white;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-val {
|
|
||||||
color: $green-10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -68,3 +68,8 @@ h4 {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 1.43;
|
line-height: 1.43;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.textCondensed {
|
||||||
|
font-family: 'Roboto Condensed', sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|||||||
169
website/client/components/inventory/equipment/attributesGrid.vue
Normal file
169
website/client/components/inventory/equipment/attributesGrid.vue
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
div.attributes-group
|
||||||
|
.popover-content-attr(v-for="attr in ATTRIBUTES", :key="attr")
|
||||||
|
.group-content
|
||||||
|
span.popover-content-attr-cell.key(:class="{'hasValue': hasSumValue(attr) }") {{ `${$t(attr)}: ` }}
|
||||||
|
span.popover-content-attr-cell.label.value(:class="{'green': hasSumValue(attr) }") {{ `${stats.sum[attr]}` }}
|
||||||
|
span.popover-content-attr-cell.label.bold(:class="{'hasValue': hasGearValue(attr) }") {{ $t('gear') }}:
|
||||||
|
span.popover-content-attr-cell.label(:class="{'hasValue': hasGearValue(attr) }") {{ stats.gear[attr] }}
|
||||||
|
span.popover-content-attr-cell.label.bold(:class="{'hasValue': hasClassBonus(attr) }") {{ $t('classEquipBonus') }}:
|
||||||
|
span.popover-content-attr-cell.label(:class="{'hasValue': hasClassBonus(attr) }") {{ `${stats.classBonus[attr]}` }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
|
||||||
|
@import '~client/assets/scss/colors.scss';
|
||||||
|
|
||||||
|
|
||||||
|
.attributes-group {
|
||||||
|
border-radius: 4px;
|
||||||
|
// unless we have a way to give a popover an id or class, it needs expand the attributes area
|
||||||
|
margin: -12px -16px;
|
||||||
|
display:flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content-attr {
|
||||||
|
font-weight: bold;
|
||||||
|
width: calc(50% - 1px);
|
||||||
|
background-color: $gray-50;
|
||||||
|
|
||||||
|
&:nth-of-type(even) {
|
||||||
|
margin-left: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(1), &:nth-child(2) {
|
||||||
|
margin-bottom: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-content {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: 4px 12px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content-attr-cell {
|
||||||
|
width: 70%;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
&:nth-of-type(even) {
|
||||||
|
text-align: right;
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.key {
|
||||||
|
color: $white;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1.33;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.label {
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 1.2;
|
||||||
|
color: $gray-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.label.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.label.value {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1.33;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
&.green {
|
||||||
|
color: $green-10;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '+';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
|
||||||
|
.group-content {
|
||||||
|
padding: 8px 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content-attr {
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
|
||||||
|
&:nth-of-type(even) {
|
||||||
|
margin-left: 1px;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content-attr-cell {
|
||||||
|
&.key {
|
||||||
|
color: $gray-400;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1.25;
|
||||||
|
|
||||||
|
&.hasValue {
|
||||||
|
color: $gray-50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.label {
|
||||||
|
color: $gray-400;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1.33;
|
||||||
|
|
||||||
|
&.hasValue {
|
||||||
|
color: $gray-200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.label.value {
|
||||||
|
|
||||||
|
&.green {
|
||||||
|
color: $green-10;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'client/libs/store';
|
||||||
|
import statsMixin from 'client/mixins/stats';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [statsMixin],
|
||||||
|
props: {
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
ATTRIBUTES: 'constants.ATTRIBUTES',
|
||||||
|
user: 'user.data',
|
||||||
|
flatGear: 'content.gear.flat',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
hasSumValue (attr) {
|
||||||
|
return this.stats.sum[attr] > 0;
|
||||||
|
},
|
||||||
|
hasGearValue (attr) {
|
||||||
|
return this.stats.gear[attr] > 0;
|
||||||
|
},
|
||||||
|
hasClassBonus (attr) {
|
||||||
|
return this.stats.classBonus[attr] > 0;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -8,15 +8,25 @@ div
|
|||||||
div(v-else)
|
div(v-else)
|
||||||
h4.popover-content-title {{ itemText }}
|
h4.popover-content-title {{ itemText }}
|
||||||
.popover-content-text {{ itemNotes }}
|
.popover-content-text {{ itemNotes }}
|
||||||
.popover-content-attr(v-for="attr in ATTRIBUTES", :key="attr")
|
attributesGrid(:item="item")
|
||||||
span.popover-content-attr-key {{ `${$t(attr)}: ` }}
|
|
||||||
span.popover-content-attr-val {{ `+${item[attr]}` }}
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.popover-content-text {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'client/libs/store';
|
import { mapState } from 'client/libs/store';
|
||||||
|
import attributesGrid from './attributesGrid';
|
||||||
|
import statsMixin from 'client/mixins/stats';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
mixins: [statsMixin],
|
||||||
|
components: {
|
||||||
|
attributesGrid,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
item: {
|
item: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -25,6 +35,7 @@ div
|
|||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
ATTRIBUTES: 'constants.ATTRIBUTES',
|
ATTRIBUTES: 'constants.ATTRIBUTES',
|
||||||
|
user: 'user.data',
|
||||||
}),
|
}),
|
||||||
itemText () {
|
itemText () {
|
||||||
if (this.item.text instanceof Function) {
|
if (this.item.text instanceof Function) {
|
||||||
|
|||||||
@@ -23,7 +23,11 @@
|
|||||||
h4.title {{ itemText }}
|
h4.title {{ itemText }}
|
||||||
div.text(v-html="itemNotes")
|
div.text(v-html="itemNotes")
|
||||||
|
|
||||||
equipmentAttributesGrid.bordered(
|
span.classTag(v-if="showClassTag")
|
||||||
|
span.svg-icon.inline.icon-24(v-html="icons[itemClass]")
|
||||||
|
span.className.textCondensed(:class="itemClass") {{ getClassName(itemClass) }}
|
||||||
|
|
||||||
|
attributesGrid.attributesGrid(
|
||||||
:item="item",
|
:item="item",
|
||||||
v-if="attributesGridVisible"
|
v-if="attributesGridVisible"
|
||||||
)
|
)
|
||||||
@@ -58,11 +62,41 @@
|
|||||||
width: 282px;
|
width: 282px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bordered {
|
.classTag {
|
||||||
border-radius: 2px;
|
height: 24px;
|
||||||
background-color: #f9f9f9;
|
display: flex;
|
||||||
margin-bottom: 24px;
|
align-items: center;
|
||||||
padding: 24px 24px 10px;
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.className {
|
||||||
|
height: 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.5;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.healer {
|
||||||
|
color: $healer-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rogue {
|
||||||
|
color: $rogue-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warrior {
|
||||||
|
color: $warrior-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard {
|
||||||
|
color: $wizard-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attributesGrid {
|
||||||
|
background-color: $gray-500;
|
||||||
|
|
||||||
|
margin: 10px 0 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
@@ -74,15 +108,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-text {
|
|
||||||
font-family: 'Roboto', sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 1.43;
|
|
||||||
|
|
||||||
width: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.btn.btn-primary {
|
button.btn.btn-primary {
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
@@ -94,19 +119,27 @@
|
|||||||
import { mapState } from 'client/libs/store';
|
import { mapState } from 'client/libs/store';
|
||||||
|
|
||||||
import svgClose from 'assets/svg/close.svg';
|
import svgClose from 'assets/svg/close.svg';
|
||||||
|
import svgWarrior from 'assets/svg/warrior.svg';
|
||||||
|
import svgWizard from 'assets/svg/wizard.svg';
|
||||||
|
import svgRogue from 'assets/svg/rogue.svg';
|
||||||
|
import svgHealer from 'assets/svg/healer.svg';
|
||||||
|
|
||||||
import Avatar from 'client/components/avatar';
|
import Avatar from 'client/components/avatar';
|
||||||
import EquipmentAttributesGrid from 'client/components/shops/market/equipmentAttributesGrid.vue';
|
import attributesGrid from 'client/components/inventory/equipment/attributesGrid.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Avatar,
|
Avatar,
|
||||||
EquipmentAttributesGrid,
|
attributesGrid,
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
icons: Object.freeze({
|
icons: Object.freeze({
|
||||||
close: svgClose,
|
close: svgClose,
|
||||||
|
warrior: svgWarrior,
|
||||||
|
wizard: svgWizard,
|
||||||
|
rogue: svgRogue,
|
||||||
|
healer: svgHealer,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -115,6 +148,9 @@
|
|||||||
content: 'content',
|
content: 'content',
|
||||||
user: 'user.data',
|
user: 'user.data',
|
||||||
}),
|
}),
|
||||||
|
showClassTag () {
|
||||||
|
return this.content.classes.includes(this.itemClass);
|
||||||
|
},
|
||||||
itemText () {
|
itemText () {
|
||||||
if (this.item.text instanceof Function) {
|
if (this.item.text instanceof Function) {
|
||||||
return this.item.text();
|
return this.item.text();
|
||||||
@@ -129,6 +165,9 @@
|
|||||||
return this.item.notes;
|
return this.item.notes;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
itemClass () {
|
||||||
|
return this.item.klass || this.item.specialClass;
|
||||||
|
},
|
||||||
attributesGridVisible () {
|
attributesGridVisible () {
|
||||||
if (this.costumeMode) {
|
if (this.costumeMode) {
|
||||||
return false;
|
return false;
|
||||||
@@ -153,6 +192,13 @@
|
|||||||
[gear.type]: gear.key,
|
[gear.type]: gear.key,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
getClassName (classType) {
|
||||||
|
if (classType === 'wizard') {
|
||||||
|
return this.$t('mage');
|
||||||
|
} else {
|
||||||
|
return this.$t(classType);
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
item: {
|
item: {
|
||||||
|
|||||||
@@ -242,7 +242,19 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
sortItems (items, sortBy) {
|
sortItems (items, sortBy) {
|
||||||
return sortBy === 'sortByName' ? _sortBy(items, sortGearTypeMap[sortBy]) : _reverse(_sortBy(items, sortGearTypeMap[sortBy]));
|
let userClass = this.user.stats.class;
|
||||||
|
|
||||||
|
return sortBy === 'sortByName' ?
|
||||||
|
_sortBy(items, sortGearTypeMap[sortBy]) :
|
||||||
|
_reverse(_sortBy(items, (item) => {
|
||||||
|
let attrToSort = sortGearTypeMap[sortBy];
|
||||||
|
let attrValue = item[attrToSort];
|
||||||
|
if (item.klass === userClass || item.specialClass === userClass) {
|
||||||
|
attrValue *= 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
return attrValue;
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
drawerToggled (newState) {
|
drawerToggled (newState) {
|
||||||
this.$store.state.equipmentDrawerOpen = newState;
|
this.$store.state.equipmentDrawerOpen = newState;
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
div.text(v-html="itemNotes")
|
div.text(v-html="itemNotes")
|
||||||
|
|
||||||
slot(name="additionalInfo", :item="item")
|
slot(name="additionalInfo", :item="item")
|
||||||
equipmentAttributesGrid.bordered(
|
equipmentAttributesGrid.attributesGrid(
|
||||||
v-if="showAttributesGrid",
|
v-if="showAttributesGrid",
|
||||||
:item="item"
|
:item="item"
|
||||||
)
|
)
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
input(type='number', min='0', v-model='selectedAmountToBuy')
|
input(type='number', min='0', v-model='selectedAmountToBuy')
|
||||||
span(:class="{'notEnough': notEnoughCurrency}")
|
span(:class="{'notEnough': notEnoughCurrency}")
|
||||||
span.svg-icon.inline.icon-32(aria-hidden="true", v-html="icons[getPriceClass()]")
|
span.svg-icon.inline.icon-32(aria-hidden="true", v-html="icons[getPriceClass()]")
|
||||||
span.value(:class="getPriceClass()") {{ item.value }}
|
span.cost(:class="getPriceClass()") {{ item.value }}
|
||||||
|
|
||||||
.gems-left(v-if='item.key === "gem"')
|
.gems-left(v-if='item.key === "gem"')
|
||||||
strong(v-if='gemsLeft > 0') {{ gemsLeft }} {{ $t('gemsRemaining') }}
|
strong(v-if='gemsLeft > 0') {{ gemsLeft }} {{ $t('gemsRemaining') }}
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
width: 74px;
|
width: 74px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
background-color: #ffffff;
|
background-color: $white;
|
||||||
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
|
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
|
||||||
margin-right: 24px;
|
margin-right: 24px;
|
||||||
|
|
||||||
@@ -144,15 +144,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-text {
|
|
||||||
font-family: 'Roboto', sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 1.43;
|
|
||||||
|
|
||||||
width: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.svg-icon.inline.icon-32 {
|
span.svg-icon.inline.icon-32 {
|
||||||
height: 32px;
|
height: 32px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
@@ -162,10 +153,9 @@
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.value {
|
.cost {
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
font-family: Roboto;
|
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
line-height: 1.33;
|
line-height: 1.33;
|
||||||
@@ -173,15 +163,15 @@
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
||||||
&.gems {
|
&.gems {
|
||||||
color: $green-10;
|
color: $gems-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.gold {
|
&.gold {
|
||||||
color: $yellow-10
|
color: $gold-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.hourglasses {
|
&.hourglasses {
|
||||||
color: $blue-10;
|
color: $hourglass-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +219,7 @@
|
|||||||
|
|
||||||
.limitedTime {
|
.limitedTime {
|
||||||
height: 32px;
|
height: 32px;
|
||||||
background-color: #6133b4;
|
background-color: $purple-300;
|
||||||
width: calc(100% + 30px);
|
width: calc(100% + 30px);
|
||||||
margin: 0 -15px; // the modal content has its own padding
|
margin: 0 -15px; // the modal content has its own padding
|
||||||
|
|
||||||
@@ -248,8 +238,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bordered {
|
.attributesGrid {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: $gray-500;
|
||||||
|
|
||||||
|
margin: 10px 0 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gems-left {
|
.gems-left {
|
||||||
@@ -277,7 +271,7 @@
|
|||||||
|
|
||||||
import { mapState } from 'client/libs/store';
|
import { mapState } from 'client/libs/store';
|
||||||
|
|
||||||
import EquipmentAttributesGrid from './market/equipmentAttributesGrid.vue';
|
import EquipmentAttributesGrid from '../inventory/equipment/attributesGrid.vue';
|
||||||
|
|
||||||
import Item from 'client/components/inventory/item';
|
import Item from 'client/components/inventory/item';
|
||||||
import Avatar from 'client/components/avatar';
|
import Avatar from 'client/components/avatar';
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
<template lang="pug">
|
|
||||||
div
|
|
||||||
.attribute-entry(v-for="attr in ATTRIBUTES", :key="attr")
|
|
||||||
span.key(:class="{'no-value': item[attr] == '0'}") {{ `${$t(attr)}: ` }}
|
|
||||||
span.val(:class="{'no-value': item[attr] == '0'}") {{ `+${item[attr]}` }}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
|
|
||||||
@import '~client/assets/scss/colors.scss';
|
|
||||||
|
|
||||||
.attribute-entry {
|
|
||||||
width: 50%;
|
|
||||||
display: inline-block;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span{
|
|
||||||
width: 38px;
|
|
||||||
height: 16px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
line-height: 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-value {
|
|
||||||
color: $gray-400 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.key {
|
|
||||||
color: $gray-100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.val {
|
|
||||||
color: $green-10;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapState } from 'client/libs/store';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
item: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapState({
|
|
||||||
ATTRIBUTES: 'constants.ATTRIBUTES',
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@@ -255,13 +255,6 @@
|
|||||||
margin: 24px auto;
|
margin: 24px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bordered {
|
|
||||||
border-radius: 2px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
padding: 24px 24px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-wrapper.bordered-item .item {
|
.item-wrapper.bordered-item .item {
|
||||||
width: 112px;
|
width: 112px;
|
||||||
height: 112px;
|
height: 112px;
|
||||||
@@ -359,7 +352,7 @@
|
|||||||
import Avatar from 'client/components/avatar';
|
import Avatar from 'client/components/avatar';
|
||||||
|
|
||||||
import SellModal from './sellModal.vue';
|
import SellModal from './sellModal.vue';
|
||||||
import EquipmentAttributesGrid from './equipmentAttributesGrid.vue';
|
import EquipmentAttributesGrid from '../../inventory/equipment/attributesGrid.vue';
|
||||||
import SelectMembersModal from 'client/components/selectMembersModal.vue';
|
import SelectMembersModal from 'client/components/selectMembersModal.vue';
|
||||||
|
|
||||||
import svgPin from 'assets/svg/pin.svg';
|
import svgPin from 'assets/svg/pin.svg';
|
||||||
|
|||||||
@@ -63,15 +63,6 @@
|
|||||||
width: 282px;
|
width: 282px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-text {
|
|
||||||
font-family: 'Roboto', sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 1.43;
|
|
||||||
|
|
||||||
width: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.svg-icon.inline.icon-32 {
|
span.svg-icon.inline.icon-32 {
|
||||||
height: 32px;
|
height: 32px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
|
|||||||
@@ -81,14 +81,6 @@
|
|||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-text {
|
|
||||||
font-family: 'Roboto', sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 1.43;
|
|
||||||
width: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-sidebar {
|
.right-sidebar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -350px;
|
right: -350px;
|
||||||
|
|||||||
@@ -241,13 +241,6 @@
|
|||||||
margin: 24px auto;
|
margin: 24px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bordered {
|
|
||||||
border-radius: 2px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
padding: 24px 24px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.group {
|
.group {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 33%;
|
width: 33%;
|
||||||
|
|||||||
@@ -149,13 +149,6 @@
|
|||||||
margin: 24px auto;
|
margin: 24px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bordered {
|
|
||||||
border-radius: 2px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
padding: 24px 24px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.group {
|
.group {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
|
|||||||
@@ -129,13 +129,6 @@
|
|||||||
margin: 24px auto;
|
margin: 24px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bordered {
|
|
||||||
border-radius: 2px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
padding: 24px 24px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.group {
|
.group {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 33%;
|
width: 33%;
|
||||||
|
|||||||
@@ -123,39 +123,26 @@ div
|
|||||||
.col-12.col-md-6
|
.col-12.col-md-6
|
||||||
h2.text-center {{$t('equipment')}}
|
h2.text-center {{$t('equipment')}}
|
||||||
.well
|
.well
|
||||||
.col-12.col-md-4.item-wrapper
|
.col-12.col-md-4.item-wrapper(v-for="(label, key) in equipTypes")
|
||||||
.box(:class='{white: equippedItems.eyewear && equippedItems.eyewear.indexOf("base_0") === -1}')
|
.box(
|
||||||
div(:class="`shop_${equippedItems.eyewear}`")
|
:id="key",
|
||||||
h3 {{$t('eyewear')}}
|
v-if="label !== 'skip'",
|
||||||
.col-12.col-md-4.item-wrapper
|
:class='{white: equippedItems[key] && equippedItems[key].indexOf("base_0") === -1}'
|
||||||
.box(:class='{white: equippedItems.head && equippedItems.head.indexOf("base_0") === -1}')
|
)
|
||||||
div(:class="`shop_${equippedItems.head}`")
|
div(:class="`shop_${equippedItems[key]}`")
|
||||||
h3 {{$t('headgearCapitalized')}}
|
b-popover(
|
||||||
.col-12.col-md-4.item-wrapper
|
v-if="label !== 'skip' && equippedItems[key] && equippedItems[key].indexOf(\"base_0\") === -1",
|
||||||
.box(:class='{white: equippedItems.headAccessory && equippedItems.headAccessory.indexOf("base_0") === -1}')
|
:target="key",
|
||||||
div(:class="`shop_${equippedItems.headAccessory}`")
|
triggers="hover",
|
||||||
h3 {{$t('headAccess')}}
|
:placement="'right'",
|
||||||
.col-12.col-md-4.item-wrapper
|
:preventOverflow="false",
|
||||||
.box(:class='{white: equippedItems.back && equippedItems.back.indexOf("base_0") === -1}')
|
)
|
||||||
div(:class="`shop_${equippedItems.back}`")
|
h4.gearTitle {{ getGearTitle(equippedItems[key]) }}
|
||||||
h3 {{$t('backAccess')}}
|
attributesGrid.attributesGrid(
|
||||||
.col-12.col-md-4.item-wrapper
|
:item="content.gear.flat[equippedItems[key]]",
|
||||||
.box(:class='{white: equippedItems.armor && equippedItems.armor.indexOf("base_0") === -1}')
|
)
|
||||||
div(:class="`shop_${equippedItems.armor}`")
|
|
||||||
h3 {{$t('armorCapitalized')}}
|
h3(v-if="label !== 'skip'") {{ label }}
|
||||||
.col-12.col-md-4.item-wrapper
|
|
||||||
.box(:class='{white: equippedItems.body && equippedItems.body.indexOf("base_0") === -1}')
|
|
||||||
div(:class="`shop_${equippedItems.body}`")
|
|
||||||
h3 {{$t('bodyAccess')}}
|
|
||||||
.col-12.col-md-4.item-wrapper
|
|
||||||
.box(:class='{white: equippedItems.weapon && equippedItems.weapon.indexOf("base_0") === -1}')
|
|
||||||
div(:class="`shop_${equippedItems.weapon}`")
|
|
||||||
h3 {{$t('mainHand')}}
|
|
||||||
.col-12.col-md-4.item-wrapper
|
|
||||||
.col-12.col-md-4.item-wrapper
|
|
||||||
.box(:class='{white: equippedItems.shield && equippedItems.shield.indexOf("base_0") === -1}')
|
|
||||||
div(:class="`shop_${equippedItems.shield}`")
|
|
||||||
h3 {{$t('offHand')}}
|
|
||||||
.col-12.col-md-6
|
.col-12.col-md-6
|
||||||
h2.text-center {{$t('costume')}}
|
h2.text-center {{$t('costume')}}
|
||||||
.well
|
.well
|
||||||
@@ -292,6 +279,11 @@ div
|
|||||||
.modal-content {
|
.modal-content {
|
||||||
background: #f9f9f9;
|
background: #f9f9f9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gearTitle {
|
||||||
|
color: white;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-icon svg {
|
.message-icon svg {
|
||||||
@@ -595,6 +587,7 @@ import achievementsLib from '../../../common/script/libs/achievements';
|
|||||||
// @TODO: EMAILS.COMMUNITY_MANAGER_EMAIL
|
// @TODO: EMAILS.COMMUNITY_MANAGER_EMAIL
|
||||||
const COMMUNITY_MANAGER_EMAIL = 'admin@habitica.com';
|
const COMMUNITY_MANAGER_EMAIL = 'admin@habitica.com';
|
||||||
import Content from '../../../common/script/content';
|
import Content from '../../../common/script/content';
|
||||||
|
import attributesGrid from 'client/components/inventory/equipment/attributesGrid';
|
||||||
const DROP_ANIMALS = keys(Content.pets);
|
const DROP_ANIMALS = keys(Content.pets);
|
||||||
const TOTAL_NUMBER_OF_DROP_ANIMALS = DROP_ANIMALS.length;
|
const TOTAL_NUMBER_OF_DROP_ANIMALS = DROP_ANIMALS.length;
|
||||||
|
|
||||||
@@ -612,6 +605,7 @@ export default {
|
|||||||
sendGemsModal,
|
sendGemsModal,
|
||||||
MemberDetails,
|
MemberDetails,
|
||||||
toggleSwitch,
|
toggleSwitch,
|
||||||
|
attributesGrid,
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -636,6 +630,17 @@ export default {
|
|||||||
selectedPage: 'profile',
|
selectedPage: 'profile',
|
||||||
achievements: {},
|
achievements: {},
|
||||||
content: Content,
|
content: Content,
|
||||||
|
equipTypes: {
|
||||||
|
eyewear: this.$t('eyewear'),
|
||||||
|
head: this.$t('headgearCapitalized'),
|
||||||
|
headAccessory: this.$t('headAccess'),
|
||||||
|
back: this.$t('backAccess'),
|
||||||
|
armor: this.$t('armorCapitalized'),
|
||||||
|
body: this.$t('bodyAccess'),
|
||||||
|
weapon: this.$t('mainHand'),
|
||||||
|
_skip: 'skip',
|
||||||
|
shield: this.$t('offHand'),
|
||||||
|
},
|
||||||
stats: {
|
stats: {
|
||||||
str: {
|
str: {
|
||||||
title: 'strength',
|
title: 'strength',
|
||||||
@@ -755,6 +760,9 @@ export default {
|
|||||||
userName: this.user.profile.name,
|
userName: this.user.profile.name,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
getGearTitle (key) {
|
||||||
|
return this.flatGear[key].text();
|
||||||
|
},
|
||||||
getProgressDisplay () {
|
getProgressDisplay () {
|
||||||
// let currentLoginDay = Content.loginIncentives[this.user.loginIncentives];
|
// let currentLoginDay = Content.loginIncentives[this.user.loginIncentives];
|
||||||
// if (!currentLoginDay) return this.$t('checkinReceivedAllRewardsMessage');
|
// if (!currentLoginDay) return this.$t('checkinReceivedAllRewardsMessage');
|
||||||
|
|||||||
69
website/client/mixins/stats.js
Normal file
69
website/client/mixins/stats.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
let emptyStats = () => {
|
||||||
|
return {
|
||||||
|
str: 0,
|
||||||
|
int: 0,
|
||||||
|
per: 0,
|
||||||
|
con: 0,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
computed: {
|
||||||
|
stats () {
|
||||||
|
let gearStats = this.getItemStats(this.item);
|
||||||
|
let classBonus = this.getClassBonus(this.item);
|
||||||
|
|
||||||
|
let sumNewGearStats = this.calculateStats(gearStats, classBonus, (a1, a2) => {
|
||||||
|
return a1 + a2;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
gear: gearStats,
|
||||||
|
classBonus,
|
||||||
|
sum: sumNewGearStats,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getItemStats (item) {
|
||||||
|
let result = emptyStats();
|
||||||
|
|
||||||
|
if (!item) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let attr of this.ATTRIBUTES) {
|
||||||
|
result[attr] = Number(item[attr]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
getClassBonus (item) {
|
||||||
|
let result = emptyStats();
|
||||||
|
|
||||||
|
if (!item) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
let itemStats = this.getItemStats(item);
|
||||||
|
|
||||||
|
let userClass = this.user.stats.class;
|
||||||
|
if (userClass === item.klass || userClass === item.specialClass) {
|
||||||
|
for (let attr of this.ATTRIBUTES) {
|
||||||
|
result[attr] = itemStats[attr] * 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
calculateStats (srcStats, otherStats, func) {
|
||||||
|
let result = emptyStats();
|
||||||
|
|
||||||
|
for (let attr of this.ATTRIBUTES) {
|
||||||
|
result[attr] = func(srcStats[attr], otherStats[attr]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -64,6 +64,7 @@
|
|||||||
"classBonusText": "Your class (Warrior, if you haven't unlocked or selected another class) uses its own equipment more effectively than gear from other classes. Equipped gear from your current class gets a 50% boost to the Stat bonus it grants.",
|
"classBonusText": "Your class (Warrior, if you haven't unlocked or selected another class) uses its own equipment more effectively than gear from other classes. Equipped gear from your current class gets a 50% boost to the Stat bonus it grants.",
|
||||||
"classEquipBonus": "Class Bonus",
|
"classEquipBonus": "Class Bonus",
|
||||||
"battleGear": "Battle Gear",
|
"battleGear": "Battle Gear",
|
||||||
|
"gear": "Gear",
|
||||||
"battleGearText": "This is the gear you wear into battle; it affects numbers when interacting with your tasks.",
|
"battleGearText": "This is the gear you wear into battle; it affects numbers when interacting with your tasks.",
|
||||||
"autoEquipBattleGear": "Auto-equip new gear",
|
"autoEquipBattleGear": "Auto-equip new gear",
|
||||||
"costume": "Costume",
|
"costume": "Costume",
|
||||||
|
|||||||
Reference in New Issue
Block a user