mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-15 21:57:22 +01:00
Client Redesign: Inventory pages, secondary menu, misc css and design items (#8631)
* add colors palette * add secondary menu component and style it * add box shadow to secondary menu * misc css, fixes for secondary menu * client: add equipment page with grouping, css: add some styles * add typography * more equipment * stable: fix linting * equipment: add styles (lots of general styles too) * remove duplicate google fonts loading * add dropdowns * design: white search input background, remove gray from items * start adding drawer and selected indicator * wip equipment * fix equipment * equipment: correctly bind new properties on items.gear.equipped * equipment: fix vue binding. version 2 * equipment: fix vue binding. version 3 * back to first fix for equip op, fix for sourcemaps, send http request when an item is equipped, load bootstrap-vue components where needed * checkboxes and radio buttons * correctly renders selected items in first postion during the first render * add search * general changes, constants part of app state, add popovers * add toggle switch, rename css * correct offset * upgrade deps * upgrade deps * drawer and lot of other work * update equipping mechanism * finish equipment * fix compilation and upgrade deps * use v-show in place of v-if to fix ui issues * v-show -> v-if * fix linting in test/client * fix es6 compilation in test/client * fix babel compilation for tests * fix groupsUtilities mixin tests * client: buttons * client: buttons: fix colors * client: finish buttons and dropdowns * upgrade bootstrap-vue, finish buttons and dropdowns * fix tasks page layout * misc fixes for buttons * add textareas * fix app menu * add inputs * fixes for toggleSwitch * typography * checkboxes and radio buttons * add checkbox icon * fix equip.js * extract strings to newClient.json * add Popover above 'Use Costume' / 'Auto Equip' slider - disable item select if costume-mode and 'useCostume' isn't active * show "you have disabled your costume" error above the drawer items * check errorMessage for null * hide star if costume not enabled * fix errorMessage (!errorMessage seems not to work for string) * show minimize / expand icon - always centered by css * drawer test * drawer: fix centering on large screens * fix show more button * add margin when two dropdowns are next to each other * adjust the page padding based on the drawer, misc fixes * drawer fixes
This commit is contained in:
2383
npm-shrinkwrap.json
generated
2383
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@@ -13,7 +13,7 @@
|
|||||||
"async": "^1.5.0",
|
"async": "^1.5.0",
|
||||||
"autoprefixer": "^6.4.0",
|
"autoprefixer": "^6.4.0",
|
||||||
"aws-sdk": "^2.0.25",
|
"aws-sdk": "^2.0.25",
|
||||||
"axios": "^0.15.3",
|
"axios": "^0.16.0",
|
||||||
"babel-core": "^6.0.0",
|
"babel-core": "^6.0.0",
|
||||||
"babel-loader": "^6.0.0",
|
"babel-loader": "^6.0.0",
|
||||||
"babel-plugin-syntax-async-functions": "^6.13.0",
|
"babel-plugin-syntax-async-functions": "^6.13.0",
|
||||||
@@ -29,13 +29,14 @@
|
|||||||
"bluebird": "^3.3.5",
|
"bluebird": "^3.3.5",
|
||||||
"body-parser": "^1.15.0",
|
"body-parser": "^1.15.0",
|
||||||
"bootstrap": "^4.0.0-alpha.6",
|
"bootstrap": "^4.0.0-alpha.6",
|
||||||
|
"bootstrap-vue": "^0.15.8",
|
||||||
"bower": "~1.3.12",
|
"bower": "~1.3.12",
|
||||||
"browserify": "~12.0.1",
|
"browserify": "~12.0.1",
|
||||||
"compression": "^1.6.1",
|
"compression": "^1.6.1",
|
||||||
"connect-ratelimit": "0.0.7",
|
"connect-ratelimit": "0.0.7",
|
||||||
"cookie-session": "^1.2.0",
|
"cookie-session": "^1.2.0",
|
||||||
"coupon-code": "^0.4.5",
|
"coupon-code": "^0.4.5",
|
||||||
"css-loader": "^0.26.1",
|
"css-loader": "^0.28.0",
|
||||||
"csv-stringify": "^1.0.2",
|
"csv-stringify": "^1.0.2",
|
||||||
"cwait": "^1.0.0",
|
"cwait": "^1.0.0",
|
||||||
"domain-middleware": "~0.1.0",
|
"domain-middleware": "~0.1.0",
|
||||||
@@ -96,7 +97,7 @@
|
|||||||
"postcss-easy-import": "^2.0.0",
|
"postcss-easy-import": "^2.0.0",
|
||||||
"pretty-data": "^0.40.0",
|
"pretty-data": "^0.40.0",
|
||||||
"ps-tree": "^1.0.0",
|
"ps-tree": "^1.0.0",
|
||||||
"pug": "^2.0.0-beta11",
|
"pug": "^2.0.0-beta.12",
|
||||||
"push-notify": "habitrpg/push-notify#v1.2.0",
|
"push-notify": "habitrpg/push-notify#v1.2.0",
|
||||||
"pusher": "^1.3.0",
|
"pusher": "^1.3.0",
|
||||||
"request": "~2.74.0",
|
"request": "~2.74.0",
|
||||||
@@ -119,10 +120,10 @@
|
|||||||
"vue-loader": "^11.0.0",
|
"vue-loader": "^11.0.0",
|
||||||
"vue-mugen-scroll": "^0.2.1",
|
"vue-mugen-scroll": "^0.2.1",
|
||||||
"vue-router": "^2.0.0-rc.5",
|
"vue-router": "^2.0.0-rc.5",
|
||||||
"vue-style-loader": "^2.0.0",
|
"vue-style-loader": "^3.0.0",
|
||||||
"vue-template-compiler": "^2.1.10",
|
"vue-template-compiler": "^2.1.10",
|
||||||
"webpack": "^2.2.1",
|
"webpack": "^2.2.1",
|
||||||
"webpack-merge": "^2.6.1",
|
"webpack-merge": "^4.0.0",
|
||||||
"winston": "^2.1.0",
|
"winston": "^2.1.0",
|
||||||
"winston-loggly-bulk": "^1.4.2",
|
"winston-loggly-bulk": "^1.4.2",
|
||||||
"xml2js": "^0.4.4"
|
"xml2js": "^0.4.4"
|
||||||
@@ -168,7 +169,7 @@
|
|||||||
"chromedriver": "^2.27.2",
|
"chromedriver": "^2.27.2",
|
||||||
"connect-history-api-fallback": "^1.1.0",
|
"connect-history-api-fallback": "^1.1.0",
|
||||||
"coveralls": "^2.11.2",
|
"coveralls": "^2.11.2",
|
||||||
"cross-env": "^3.1.4",
|
"cross-env": "^4.0.0",
|
||||||
"cross-spawn": "^5.0.1",
|
"cross-spawn": "^5.0.1",
|
||||||
"csv": "~0.3.6",
|
"csv": "~0.3.6",
|
||||||
"deep-diff": "~0.1.4",
|
"deep-diff": "~0.1.4",
|
||||||
@@ -192,7 +193,7 @@
|
|||||||
"karma-mocha": "^0.2.0",
|
"karma-mocha": "^0.2.0",
|
||||||
"karma-mocha-reporter": "^1.1.1",
|
"karma-mocha-reporter": "^1.1.1",
|
||||||
"karma-phantomjs-launcher": "^1.0.0",
|
"karma-phantomjs-launcher": "^1.0.0",
|
||||||
"karma-sinon-chai": "^1.2.0",
|
"karma-sinon-chai": "~1.2.0",
|
||||||
"karma-sinon-stub-promise": "^1.0.0",
|
"karma-sinon-stub-promise": "^1.0.0",
|
||||||
"karma-sourcemap-loader": "^0.3.7",
|
"karma-sourcemap-loader": "^0.3.7",
|
||||||
"karma-spec-reporter": "0.0.24",
|
"karma-spec-reporter": "0.0.24",
|
||||||
|
|||||||
@@ -2,5 +2,10 @@
|
|||||||
"env": {
|
"env": {
|
||||||
"node": true,
|
"node": true,
|
||||||
"browser": true,
|
"browser": true,
|
||||||
}
|
},
|
||||||
|
"extends": [
|
||||||
|
"habitrpg/browser",
|
||||||
|
"habitrpg/mocha",
|
||||||
|
"habitrpg/esnext",
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
19
test/client/unit/specs/components/inventory/drawer.js
Normal file
19
test/client/unit/specs/components/inventory/drawer.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import Vue from 'vue';
|
||||||
|
import DrawerComponent from 'client/components/inventory/drawer.vue';
|
||||||
|
|
||||||
|
describe('DrawerComponent', () => {
|
||||||
|
it('sets the correct default data', () => {
|
||||||
|
expect(DrawerComponent.data).to.be.a('function');
|
||||||
|
const defaultData = DrawerComponent.data();
|
||||||
|
expect(defaultData.open).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the correct title', () => {
|
||||||
|
const Ctor = Vue.extend(DrawerComponent);
|
||||||
|
const vm = new Ctor({propsData: {
|
||||||
|
title: 'My title',
|
||||||
|
}}).$mount();
|
||||||
|
|
||||||
|
expect(vm.$el.textContent).to.be.equal('My title');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import groupsUtilities from 'client/mixins/groupsUtilities';
|
import groupsUtilities from 'client/mixins/groupsUtilities';
|
||||||
import { TAVERN_ID } from 'common/script/constants';
|
import { TAVERN_ID } from 'common/script/constants';
|
||||||
|
import generateStore from 'client/store';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
describe('Groups Utilities Mixin', () => {
|
describe('Groups Utilities Mixin', () => {
|
||||||
@@ -7,6 +8,7 @@ describe('Groups Utilities Mixin', () => {
|
|||||||
|
|
||||||
before(() => {
|
before(() => {
|
||||||
instance = new Vue({
|
instance = new Vue({
|
||||||
|
store: generateStore(),
|
||||||
mixins: [groupsUtilities],
|
mixins: [groupsUtilities],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -17,21 +17,31 @@ const baseConfig = {
|
|||||||
path: config.build.assetsRoot,
|
path: config.build.assetsRoot,
|
||||||
publicPath: IS_PROD ? config.build.assetsPublicPath : config.dev.assetsPublicPath,
|
publicPath: IS_PROD ? config.build.assetsPublicPath : config.dev.assetsPublicPath,
|
||||||
filename: '[name].js',
|
filename: '[name].js',
|
||||||
|
devtoolModuleFilenameTemplate (info) {
|
||||||
|
// Fix source maps, code from
|
||||||
|
// https://github.com/Darkside73/bbsmile.com.ua/commit/3596d3c42ef91b69d8380359c3e8908edc08acdb
|
||||||
|
let filename = info.resourcePath;
|
||||||
|
if (info.resource.match(/\.vue$/) && !info.allLoaders.match(/type=script/)) {
|
||||||
|
filename = 'generated';
|
||||||
|
}
|
||||||
|
|
||||||
|
return filename;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['*', '.js', '.vue', '.json'],
|
extensions: ['*', '.js', '.vue', '.json'],
|
||||||
modules: [
|
modules: [
|
||||||
path.join(__dirname, '..', 'website'),
|
path.join(projectRoot, 'website'),
|
||||||
path.join(__dirname, '..', 'test/client/unit'),
|
path.join(projectRoot, 'test/client/unit'),
|
||||||
path.join(__dirname, '..', 'node_modules'),
|
path.join(projectRoot, 'node_modules'),
|
||||||
],
|
],
|
||||||
alias: {
|
alias: {
|
||||||
jquery: 'jquery/src/jquery',
|
jquery: 'jquery/src/jquery',
|
||||||
website: path.resolve(__dirname, '../website'),
|
website: path.resolve(projectRoot, 'website'),
|
||||||
common: path.resolve(__dirname, '../website/common'),
|
common: path.resolve(projectRoot, 'website/common'),
|
||||||
client: path.resolve(__dirname, '../website/client'),
|
client: path.resolve(projectRoot, 'website/client'),
|
||||||
assets: path.resolve(__dirname, '../website/client/assets'),
|
assets: path.resolve(projectRoot, 'website/client/assets'),
|
||||||
components: path.resolve(__dirname, '../website/client/components'),
|
components: path.resolve(projectRoot, 'website/client/components'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
@@ -63,8 +73,14 @@ const baseConfig = {
|
|||||||
{
|
{
|
||||||
test: /\.js$/,
|
test: /\.js$/,
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
include: projectRoot,
|
include: [
|
||||||
exclude: /node_modules/,
|
path.join(projectRoot, 'test'),
|
||||||
|
path.join(projectRoot, 'website'),
|
||||||
|
path.join(projectRoot, 'node_modules', 'bootstrap-vue'),
|
||||||
|
],
|
||||||
|
options: {
|
||||||
|
cacheDirectory: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
|
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
|
||||||
|
|||||||
5
website/client/assets/drawer/expand.svg
Normal file
5
website/client/assets/drawer/expand.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="11" viewBox="0 0 10 11">
|
||||||
|
<g fill="none" fill-rule="evenodd" stroke="#878190" stroke-width="2">
|
||||||
|
<path d="M5 3v8M9 6L5 2 1 6"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 213 B |
3
website/client/assets/drawer/minimize.svg
Normal file
3
website/client/assets/drawer/minimize.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="2" viewBox="0 0 10 2">
|
||||||
|
<path fill="none" fill-rule="evenodd" stroke="#878190" stroke-width="2" d="M0 1h10"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 179 B |
15
website/client/assets/scss/badge.scss
Normal file
15
website/client/assets/scss/badge.scss
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
.badge {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1.33;
|
||||||
|
color: $gray-200;
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-pill {
|
||||||
|
border-radius: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-default {
|
||||||
|
background: $gray-500;
|
||||||
|
}
|
||||||
125
website/client/assets/scss/button.scss
Normal file
125
website/client/assets/scss/button.scss
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
@mixin btn-focus-hover-shadow () {
|
||||||
|
box-shadow: 0 4px 4px 0 rgba($black, 0.16), 0 1px 8px 0 rgba($black, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: 'Roboto Condensed', sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1.5;
|
||||||
|
border: 1px solid transparent !important;
|
||||||
|
padding: 7.5px 15.5px;
|
||||||
|
border-radius: 2px;
|
||||||
|
box-shadow: 0 2px 2px 0 rgba($black, 0.16), 0 1px 4px 0 rgba($black, 0.12);
|
||||||
|
color: $white;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: $purple-400;
|
||||||
|
@include btn-focus-hover-shadow();
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
@include btn-focus-hover-shadow();
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
box-shadow: none;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:disabled, .btn.disabled {
|
||||||
|
box-shadow: none;
|
||||||
|
opacity: 0.64;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: $purple-200;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background: $purple-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(:disabled), &:active {
|
||||||
|
background: #5d3b9c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary, .dropdown > .btn-secondary {
|
||||||
|
color: $gray-50;
|
||||||
|
background: $white !important;
|
||||||
|
|
||||||
|
&:hover:not(:disabled):not(.disabled), &:active, &:focus {
|
||||||
|
color: $purple-200 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active, &:focus {
|
||||||
|
border-color: $purple-500 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled, &.disabled {
|
||||||
|
background: $gray-500 !important;
|
||||||
|
color: $gray-100 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success {
|
||||||
|
background: $green-10;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background: $green-10;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(:disabled), &:active {
|
||||||
|
background: $green-50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-info {
|
||||||
|
background: $blue-50;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background: $blue-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(:disabled), &:active {
|
||||||
|
background: $blue-100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger {
|
||||||
|
background: $red-50;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background: $red-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(:disabled), &:active {
|
||||||
|
background: $red-100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-show-more {
|
||||||
|
display: block;
|
||||||
|
width: 50%;
|
||||||
|
max-width: 448px;
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.43;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
background: $gray-600;
|
||||||
|
color: $gray-200 !important; // Otherwise it gets ignored when used on an A element
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: none;
|
||||||
|
color: inherit !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
58
website/client/assets/scss/colors.scss
Normal file
58
website/client/assets/scss/colors.scss
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// Colors taken from the Habitica Color Palette
|
||||||
|
// The palette is available at TODO ADD LINK TO PALETTE PDF
|
||||||
|
// The colors are named from the darkest to the lightest
|
||||||
|
|
||||||
|
$white: #FFFFFF;
|
||||||
|
$black: #1A181D;
|
||||||
|
|
||||||
|
$gray-10: #34313A;
|
||||||
|
$gray-50: #4E4A57;
|
||||||
|
$gray-100: #686274;
|
||||||
|
$gray-200: #878190;
|
||||||
|
$gray-300: #A5A1AC;
|
||||||
|
$gray-400: #C3C0C7;
|
||||||
|
$gray-500: #E1E0E3;
|
||||||
|
$gray-600: #EDECEE;
|
||||||
|
$gray-700: #F9F9F9;
|
||||||
|
|
||||||
|
$purple-50: #36205D;
|
||||||
|
$purple-100: #432874;
|
||||||
|
$purple-200: #4F2A93;
|
||||||
|
$purple-300: #6133B4;
|
||||||
|
$purple-400: #9A62FF;
|
||||||
|
$purple-500: #BDA8FF;
|
||||||
|
|
||||||
|
$red-10: #F23035;
|
||||||
|
$red-50: #F74E52;
|
||||||
|
$red-100: #FF6165;
|
||||||
|
$red-500: #FFB6B8;
|
||||||
|
|
||||||
|
$maroon-10: #B01515;
|
||||||
|
$maroon-50: #C92B2B;
|
||||||
|
$maroon-100: #DE3F3F;
|
||||||
|
$maroon-500: #F19595;
|
||||||
|
|
||||||
|
$yellow-10: #FFA623;
|
||||||
|
$yellow-50: #FFB445;
|
||||||
|
$yellow-100: #FFBE5D;
|
||||||
|
$yellow-500: #FFD9A0;
|
||||||
|
|
||||||
|
$orange-10: #F47825;
|
||||||
|
$orange-50: #FA8537;
|
||||||
|
$orange-100: #FF944C;
|
||||||
|
$orange-500: #FFBF98;
|
||||||
|
|
||||||
|
$blue-10: #2995CD;
|
||||||
|
$blue-50: #46A7D9;
|
||||||
|
$blue-100: #50B5E9;
|
||||||
|
$blue-500: #A9DCF6;
|
||||||
|
|
||||||
|
$teal-10: #20B2BF;
|
||||||
|
$teal-50: #3BCAD7;
|
||||||
|
$teal-100: #5EDDE9;
|
||||||
|
$teal-500: #A5F7FF;
|
||||||
|
|
||||||
|
$green-10: #24CC8F;
|
||||||
|
$green-50: #3FDAA2;
|
||||||
|
$green-100: #5AEAB2;
|
||||||
|
$green-500: #A6FFDF;
|
||||||
51
website/client/assets/scss/dropdown.scss
Normal file
51
website/client/assets/scss/dropdown.scss
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
.dropdown > .btn {
|
||||||
|
padding: 9px 15.5px;
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 1.43;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown.show > .dropdown-toggle {
|
||||||
|
color: $purple-200;
|
||||||
|
border-color: $purple-500 !important;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-toggle::after {
|
||||||
|
margin-left: 16px;
|
||||||
|
border-top: 6px solid;
|
||||||
|
border-right: 5px solid transparent;
|
||||||
|
border-left: 5px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
padding: 0px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 2px 2px 0 rgba($black, 0.15), 0 1px 4px 0 rgba($white, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item {
|
||||||
|
padding-left: 24px;
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.71;
|
||||||
|
color: $gray-50;
|
||||||
|
cursor: pointer;
|
||||||
|
border-bottom: 1px solid $gray-500;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active, &:hover, &:focus, &.active, &.dropdown-item-active {
|
||||||
|
background-color: rgba(#d5c8ff, 0.32);
|
||||||
|
color: $purple-200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown + .dropdown {
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
168
website/client/assets/scss/form.scss
Normal file
168
website/client/assets/scss/form.scss
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
.nested-field {
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inputs and texteares
|
||||||
|
|
||||||
|
input, textarea, input.form-control, textarea.form-control {
|
||||||
|
padding: 10px 16px;
|
||||||
|
border-radius: 2px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.43;
|
||||||
|
color: $gray-200;
|
||||||
|
border: 1px solid $gray-400;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
border-color: $gray-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active:not(:disabled), &:focus:not(:disabled) {
|
||||||
|
border-color: $purple-500;
|
||||||
|
color: $gray-50;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.64;
|
||||||
|
background: $gray-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.input-search { // TODO Abstract to work with all icons
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center left 16px;
|
||||||
|
background-size: 16px 16px;
|
||||||
|
background-image: url(~client/assets/svg/search.svg);
|
||||||
|
padding-left: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.input-valid, &.input-invalid {
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center right 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.input-valid {
|
||||||
|
padding-right: 37px;
|
||||||
|
background-image: url(~client/assets/svg/check.svg);
|
||||||
|
background-size: 13px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.input-invalid {
|
||||||
|
padding-right: 40px;
|
||||||
|
background-image: url(~client/assets/svg/alert.svg);
|
||||||
|
background-size: 16px 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checkboxes and radios
|
||||||
|
$bg-focused-active-control: #4f2993;
|
||||||
|
$bg-disabled-control: #34303a;
|
||||||
|
|
||||||
|
.custom-control {
|
||||||
|
&-description {
|
||||||
|
padding-top: 3px;
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .custom-control-indicator {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
background-size: 75% 75%;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 2px solid $gray-200;
|
||||||
|
transition-property: box-shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .custom-control-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-checkbox {
|
||||||
|
.custom-control-indicator {
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-control-input {
|
||||||
|
&:checked~.custom-control-indicator {
|
||||||
|
background-image: url(~client/assets/svg/checkbox-white.svg);
|
||||||
|
background-color: $purple-400;
|
||||||
|
border-color: $purple-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active~.custom-control-indicator {
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus:not(:checked):not(:disabled)~.custom-control-indicator, &:active:not(:checked):not(:disabled)~.custom-control-indicator {
|
||||||
|
box-shadow: 0 0 0 6px rgba($bg-focused-active-control, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus:checked:not(:disabled)~.custom-control-indicator, &:active:checked:not(:disabled)~.custom-control-indicator {
|
||||||
|
box-shadow: 0 0 0 6px rgba($bg-focused-active-control, 0.1);
|
||||||
|
border-color: $purple-400;
|
||||||
|
background-color: $purple-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus:disabled~.custom-control-indicator, &:active:disabled~.custom-control-indicator {
|
||||||
|
box-shadow: 0 0 0 6px rgba($bg-disabled-control, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled:checked~.custom-control-indicator {
|
||||||
|
border-color: $gray-400;
|
||||||
|
background-color: $gray-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled:not(:checked)~.custom-control-indicator {
|
||||||
|
border-color: $gray-400;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin custom-radio-checked-icon ($bg-color) {
|
||||||
|
background-image: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='#{$bg-color}'/%3E%3C/svg%3E"), "#", "%23");
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-radio .custom-control-input {
|
||||||
|
&:checked~.custom-control-indicator {
|
||||||
|
@include custom-radio-checked-icon($purple-400);
|
||||||
|
background-color: $gray-700;
|
||||||
|
background-size: 12px 12px;
|
||||||
|
border-color: $purple-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active~.custom-control-indicator {
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus:not(:checked):not(:disabled)~.custom-control-indicator, &:active:not(:checked):not(:disabled)~.custom-control-indicator {
|
||||||
|
box-shadow: 0 0 0 6px rgba($bg-focused-active-control, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus:checked:not(:disabled)~.custom-control-indicator, &:active:checked:not(:disabled)~.custom-control-indicator {
|
||||||
|
box-shadow: 0 0 0 6px rgba($bg-focused-active-control, 0.1);
|
||||||
|
border-color: $purple-400;
|
||||||
|
background-color: rgba($bg-focused-active-control, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled:checked~.custom-control-indicator {
|
||||||
|
border-color: $gray-400;
|
||||||
|
background-color: transparent;
|
||||||
|
@include custom-radio-checked-icon($gray-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled:not(:checked)~.custom-control-indicator {
|
||||||
|
border-color: $gray-300;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus:disabled~.custom-control-indicator, &:active:disabled~.custom-control-indicator {
|
||||||
|
box-shadow: 0 0 0 6px rgba($bg-disabled-control, 0.1);
|
||||||
|
border-color: $gray-300;
|
||||||
|
background-color: rgba($bg-disabled-control, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus:disabled:checked~.custom-control-indicator, &:active:disabled:checked~.custom-control-indicator {
|
||||||
|
border-color: $gray-400;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
.nested-field {
|
|
||||||
padding-left: 1.5rem;
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,44 @@
|
|||||||
// CSS that doesn't belong to any specific Vue compoennt
|
// Functions
|
||||||
@import './forms';
|
|
||||||
|
// From Bootstrap 4
|
||||||
|
// Replace `$search` with `$replace` in `$string`
|
||||||
|
// @author Hugo Giraudel
|
||||||
|
// @param {String} $string - Initial string
|
||||||
|
// @param {String} $search - Substring to replace
|
||||||
|
// @param {String} $replace ('') - New value
|
||||||
|
// @return {String} - Updated string
|
||||||
|
@function str-replace($string, $search, $replace: "") {
|
||||||
|
$index: str-index($string, $search);
|
||||||
|
|
||||||
|
@if $index {
|
||||||
|
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
|
||||||
|
}
|
||||||
|
|
||||||
|
@return $string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variables
|
||||||
|
@import './colors';
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
background: $gray-700;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
* {
|
||||||
text-rendering: optimizeLegibility;
|
transition-duration: .15s;
|
||||||
-webkit-font-smoothing: antialiased;
|
transition-property: border-color, box-shadow, color;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
transition-timing-function: ease-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Global styles
|
||||||
|
@import './typography';
|
||||||
|
@import './form';
|
||||||
|
@import './button';
|
||||||
|
@import './badge';
|
||||||
|
@import './dropdown';
|
||||||
|
@import './popover';
|
||||||
|
@import './item';
|
||||||
|
|
||||||
|
// Generic components
|
||||||
|
@import './page';
|
||||||
89
website/client/assets/scss/item.scss
Normal file
89
website/client/assets/scss/item.scss
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// TODO move to item component?
|
||||||
|
|
||||||
|
.items > div {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.items > div:last-of-type {
|
||||||
|
margin-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-one-line .item-wrapper {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
position: relative;
|
||||||
|
width: 94px;
|
||||||
|
height: 92px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: $white;
|
||||||
|
box-shadow: 0 2px 2px 0 rgba($black, 0.15), 0 1px 4px 0 rgba($black, 0.1);
|
||||||
|
border: 1px solid transparent;
|
||||||
|
|
||||||
|
&-empty {
|
||||||
|
background: $gray-10;
|
||||||
|
box-shadow: none;
|
||||||
|
cursor: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 4px 4px 0 rgba($black, 0.16), 0 1px 8px 0 rgba($black, 0.12);
|
||||||
|
border-color: $purple-500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-content .item:hover {
|
||||||
|
border-color: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item .item-content {
|
||||||
|
position: absolute;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
padding: 4px;
|
||||||
|
top: 22px;
|
||||||
|
right: 26px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-label {
|
||||||
|
display: block;
|
||||||
|
width: 94px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1.33;
|
||||||
|
text-align: center;
|
||||||
|
color: $gray-400;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item > .badge {
|
||||||
|
cursor: pointer;
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: -12px;
|
||||||
|
left: -12px;
|
||||||
|
color: $gray-400;
|
||||||
|
background: $white;
|
||||||
|
padding: 4.5px 6px;
|
||||||
|
box-shadow: 0 1px 1px 0 rgba($black, 0.12);
|
||||||
|
|
||||||
|
&.item-selected-badge {
|
||||||
|
display: block;
|
||||||
|
background: $teal-50;
|
||||||
|
color: $white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item:hover > .badge {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
14
website/client/assets/scss/page.scss
Normal file
14
website/client/assets/scss/page.scss
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
.standard-sidebar {
|
||||||
|
background: $gray-600;
|
||||||
|
padding: 24px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.43;
|
||||||
|
}
|
||||||
|
|
||||||
|
.standard-page {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
color: $purple-200;
|
||||||
|
}
|
||||||
41
website/client/assets/scss/popover.scss
Normal file
41
website/client/assets/scss/popover.scss
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
.popover {
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: rgba(52, 49, 58, 0.96);
|
||||||
|
box-shadow: 0 2px 2px 0 rgba($black, 0.16), 0 1px 4px 0 rgba($black, 0.12);
|
||||||
|
|
||||||
|
&::after, &::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content {
|
||||||
|
padding: 12px 16px;
|
||||||
|
text-align: center;
|
||||||
|
color: $gray-500;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.33;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content-title {
|
||||||
|
color: $white;
|
||||||
|
line-height: 1.14;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content-text {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content-attr {
|
||||||
|
width: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
|
||||||
|
&-key {
|
||||||
|
color: $white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-val {
|
||||||
|
color: $green-10;
|
||||||
|
}
|
||||||
|
}
|
||||||
51
website/client/assets/scss/typography.scss
Normal file
51
website/client/assets/scss/typography.scss
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
body {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
color: $gray-50;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.43;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-text {
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: italic;
|
||||||
|
line-height: 1.33;
|
||||||
|
color: $gray-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Headers
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-family: 'Roboto Condensed', sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
color: $gray-10;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1.67;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: $gray-50;
|
||||||
|
margin-bottom: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.43;
|
||||||
|
}
|
||||||
3
website/client/assets/svg/alert.svg
Normal file
3
website/client/assets/svg/alert.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<path fill="#FF6165" fill-rule="evenodd" d="M0 1.994C0 .893.895 0 1.994 0h12.012C15.107 0 16 .895 16 1.994v12.012A1.995 1.995 0 0 1 14.006 16H1.994A1.995 1.995 0 0 1 0 14.006V1.994zM2 2v12h12V2H2zm5 2h2v5H7V4zm0 6h2v2H7v-2z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 322 B |
3
website/client/assets/svg/check.svg
Normal file
3
website/client/assets/svg/check.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="10" viewBox="0 0 13 10">
|
||||||
|
<path fill="#24CC8F" fill-rule="evenodd" d="M4.662 9.832c-.312 0-.61-.123-.831-.344L0 5.657l1.662-1.662 2.934 2.934L10.534 0l1.785 1.529-6.764 7.893a1.182 1.182 0 0 1-.848.409l-.045.001"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 284 B |
3
website/client/assets/svg/checkbox-white.svg
Normal file
3
website/client/assets/svg/checkbox-white.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="10" viewBox="0 0 13 10">
|
||||||
|
<path fill="#FFF" fill-rule="evenodd" d="M4.662 9.832c-.312 0-.61-.123-.83-.344L0 5.657l1.662-1.662 2.934 2.934L10.534 0l1.785 1.529-6.764 7.893a1.182 1.182 0 0 1-.848.409l-.045.001"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 280 B |
3
website/client/assets/svg/search.svg
Normal file
3
website/client/assets/svg/search.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<path fill="#878190" fill-rule="evenodd" d="M15.7 14.3l-4.8-4.8c.7-1 1.1-2.2 1.1-3.5 0-3.3-2.7-6-6-6S0 2.7 0 6s2.7 6 6 6c1.3 0 2.5-.4 3.5-1.1l4.8 4.8c.4.4 1 .4 1.4 0 .4-.4.4-1 0-1.4zM6 10c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 334 B |
@@ -7,8 +7,8 @@
|
|||||||
.progress-container.d-flex
|
.progress-container.d-flex
|
||||||
img.icon(src="~assets/header/png/health@3x.png")
|
img.icon(src="~assets/header/png/health@3x.png")
|
||||||
.progress
|
.progress
|
||||||
.progress-bar.bg-danger(:style="{width: `${percent(user.stats.hp, maxHealth)}%`}")
|
.progress-bar.bg-danger(:style="{width: `${percent(user.stats.hp, MAX_HEALTH)}%`}")
|
||||||
span {{user.stats.hp | round}} / {{maxHealth}}
|
span {{user.stats.hp | round}} / {{MAX_HEALTH}}
|
||||||
.progress-container.d-flex
|
.progress-container.d-flex
|
||||||
img.icon(src="~assets/header/png/experience@3x.png")
|
img.icon(src="~assets/header/png/experience@3x.png")
|
||||||
.progress
|
.progress
|
||||||
@@ -21,14 +21,20 @@
|
|||||||
span {{user.stats.mp | round}} / {{maxMP}}
|
span {{user.stats.mp | round}} / {{maxMP}}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style lang="scss" scoped>
|
||||||
|
@import '~client/assets/scss/colors.scss';
|
||||||
|
|
||||||
|
// TODO move to colors.scss if used in other places
|
||||||
|
$header-dark-background: #271B3D;
|
||||||
|
$header-text-color: #D5C8FF;
|
||||||
|
|
||||||
/* TODO refactor: only partially ported from SemanticUI; */
|
/* TODO refactor: only partially ported from SemanticUI; */
|
||||||
#app-header {
|
#app-header {
|
||||||
padding-left: 14px;
|
padding-left: 14px;
|
||||||
margin-top: 56px;
|
margin-top: 56px;
|
||||||
background: #36205d;
|
background: $purple-50;
|
||||||
height: 192px;
|
height: 192px;
|
||||||
color: #d5c8ff;
|
color: $header-text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.character-name {
|
.character-name {
|
||||||
@@ -36,7 +42,7 @@
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin-top: 32px;
|
margin-top: 32px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
color: #fff;
|
color: $white;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +57,6 @@
|
|||||||
#header-avatar {
|
#header-avatar {
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
margin-right: 1rem;
|
margin-right: 1rem;
|
||||||
box-shadow: 0 2px 4px 0 rgba(53, 32, 93, 0.4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-container {
|
.progress-container {
|
||||||
@@ -75,7 +80,7 @@
|
|||||||
margin: 0px;
|
margin: 0px;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
background-color: rgba(0, 0, 0, 0.35);
|
background-color: $header-dark-background;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-container > .progress > .progress-bar {
|
.progress-container > .progress > .progress-bar {
|
||||||
@@ -90,25 +95,21 @@ import Avatar from './avatar';
|
|||||||
import { mapState } from 'client/libs/store';
|
import { mapState } from 'client/libs/store';
|
||||||
|
|
||||||
import { toNextLevel } from '../../common/script/statHelpers';
|
import { toNextLevel } from '../../common/script/statHelpers';
|
||||||
import { MAX_HEALTH as maxHealth } from '../../common/script/constants';
|
|
||||||
import statsComputed from '../../common/script/libs/statsComputed';
|
import statsComputed from '../../common/script/libs/statsComputed';
|
||||||
import percent from '../../common/script/libs/percent';
|
import percent from '../../common/script/libs/percent';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'header',
|
|
||||||
components: {
|
components: {
|
||||||
Avatar,
|
Avatar,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
percent,
|
percent,
|
||||||
},
|
},
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
maxHealth,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({user: 'user.data'}),
|
...mapState({
|
||||||
|
user: 'user.data',
|
||||||
|
MAX_HEALTH: 'constants.MAX_HEALTH',
|
||||||
|
}),
|
||||||
maxMP () {
|
maxMP () {
|
||||||
return statsComputed(this.user).maxMP;
|
return statsComputed(this.user).maxMP;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -52,8 +52,10 @@ nav.navbar.navbar-inverse.fixed-top.navbar-toggleable-sm
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@import '~client/assets/scss/colors.scss';
|
||||||
|
|
||||||
nav.navbar {
|
nav.navbar {
|
||||||
background: #432874 url(~assets/header/png/bits.png) right no-repeat;
|
background: $purple-100 url(~assets/header/png/bits.png) right no-repeat;
|
||||||
padding: 0 1.5rem;
|
padding: 0 1.5rem;
|
||||||
height: 56px;
|
height: 56px;
|
||||||
}
|
}
|
||||||
@@ -66,26 +68,26 @@ nav.navbar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$active-purple: #6133b4;
|
|
||||||
|
|
||||||
.nav-item {
|
.nav-item {
|
||||||
.nav-link {
|
.nav-link {
|
||||||
color: #fff;
|
font-size: 16px;
|
||||||
|
color: $white;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
padding: 1rem 1.5rem;
|
padding: 1rem 1.5rem;
|
||||||
|
transition: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.nav-link {
|
.nav-link {
|
||||||
color: #fff;
|
color: $white;
|
||||||
background: $active-purple;
|
background: $purple-300;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active,&:hover {
|
&.active,&:hover {
|
||||||
.nav-link {
|
.nav-link {
|
||||||
box-shadow: 0px -4px 0px #6133b4 inset;
|
box-shadow: 0px -4px 0px $purple-300 inset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,25 +99,38 @@ $active-purple: #6133b4;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu:not(.user-dropdown) {
|
.dropdown-menu:not(.user-dropdown) {
|
||||||
background: $active-purple;
|
background: $purple-300;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
border: none;
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
padding: 0px;
|
||||||
|
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
border-bottom-left-radius: 5px;
|
||||||
|
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
color: #fff;
|
font-size: 16px;
|
||||||
|
box-shadow: none;
|
||||||
|
color: $white;
|
||||||
|
border: none;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background: $active-purple;
|
background: $purple-300;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #4f2a93;
|
background: $purple-200;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
border-bottom-left-radius: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-with-icon {
|
.item-with-icon {
|
||||||
color: #fff;
|
color: $white;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
padding: 0.75rem 0;
|
padding: 0.75rem 0;
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
|
|||||||
@@ -67,7 +67,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'avatar',
|
|
||||||
props: {
|
props: {
|
||||||
user: {
|
user: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
|||||||
159
website/client/components/inventory/drawer.vue
Normal file
159
website/client/components/inventory/drawer.vue
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
.drawer-container
|
||||||
|
.drawer-title(@click="open = !open")
|
||||||
|
| {{title}}
|
||||||
|
img.drawer-toggle-icon(src="~assets/drawer/minimize.svg", v-if="open")
|
||||||
|
img.drawer-toggle-icon.closed(src="~assets/drawer/expand.svg", v-else)
|
||||||
|
transition(name="slide-up", @afterLeave="adjustPagePadding", @afterEnter="adjustPagePadding")
|
||||||
|
.drawer-content(v-show="open")
|
||||||
|
slot(name="drawer-header")
|
||||||
|
.drawer-slider
|
||||||
|
slot(name="drawer-slider")
|
||||||
|
div.message(v-if="errorMessage != null")
|
||||||
|
.content {{ errorMessage }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '~client/assets/scss/colors.scss';
|
||||||
|
|
||||||
|
.drawer-container {
|
||||||
|
z-index: 19;
|
||||||
|
position: fixed;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
bottom: 0;
|
||||||
|
left: 19%;
|
||||||
|
right: 3%;
|
||||||
|
max-width: 80%;
|
||||||
|
|
||||||
|
@media screen and (min-width: 1241px) {
|
||||||
|
max-width: 968px;
|
||||||
|
// 16.67% is the width of the .col-2 sidebar
|
||||||
|
left: calc((100% + 16.67% - 968px) / 2);
|
||||||
|
right: 0%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-toggle-icon {
|
||||||
|
float: right;
|
||||||
|
margin: 10px;
|
||||||
|
|
||||||
|
&.closed {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-title {
|
||||||
|
background-color: $gray-10;
|
||||||
|
box-shadow: 0 1px 2px 0 rgba($black, 0.2);
|
||||||
|
cursor: pointer;
|
||||||
|
border-top-right-radius: 8px;
|
||||||
|
border-top-left-radius: 8px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.67;
|
||||||
|
color: $white;
|
||||||
|
padding: 6px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-content {
|
||||||
|
line-height: 1.33;
|
||||||
|
max-height: 300px;
|
||||||
|
background-color: $gray-50;
|
||||||
|
color: $gray-500;
|
||||||
|
box-shadow: 0 2px 16px 0 rgba($black, 0.3);
|
||||||
|
padding-top: 6px;
|
||||||
|
padding-left: 24px;
|
||||||
|
padding-right: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-tab {
|
||||||
|
&-container {
|
||||||
|
display: flex;
|
||||||
|
margin-left: 24px;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1.67;
|
||||||
|
text-align: center;
|
||||||
|
color: $white;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
padding: 0px 8px 8px 8px;
|
||||||
|
|
||||||
|
&-active {
|
||||||
|
border-color: $purple-400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-slider {
|
||||||
|
padding: 12px 0 0 24px;
|
||||||
|
margin-left: -24px;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
& .message {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
top: calc(50% - 30px);
|
||||||
|
left: 24px;
|
||||||
|
right: 0;
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
& .content {
|
||||||
|
background-color: rgba($gray-200, 0.5);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-active, .slide-up-leave-active {
|
||||||
|
transition-property: all;
|
||||||
|
transition-duration: 450ms;
|
||||||
|
transition-timing-function: cubic-bezier(0.445, 0.05, 0.55, 0.95);
|
||||||
|
}
|
||||||
|
.slide-up-enter, .slide-up-leave-to {
|
||||||
|
max-height: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
errorMessage: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
open: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
adjustPagePadding () {
|
||||||
|
let minPaddingBottom = 20;
|
||||||
|
let drawerHeight = this.$el.offsetHeight;
|
||||||
|
let standardPage = document.getElementsByClassName('standard-page')[0];
|
||||||
|
standardPage.style.paddingBottom = `${drawerHeight + minPaddingBottom}px`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
// Make sure the page has enough space so the drawer does not overlap content
|
||||||
|
this.adjustPagePadding();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
292
website/client/components/inventory/equipment.vue
Normal file
292
website/client/components/inventory/equipment.vue
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
.row
|
||||||
|
.col-2.standard-sidebar
|
||||||
|
.form-group
|
||||||
|
input.form-control.input-search(type="text", v-model="searchText", :placeholder="$t('search')")
|
||||||
|
|
||||||
|
.form
|
||||||
|
h2(v-once) {{ $t('filter') }}
|
||||||
|
h3 {{ this.groupBy === 'type' ? 'Type' : $t('class') }}
|
||||||
|
.form-group
|
||||||
|
.form-check(
|
||||||
|
v-for="group in itemsGroups",
|
||||||
|
:key="group.key",
|
||||||
|
)
|
||||||
|
label.custom-control.custom-checkbox
|
||||||
|
input.custom-control-input(type="checkbox", v-model="viewOptions[group.key].selected")
|
||||||
|
span.custom-control-indicator
|
||||||
|
span.custom-control-description(v-once) {{ $t(group.label) }}
|
||||||
|
|
||||||
|
.col-10.standard-page
|
||||||
|
.clearfix
|
||||||
|
h1.float-left.mb-0.page-header(v-once) {{ $t('equipment') }}
|
||||||
|
.float-right
|
||||||
|
b-dropdown(text="Sort by", right=true)
|
||||||
|
b-dropdown-item(href="#") Option 1
|
||||||
|
b-dropdown-item(href="#") Option 2
|
||||||
|
b-dropdown-item(href="#") Option 3
|
||||||
|
b-dropdown(text="Group by", right=true)
|
||||||
|
b-dropdown-item(@click="groupBy = 'type'", :class="{'dropdown-item-active': groupBy === 'type'}") Type
|
||||||
|
b-dropdown-item(@click="groupBy = 'class'", :class="{'dropdown-item-active': groupBy === 'class'}") {{ $t('class') }}
|
||||||
|
|
||||||
|
drawer(
|
||||||
|
:title="$t('equipment')",
|
||||||
|
:errorMessage="(costume && !user.preferences.costume) ? $t('costumeDisabled') : null",
|
||||||
|
)
|
||||||
|
div(slot="drawer-header")
|
||||||
|
.drawer-tab-container
|
||||||
|
.drawer-tab.text-right
|
||||||
|
a.drawer-tab-text(
|
||||||
|
@click="costume = false",
|
||||||
|
:class="{'drawer-tab-text-active': costume === false}",
|
||||||
|
) {{ $t('equipment') }}
|
||||||
|
.clearfix
|
||||||
|
.drawer-tab.float-left
|
||||||
|
a.drawer-tab-text(
|
||||||
|
@click="costume = true",
|
||||||
|
:class="{'drawer-tab-text-active': costume === true}",
|
||||||
|
) {{ $t('costume') }}
|
||||||
|
|
||||||
|
b-popover(
|
||||||
|
:triggers="['hover']",
|
||||||
|
:placement="'top'"
|
||||||
|
)
|
||||||
|
span(slot="content")
|
||||||
|
.popover-content-title {{ $t(drawerPreference+'PopoverText') }}
|
||||||
|
|
||||||
|
toggle-switch.float-right(
|
||||||
|
:label="$t(costume ? 'useCostume' : 'autoEquipBattleGear')",
|
||||||
|
:checked="user.preferences[drawerPreference]",
|
||||||
|
@change="changeDrawerPreference",
|
||||||
|
)
|
||||||
|
|
||||||
|
.items.items-one-line(slot="drawer-slider")
|
||||||
|
item(
|
||||||
|
v-for="(label, group) in gearTypesToStrings",
|
||||||
|
:key="group",
|
||||||
|
:item="flatGear[activeItems[group]]",
|
||||||
|
:label="$t(label)",
|
||||||
|
:selected="true",
|
||||||
|
:popoverPosition="'top'",
|
||||||
|
:starVisible="!costume || user.preferences.costume",
|
||||||
|
@click="equip",
|
||||||
|
)
|
||||||
|
div(
|
||||||
|
v-for="group in itemsGroups",
|
||||||
|
v-if="viewOptions[group.key].selected",
|
||||||
|
:key="group.key",
|
||||||
|
)
|
||||||
|
h2
|
||||||
|
| {{ $t(group.label) }}
|
||||||
|
|
|
||||||
|
span.badge.badge-pill.badge-default {{items[group.key].length}}
|
||||||
|
|
||||||
|
.items
|
||||||
|
item(
|
||||||
|
v-for="(item, index) in items[group.key]",
|
||||||
|
v-if="viewOptions[group.key].open || index < itemsPerLine",
|
||||||
|
:item="item",
|
||||||
|
:key="item.key",
|
||||||
|
:selected="activeItems[item.type] === item.key",
|
||||||
|
:starVisible="!costume || user.preferences.costume",
|
||||||
|
@click="equip",
|
||||||
|
)
|
||||||
|
div(v-if="items[group.key].length === 0")
|
||||||
|
p(v-once) {{ $t('noGearItemsOfType', { type: $t(group.label) }) }}
|
||||||
|
a.btn.btn-show-more(
|
||||||
|
v-if="items[group.key].length > itemsPerLine",
|
||||||
|
@click="viewOptions[group.key].open = !viewOptions[group.key].open"
|
||||||
|
) {{ viewOptions[group.key].open ? $t('showLessGearItems', { type: $t(group.label) }) : $t('showAllGearItems', { type: $t(group.label), items: items[group.key].length }) }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
h2 {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'client/libs/store';
|
||||||
|
import each from 'lodash/each';
|
||||||
|
import map from 'lodash/map';
|
||||||
|
import throttle from 'lodash/throttle';
|
||||||
|
|
||||||
|
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||||
|
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
|
||||||
|
import bPopover from 'bootstrap-vue/lib/components/popover';
|
||||||
|
import toggleSwitch from 'client/components/ui/toggleSwitch';
|
||||||
|
|
||||||
|
import Item from 'client/components/inventory/item';
|
||||||
|
import Drawer from 'client/components/inventory/drawer';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Item,
|
||||||
|
Drawer,
|
||||||
|
bDropdown,
|
||||||
|
bDropdownItem,
|
||||||
|
bPopover,
|
||||||
|
toggleSwitch,
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
itemsPerLine: 9,
|
||||||
|
searchText: null,
|
||||||
|
searchTextThrottled: null,
|
||||||
|
costume: false,
|
||||||
|
groupBy: 'type', // or 'class' TODO move to router?
|
||||||
|
gearTypesToStrings: Object.freeze({ // TODO use content.itemList?
|
||||||
|
headAccessory: 'headAccessoryCapitalized',
|
||||||
|
head: 'headgearCapitalized',
|
||||||
|
eyewear: 'eyewear',
|
||||||
|
weapon: 'weaponCapitalized',
|
||||||
|
shield: 'offhandCapitalized',
|
||||||
|
armor: 'armorCapitalized',
|
||||||
|
body: 'body',
|
||||||
|
back: 'back',
|
||||||
|
}),
|
||||||
|
gearClassesToStrings: Object.freeze({
|
||||||
|
warrior: 'warrior', // TODO immediately calculate $(label) instead of all the times
|
||||||
|
wizard: 'mage',
|
||||||
|
rogue: 'rogue',
|
||||||
|
healer: 'healer',
|
||||||
|
special: 'special',
|
||||||
|
mystery: 'mystery',
|
||||||
|
armoire: 'armoireText',
|
||||||
|
}),
|
||||||
|
viewOptions: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
searchText: throttle(function throttleSearch () {
|
||||||
|
this.searchTextThrottled = this.searchText;
|
||||||
|
}, 250),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
equip (item) {
|
||||||
|
this.$store.dispatch('common:equip', {key: item.key, type: this.costume ? 'costume' : 'equipped'});
|
||||||
|
},
|
||||||
|
changeDrawerPreference (newVal) {
|
||||||
|
this.$store.dispatch('user:set', {
|
||||||
|
[`preferences.${this.drawerPreference}`]: newVal,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
content: 'content',
|
||||||
|
user: 'user.data',
|
||||||
|
ownedItems: 'user.data.items.gear.owned',
|
||||||
|
equippedItems: 'user.data.items.gear.equipped',
|
||||||
|
costumeItems: 'user.data.items.gear.costume',
|
||||||
|
flatGear: 'content.gear.flat',
|
||||||
|
}),
|
||||||
|
drawerPreference () {
|
||||||
|
return this.costume === true ? 'costume' : 'autoEquip';
|
||||||
|
},
|
||||||
|
activeItems () {
|
||||||
|
return this.costume === true ? this.costumeItems : this.equippedItems;
|
||||||
|
},
|
||||||
|
gearItemsByType () {
|
||||||
|
const searchText = this.searchTextThrottled;
|
||||||
|
const gearItemsByType = {};
|
||||||
|
each(this.gearTypesToStrings, (string, type) => {
|
||||||
|
gearItemsByType[type] = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
each(this.ownedItems, (isOwned, gearKey) => {
|
||||||
|
if (isOwned === true) {
|
||||||
|
const ownedItem = this.flatGear[gearKey];
|
||||||
|
|
||||||
|
const isSearched = !searchText || ownedItem.text().toLowerCase().indexOf(searchText) !== -1;
|
||||||
|
|
||||||
|
if (ownedItem.klass !== 'base' && isSearched) {
|
||||||
|
const type = ownedItem.type;
|
||||||
|
const isEquipped = this.activeItems[type] === ownedItem.key;
|
||||||
|
const viewOptions = this.viewOptions[type];
|
||||||
|
const firstRender = viewOptions.firstRender;
|
||||||
|
const itemsInFirstPosition = viewOptions.itemsInFirstPosition;
|
||||||
|
|
||||||
|
// Render selected items in first postion only for the first render
|
||||||
|
if (itemsInFirstPosition.indexOf(ownedItem.key) !== -1 && firstRender === false) {
|
||||||
|
gearItemsByType[type].unshift(ownedItem);
|
||||||
|
} else if (isEquipped === true && firstRender === true) {
|
||||||
|
gearItemsByType[type].unshift(ownedItem);
|
||||||
|
itemsInFirstPosition.push(ownedItem.key);
|
||||||
|
} else {
|
||||||
|
gearItemsByType[type].push(ownedItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
each(this.gearTypesToStrings, (string, type) => {
|
||||||
|
this.viewOptions[type].firstRender = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return gearItemsByType;
|
||||||
|
},
|
||||||
|
gearItemsByClass () {
|
||||||
|
const searchText = this.searchTextThrottled;
|
||||||
|
const gearItemsByClass = {};
|
||||||
|
each(this.gearClassesToStrings, (string, klass) => {
|
||||||
|
gearItemsByClass[klass] = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
each(this.ownedItems, (isOwned, gearKey) => {
|
||||||
|
if (isOwned === true) {
|
||||||
|
const ownedItem = this.flatGear[gearKey];
|
||||||
|
const klass = ownedItem.klass;
|
||||||
|
|
||||||
|
const isSearched = !searchText || ownedItem.text().toLowerCase().indexOf(searchText) !== -1;
|
||||||
|
|
||||||
|
if (klass !== 'base' && isSearched) {
|
||||||
|
const isEquipped = this.activeItems[ownedItem.type] === ownedItem.key;
|
||||||
|
const viewOptions = this.viewOptions[klass];
|
||||||
|
const firstRender = viewOptions.firstRender;
|
||||||
|
const itemsInFirstPosition = viewOptions.itemsInFirstPosition;
|
||||||
|
|
||||||
|
// Render selected items in first postion only for the first render
|
||||||
|
if (itemsInFirstPosition.indexOf(ownedItem.key) !== -1 && firstRender === false) {
|
||||||
|
gearItemsByClass[klass].unshift(ownedItem);
|
||||||
|
} else if (isEquipped === true && firstRender === true) {
|
||||||
|
gearItemsByClass[klass].unshift(ownedItem);
|
||||||
|
itemsInFirstPosition.push(ownedItem.key);
|
||||||
|
} else {
|
||||||
|
gearItemsByClass[klass].push(ownedItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
each(this.gearClassesToStrings, (string, klass) => {
|
||||||
|
this.viewOptions[klass].firstRender = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return gearItemsByClass;
|
||||||
|
},
|
||||||
|
groups () {
|
||||||
|
return this.groupBy === 'type' ? this.gearTypesToStrings : this.gearClassesToStrings;
|
||||||
|
},
|
||||||
|
items () {
|
||||||
|
return this.groupBy === 'type' ? this.gearItemsByType : this.gearItemsByClass;
|
||||||
|
},
|
||||||
|
itemsGroups () {
|
||||||
|
return map(this.groups, (label, group) => {
|
||||||
|
this.$set(this.viewOptions, group, {
|
||||||
|
selected: true,
|
||||||
|
open: false,
|
||||||
|
itemsInFirstPosition: [],
|
||||||
|
firstRender: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: group,
|
||||||
|
label,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,10 +1,19 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
.row
|
.row
|
||||||
.col-12
|
secondary-menu.col-12
|
||||||
nav.nav
|
router-link.nav-link(:to="{name: 'inventory'}", exact) {{ $t('inventory') }}
|
||||||
router-link.nav-link(:to="{name: 'inventory'}", exact) {{ $t('inventory') }}
|
router-link.nav-link(:to="{name: 'equipment'}") {{ $t('equipment') }}
|
||||||
router-link.nav-link(:to="{name: 'equipment'}") {{ $t('equipment') }}
|
router-link.nav-link(:to="{name: 'stable'}") {{ $t('stable') }}
|
||||||
router-link.nav-link(:to="{name: 'stable'}") {{ $t('stable') }}
|
|
||||||
.col-12
|
.col-12
|
||||||
router-view
|
router-view
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SecondaryMenu from 'client/components/secondaryMenu';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
SecondaryMenu,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
68
website/client/components/inventory/item.vue
Normal file
68
website/client/components/inventory/item.vue
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
b-popover(
|
||||||
|
:triggers="['hover']",
|
||||||
|
:placement="popoverPosition",
|
||||||
|
v-if="item && item.key.indexOf('_base_0') === -1",
|
||||||
|
)
|
||||||
|
span(slot="content")
|
||||||
|
h4.popover-content-title {{ item.text() }}
|
||||||
|
.popover-content-text {{ item.notes() }}
|
||||||
|
.popover-content-attr(v-for="attr in ATTRIBUTES")
|
||||||
|
span.popover-content-attr-key {{ `${$t(attr)}: ` }}
|
||||||
|
span.popover-content-attr-val {{ `+${item[attr]}` }}
|
||||||
|
|
||||||
|
.item-wrapper
|
||||||
|
.item
|
||||||
|
span.badge.badge-pill(
|
||||||
|
:class="{'item-selected-badge': selected === true}",
|
||||||
|
@click="click",
|
||||||
|
v-if="starVisible"
|
||||||
|
) ★
|
||||||
|
span.item-content(:class="'shop_' + item.key")
|
||||||
|
span.item-label(v-if="label") {{ label }}
|
||||||
|
div(v-else)
|
||||||
|
.item-wrapper
|
||||||
|
.item.item-empty
|
||||||
|
.item-content
|
||||||
|
span.item-label(v-if="label") {{ label }}
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import bPopover from 'bootstrap-vue/lib/components/popover';
|
||||||
|
import { mapState } from 'client/libs/store';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
bPopover,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
starVisible: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
popoverPosition: {
|
||||||
|
type: String,
|
||||||
|
default: 'bottom',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
ATTRIBUTES: 'constants.ATTRIBUTES',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
click () {
|
||||||
|
this.$emit('click', this.item);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
.row
|
.row
|
||||||
.col-3
|
.col-2.standard-sidebar
|
||||||
.form-group
|
.form-group
|
||||||
input.form-control(type="text", :placeholder="$t('search')")
|
input.form-control(type="text", :placeholder="$t('search')")
|
||||||
|
|
||||||
@@ -41,30 +41,33 @@
|
|||||||
input.form-check-input(type="checkbox")
|
input.form-check-input(type="checkbox")
|
||||||
span {{ $t('special') }}
|
span {{ $t('special') }}
|
||||||
|
|
||||||
|
.col-10.standard-page
|
||||||
.col-9
|
h4 Pets
|
||||||
h2 Pets
|
.inventory-item-container(v-for="pet in dropPets")
|
||||||
.inventory-item-container(v-for="pet in listAnimals('pet', content.dropEggs, content.dropHatchingPotions)")
|
|
||||||
.PixelPaw
|
.PixelPaw
|
||||||
|
.btn.btn-secondary.d-block(@click="open.dropPets = !open.dropPets") {{ open.dropPets ? 'Close' : 'Open' }}
|
||||||
|
|
||||||
h2 Magic Potions Pets
|
h2 Magic Potions Pets
|
||||||
ul
|
.inventory-item-container(v-for="pet in magicPets")
|
||||||
li(v-for="pet in listAnimals('pet', content.dropEggs, content.premiumHatchingPotions)") {{pet}}
|
.PixelPaw
|
||||||
|
.btn.btn-secondary.d-block(@click="open.magicPets = !open.magicPets") {{ open.magicPets ? 'Close' : 'Open' }}
|
||||||
|
|
||||||
h2 Quest Pets
|
h2 Quest Pets
|
||||||
ul
|
.inventory-item-container(v-for="pet in questPets")
|
||||||
li(v-for="pet in listAnimals('pet', content.questEggs, content.dropHatchingPotions)") {{pet}}
|
.PixelPaw
|
||||||
|
.btn.btn-secondary.d-block(@click="open.questPets = !open.questPets") {{ open.questPets ? 'Close' : 'Open' }}
|
||||||
|
|
||||||
h2 Rare Pets
|
h2 Rare Pets
|
||||||
ul
|
.inventory-item-container(v-for="pet in rarePets")
|
||||||
li(v-for="pet in listAnimals('pet', content.dropEggs, content.dropHatchingPotions)") {{pet}}
|
.PixelPaw
|
||||||
|
.btn.btn-secondary.d-block(@click="open.rarePets = !open.rarePets") {{ open.rarePets ? 'Close' : 'Open' }}
|
||||||
|
|
||||||
h2 Mounts
|
h2 Mounts
|
||||||
h2 Quest Mounts
|
h2 Quest Mounts
|
||||||
h2 Rare Mounts
|
h2 Rare Mounts
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style lang="scss">
|
||||||
.inventory-item-container {
|
.inventory-item-container {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
@@ -76,15 +79,45 @@
|
|||||||
import { mapState } from 'client/libs/store';
|
import { mapState } from 'client/libs/store';
|
||||||
import each from 'lodash/each';
|
import each from 'lodash/each';
|
||||||
|
|
||||||
|
// TODO Normalize special pets and mounts
|
||||||
|
// import Store from 'client/store';
|
||||||
|
// import deepFreeze from 'client/libs/deepFreeze';
|
||||||
|
// const specialMounts =
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
open: {
|
||||||
|
dropPets: false,
|
||||||
|
magicPets: false,
|
||||||
|
questPets: false,
|
||||||
|
rarePets: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['content']),
|
...mapState(['content']),
|
||||||
|
dropPets () {
|
||||||
|
return this.listAnimals('pet', this.content.dropEggs, this.content.dropHatchingPotions, this.open.dropPets);
|
||||||
|
},
|
||||||
|
magicPets () {
|
||||||
|
return this.listAnimals('pet', this.content.dropEggs, this.content.premiumHatchingPotions, this.open.magicPets);
|
||||||
|
},
|
||||||
|
questPets () {
|
||||||
|
return this.listAnimals('pet', this.content.questEggs, this.content.dropHatchingPotions, this.open.questPets);
|
||||||
|
},
|
||||||
|
rarePets () {
|
||||||
|
return this.listAnimals('pet', this.content.dropEggs, this.content.dropHatchingPotions, this.open.rarePets);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
listAnimals (type, eggSource, potionSource) {
|
listAnimals (type, eggSource, potionSource, isOpen = false) {
|
||||||
let animals = [];
|
let animals = [];
|
||||||
|
let iteration = 0;
|
||||||
|
|
||||||
each(eggSource, (egg) => {
|
each(eggSource, (egg) => {
|
||||||
|
if (iteration === 1 && !isOpen) return false;
|
||||||
|
iteration++;
|
||||||
each(potionSource, (potion) => {
|
each(potionSource, (potion) => {
|
||||||
let animalKey = `${egg.key}-${potion.key}`;
|
let animalKey = `${egg.key}-${potion.key}`;
|
||||||
animals.push(this.content[`${type}Info`][animalKey].text());
|
animals.push(this.content[`${type}Info`][animalKey].text());
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
.row
|
.row
|
||||||
.col
|
.col
|
||||||
h2 Page
|
h1.page-header Page
|
||||||
p {{ $route.path }}
|
p {{ $route.path }}
|
||||||
</template>
|
</template>
|
||||||
31
website/client/components/secondaryMenu.vue
Normal file
31
website/client/components/secondaryMenu.vue
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
nav.nav.d-flex.justify-content-center.secondary-menu
|
||||||
|
slot
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '~client/assets/scss/colors.scss';
|
||||||
|
|
||||||
|
.secondary-menu {
|
||||||
|
background: $gray-600;
|
||||||
|
box-shadow: 0 1px 2px 0 rgba($black, 0.2);
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.5;
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: $gray-50;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: $purple-200;
|
||||||
|
box-shadow: 0px -4px 0px $purple-300 inset;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $gray-500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
.row
|
.row
|
||||||
.col-3
|
.col-2.standard-sidebar
|
||||||
.form-group
|
.form-group
|
||||||
input.form-control(type="text", :placeholder="$t('search')")
|
input.form-control(type="text", :placeholder="$t('search')")
|
||||||
|
|
||||||
@@ -22,8 +22,8 @@
|
|||||||
input.form-check-input(type="checkbox")
|
input.form-check-input(type="checkbox")
|
||||||
span Animals
|
span Animals
|
||||||
|
|
||||||
.col-9
|
.col-10.standard-page
|
||||||
h2(v-once) {{ $t('publicGuilds') }}
|
h1.page-header(v-once) {{ $t('publicGuilds') }}
|
||||||
public-guild-item(v-for="guild in guilds", :key='guild._id', :guild="guild")
|
public-guild-item(v-for="guild in guilds", :key='guild._id', :guild="guild")
|
||||||
mugen-scroll(
|
mugen-scroll(
|
||||||
:handler="fetchGuilds",
|
:handler="fetchGuilds",
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import MugenScroll from 'vue-mugen-scroll';
|
import MugenScroll from 'vue-mugen-scroll';
|
||||||
import PublicGuildItem from './publicGuildItem';
|
import PublicGuildItem from './publicGuildItem';
|
||||||
import { GUILDS_PER_PAGE } from 'common/script/constants';
|
import { mapState } from 'client/libs/store';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { PublicGuildItem, MugenScroll },
|
components: { PublicGuildItem, MugenScroll },
|
||||||
@@ -50,6 +50,11 @@ export default {
|
|||||||
guilds: [],
|
guilds: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
GUILDS_PER_PAGE: 'constants.GUILDS_PER_PAGE',
|
||||||
|
}),
|
||||||
|
},
|
||||||
created () {
|
created () {
|
||||||
this.fetchGuilds();
|
this.fetchGuilds();
|
||||||
},
|
},
|
||||||
@@ -65,7 +70,7 @@ export default {
|
|||||||
});
|
});
|
||||||
let guilds = response.data.data;
|
let guilds = response.data.data;
|
||||||
this.guilds.push(...guilds);
|
this.guilds.push(...guilds);
|
||||||
if (guilds.length < GUILDS_PER_PAGE) this.hasLoadedAllGuilds = true;
|
if (guilds.length < this.GUILDS_PER_PAGE) this.hasLoadedAllGuilds = true;
|
||||||
this.lastPageLoaded++;
|
this.lastPageLoaded++;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
.row(v-if="guild")
|
.row(v-if="guild")
|
||||||
.clearfix.col-12
|
.clearfix.col-12
|
||||||
.float-left
|
.float-left
|
||||||
h2 {{guild.name}}
|
h1.page-header {{guild.name}}
|
||||||
strong.float-left {{$t('groupLeader')}}
|
strong.float-left {{$t('groupLeader')}}
|
||||||
span.float-left : {{guild.leader.profile.name}}
|
span.float-left : {{guild.leader.profile.name}}
|
||||||
.float-right
|
.float-right
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
.row
|
.row
|
||||||
.col-12
|
.col-12
|
||||||
h2 Conversation
|
h1.page-header Conversation
|
||||||
.card(v-for="message in messages")
|
.card(v-for="message in messages")
|
||||||
.card-block
|
.card-block
|
||||||
strong {{message.from}}
|
strong {{message.from}}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
.row
|
.row
|
||||||
.col-12
|
.col-12
|
||||||
h2(v-once) {{ $t('inbox') }}
|
h1.page-header(v-once) {{ $t('inbox') }}
|
||||||
.card(v-for="conversation in conversations")
|
.card(v-for="conversation in conversations")
|
||||||
.card-block
|
.card-block
|
||||||
router-link(:to="{ name: 'conversation', params: { id: conversation.fromUserId } }")
|
router-link(:to="{ name: 'conversation', params: { id: conversation.fromUserId } }")
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
.row
|
.row
|
||||||
.col-12
|
secondary-menu.col-12
|
||||||
nav.nav
|
router-link.nav-link(:to="{name: 'tavern'}", exact) {{ $t('tavern') }}
|
||||||
router-link.nav-link(:to="{name: 'tavern'}", exact) {{ $t('tavern') }}
|
router-link.nav-link(:to="{name: 'inbox'}") {{ $t('inbox') }}
|
||||||
router-link.nav-link(:to="{name: 'inbox'}") {{ $t('inbox') }}
|
router-link.nav-link(:to="{name: 'guildsDiscovery'}") {{ $t('guilds') }}
|
||||||
router-link.nav-link(:to="{name: 'guildsDiscovery'}") {{ $t('guilds') }}
|
|
||||||
.col-12
|
.col-12
|
||||||
router-view
|
router-view
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SecondaryMenu from 'client/components/secondaryMenu';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
SecondaryMenu,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
.row
|
.row
|
||||||
h2.col-12 Tavern
|
h1.page-header.col-12 Tavern
|
||||||
// TODO Example code based on Semantic UI .ui.grid
|
// TODO Example code based on Semantic UI .ui.grid
|
||||||
.four.wide.column
|
.four.wide.column
|
||||||
h2.ui.dividing.header SideMenu
|
h2.ui.dividing.header SideMenu
|
||||||
|
|||||||
119
website/client/components/ui/toggleSwitch.vue
Normal file
119
website/client/components/ui/toggleSwitch.vue
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
.clearfix.toggle-switch-container
|
||||||
|
.float-left.toggle-switch-description {{ label }}
|
||||||
|
.toggle-switch.float-left
|
||||||
|
input.toggle-switch-checkbox(
|
||||||
|
type='checkbox', :id="id",
|
||||||
|
@change="$emit('change', $event.target.checked)",
|
||||||
|
:checked="checked",
|
||||||
|
)
|
||||||
|
label.toggle-switch-label(:for="id")
|
||||||
|
span.toggle-switch-inner
|
||||||
|
span.toggle-switch-switch
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '~client/assets/scss/colors.scss';
|
||||||
|
|
||||||
|
.toggle-switch-container {
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch {
|
||||||
|
position: relative;
|
||||||
|
width: 40px;
|
||||||
|
user-select: none;
|
||||||
|
margin-left: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch-description {
|
||||||
|
height: 20px;
|
||||||
|
border-bottom: 1px dashed $gray-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch-checkbox {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch-label {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 100px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch-inner {
|
||||||
|
display: block;
|
||||||
|
width: 200%;
|
||||||
|
margin-left: -100%;
|
||||||
|
transition: margin 0.3s ease-in 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch-inner:before, .toggle-switch-inner:after {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
width: 50%;
|
||||||
|
height: 16px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch-inner:before {
|
||||||
|
content: "";
|
||||||
|
padding-left: 10px;
|
||||||
|
background-color: $purple-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch-inner:after {
|
||||||
|
content: "";
|
||||||
|
padding-right: 10px;
|
||||||
|
background-color: $gray-200;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch-switch {
|
||||||
|
box-shadow: 0 1px 2px 0 rgba($black, 0.32);
|
||||||
|
display: block;
|
||||||
|
width: 20px;
|
||||||
|
margin: -2px;
|
||||||
|
margin-top: 1px;
|
||||||
|
height: 20px;
|
||||||
|
background: $white;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 22px;
|
||||||
|
border-radius: 100px;
|
||||||
|
transition: all 0.3s ease-in 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch-checkbox:checked + .toggle-switch-label .toggle-switch-inner {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch-checkbox:checked + .toggle-switch-label .toggle-switch-switch {
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
// A value is required for the required for the for and id attributes
|
||||||
|
id: Math.random(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
checked: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,18 +1,155 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
.row
|
.row
|
||||||
.col(v-for="taskType in tasksTypes")
|
.col-12
|
||||||
h3 {{taskType}}s
|
.row
|
||||||
ul
|
.col-3.p-4
|
||||||
task(v-for="task in tasks", v-if="task.type === taskType", :key="task.id", :task="task")
|
h3 Input
|
||||||
|
input.form-control(type="text", placeholder="Placeholder")
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Input Disabled
|
||||||
|
input.form-control(type="text", placeholder="Placeholder", disabled)
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Input With Icon
|
||||||
|
input.form-control.input-search(type="text", placeholder="Placeholder")
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Input With Icon Disabled
|
||||||
|
input.form-control.input-search(type="text", placeholder="Placeholder", disabled)
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Input Valid
|
||||||
|
input.form-control.input-valid(type="text", placeholder="Placeholder")
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Input Invalid
|
||||||
|
input.form-control.input-invalid(type="text", placeholder="Placeholder")
|
||||||
|
.row
|
||||||
|
.col-6.p-4
|
||||||
|
h3 Textarea
|
||||||
|
textarea.form-control(rows="5", cols="50")
|
||||||
|
.col-6.p-4
|
||||||
|
h3 Textarea Disabled
|
||||||
|
textarea.form-control(disabled, rows="10", cols="50")
|
||||||
|
.row
|
||||||
|
.col-2.p-4
|
||||||
|
toggleSwitch(label="Toggle Switch")
|
||||||
|
.row
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Checkbox
|
||||||
|
label.custom-control.custom-checkbox
|
||||||
|
input.custom-control-input(type='checkbox')
|
||||||
|
span.custom-control-indicator
|
||||||
|
span.custom-control-description Check this custom checkbox
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Checkbox Disabled Checked
|
||||||
|
label.custom-control.custom-checkbox
|
||||||
|
input.custom-control-input(type='checkbox', disabled, checked)
|
||||||
|
span.custom-control-indicator
|
||||||
|
span.custom-control-description Check this custom checkbox
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Checkbox Disabled Not Checked
|
||||||
|
label.custom-control.custom-checkbox
|
||||||
|
input.custom-control-input(type='checkbox', disabled)
|
||||||
|
span.custom-control-indicator
|
||||||
|
span.custom-control-description Check this custom checkbox
|
||||||
|
.col-6.p-4
|
||||||
|
h3 Radio Button
|
||||||
|
form
|
||||||
|
label.custom-control.custom-radio
|
||||||
|
input#radio1.custom-control-input(name='radio', type='radio')
|
||||||
|
span.custom-control-indicator
|
||||||
|
span.custom-control-description Toggle this custom radio
|
||||||
|
label.custom-control.custom-radio
|
||||||
|
input#radio2.custom-control-input(name='radio', type='radio')
|
||||||
|
span.custom-control-indicator
|
||||||
|
span.custom-control-description Toggle this custom radio
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Radio Button Disabled Checked
|
||||||
|
form
|
||||||
|
label.custom-control.custom-radio
|
||||||
|
input#radio3.custom-control-input(name='radio', type='radio', disabled, checked)
|
||||||
|
span.custom-control-indicator
|
||||||
|
span.custom-control-description Toggle this custom radio
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Radio Button Disabled Not Checked
|
||||||
|
form
|
||||||
|
label.custom-control.custom-radio
|
||||||
|
input#radio3.custom-control-input(name='radio', type='radio', disabled)
|
||||||
|
span.custom-control-indicator
|
||||||
|
span.custom-control-description Toggle this custom radio
|
||||||
|
.row
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Main Button
|
||||||
|
button.btn.btn-primary Button
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Secondary Button
|
||||||
|
button.btn.btn-secondary Button
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Green Button
|
||||||
|
button.btn.btn-success Button
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Blue Button
|
||||||
|
button.btn.btn-info Button
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Red Button
|
||||||
|
button.btn.btn-danger Button
|
||||||
|
.row
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Main Button Disabled
|
||||||
|
button.btn.btn-primary(disabled=true) Button
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Secondary Button Disabled
|
||||||
|
button.btn.btn-secondary(disabled=true) Button
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Green Button Disabled
|
||||||
|
button.btn.btn-success(disabled=true) Button
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Blue Button Disabled
|
||||||
|
button.btn.btn-info(disabled=true) Button
|
||||||
|
.col-3.p-4
|
||||||
|
h3 Red Button Disabled
|
||||||
|
button.btn.btn-danger(disabled=true) Button
|
||||||
|
.row
|
||||||
|
.col-6.p-4
|
||||||
|
h3 Dropdown Menu
|
||||||
|
b-dropdown(text="Menu", right=false)
|
||||||
|
b-dropdown-item(href="#") Menu item 1
|
||||||
|
b-dropdown-item(href="#") Menu item 2
|
||||||
|
b-dropdown-item(href="#") Menu item 3
|
||||||
|
b-dropdown-item(href="#") Menu item 4
|
||||||
|
.col-6.p-4
|
||||||
|
h3 Dropdown Menu Disabled
|
||||||
|
b-dropdown(text="Menu", disabled)
|
||||||
|
b-dropdown-item(href="#") Menu item 1
|
||||||
|
b-dropdown-item(href="#") Menu item 2
|
||||||
|
b-dropdown-item(href="#") Menu item 3
|
||||||
|
b-dropdown-item(href="#") Menu item 4
|
||||||
|
.row
|
||||||
|
.col-6.p-4
|
||||||
|
h1 Heading 1
|
||||||
|
h2 Heading 2
|
||||||
|
h3 Heading 3
|
||||||
|
h4 Heading 4
|
||||||
|
.col-6.p-4
|
||||||
|
p Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vehicula, purus sit amet sodales pharetra, ipsum ipsum mollis orci, id pharetra velit diam et dui. Sed placerat ipsum eget pharetra rutrum. Ut vitae rutrum lacus, eu imperdiet velit. Pellentesque eu velit cursus, scelerisque dui quis, dapibus magna. Vestibulum molestie sed sapien et ultricies. Nam porta ipsum leo, non congue magna vestibulum a. Etiam dictum felis sit amet augue varius tincidunt. Sed eget urna auctor, convallis felis in, pretium justo. Curabitur aliquet, ligula id tincidunt ullamcorper, orci lorem pharetra neque, in ornare arcu magna accumsan arcu. Maecenas dignissim lorem sed eros accumsan scelerisque.
|
||||||
|
p.small-text Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vehicula, purus sit amet sodales pharetra, ipsum ipsum mollis orci, id pharetra velit diam et dui.
|
||||||
|
.row
|
||||||
|
.col(v-for="taskType in tasksTypes")
|
||||||
|
h3 {{taskType}}s
|
||||||
|
ul
|
||||||
|
task(v-for="task in tasks", v-if="task.type === taskType", :key="task.id", :task="task")
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Task from './task';
|
import Task from './task';
|
||||||
import { mapState } from 'client/libs/store';
|
import { mapState } from 'client/libs/store';
|
||||||
|
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||||
|
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
|
||||||
|
import toggleSwitch from 'client/components/ui/toggleSwitch';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Task,
|
Task,
|
||||||
|
bDropdown,
|
||||||
|
bDropdownItem,
|
||||||
|
toggleSwitch,
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>Habitica</title>
|
<title>Habitica</title>
|
||||||
|
<!-- TODO load google fonts separately as @import is slow, find alternative -->
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Roboto+Condensed:700|Roboto:400,400i,700,700i" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- #loading-screen needs to be rendered before vue, will be deleted once app is loaded -->
|
<!-- #loading-screen needs to be rendered before vue, will be deleted once app is loaded -->
|
||||||
|
|||||||
@@ -58,8 +58,7 @@ export default new Vue({
|
|||||||
this.$store.dispatch('user:fetch'),
|
this.$store.dispatch('user:fetch'),
|
||||||
this.$store.dispatch('tasks:fetchUserTasks'),
|
this.$store.dispatch('tasks:fetchUserTasks'),
|
||||||
]).catch((err) => {
|
]).catch((err) => {
|
||||||
console.error(err); // eslint-disable-line no-console
|
console.error('Impossible to fetch user. Copy into localStorage a valid habit-mobile-settings object.', err); // eslint-disable-line no-console
|
||||||
alert('Impossible to fetch user. Copy into localStorage a valid habit-mobile-settings object.');
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
mounted () { // Remove the loading screen when the app is mounted
|
mounted () { // Remove the loading screen when the app is mounted
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
// TODO if we only have a single method here, move it to an utility
|
|
||||||
// a full mixin is not needed
|
|
||||||
|
|
||||||
import { TAVERN_ID } from '../../common/script/constants';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
methods: {
|
methods: {
|
||||||
isMemberOfGroup (user, group) {
|
isMemberOfGroup (user, group) {
|
||||||
if (group._id === TAVERN_ID) return true;
|
if (group._id === this.$store.state.constants.TAVERN_ID) return true;
|
||||||
|
|
||||||
// If the group is a guild, just check for an intersection with the
|
// If the group is a guild, just check for an intersection with the
|
||||||
// current user's guilds, rather than checking the members of the group.
|
// current user's guilds, rather than checking the members of the group.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import UserTasks from './components/userTasks';
|
|||||||
|
|
||||||
// Inventory
|
// Inventory
|
||||||
import InventoryContainer from './components/inventory/index';
|
import InventoryContainer from './components/inventory/index';
|
||||||
|
import EquipmentPage from './components/inventory/equipment';
|
||||||
import StablePage from './components/inventory/stable';
|
import StablePage from './components/inventory/stable';
|
||||||
|
|
||||||
// Social
|
// Social
|
||||||
@@ -39,7 +40,7 @@ export default new VueRouter({
|
|||||||
component: InventoryContainer,
|
component: InventoryContainer,
|
||||||
children: [
|
children: [
|
||||||
{ name: 'inventory', path: '', component: Page },
|
{ name: 'inventory', path: '', component: Page },
|
||||||
{ name: 'equipment', path: 'equipment', component: Page },
|
{ name: 'equipment', path: 'equipment', component: EquipmentPage },
|
||||||
{ name: 'stable', path: 'stable', component: StablePage },
|
{ name: 'stable', path: 'stable', component: StablePage },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
12
website/client/store/actions/common.js
Normal file
12
website/client/store/actions/common.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import equipOp from 'common/script/ops/equip';
|
||||||
|
|
||||||
|
export function equip (store, params) {
|
||||||
|
const user = store.state.user.data;
|
||||||
|
equipOp(user, {params});
|
||||||
|
axios
|
||||||
|
.post(`/api/v3/user/equip/${params.type}/${params.key}`);
|
||||||
|
// TODO
|
||||||
|
// .then((res) => console.log('equip', res))
|
||||||
|
// .catch((err) => console.error('equip', err));
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { flattenAndNamespace } from 'client/libs/store/helpers/internals';
|
import { flattenAndNamespace } from 'client/libs/store/helpers/internals';
|
||||||
|
|
||||||
|
import * as common from './common';
|
||||||
import * as user from './user';
|
import * as user from './user';
|
||||||
import * as tasks from './tasks';
|
import * as tasks from './tasks';
|
||||||
|
|
||||||
@@ -7,6 +8,7 @@ import * as tasks from './tasks';
|
|||||||
// Example: fetch in user.js -> 'user:fetch'
|
// Example: fetch in user.js -> 'user:fetch'
|
||||||
|
|
||||||
const actions = flattenAndNamespace({
|
const actions = flattenAndNamespace({
|
||||||
|
common,
|
||||||
user,
|
user,
|
||||||
tasks,
|
tasks,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { loadAsyncResource } from 'client/libs/asyncResource';
|
import { loadAsyncResource } from 'client/libs/asyncResource';
|
||||||
|
import setProps from 'lodash/set';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
export function fetch (store, forceLoad = false) { // eslint-disable-line no-shadow
|
export function fetch (store, forceLoad = false) { // eslint-disable-line no-shadow
|
||||||
return loadAsyncResource({
|
return loadAsyncResource({
|
||||||
@@ -10,4 +12,17 @@ export function fetch (store, forceLoad = false) { // eslint-disable-line no-sha
|
|||||||
},
|
},
|
||||||
forceLoad,
|
forceLoad,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function set (store, changes) {
|
||||||
|
const user = store.state.user.data;
|
||||||
|
|
||||||
|
for (let key in changes) {
|
||||||
|
setProps(user, key, changes[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
axios.put('/api/v3/user', changes);
|
||||||
|
// TODO
|
||||||
|
// .then((res) => console.log('set', res))
|
||||||
|
// .catch((err) => console.error('set', err));
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import Store from 'client/libs/store';
|
import Store from 'client/libs/store';
|
||||||
import deepFreeze from 'client/libs/deepFreeze';
|
import deepFreeze from 'client/libs/deepFreeze';
|
||||||
import content from 'common/script/content/index';
|
import content from 'common/script/content/index';
|
||||||
|
import * as constants from 'common/script/constants';
|
||||||
import { asyncResourceFactory } from 'client/libs/asyncResource';
|
import { asyncResourceFactory } from 'client/libs/asyncResource';
|
||||||
|
|
||||||
import actions from './actions';
|
import actions from './actions';
|
||||||
@@ -20,6 +21,7 @@ export default function () {
|
|||||||
// TODO apply freezing to the entire codebase (the server) and not only to the client side?
|
// TODO apply freezing to the entire codebase (the server) and not only to the client side?
|
||||||
// NOTE this takes about 10-15ms on a fast computer
|
// NOTE this takes about 10-15ms on a fast computer
|
||||||
content: deepFreeze(content),
|
content: deepFreeze(content),
|
||||||
|
constants: deepFreeze(constants),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
8
website/common/locales/en/newClient.json
Normal file
8
website/common/locales/en/newClient.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"costumePopoverText": "Select \"Use Costume\" to equip items to your avatar without affecting the stats from your Battle Gear! This means that you can dress up your avatar in whatever outfit you like while still having your best Battle Gear equipped.",
|
||||||
|
"autoEquipPopoverText": "Select this option to automatically equip gear as soon as you purchase it.",
|
||||||
|
"showAllGearItems": "Show All <%= items %> <%= type %> Gear Items",
|
||||||
|
"showLessGearItems": "Show Less <%= type %> Gear Items",
|
||||||
|
"noGearItemsOfType": "You don't own any pieces of <%= type %>.",
|
||||||
|
"costumeDisabled": "You have disabled your costume."
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"stable": "Stable",
|
||||||
"pets": "Pets",
|
"pets": "Pets",
|
||||||
"activePet": "Active Pet",
|
"activePet": "Active Pet",
|
||||||
"noActivePet": "No Active Pet",
|
"noActivePet": "No Active Pet",
|
||||||
|
|||||||
@@ -46,12 +46,20 @@ module.exports = function equip (user, req = {}) {
|
|||||||
let item = content.gear.flat[key];
|
let item = content.gear.flat[key];
|
||||||
|
|
||||||
if (user.items.gear[type][item.type] === key) {
|
if (user.items.gear[type][item.type] === key) {
|
||||||
user.items.gear[type][item.type] = `${item.type}_base_0`;
|
user.items.gear[type] = Object.assign(
|
||||||
|
{},
|
||||||
|
user.items.gear[type].toObject ? user.items.gear[type].toObject() : user.items.gear[type],
|
||||||
|
{[item.type]: `${item.type}_base_0`}
|
||||||
|
);
|
||||||
message = i18n.t('messageUnEquipped', {
|
message = i18n.t('messageUnEquipped', {
|
||||||
itemText: item.text(req.language),
|
itemText: item.text(req.language),
|
||||||
}, req.language);
|
}, req.language);
|
||||||
} else {
|
} else {
|
||||||
user.items.gear[type][item.type] = item.key;
|
user.items.gear[type] = Object.assign(
|
||||||
|
{},
|
||||||
|
user.items.gear[type].toObject ? user.items.gear[type].toObject() : user.items.gear[type],
|
||||||
|
{[item.type]: item.key}
|
||||||
|
);
|
||||||
message = handleTwoHanded(user, item, type, req);
|
message = handleTwoHanded(user, item, type, req);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user