├── docs ├── .nojekyll ├── assets │ └── rg-main.png ├── index.html ├── README.md ├── ru │ └── README.md ├── ua │ └── README.md └── fr │ └── README.md ├── .eslintrc ├── CONTRIBUTING.md ├── .travis.yml ├── .npmignore ├── PULL_REQUEST_TEMPLATE.md ├── test ├── GoodWithoutOrigin.vue ├── GoodOrigin.vue ├── classNameOrigin.vue ├── ClassNameWithoutOrigin.vue └── AllRealCases.spec.js ├── .babelrc ├── assets └── vue-mods-names.gif ├── .editorconfig ├── .gitignore ├── .github └── ISSUE_TEMPLATE │ ├── Feature_request.md │ └── Bug_report.md ├── karma.conf.js ├── LICENSE ├── dist ├── vue-mods-names.js └── vue-mods-names.js.map ├── webpack.config.js ├── src └── VueModsNames.js ├── package.json ├── README.md └── CODE_OF_CONDUCT.md /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "standard" 3 | } 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # All contibutions are welcome 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | .babelrc 3 | .editorconfig 4 | .eslintrc 5 | 6 | *.js 7 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | - [X] Check the rules 2 | - [ ] All tests are passed 3 | -------------------------------------------------------------------------------- /test/GoodWithoutOrigin.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /test/GoodOrigin.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /docs/assets/rg-main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RGRU/vue-mods-names/HEAD/docs/assets/rg-main.png -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { "modules": false }], 4 | "stage-3" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /assets/vue-mods-names.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RGRU/vue-mods-names/HEAD/assets/vue-mods-names.gif -------------------------------------------------------------------------------- /test/classNameOrigin.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /test/ClassNameWithoutOrigin.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | coverage/ 5 | npm-debug.log 6 | yarn-error.log 7 | 8 | # Editor directories and files 9 | .idea 10 | *.suo 11 | *.ntvs* 12 | *.njsproj 13 | *.sln 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // This is a karma config file. For more details see 2 | // http://karma-runner.github.io/0.13/config/configuration-file.html 3 | // we are also using it with karma-webpack 4 | // https://github.com/webpack/karma-webpack 5 | 6 | var webpackConfig = require('./webpack.config.js') 7 | 8 | module.exports = function (config) { 9 | config.set({ 10 | browsers: ['ChromeHeadless'], 11 | frameworks: ['mocha', 'chai'], 12 | reporters: ['spec', 'coverage'], 13 | files: [ 14 | {pattern: 'test/*.spec.js', watched: false} 15 | ], 16 | preprocessors: { 17 | './test/AllRealCases.spec.js': ['webpack'] 18 | }, 19 | webpack: webpackConfig, 20 | webpackMiddleware: { 21 | noInfo: true 22 | }, 23 | coverageReporter: { 24 | dir: './coverage', 25 | reporters: [ 26 | { type: 'lcov', subdir: '.' }, 27 | { type: 'text-summary' } 28 | ] 29 | } 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 RG.RU 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dist/vue-mods-names.js: -------------------------------------------------------------------------------- 1 | !function(s,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("vue-mods-names",[],t):"object"==typeof exports?exports["vue-mods-names"]=t():s["vue-mods-names"]=t()}(this,function(){return function(s){function t(n){if(e[n])return e[n].exports;var r=e[n]={i:n,l:!1,exports:{}};return s[n].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var e={};return t.m=s,t.c=e,t.d=function(s,e,n){t.o(s,e)||Object.defineProperty(s,e,{configurable:!1,enumerable:!0,get:n})},t.n=function(s){var e=s&&s.__esModule?function(){return s.default}:function(){return s};return t.d(e,"a",e),e},t.o=function(s,t){return Object.prototype.hasOwnProperty.call(s,t)},t.p="/dist/",t(t.s=0)}([function(s,t,e){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={install:function(s){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"_";s.directive("mods-names",{inserted:function(s,t,e){var n=e.context,r=e.elm;n.$baseClass=t.arg||r.className,n.$addOriginClass=t.modifiers["no-origin"],r.setAttribute("class",r.classList+" "+n.$modsNames)},update:function(s,t,e,n){var r=e.data,i=e.elm,o=e.context;r.class!==n.data.class&&(r.class?i.classList=o.$modsNames+" "+r.class:i.classList=o.$modsNames)}}),s.mixin({data:function(){return{$baseClass:null,$addOriginClass:!0}},props:{mods:{type:[String,Array]}},computed:{$modsNames:function(){var s=this,e=function(e){return s.$baseClass+t+e};if(Array.isArray(this.mods)){var n=!!this.mods&&this.mods.map(function(s){return e(s)}),r=n.slice(0);return this.$addOriginClass||r.unshift(this.$baseClass),r.join(" ")}return this.mods?this.$addOriginClass?e(this.mods):this.$baseClass+" "+e(this.mods):this.$baseClass}}})}}}])}); 2 | //# sourceMappingURL=vue-mods-names.js.map -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var webpack = require('webpack') 3 | 4 | module.exports = { 5 | entry: './src/VueModsNames.js', 6 | output: { 7 | path: path.resolve(__dirname, './dist'), 8 | publicPath: '/dist/', 9 | filename: 'vue-mods-names.js', 10 | libraryTarget: 'umd', 11 | library: 'vue-mods-names', 12 | umdNamedDefine: true 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.js$/, 18 | enforce: 'pre', 19 | // exclude: 'test', 20 | use: [ 21 | 'eslint-loader' 22 | ] 23 | }, 24 | { 25 | test: /\.vue$/, 26 | loader: 'vue-loader', 27 | options: { 28 | loaders: { 29 | } 30 | // other vue-loader options go here 31 | } 32 | }, 33 | { 34 | test: /\.js$/, 35 | loader: 'babel-loader', 36 | exclude: /node_modules/ 37 | } 38 | ] 39 | }, 40 | devServer: { 41 | historyApiFallback: true, 42 | noInfo: true, 43 | overlay: true 44 | }, 45 | performance: { 46 | hints: false 47 | }, 48 | devtool: '#eval-source-map' 49 | } 50 | 51 | if (process.env.NODE_ENV === 'production') { 52 | module.exports.devtool = '#source-map' 53 | // http://vue-loader.vuejs.org/en/workflow/production.html 54 | module.exports.plugins = (module.exports.plugins || []).concat([ 55 | new webpack.DefinePlugin({ 56 | 'process.env': { 57 | NODE_ENV: '"production"' 58 | } 59 | }), 60 | new webpack.optimize.UglifyJsPlugin({ 61 | sourceMap: true, 62 | compress: { 63 | warnings: false 64 | } 65 | }), 66 | new webpack.LoaderOptionsPlugin({ 67 | minimize: true 68 | }) 69 | ]) 70 | } 71 | -------------------------------------------------------------------------------- /src/VueModsNames.js: -------------------------------------------------------------------------------- 1 | export default { 2 | install (Vue, prefix = '_') { 3 | Vue.directive('mods-names', { 4 | inserted (el, binding, { context, elm }) { 5 | context.$baseClass = binding.arg || elm.className 6 | context.$addOriginClass = binding.modifiers['no-origin'] 7 | elm.setAttribute('class', elm.classList + ' ' + context.$modsNames) 8 | }, 9 | update (el, binding, { data, elm, context }, oldVnode) { 10 | if (data.class !== oldVnode.data.class) { 11 | if (data.class) { 12 | elm.classList = context.$modsNames + ' ' + data.class 13 | } else { 14 | elm.classList = context.$modsNames 15 | } 16 | } 17 | } 18 | }) 19 | Vue.mixin({ 20 | data () { 21 | return { $baseClass: null, $addOriginClass: true } 22 | }, 23 | props: { 24 | mods: { 25 | type: [ String, Array ] 26 | } 27 | }, 28 | computed: { 29 | $modsNames () { 30 | let generateAdditionClass = mods => this.$baseClass + prefix + mods 31 | 32 | if (Array.isArray(this.mods)) { 33 | let classArr = !!this.mods && this.mods.map(i => generateAdditionClass(i)) 34 | // Don't touch original array of classes 35 | let classArrWithOrigin = classArr.slice(0) 36 | // If comopnent should render with base class, then add base class to begin of array 37 | if (!this.$addOriginClass) classArrWithOrigin.unshift(this.$baseClass) 38 | return classArrWithOrigin.join(' ') 39 | } 40 | 41 | if (!this.mods) return this.$baseClass 42 | 43 | return !this.$addOriginClass 44 | ? this.$baseClass + ' ' + generateAdditionClass(this.mods) : generateAdditionClass(this.mods) 45 | } 46 | } 47 | }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-mods-names", 3 | "description": "Simple adding mods to all components class names in your vue app", 4 | "version": "0.0.12", 5 | "author": "RGRU team | KamilOcean (https://github.com/RGRU/)", 6 | "license": "MIT", 7 | "main": "dist/vue-mods-names.js", 8 | "keywords": [ 9 | "mods", 10 | "classNames", 11 | "Vue", 12 | "Vue-components", 13 | "BEM" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/RGRU/vue-mods-names" 18 | }, 19 | "scripts": { 20 | "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot", 21 | "build": "cross-env NODE_ENV=production webpack --progress --hide-modules", 22 | "test": "cross-env BABEL_ENV=test karma start karma.conf.js --single-run" 23 | }, 24 | "dependencies": {}, 25 | "browserslist": [ 26 | "> 1%", 27 | "last 2 versions", 28 | "not ie <= 8" 29 | ], 30 | "devDependencies": { 31 | "babel-core": "^6.26.0", 32 | "babel-loader": "^7.1.2", 33 | "babel-preset-env": "^1.6.0", 34 | "babel-preset-stage-3": "^6.24.1", 35 | "chai": "^4.1.2", 36 | "cross-env": "^5.0.5", 37 | "css-loader": "^0.28.7", 38 | "eslint": "^4.11.0", 39 | "eslint-loader": "^1.9.0", 40 | "eslint-plugin-standard": "^3.0.1", 41 | "friendly-errors-webpack-plugin": "^1.6.1", 42 | "karma": "^1.7.1", 43 | "karma-chai": "^0.1.0", 44 | "karma-chrome-launcher": "^2.2.0", 45 | "karma-coverage": "^1.1.1", 46 | "karma-jasmine": "^1.1.0", 47 | "karma-mocha": "^1.3.0", 48 | "karma-spec-reporter": "0.0.31", 49 | "karma-webpack": "^2.0.6", 50 | "mocha": "^4.0.1", 51 | "source-map": "^0.6.1", 52 | "standard": "^10.0.3", 53 | "vue": "^2.5.8", 54 | "vue-loader": "^13.5.0", 55 | "vue-template-compiler": "^2.5.8", 56 | "webpack": "^3.6.0", 57 | "webpack-dev-server": "^2.9.1" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vue-mods-names - Simple adding mods to all components class names in your vue app 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 |
Loading...
27 | 37 | 38 | 39 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-mods-names 2 | > Simple adding class names mods to all components in your vue app 3 | 4 | ![example](./assets/vue-mods-names.gif) 5 | 6 | [![npm version](https://badge.fury.io/js/vue-mods-names.svg)](https://badge.fury.io/js/vue-mods-names) 7 | [![Build Status](https://travis-ci.org/RGRU/vue-mods-names.svg?branch=master)](https://travis-ci.org/RGRU/vue-mods-names) 8 | [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) 9 | 10 | Docs with examples and playgrounds:
11 | [English](https://rgru.github.io/vue-mods-names/#/)
12 | [Русский](https://rgru.github.io/vue-mods-names/#/ru/)
13 | [Українська](https://rgru.github.io/vue-mods-names/#/ua/)
14 | [français](https://rgru.github.io/vue-mods-names/#/fr/)
15 | 16 | ## Install 17 | 18 | So typically 19 | 20 | ```js 21 | npm i vue-mods-names -D 22 | ``` 23 | 24 | ## Usage 25 | ```js 26 | import VueModsNames from 'vue-mods-names' 27 | 28 | Vue.use(VueModsNames) 29 | ``` 30 | If you want to change prefix (by default is equal: `_`), then just pass it in plugin options, like this: 31 | ```js 32 | import VueModsNames from 'vue-mods-names' 33 | 34 | Vue.use(VueModsNames, { prefix: '-' }) 35 | ``` 36 | After that any component in your app is ready to use mods. It will work if you declare name of base class in directive `v-mods-names:` 37 | 38 | Declare behavior by adding special directive `v-mods-names` in your template. `good` - is name of base class, that will join with other mods. 39 | 40 | `Good.vue` 41 | ```html 42 | 43 |
It's cool
44 | ``` 45 | 46 | Now we may use component with different mods: 47 | 48 | `main page` 49 | ```html 50 | 51 | ``` 52 | 53 | In the issue you have component on main page, that has classes after render: 54 | 55 | ```html 56 |
It's cool
57 | ``` 58 | 59 | > All style modifications of component typically describe in component file itself (one file component). 60 | 61 | ```css 62 | .good {} 63 | 64 | .good_main {} 65 | 66 | .good_big {} 67 | ``` 68 | 69 | You can pass Array of mods: 70 | 71 | ```html 72 | 73 | ``` 74 | 75 | After render we get this: 76 | 77 | ```html 78 |
It's cool
79 | ``` 80 | 81 | [KamilOcean](https://twitter.com/kamil_ocean) 82 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at dev@rg.ru. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /test/AllRealCases.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueModsNames from '../src/VueModsNames' 3 | 4 | import GoodOrigin from './GoodOrigin.vue' 5 | import GoodWithoutOrigin from './GoodWithoutOrigin.vue' 6 | import classNameOrigin from './classNameOrigin.vue' 7 | // import ClassNameWithoutOrigin from './ClassNameWithoutOrigin.vue' 8 | 9 | let baseClass = 'good' 10 | 11 | Vue.use(VueModsNames) 12 | 13 | let createVM = (ctx, props) => { 14 | let Constructor = Vue.extend(ctx) 15 | return new Constructor({ propsData: { mods: props } }).$mount() 16 | } 17 | 18 | describe('Main test', () => { 19 | it('Has one mod + no-origin + baseClass(arg)', () => { 20 | let vm = createVM(GoodOrigin, 'first-mod') 21 | expect(vm.$el.classList.contains(baseClass + '_first-mod')).to.be.true 22 | expect(vm.$el.classList.contains(baseClass)).not.to.be.true 23 | }) 24 | 25 | it('Has array of mod + no-origin + baseClass(arg)', () => { 26 | let vm = createVM(GoodOrigin, ['first-mod', 'second-mod']) 27 | expect(vm.$el.classList.contains(baseClass + '_first-mod')).to.be.true 28 | expect(vm.$el.classList.contains(baseClass + '_second-mod')).to.be.true 29 | expect(vm.$el.classList.contains(baseClass)).not.to.be.true 30 | }) 31 | 32 | it('Has one mod + no-origin - baseClass(arg)', () => { 33 | let vm = createVM(classNameOrigin, 'first-mod') 34 | expect(vm.$el.classList.contains(baseClass + '_first-mod')).to.be.true 35 | expect(vm.$el.classList.contains(baseClass)).to.be.true 36 | }) 37 | 38 | it('Has array of mod + no-origin - baseClass(arg)', () => { 39 | let vm = createVM(classNameOrigin, ['first-mod', 'second-mod', 'third']) 40 | expect(vm.$el.classList.contains(baseClass + '_first-mod')).to.be.true 41 | expect(vm.$el.classList.contains(baseClass + '_second-mod')).to.be.true 42 | expect(vm.$el.classList.contains(baseClass + '_third')).to.be.true 43 | expect(vm.$el.classList.contains(baseClass)).to.be.true 44 | }) 45 | 46 | it('Has no mod + no-origin + baseClass(arg)', () => { 47 | let vm = createVM(GoodOrigin) 48 | expect(vm.$el.classList.contains(baseClass + '_first-mod')).not.to.be.true 49 | expect(vm.$el.classList.contains(baseClass)).to.be.true 50 | }) 51 | 52 | it('Has no mod + no-origin - baseClass(arg)', () => { 53 | let vm = createVM(classNameOrigin) 54 | expect(vm.$el.classList.contains(baseClass + '_first-mod')).not.to.be.true 55 | expect(vm.$el.classList.contains(baseClass)).to.be.true 56 | }) 57 | 58 | it('Has one mod - no-origin + baseClass(arg)', () => { 59 | let vm = createVM(GoodWithoutOrigin, 'first-mod') 60 | expect(vm.$el.classList.contains(baseClass + '_first-mod')).to.be.true 61 | expect(vm.$el.classList.contains(baseClass)).to.be.true 62 | }) 63 | 64 | it('Has array of mod - no-origin + baseClass(arg)', () => { 65 | let vm = createVM(GoodWithoutOrigin, ['first-mod', 'second-mod']) 66 | expect(vm.$el.classList.contains(baseClass + '_first-mod')).to.be.true 67 | expect(vm.$el.classList.contains(baseClass + '_second-mod')).to.be.true 68 | expect(vm.$el.classList.contains(baseClass)).to.be.true 69 | }) 70 | 71 | it('Has one mod - no-origin - baseClass(arg)', () => { 72 | let vm = createVM(GoodWithoutOrigin, 'first-mod') 73 | expect(vm.$el.classList.contains(baseClass + '_first-mod')).to.be.true 74 | expect(vm.$el.classList.contains(baseClass)).to.be.true 75 | }) 76 | 77 | it('Has array of mod - no-origin - baseClass(arg)', () => { 78 | let vm = createVM(GoodWithoutOrigin, ['first-mod', 'second-mod']) 79 | expect(vm.$el.classList.contains(baseClass + '_first-mod')).to.be.true 80 | expect(vm.$el.classList.contains(baseClass + '_second-mod')).to.be.true 81 | expect(vm.$el.classList.contains(baseClass)).to.be.true 82 | }) 83 | 84 | it('Has no mods - no-origin - baseClass(arg)', () => { 85 | let vm = createVM(GoodWithoutOrigin) 86 | expect(vm.$el.classList.contains(baseClass)).to.be.true 87 | }) 88 | }) 89 | -------------------------------------------------------------------------------- /dist/vue-mods-names.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///vue-mods-names.js","webpack:///webpack/bootstrap 1e456c48dcd66a9af5a9","webpack:///./src/VueModsNames.js"],"names":["root","factory","exports","module","define","amd","this","modules","__webpack_require__","moduleId","installedModules","i","l","call","m","c","d","name","getter","o","Object","defineProperty","configurable","enumerable","get","n","__esModule","object","property","prototype","hasOwnProperty","p","s","__webpack_exports__","value","install","Vue","prefix","arguments","length","undefined","directive","inserted","el","binding","_ref","context","elm","$baseClass","arg","className","$addOriginClass","modifiers","setAttribute","classList","$modsNames","update","_ref2","oldVnode","data","class","mixin","props","mods","type","String","Array","computed","_this","generateAdditionClass","isArray","classArr","map","classArrWithOrigin","slice","unshift","join"],"mappings":"CAAA,SAAAA,EAAAC,GACA,gBAAAC,UAAA,gBAAAC,QACAA,OAAAD,QAAAD,IACA,kBAAAG,gBAAAC,IACAD,OAAA,oBAAAH,GACA,gBAAAC,SACAA,QAAA,kBAAAD,IAEAD,EAAA,kBAAAC,KACCK,KAAA,WACD,MCAgB,UAAUC,GCN1B,QAAAC,GAAAC,GAGA,GAAAC,EAAAD,GACA,MAAAC,GAAAD,GAAAP,OAGA,IAAAC,GAAAO,EAAAD,IACAE,EAAAF,EACAG,GAAA,EACAV,WAUA,OANAK,GAAAE,GAAAI,KAAAV,EAAAD,QAAAC,IAAAD,QAAAM,GAGAL,EAAAS,GAAA,EAGAT,EAAAD,QAvBA,GAAAQ,KA4DA,OAhCAF,GAAAM,EAAAP,EAGAC,EAAAO,EAAAL,EAGAF,EAAAQ,EAAA,SAAAd,EAAAe,EAAAC,GACAV,EAAAW,EAAAjB,EAAAe,IACAG,OAAAC,eAAAnB,EAAAe,GACAK,cAAA,EACAC,YAAA,EACAC,IAAAN,KAMAV,EAAAiB,EAAA,SAAAtB,GACA,GAAAe,GAAAf,KAAAuB,WACA,WAA2B,MAAAvB,GAAA,SAC3B,WAAiC,MAAAA,GAEjC,OADAK,GAAAQ,EAAAE,EAAA,IAAAA,GACAA,GAIAV,EAAAW,EAAA,SAAAQ,EAAAC,GAAsD,MAAAR,QAAAS,UAAAC,eAAAjB,KAAAc,EAAAC,IAGtDpB,EAAAuB,EAAA,SAGAvB,IAAAwB,EAAA,KDgBM,SAAU7B,EAAQ8B,EAAqBzB,GAE7C,YACAY,QAAOC,eAAeY,EAAqB,cAAgBC,OAAO,IEhFlED,EAAA,SACEE,QADa,SACJC,GAAmB,GAAdC,GAAcC,UAAAC,OAAA,OAAAC,KAAAF,UAAA,GAAAA,UAAA,GAAL,GACrBF,GAAIK,UAAU,cACZC,SAD0B,SAChBC,EAAIC,EADYC,GACe,GAAhBC,GAAgBD,EAAhBC,QAASC,EAAOF,EAAPE,GAChCD,GAAQE,WAAaJ,EAAQK,KAAOF,EAAIG,UACxCJ,EAAQK,gBAAkBP,EAAQQ,UAAU,aAC5CL,EAAIM,aAAa,QAASN,EAAIO,UAAY,IAAMR,EAAQS,aAE1DC,OAN0B,SAMlBb,EAAIC,EANca,EAMmBC,GAAU,GAAhCC,GAAgCF,EAAhCE,KAAMZ,EAA0BU,EAA1BV,IAAKD,EAAqBW,EAArBX,OAC5Ba,GAAKC,QAAUF,EAASC,KAAKC,QAC3BD,EAAKC,MACPb,EAAIO,UAAYR,EAAQS,WAAa,IAAMI,EAAKC,MAEhDb,EAAIO,UAAYR,EAAQS,eAKhCnB,EAAIyB,OACFF,KADQ,WAEN,OAASX,WAAY,KAAMG,iBAAiB,IAE9CW,OACEC,MACEC,MAAQC,OAAQC,SAGpBC,UACEZ,WADQ,WACM,GAAAa,GAAA9D,KACR+D,EAAwB,SAAAN,GAAA,MAAQK,GAAKpB,WAAaX,EAAS0B,EAE/D,IAAIG,MAAMI,QAAQhE,KAAKyD,MAAO,CAC5B,GAAIQ,KAAajE,KAAKyD,MAAQzD,KAAKyD,KAAKS,IAAI,SAAA7D,GAAA,MAAK0D,GAAsB1D,KAEnE8D,EAAqBF,EAASG,MAAM,EAGxC,OADKpE,MAAK6C,iBAAiBsB,EAAmBE,QAAQrE,KAAK0C,YACpDyB,EAAmBG,KAAK,KAGjC,MAAKtE,MAAKyD,KAEFzD,KAAK6C,gBACkDkB,EAAsB/D,KAAKyD,MAAtFzD,KAAK0C,WAAa,IAAMqB,EAAsB/D,KAAKyD,MAHhCzD,KAAK0C","file":"vue-mods-names.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"vue-mods-names\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"vue-mods-names\"] = factory();\n\telse\n\t\troot[\"vue-mods-names\"] = factory();\n})(this, function() {\nreturn \n\n\n// WEBPACK FOOTER //\n// webpack/universalModuleDefinition","(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"vue-mods-names\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"vue-mods-names\"] = factory();\n\telse\n\t\troot[\"vue-mods-names\"] = factory();\n})(this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"/dist/\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 0);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n install: function install(Vue) {\n var prefix = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '_';\n\n Vue.directive('mods-names', {\n inserted: function inserted(el, binding, _ref) {\n var context = _ref.context,\n elm = _ref.elm;\n\n context.$baseClass = binding.arg || elm.className;\n context.$addOriginClass = binding.modifiers['no-origin'];\n elm.setAttribute('class', elm.classList + ' ' + context.$modsNames);\n },\n update: function update(el, binding, _ref2, oldVnode) {\n var data = _ref2.data,\n elm = _ref2.elm,\n context = _ref2.context;\n\n if (data.class !== oldVnode.data.class) {\n if (data.class) {\n elm.classList = context.$modsNames + ' ' + data.class;\n } else {\n elm.classList = context.$modsNames;\n }\n }\n }\n });\n Vue.mixin({\n data: function data() {\n return { $baseClass: null, $addOriginClass: true };\n },\n\n props: {\n mods: {\n type: [String, Array]\n }\n },\n computed: {\n $modsNames: function $modsNames() {\n var _this = this;\n\n var generateAdditionClass = function generateAdditionClass(mods) {\n return _this.$baseClass + prefix + mods;\n };\n\n if (Array.isArray(this.mods)) {\n var classArr = !!this.mods && this.mods.map(function (i) {\n return generateAdditionClass(i);\n });\n // Don't touch original array of classes\n var classArrWithOrigin = classArr.slice(0);\n // If comopnent should render with base class, then add base class to begin of array\n if (!this.$addOriginClass) classArrWithOrigin.unshift(this.$baseClass);\n return classArrWithOrigin.join(' ');\n }\n\n if (!this.mods) return this.$baseClass;\n\n return !this.$addOriginClass ? this.$baseClass + ' ' + generateAdditionClass(this.mods) : generateAdditionClass(this.mods);\n }\n }\n });\n }\n});\n\n/***/ })\n/******/ ]);\n});\n\n\n// WEBPACK FOOTER //\n// vue-mods-names.js"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/dist/\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 1e456c48dcd66a9af5a9","export default {\r\n install (Vue, prefix = '_') {\r\n Vue.directive('mods-names', {\r\n inserted (el, binding, { context, elm }) {\r\n context.$baseClass = binding.arg || elm.className\r\n context.$addOriginClass = binding.modifiers['no-origin']\r\n elm.setAttribute('class', elm.classList + ' ' + context.$modsNames)\r\n },\r\n update (el, binding, { data, elm, context }, oldVnode) {\r\n if (data.class !== oldVnode.data.class) {\r\n if (data.class) {\r\n elm.classList = context.$modsNames + ' ' + data.class\r\n } else {\r\n elm.classList = context.$modsNames\r\n }\r\n }\r\n }\r\n })\r\n Vue.mixin({\r\n data () {\r\n return { $baseClass: null, $addOriginClass: true }\r\n },\r\n props: {\r\n mods: {\r\n type: [ String, Array ]\r\n }\r\n },\r\n computed: {\r\n $modsNames () {\r\n let generateAdditionClass = mods => this.$baseClass + prefix + mods\r\n\r\n if (Array.isArray(this.mods)) {\r\n let classArr = !!this.mods && this.mods.map(i => generateAdditionClass(i))\r\n // Don't touch original array of classes\r\n let classArrWithOrigin = classArr.slice(0)\r\n // If comopnent should render with base class, then add base class to begin of array\r\n if (!this.$addOriginClass) classArrWithOrigin.unshift(this.$baseClass)\r\n return classArrWithOrigin.join(' ')\r\n }\r\n\r\n if (!this.mods) return this.$baseClass\r\n\r\n return !this.$addOriginClass\r\n ? this.$baseClass + ' ' + generateAdditionClass(this.mods) : generateAdditionClass(this.mods)\r\n }\r\n }\r\n })\r\n }\r\n}\r\n\n\n\n// WEBPACK FOOTER //\n// ./src/VueModsNames.js"],"sourceRoot":""} -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## Intro 2 | 3 | ### About CSS methodology 4 | 5 | Always we have to create similar components, but that has a different views or small modifications by using in other different places. For reach this goal, developers often use common methodologies for manage CSS class names. 6 | For example BEM. By this methodology each block (component in our case) has custom modifications, that add by same CSS class name with addition modify (`mod`). See example below: 7 | 8 | 9 | 73 | 74 | We wanna use this component in other place with other colors and without plus button. Following methodology rules we have to add mod to root block class name. This allows to us change view of particular block with mods, but don't break other same blocks without mods. In real case it's look like this example: 75 | 76 | > Add class with mod cyan - `good_cyan`. Often you may see version, when developers use `-` instead `__` for blocks. And `--` instead `__` for elements. `vue-mods-names` allow pass any symbols for custom prefix for using in your components. It's not so necessary. Note that, good practice is name mods by them usage on particular page or functional that component implement in this place, but not colors. In our case we use mod `cyan` only for more visibility. 77 | 78 | 79 | 157 | 158 | We use same component, but add one CSS class with mod and now we just need to add custom styles for our component (block) with mod. 159 | 160 | ``` css 161 | .good_cyan .good__header { 162 | background-color: #00bcd4; 163 | color: #000; 164 | } 165 | 166 | .good_cyan .good__strip { 167 | background-color: #008ba3; 168 | color: #000; 169 | } 170 | 171 | .good_cyan .good__button { 172 | display: none; 173 | } 174 | ``` 175 | 176 | It's look little bit better when you use a preprocessor for CSS: 177 | 178 | ``` css 179 | .good_cyan .good__header, 180 | .good_cyan .good__strip 181 | background-color: #00bcd4 182 | color: #000 183 | 184 | .good_cyan .good__button 185 | display: none 186 | ``` 187 | 188 | Now you may imagine how it will be good, when you can manage view of particular components by these mods. You just write styles in other mod and use them where you need, You may use many of mods in one time. For example, you may add `.good_with-border` and `.good_red` in same component. Also mention, that name of mod choosing by usage component. For example, `.good_main-page` and `.good_article-page` is more better for naming mods, because you can look where this component use at first look and you may not figure out where it use in your templates. 189 | 190 | ### Components in Vue 191 | 192 | This method of manage CSS classes is good for using by component oriented framework and it's even really easy to use. To use component with other mods, you have to define receiving mods via props. In our case it will be look something like this: 193 | 194 | `MyComponent.vue 72 | 73 | Нам нужно использовать этот компонент в другом месте в другой цветовой схеме и без кнопки с плюсиком. Согласно методологии нам нужно добавить модификатор к этому классу главного блока, чтобы не сломать оформление самого блока, а менять только стили его модификации. На практике это выглядит так: 74 | 75 | > Добавляем класс с модфикатором cyan - `good_cyan`. Часто вы можете встретить вариант, когда используют `-` вместо `_`, а для обозначения элемента блока `--`, а не `__`. Но это уже нюансы. `vue-mods-names` позволяет использовать любые символы в качестве префикса. Принимает их как опции плагина при его установке. Также прошу обратить ваше внимание, что хорошей практикой является наименование модификаторов в соответствии с их принадлежностью к определенной странице на которой он используется или функционалу, но не цветовому оформлению. В данном случае использовался модификатор `cyan` для большей наглядности. 76 | 77 | 78 | 156 | 157 | Мы использовали тот же компонент, но указали один класс с модификатором и в стилях компонента просто добавили стили для данной модификации этого блока: 158 | 159 | ``` css 160 | .good_cyan .good__header { 161 | background-color: #00bcd4; 162 | color: #000; 163 | } 164 | 165 | .good_cyan .good__strip { 166 | background-color: #008ba3; 167 | color: #000; 168 | } 169 | 170 | .good_cyan .good__button { 171 | display: none; 172 | } 173 | ``` 174 | 175 | При использовании препроцессоров это будет выглядить еще красивее и компактнее: 176 | 177 | ``` css 178 | .good_cyan .good__header, 179 | .good_cyan .good__strip 180 | background-color: #00bcd4 181 | color: #000 182 | 183 | .good_cyan .good__button 184 | display: none 185 | ``` 186 | 187 | Теперь представьте как хорошо контролировать оформление определённого блока с помощью таких модификаторов. Вы просто описываете нужные вам стили в другом модификаторе и применяете их где нужно, можно применять разное количество модификаторов. Например, в один и тот же блок добавить `.good_with-border` и `.good_red`. Так же хочу ещё раз напомнить, что в основном блоки называют не в соответствии с их цветовым оформлением, а в соответствии с их принадлежностью (контекстом использования), например, `.good_main-page` и `.good_article-page`. И из названия сразу становится понятно, что этот блок с такими стилями используется на главной странице, но такой же блок с модифицированными стилями используется на странице статей. Большинство разработчиков используют такой подход и не жалеют об этом. Одно из важных приемуществ при данном подходе - это возможность оградить себя от ненужных изменений в основном блоке, менять только его модификации. 188 | 189 | ### Компоненты во Vue 190 | 191 | При компонентном подходе такая методика разработки оказывается очень кстати и более того становится очень лёгкой для использования. Чтобы использовать компонент с другими модификаторами, вы конечно должны позволить компоненту принимать эти модификаторы через входные свойства (props). В нашем примере, это будет выглядить примерно так: 192 | 193 | `MyComponent.vue 72 | 73 | Нам потрібно використовувати цей компонент в іншому місці в іншій кольоровій схемі і без кнопки з плюсом. Згідно з методологією нам потрібно додати модифікатор до цього класу головного блоку, щоб не зламати оформлення самого блоку, а міняти тільки стилі його модифікації. На практиці це виглядає так: 74 | 75 | > Додаємо клас з модфікатором cyan - `good_cyan`. Часто ви можете зустріти варіант, коли використовують `-` замість ` _`, а для позначення елемента блоку `--`, а не `__`. Але це вже нюанси. Так само прошу звернути вашу увагу, що гарним вибором найменування модифікаторів відповідно до їх належності до певної сторінки на якій він використовується або функціоналу, але не кольорового оформлення. У даному випадку використовувався модифікатор `cyan` для більшої наглядності. 76 | 77 | 78 | 156 | 157 | Ми використовували той же компонент, але вказали один клас з модифікатором і в стилях компонента просто додали стилі для даної модифікації цього блоку: 158 | 159 | ``` css 160 | .good_cyan .good__header { 161 | background-color: #00bcd4; 162 | color: #000; 163 | } 164 | 165 | .good_cyan .good__strip { 166 | background-color: #008ba3; 167 | color: #000; 168 | } 169 | 170 | .good_cyan .good__button { 171 | display: none; 172 | } 173 | ``` 174 | 175 | При використанні препроцесорів це буде виглядати ще гарніше і компактніше: 176 | 177 | ``` css 178 | .good_cyan .good__header, 179 | .good_cyan .good__strip 180 | background-color: #00bcd4 181 | color: #000 182 | 183 | .good_cyan .good__button 184 | display: none 185 | ``` 186 | 187 | Тепер уявіть як добре контролювати оформлення певного блоку за допомогою таких модифікаторів. Ви просто описуєте потрібні вам стилі в іншому модификаторі і застосовуєте їх де потрібно, можна застосовувати різну кількість модифікаторів. Наприклад, в один і той же блок додати `.good_with-border` і `.good_red`. Так само хочу ще раз нагадати, що в основному блоки називають не відповідно до їх кольорового оформлення, а відповідно до їх належності (контекстом використання), наприклад, `.good_main-page` і `.good_article-page`. І з назви відразу стає зрозуміло, що цей блок з такими стилями використовується на головній сторінці, але такий же блок з модифікованими стилями використовується на сторінці статей. Більшість розробників використовують такий підхід і не шкодують про це. Одна з важливих переваг при даному підході - це можливість застерегти себе від непотрібних змін в основному блоці, міняти тільки його модифікації. 188 | 189 | ### Компоненти у вашому фреймворці 190 | 191 | При компонентному підході така методика розробки виявляється дуже до речі і більш того стає дуже легкою для використання. Щоб використовувати компонент з іншими модифікаторами, ви звичайно повинні дозволити компоненту приймати ці модифікатори через вхідні властивості (props). У нашому прикладі, це буде виглядати приблизно так: 192 | 193 | `MyComponent.vue 72 | 73 | Nous devons utiliser ce composant ailleurs dans un jeu de couleurs différentes et sans un bouton avec plus. Selon la méthodologie, il faut ajouter un modificateur à cette classe du bloc principal afin de ne pas casser la conception du bloc lui-même, mais seulement changer les styles de sa modification. Voilà à quoi ça ressemble: 74 | 75 | > Nous ajoutons une classe avec le modificateur cyan - `good_cyan`. 76 | Souvent, vous pouvez rencontrer une variante quand ils utilisent `-` au lieu de `_`, et pour indiquer l'élément du bloc `--` plutôt que `__`. Mais ce sont des nuances. `vue-mods-names` vous permet d'utiliser n'importe quel caractère comme préfixe. Il les accepte comme les options de plug-in lors de l'installation. Aussi, je vous prie de noter que le nom des modificateurs en fonction de leur appartenance à une certaine page sur laquelle ils sont utilisés ou fonctionnels, mais pas de la couleur, est une bonne pratique. 77 | Dans ce cas, le modificateur `cyan` a été utilisé pour plus de clarté. 78 | 79 | 80 | 158 | 159 | Nous avons utilisé le même composant, mais nous avons indiqué une classe avec un modificateur et dans les styles de composant, nous avons simplement ajouté les styles pour cette modification de ce bloc: 160 | 161 | ``` css 162 | .good_cyan .good__header { 163 | background-color: #00bcd4; 164 | color: #000; 165 | } 166 | 167 | .good_cyan .good__strip { 168 | background-color: #008ba3; 169 | color: #000; 170 | } 171 | 172 | .good_cyan .good__button { 173 | display: none; 174 | } 175 | ``` 176 | 177 | Lors de l'utilisation de préprocesseurs, cela sera encore plus beau et compact: 178 | 179 | ``` css 180 | .good_cyan .good__header, 181 | .good_cyan .good__strip 182 | background-color: #00bcd4 183 | color: #000 184 | 185 | .good_cyan .good__button 186 | display: none 187 | ``` 188 | 189 | Maintenant, imaginez à quel point il est bon de contrôler la conception d'un bloc particulier à l'aide de ces modificateurs. 190 | Vous décrivez simplement les styles dont vous avez besoin dans un autre modificateur et vous les appliquez si nécessaire, vous pouvez appliquer un nombre différent de modificateurs. 191 | Par exemple, dans le même bloc, ajoutez `.good_with-border` et `.good_red`. 192 | Aussi, je tiens à vous rappeler que les blocs sont essentiellement appelés non pas en fonction de leur conception de couleur, mais en fonction de leur appartenance (contexte d'utilisation), par exemple, `.good_main-page` et `.good_article-page`. 193 | Et à partir du nom, il devient immédiatement clair que ce bloc avec de tels styles est utilisé sur la page principale, mais le même bloc avec les styles modifiés est utilisé sur la page des articles. 194 | La plupart des développeurs utilisent cette approche et ne le regrettent pas. 195 | L'un des avantages importants de cette approche est la possibilité de se protéger des changements inutiles dans l'unité principale, de ne modifier que ses modifications. 196 | 197 | 198 | ### Composants dans Vue 199 | 200 | Avec l'approche par composants, cette méthode de développement est très utile et elle devient très facile à utiliser. 201 | Pour utiliser un composant avec d'autres modificateurs, vous devez autoriser le composant à adopter ces modificateurs par les propriétés d'entrée (props). 202 | Voilà à quoi ressemble: 203 | 204 | `MyComponent.vue