├── .gitignore ├── .npmignore ├── README.md ├── package.json └── source └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .idea/* 4 | .DS_Store -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | source -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-body-class 2 | 3 | Control your page body classes with vue-router easily: 4 | 5 | + add classes to parent and children routes 6 | + add classes for homepage 7 | + overwrite classes defined in parent routes 8 | + dynamic routes support 9 | + written on top of ES6, yet is ES5 safe 10 | 11 | ## Dependencies 12 | + vue.js 2.x 13 | + vue-router 3.x 14 | 15 | ## Installation 16 | 17 | `npm install vue-body-class --save` 18 | 19 | ## Get started with Vue.js 20 | 21 | 1. Import Vue Body Class 22 | 23 | ```js 24 | import VueBodyClass from 'vue-body-class'; 25 | ``` 26 | 2. Set up routes and Vue Router as described in Vue Router [Installation](https://router.vuejs.org/installation.html) & [Getting Strated](https://router.vuejs.org/guide/) sections. 27 | 28 | 3. Add Vue Body Class Guard to the Router instance (Important: pass `routes` to the `VueBodyClass` construstor) : 29 | 30 | ```js 31 | const vueBodyClass = new VueBodyClass(routes); 32 | router.beforeEach((to, from, next) => { vueBodyClass.guard(to, next) }); 33 | ``` 34 | 35 | You will end up with something like this: 36 | 37 | ```js 38 | import Vue from 'vue' 39 | import VueRouter from 'vue-router' 40 | import VueBodyClass from 'vue-body-class'; 41 | 42 | Vue.use(VueRouter) 43 | 44 | const routes = [ 45 | //...your routes here 46 | ]; 47 | 48 | const router = new VueRouter({ 49 | routes 50 | }); 51 | 52 | const vueBodyClass = new VueBodyClass(routes); 53 | router.beforeEach((to, from, next) => { vueBodyClass.guard(to, next) }); 54 | 55 | new Vue({ 56 | router, 57 | render: h => h(App), 58 | }).$mount('#app'); 59 | 60 | ``` 61 | 62 | ### Set Up classes 63 | 64 | Just add `bodyClass` to meta property of a route object in your `vue-router` routes. 65 | 66 | ```js 67 | name: 'dashboard', 68 | path: '/dashboard', 69 | meta: { bodyClass: 'dashboard' }, 70 | ... 71 | ``` 72 | 73 | For child routes, all parent classes will be applied too. 74 | 75 | ```js 76 | name: 'dashboard', 77 | path: '/dashboard', 78 | meta: { bodyClass: 'dashboard' }, 79 | component: dashboard, 80 | children: [ 81 | 82 | { 83 | name: 'dashboard.profile', 84 | path: 'profile', 85 | meta: { bodyClass: 'profile' }, 86 | component: profile 87 | }, 88 | 89 | ... 90 | 91 | ] 92 | ``` 93 | 94 | will result in 95 | 96 | ``` 97 | class = 'dashboard profile' 98 | ``` 99 | 100 | You can overwrite parent classes by adding `!` at the beginning of the class: 101 | ```js 102 | name: 'dashboard', 103 | path: '/dashboard', 104 | meta: { bodyClass: 'dashboard' }, 105 | component: dashboard, 106 | children: [ 107 | 108 | { 109 | name: 'dashboard.profile', 110 | path: 'profile', 111 | meta: { bodyClass: '!profile' }, 112 | component: profile, 113 | children: [ 114 | 115 | { 116 | name: 'dashboard.profile.personal', 117 | path: 'personal', 118 | meta: { bodyClass: 'personal' }, 119 | component: personal 120 | }, 121 | 122 | ... 123 | 124 | ] 125 | }, 126 | 127 | ... 128 | 129 | ] 130 | ``` 131 | will result in 132 | 133 | ``` 134 | class = 'profile personal' 135 | ``` 136 | 137 | as `!profile` overwrites `dashboard` class. 138 | 139 | The plugin will save your original body classes and new classes will be appended. 140 | 141 | ## Get started with Nuxt.js 142 | 143 | Nuxt.js is not supported in current package version, but there are plans to implement the integration in further versions. 144 | Meanwhile, please use Nuxt.js built in solution, described [here](https://github.com/nuxt/nuxt.js/issues/188). 145 | This does not provider same flexibility and accumulating parent route classes functionality, but is useful in most of cases. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-body-class", 3 | "version": "3.0.2", 4 | "description": "Control you body classes easily with vue-router", 5 | "main": "./dist/index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/nikolaynesov/vue-body-class" 9 | }, 10 | "keywords": [ 11 | "vue", 12 | "vue.js", 13 | "vue-router", 14 | "body", 15 | "class" 16 | ], 17 | "author": "Nikolay Nesov", 18 | "license": "ISC", 19 | "bugs": { 20 | "url": "https://github.com/nikolaynesov/vue-body-class/issues" 21 | }, 22 | "homepage": "https://github.com/nikolaynesov/vue-body-class#readme", 23 | "scripts": { 24 | "build": "babel source --presets babel-preset-es2015 --out-dir dist", 25 | "prepublish": "npm run build" 26 | }, 27 | "devDependencies": { 28 | "babel-cli": "^6.26.0", 29 | "babel-preset-es2015": "^6.24.1" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /source/index.js: -------------------------------------------------------------------------------- 1 | export default class VueBodyClass { 2 | 3 | constructor(routes) { 4 | 5 | this.bodyClass = document.body.className; 6 | this.routes = routes; 7 | 8 | } 9 | 10 | guard(to, next) { 11 | 12 | var parent = this.routes; 13 | var matched = this.parseMatched(to.matched); 14 | var additionalClassName = ""; 15 | 16 | //is a home page? 17 | if(to.path == '/') { 18 | 19 | additionalClassName = this.updateClassFromRoute(additionalClassName, to); 20 | 21 | } 22 | //not homepage 23 | else if (matched.length > 0) { 24 | 25 | for (let index in matched) { 26 | 27 | let routes = parent.children ? parent.children : parent; 28 | let found = this.findMatchInRoutesByPath(routes, matched[index]); 29 | 30 | if (found) { 31 | 32 | parent = found; 33 | additionalClassName = this.updateClassFromRoute(additionalClassName, found); 34 | 35 | } 36 | 37 | } 38 | 39 | } 40 | 41 | document.body.className = (this.bodyClass + additionalClassName).trim(); 42 | 43 | next(); 44 | 45 | } 46 | 47 | parseMatched(matchedArray) { 48 | 49 | var matched = []; 50 | 51 | for (let index in matchedArray) { 52 | 53 | let prev = matched.join('/'); 54 | 55 | matched.push( 56 | 57 | matchedArray[index].path 58 | .replace(/^\/|\/$/g, '') 59 | .replace(prev, '') 60 | .replace(/^\/|\/$/g, '') 61 | 62 | ); 63 | 64 | } 65 | 66 | return matched; 67 | 68 | } 69 | 70 | findMatchInRoutesByPath(routes, matchedItem) { 71 | 72 | return routes.find((o)=> { 73 | 74 | return o.path.replace(/^\/|\/$/g, '') == matchedItem; 75 | 76 | }); 77 | 78 | } 79 | 80 | getClassForRoute(route) { 81 | 82 | return route.meta ? route.meta.bodyClass : null; 83 | 84 | } 85 | 86 | updateClassFromRoute(className, route) { 87 | 88 | var routeClass = this.getClassForRoute(route); 89 | 90 | if (routeClass) { 91 | 92 | let routeBodyClass = routeClass.replace(/^!/, ''); 93 | 94 | if (routeClass.indexOf('!') === 0) { 95 | 96 | className = " " + routeBodyClass; 97 | 98 | } 99 | else { 100 | 101 | className += " " + routeBodyClass; 102 | 103 | } 104 | 105 | } 106 | 107 | return className; 108 | 109 | } 110 | 111 | } --------------------------------------------------------------------------------