Skip to content

Commit 9d0bca9

Browse files
committed
initial commit
1 parent 3b3188f commit 9d0bca9

23 files changed

+10490
-0
lines changed

.browserslistrc

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
> 1%
2+
last 2 versions

.editorconfig

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[*.{js,jsx,ts,tsx,vue}]
2+
indent_style = space
3+
indent_size = 2
4+
end_of_line = lf
5+
trim_trailing_whitespace = true
6+
insert_final_newline = true
7+
max_line_length = 100

.eslintrc.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module.exports = {
2+
root: true,
3+
env: {
4+
node: true,
5+
},
6+
extends: [
7+
'plugin:vue/essential',
8+
'@vue/airbnb',
9+
],
10+
rules: {
11+
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
12+
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
13+
},
14+
parserOptions: {
15+
parser: 'babel-eslint',
16+
},
17+
};

.gitignore

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.DS_Store
2+
node_modules
3+
/dist
4+
5+
# local env files
6+
.env.local
7+
.env.*.local
8+
9+
# Log files
10+
npm-debug.log*
11+
yarn-debug.log*
12+
yarn-error.log*
13+
14+
# Editor directories and files
15+
.idea
16+
.vscode
17+
*.suo
18+
*.ntvs*
19+
*.njsproj
20+
*.sln
21+
*.sw?

.prettierrc

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"singleQuote": true,
3+
"trailingComma": "all"
4+
}

README.md

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# vue-shop-app
2+
3+
## Project setup
4+
```
5+
yarn install
6+
```
7+
8+
### Compiles and hot-reloads for development
9+
```
10+
yarn run serve
11+
```
12+
13+
### Compiles and minifies for production
14+
```
15+
yarn run build
16+
```
17+
18+
### Run your tests
19+
```
20+
yarn run test
21+
```
22+
23+
### Lints and fixes files
24+
```
25+
yarn run lint
26+
```
27+
28+
### Run your unit tests
29+
```
30+
yarn run test:unit
31+
```
32+
33+
### Customize configuration
34+
See [Configuration Reference](https://cli.vuejs.org/config/).

babel.config.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
presets: [
3+
'@vue/app',
4+
],
5+
};

jest.config.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
module.exports = {
2+
moduleFileExtensions: [
3+
'js',
4+
'jsx',
5+
'json',
6+
'vue',
7+
],
8+
transform: {
9+
'^.+\\.vue$': 'vue-jest',
10+
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
11+
'^.+\\.jsx?$': 'babel-jest',
12+
},
13+
transformIgnorePatterns: [
14+
'/node_modules/',
15+
],
16+
moduleNameMapper: {
17+
'^@/(.*)$': '<rootDir>/src/$1',
18+
},
19+
snapshotSerializers: [
20+
'jest-serializer-vue',
21+
],
22+
testMatch: [
23+
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)',
24+
],
25+
testURL: 'http://localhost/',
26+
watchPlugins: [
27+
'jest-watch-typeahead/filename',
28+
'jest-watch-typeahead/testname',
29+
],
30+
};

package.json

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "vue-shop-app",
3+
"version": "0.1.0",
4+
"private": true,
5+
"scripts": {
6+
"serve": "vue-cli-service serve",
7+
"build": "vue-cli-service build",
8+
"lint": "vue-cli-service lint",
9+
"test:unit": "vue-cli-service test:unit"
10+
},
11+
"dependencies": {
12+
"core-js": "^2.6.5",
13+
"vue": "^2.6.10",
14+
"vue-router": "^3.0.3",
15+
"vuex": "^3.0.1",
16+
"vuex-persist": "^2.0.1"
17+
},
18+
"devDependencies": {
19+
"@vue/cli-plugin-babel": "^3.8.0",
20+
"@vue/cli-plugin-eslint": "^3.8.0",
21+
"@vue/cli-plugin-unit-jest": "^3.8.0",
22+
"@vue/cli-service": "^3.8.0",
23+
"@vue/eslint-config-airbnb": "^4.0.0",
24+
"@vue/test-utils": "1.0.0-beta.29",
25+
"babel-core": "7.0.0-bridge.0",
26+
"babel-eslint": "^10.0.1",
27+
"babel-jest": "^23.6.0",
28+
"eslint": "^5.16.0",
29+
"eslint-plugin-vue": "^5.0.0",
30+
"vue-template-compiler": "^2.6.10"
31+
}
32+
}

postcss.config.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
plugins: {
3+
autoprefixer: {},
4+
},
5+
};

public/favicon.ico

4.19 KB
Binary file not shown.

public/index.html

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width,initial-scale=1.0">
7+
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
8+
<title>vue-shop-app</title>
9+
</head>
10+
<body>
11+
<noscript>
12+
<strong>We're sorry but vue-shop-app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
13+
</noscript>
14+
<div id="app"></div>
15+
<!-- built files will be auto injected -->
16+
</body>
17+
</html>

src/App.vue

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<template>
2+
<div id="app">
3+
<div id="nav">
4+
<router-link to="/">Shop</router-link> |
5+
<router-link to="/cart">Cart ({{cartCount}})</router-link>
6+
</div>
7+
<router-view />
8+
</div>
9+
</template>
10+
<script>
11+
import { mapActions, mapGetters } from 'vuex';
12+
13+
export default {
14+
computed: { ...mapGetters(['cartCount']) },
15+
methods: mapActions(['listenToStorage']),
16+
created() {
17+
this.listenToStorage();
18+
},
19+
};
20+
</script>
21+
22+
23+
<style>
24+
#app {
25+
font-family: 'Avenir', Helvetica, Arial, sans-serif;
26+
-webkit-font-smoothing: antialiased;
27+
-moz-osx-font-smoothing: grayscale;
28+
text-align: center;
29+
color: #2c3e50;
30+
}
31+
#nav {
32+
padding: 30px;
33+
}
34+
35+
#nav a {
36+
font-weight: bold;
37+
color: #2c3e50;
38+
}
39+
40+
#nav a.router-link-exact-active {
41+
color: #42b983;
42+
}
43+
</style>

src/assets/logo.png

6.69 KB
Loading

src/components/ShopItem.vue

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<template>
2+
<div class="container">
3+
<span>{{name}}</span>
4+
<div class="price">
5+
<span>{{price}}</span>
6+
<button @click="addToCart($props)">Add to cart</button>
7+
</div>
8+
</div>
9+
</template>
10+
<script>
11+
import { mapMutations } from 'vuex';
12+
13+
export default {
14+
props: ['name', 'price'],
15+
methods: {
16+
...mapMutations(['addToCart']),
17+
},
18+
};
19+
</script>
20+
<style scoped>
21+
.container {
22+
display: flex;
23+
border: 3px solid #d3d3d3;
24+
padding: 1em;
25+
justify-content: space-between;
26+
align-items: center;
27+
}
28+
.price {
29+
display: flex;
30+
flex-direction: column;
31+
}
32+
.price > * {
33+
margin: 0.3em;
34+
}
35+
</style>

src/main.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import Vue from 'vue';
2+
import App from './App.vue';
3+
import router from './router';
4+
import store from './store';
5+
6+
Vue.config.productionTip = false;
7+
8+
new Vue({
9+
router,
10+
store,
11+
render: h => h(App),
12+
}).$mount('#app');

src/router.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import Vue from 'vue';
2+
import Router from 'vue-router';
3+
import Shop from './views/Shop.vue';
4+
5+
Vue.use(Router);
6+
7+
export default new Router({
8+
mode: 'history',
9+
base: process.env.BASE_URL,
10+
routes: [
11+
{
12+
path: '/',
13+
name: 'shop',
14+
component: Shop,
15+
},
16+
{
17+
path: '/cart',
18+
name: 'cart',
19+
component: () => import('./views/Cart.vue'),
20+
},
21+
],
22+
});

src/store.js

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import Vue from 'vue';
2+
import Vuex from 'vuex';
3+
import VuexPersistence from 'vuex-persist';
4+
5+
Vue.use(Vuex);
6+
7+
const localStorageKey = 'vuex';
8+
9+
const vuexPersistence = new VuexPersistence({
10+
key: localStorageKey,
11+
storage: window.localStorage,
12+
reducer: state => ({ cart: state.cart }),
13+
});
14+
15+
/* eslint-disable no-param-reassign */
16+
export default new Vuex.Store({
17+
state: {
18+
cart: {},
19+
listener: null,
20+
},
21+
getters: {
22+
cartCount(state) {
23+
return Object.values(state.cart).reduce((acc, cur) => acc + cur.count, 0);
24+
},
25+
cartTotal(state) {
26+
return Object.values(state.cart)
27+
.reduce((acc, cur) => acc + cur.price * cur.count, 0)
28+
.toFixed(2);
29+
},
30+
},
31+
mutations: {
32+
addToCart(state, item) {
33+
if (!state.cart[item.name]) {
34+
state.cart[item.name] = { ...item, count: 0 };
35+
}
36+
state.cart[item.name].count += 1;
37+
},
38+
setListener(state, listener) {
39+
state.listener = listener;
40+
},
41+
setCart(state, cart) {
42+
state.cart = cart;
43+
},
44+
setItemCount(state, { name, count }) {
45+
if (count === 0) {
46+
Vue.delete(state.cart, name);
47+
return;
48+
}
49+
state.cart[name].count = count;
50+
},
51+
removeCartItem(state, name) {
52+
Vue.delete(state.cart, name);
53+
},
54+
},
55+
actions: {
56+
listenToStorage({ commit, state }) {
57+
console.log('listening');
58+
59+
const listener = (e) => {
60+
if (e.key !== localStorageKey) {
61+
return;
62+
}
63+
const current = JSON.stringify({ cart: state.cart });
64+
if (current === e.newValue) {
65+
return;
66+
}
67+
console.log('updating cart from other tab');
68+
69+
commit('setCart', JSON.parse(e.newValue).cart);
70+
};
71+
window.addEventListener('storage', listener);
72+
commit('setListener', listener);
73+
},
74+
unlisten({ commit, state }) {
75+
if (state.listener) {
76+
window.removeEventListener('storage', state.listener);
77+
commit('setListener', null);
78+
}
79+
},
80+
},
81+
plugins: [vuexPersistence.plugin],
82+
});

0 commit comments

Comments
 (0)