├── docs ├── .nojekyll ├── CNAME ├── helpers.md ├── assets │ └── images │ │ └── logo.png ├── styles.md ├── directives.md ├── mixins.md ├── config.js ├── 404.html ├── index.html ├── README.md └── components.md ├── .eslintignore ├── src ├── directives.js ├── mixins.js ├── components │ ├── Icon.vue │ ├── InputBox.js │ ├── CheckboxWrapper.vue │ ├── SmartForm.vue │ ├── Breadcrumb.vue │ ├── InputButton.vue │ ├── BootstrapBox.vue │ ├── Alert.vue │ ├── InputSearch.vue │ ├── InputText.vue │ ├── CustomBox.vue │ ├── InputTextarea.vue │ ├── Dropdown.vue │ ├── InputSelect.vue │ ├── InputTypeahead.vue │ ├── InputSwitch.vue │ ├── Search.vue │ ├── Modal.vue │ ├── Typeahead.vue │ └── Tooltip.vue ├── mixins │ ├── inheritComponents.js │ ├── formHelper.js │ ├── inputHelper.js │ └── checkbox.js ├── directives │ └── tooltip.js ├── index.js ├── utils.js └── components.js ├── .npmignore ├── .gitignore ├── .stylelintrc ├── test ├── .eslintrc └── unit │ ├── specs │ ├── components │ │ ├── InputSearch.spec.js │ │ ├── Dropdown.spec.js │ │ ├── Icon.spec.js │ │ ├── Typeahead.spec.js │ │ ├── InputTypeahead.spec.js │ │ ├── Breadcrumb.spec.js │ │ ├── Modal.spec.js │ │ ├── InputButton.spec.js │ │ ├── BootstrapBox.spec.js │ │ ├── CustomBox.spec.js │ │ ├── InputSwitch.spec.js │ │ ├── Alert.spec.js │ │ ├── CheckboxWrapper.spec.js │ │ ├── InputText.spec.js │ │ └── InputSelect.spec.js │ └── directives │ │ └── tooltip.spec.js │ ├── index.js │ ├── util.js │ ├── visual.js │ ├── karma.conf.js │ └── helpers │ └── style.scss ├── .editorconfig ├── .babelrc ├── .eslintrc.js ├── LICENSE ├── CONTRIBUTING.md ├── README.md └── package.json /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/*.js 2 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | bootstrap-for-vue.znck.me -------------------------------------------------------------------------------- /docs/helpers.md: -------------------------------------------------------------------------------- 1 | ## Helpers 2 | 3 | #### ErrorBag -------------------------------------------------------------------------------- /src/directives.js: -------------------------------------------------------------------------------- 1 | export { default as Tooltip } from './directives/tooltip' 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | build 2 | config 3 | static 4 | test 5 | circle.yml 6 | index.html 7 | .* 8 | -------------------------------------------------------------------------------- /docs/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/znck/bootstrap-for-vue/HEAD/docs/assets/images/logo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log 4 | test/unit/coverage 5 | dist 6 | .idea/ 7 | yarn-error.log 8 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "processors": ["stylelint-processor-html"], 3 | "extends": "stylelint-config-standard" 4 | } 5 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "globals": { 6 | "expect": true, 7 | "sinon": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/mixins.js: -------------------------------------------------------------------------------- 1 | export { default as formHelper } from './mixins/formHelper' 2 | export { default as inputHelper } from './mixins/inputHelper' 3 | export { default as inheritComponents } from './mixins/inheritComponents' 4 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"], 3 | "plugins": ["transform-runtime", "transform-vue-jsx"], 4 | "comments": false, 5 | "env": { 6 | "test": { 7 | "plugins": [ "istanbul" ] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/components/Icon.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 23 | -------------------------------------------------------------------------------- /src/components/InputBox.js: -------------------------------------------------------------------------------- 1 | import CustomBox from './CustomBox.vue' 2 | import BootstrapBox from './BootstrapBox.vue' 3 | 4 | export default { 5 | functional: true, 6 | 7 | render (h, { props, data, children }) { 8 | if (props.custom) return h(CustomBox, data, children) 9 | 10 | return h(BootstrapBox, data, children) 11 | }, 12 | 13 | props: { 14 | custom: { 15 | type: Boolean, 16 | default: false 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: 'babel-eslint', 4 | parserOptions: { 5 | sourceType: 'module' 6 | }, 7 | extends: 'vue', 8 | globals: { 9 | 'jQuery': true, 10 | }, 11 | // add your custom rules here 12 | 'rules': { 13 | // allow async-await 14 | 'generator-star-spacing': 0, 15 | // allow debugger during development 16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/mixins/inheritComponents.js: -------------------------------------------------------------------------------- 1 | export default { 2 | beforeCreate () { 3 | if (!this.$parent || !this.$parent.$options.components) return 4 | 5 | this.$options.components = this.$options.components || {} 6 | 7 | const names = Object.keys(this.$parent.$options.components) 8 | 9 | names.forEach(name => { 10 | if (!(name in this.$options.components)) { 11 | this.$options.components[name] = this.$parent.$options.components[name] 12 | } 13 | }) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/directives/tooltip.js: -------------------------------------------------------------------------------- 1 | import '../components/Tooltip.vue' 2 | 3 | export default { 4 | bind (el, binding) { 5 | const position = binding.arg ? `${binding.arg}-center`.split('-').slice(0, 2).join(' ') : 'bottom center' 6 | 7 | el.setAttribute('data-tooltip', binding.value) 8 | el.setAttribute('data-position', position) 9 | }, 10 | 11 | update (el, binding) { 12 | if (binding.value !== binding.oldValue) { 13 | el.setAttribute('data-tooltip', binding.value) 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/components/CheckboxWrapper.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 20 | 21 | 26 | -------------------------------------------------------------------------------- /src/components/SmartForm.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 26 | -------------------------------------------------------------------------------- /test/unit/specs/components/InputSearch.spec.js: -------------------------------------------------------------------------------- 1 | import { render } from '../../util' 2 | import InputSearch from 'src/components/InputSearch.vue' 3 | 4 | function getComponent (ctx, template) { 5 | return render(ctx, { 6 | template, 7 | components: { InputSearch }, 8 | data () { 9 | return { 10 | search: '' 11 | } 12 | } 13 | }) 14 | } 15 | 16 | describe('InputSearch.vue', function () { 17 | it('should render correctly', function () { 18 | const vm = getComponent(this, '') 19 | 20 | // vm.$('.form-control').should.exist 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /docs/styles.md: -------------------------------------------------------------------------------- 1 | # Styles 2 | You can override scss variables to customize UI. 3 | 4 | ## Exports 5 |
 6 | ──dist
 7 |    │
 8 |    ├──scss
 9 |    │   │
10 |    │   ├──components
11 |    │   │   │
12 |    │   │   ├── BootstrapBox.scss
13 |    │   │   ├── CheckboxWrapper.scss
14 |    │   │   ├── CustomBox.scss
15 |    │   │   ├── CustomModal.scss
16 |    │   │   ├── Dropdown.scss
17 |    │   │   ├── InputSwitch.scss
18 |    │   │   ├── InputText.scss
19 |    │   │   ├── InputTextarea.scss
20 |    │   │   ├── Search.scss
21 |    │   │   ├── Tooltip.scss
22 |    │   │   └── Typeahead.scss
23 |    │   │
24 |    │   └── bootstrap-for-vue.scss
25 |    ├── bootstrap-for-vue.css
26 |    ├── ...
27 |    ...
28 | 
29 | -------------------------------------------------------------------------------- /test/unit/specs/components/Dropdown.spec.js: -------------------------------------------------------------------------------- 1 | import { render } from '../../util' 2 | import Dropdown from 'src/components/Dropdown.vue' 3 | 4 | function getComponent (ctx, template, data) { 5 | return render(ctx, { 6 | template, 7 | components: { Dropdown }, 8 | data () { 9 | return data || { show: false, items: [{ name: 'Hello' }, { name: 'Good Morning' }, { name: 'Good Bye' }] } 10 | } 11 | }) 12 | } 13 | 14 | describe('Dropdown.vue', function () { 15 | it('should render correctly', function () { 16 | const vm = getComponent( 17 | this, 18 | '' 19 | ) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /test/unit/specs/components/Icon.spec.js: -------------------------------------------------------------------------------- 1 | import { render } from '../../util' 2 | import Icon from 'src/components/Icon.vue' 3 | 4 | function getComponent (ctx, template) { 5 | return render(ctx, { 6 | template, 7 | data () { 8 | return { name: 'circle' } 9 | }, 10 | components: { Icon } 11 | }) 12 | } 13 | 14 | describe('Icon.vue', function () { 15 | it('should render correctly', function () { 16 | const vm = getComponent(this, ``) 17 | 18 | vm.$el.should.have.class('fa-circle') 19 | .and.have.text('') 20 | }) 21 | 22 | it('should render without binding', function () { 23 | const vm = getComponent(this, ``) 24 | 25 | vm.$el.should.have.class('fa-circle') 26 | .and.have.text('') 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /src/components/Breadcrumb.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 37 | -------------------------------------------------------------------------------- /test/unit/specs/components/Typeahead.spec.js: -------------------------------------------------------------------------------- 1 | import { render } from '../../util' 2 | import Typeahead from 'src/components/Typeahead.vue' 3 | 4 | function getComponent (ctx, template) { 5 | return render(ctx, { 6 | template, 7 | components: { Typeahead }, 8 | data () { 9 | return { 10 | search: [], 11 | suggestions: [ 12 | { id: 1, name: 'Rahul Kadyan' }, 13 | { id: 2, name: 'Foo Bar' } 14 | ] 15 | } 16 | } 17 | }) 18 | } 19 | 20 | describe('Typeahead.vue', function () { 21 | it('should render correctly', function () { 22 | const vm = getComponent(this, '') 23 | 24 | // vm.$('.form-control').should.exist 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /docs/directives.md: -------------------------------------------------------------------------------- 1 | ## Directives 2 | Check out [Vue docs](https://vuejs.org/v2/guide/syntax.html#Directives) to know about directives. 3 | 4 | #### Tooltip 5 | It uses [bootstrap tooltip](https://v4-alpha.getbootstrap.com/components/tooltips/#usage) to render a tooltip. 6 | 7 | ``` 8 | v-tooltip:position="expression" 9 | ``` 10 | 11 | Position could be `top`, `right`, `bottom` & `left` or any combination of these. 12 | 13 | ##### Example 14 | 15 | **Tooltip:** 16 | ````vue 17 | 22 | 23 | 30 | ```` -------------------------------------------------------------------------------- /src/components/InputButton.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 46 | -------------------------------------------------------------------------------- /test/unit/specs/components/InputTypeahead.spec.js: -------------------------------------------------------------------------------- 1 | import { render } from '../../util' 2 | import InputTypeahead from 'src/components/InputTypeahead.vue' 3 | 4 | function getComponent (ctx, template) { 5 | return render(ctx, { 6 | template, 7 | components: { InputTypeahead }, 8 | data () { 9 | return { 10 | search: '', 11 | suggestions: [ 12 | { id: 1, name: 'Rahul Kadyan' }, 13 | { id: 2, name: 'Foo Bar' } 14 | ] 15 | } 16 | } 17 | }) 18 | } 19 | 20 | describe('InputTypeahead.vue', function () { 21 | it('should render correctly', function () { 22 | const vm = getComponent(this, '') 23 | 24 | // vm.$('.form-control').should.exist 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /src/components/BootstrapBox.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | 19 | 27 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import * as directives from './directives' 2 | import * as components from './components' 3 | import { each } from './utils' 4 | import { version } from '../package.json' 5 | 6 | export { formHelper } from './mixins' 7 | export { ErrorBag } from './utils' 8 | 9 | const plugin = { 10 | version, 11 | install (Vue, options = {}) { 12 | if (options.custom === true) { 13 | components.Modal.props.custom.default = true 14 | components.InputBox.props.custom.default = true 15 | } 16 | 17 | each(components, (component, name) => Vue.component(name, component)) 18 | each(directives, (directive, name) => Vue.directive(name, directive)) 19 | } 20 | } 21 | 22 | export default plugin 23 | 24 | // Install by default if using the script tag 25 | if (typeof window !== 'undefined' && 'Vue' in window) { 26 | Vue.use(plugin, window.bootstrapForVueConfig || {}) // eslint-disable-line no-undef 27 | } 28 | -------------------------------------------------------------------------------- /src/components/Alert.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 49 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | // Polyfill fn.bind() for PhantomJS 2 | import bind from 'function-bind' 3 | /* eslint-disable no-extend-native */ 4 | Function.prototype.bind = bind 5 | 6 | // Polyfill Object.assign for PhantomJS 7 | import objectAssign from 'object-assign' 8 | Object.assign = objectAssign 9 | 10 | // require all src files for coverage. 11 | // you can also change this to match only the subset of files that 12 | // you want coverage for. 13 | const srcContext = require.context('../../src', true, /^\.\/(?!index(\.js)?$)/) 14 | srcContext.keys().forEach(srcContext) 15 | 16 | // Use a div to insert elements 17 | before(function () { 18 | const el = document.createElement('DIV') 19 | el.id = 'tests' 20 | document.body.appendChild(el) 21 | }) 22 | 23 | // Remove every test html scenario 24 | afterEach(function () { 25 | const el = document.getElementById('tests') 26 | ;[...el.children].forEach(el.removeChild.bind(el)) 27 | }) 28 | 29 | const specsContext = require.context('./specs', true) 30 | specsContext.keys().forEach(specsContext) 31 | -------------------------------------------------------------------------------- /src/components/InputSearch.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 33 | -------------------------------------------------------------------------------- /test/unit/specs/components/Breadcrumb.spec.js: -------------------------------------------------------------------------------- 1 | import { render } from '../../util' 2 | import Breadcrumb from 'src/components/Breadcrumb.vue' 3 | 4 | function getComponent(ctx, template) { 5 | return render(ctx, { 6 | template, 7 | components: { Breadcrumb }, 8 | computed: { 9 | crumbs () { 10 | return [ 11 | { link: '/', title: 'Home' }, 12 | { link: '/users', title: 'Users' }, 13 | { link: '/users/me', title: 'Me' } 14 | ] 15 | }, 16 | links () { 17 | return ['home', 'users', 'me'] 18 | } 19 | } 20 | }) 21 | } 22 | 23 | describe('Breadcrumb.vue', function () { 24 | it('should render correctly', function () { 25 | const vm = getComponent(this, ``) 26 | const a = vm.$('a') 27 | 28 | vm.$el.should.exist 29 | a.should.have.text('Home') 30 | .and.have.attr('to', '/') 31 | }) 32 | 33 | it('should render correctly', function () { 34 | const vm = getComponent(this, ``) 35 | 36 | vm.$el.should.exist 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Rahul Kadyan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | We accept contributions via Pull Requests on [Github](https://github.com/znck/bootstrap-for-vue). 6 | 7 | 8 | ## Pull Requests 9 | 10 | - **Keep the same style** - eslint will automatically be ran before committing 11 | 12 | - **Tip** to pass lint tests easier use the `npm run lint:fix` command 13 | 14 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 15 | 16 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 17 | 18 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 19 | 20 | - **Create feature branches** - Don't ask us to pull from your master branch. 21 | 22 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 23 | 24 | - **Send coherent history** - Make sure your commits message means something 25 | 26 | 27 | ## Running Tests 28 | 29 | Launch visual tests and watch the components at the same time 30 | 31 | ``` bash 32 | $ npm run dev 33 | ``` 34 | 35 | 36 | **Happy coding**! 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | 7 | [![npm](https://img.shields.io/npm/v/bootstrap-for-vue.svg)](https://www.npmjs.com/package/bootstrap-for-vue) 8 | [![vue2](https://img.shields.io/badge/vue-2.x-brightgreen.svg)](https://vuejs.org/) 9 | 10 |
11 | 12 | ## Introduction 13 | **Bootstrap for Vue** is collection of bootstrap 4 based reusable components, (no jquery, no tether, no bootstrap.js). It is used by 14 | [Vue Issue](https://new-issue.vuejs.org) and [Zero](https://zero.institute). 15 | 16 | 17 | > Looking to contribute? Take up a task from [Dev Board](https://github.com/znck/bootstrap-for-vue/projects/1) 18 | 19 | ## Documentation 20 | http://bootstrap-for-vue.znck.me/ 21 | 22 | ## Development 23 | 24 | #### Launch visual tests 25 | 26 | ```bash 27 | npm run dev 28 | ``` 29 | 30 | #### Launch Karma with coverage 31 | 32 | ```bash 33 | npm run dev:coverage 34 | ``` 35 | 36 | #### Build 37 | 38 | Bundle the js and css of to the `dist` folder: 39 | 40 | ```bash 41 | npm run build 42 | ``` 43 | 44 | ## License 45 | 46 | [MIT](http://opensource.org/licenses/MIT) 47 | -------------------------------------------------------------------------------- /test/unit/util.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { ErrorBag } from 'src/utils' 3 | 4 | export function render(context, component) { 5 | if (!context.DOM) { 6 | const app = new Vue(component) 7 | context.$ = app.$ = s => app.$el.querySelector(s) 8 | context.tick = app.tick = () => new Promise(r => app.$nextTick(r)) 9 | 10 | app.$mount() 11 | 12 | return app 13 | } else { 14 | const app = new Vue({ 15 | el: context.DOM, 16 | data () { 17 | return { show: false } 18 | }, 19 | template: ` 20 |
21 | 22 |
23 |
`, 24 | components: { 25 | TestCase: component 26 | } 27 | }) 28 | 29 | const vm = app.$children[0] 30 | 31 | context.$app = app 32 | context.$vm = vm 33 | context.$ = vm.$ = s => vm.$el.querySelector(s) 34 | context.tick = vm.tick = () => new Promise(r => app.$nextTick(r)) 35 | 36 | return vm 37 | } 38 | } 39 | 40 | export function makeErrors(any) { 41 | return new ErrorBag(any) 42 | } 43 | -------------------------------------------------------------------------------- /test/unit/visual.js: -------------------------------------------------------------------------------- 1 | import 'style-loader!css-loader!mocha-css' 2 | import 'style-loader!css-loader!sass-loader!./helpers/style.scss' 3 | 4 | // create a div where mocha can add its stuff 5 | const mochaDiv = document.createElement('DIV') 6 | mochaDiv.id = 'mocha' 7 | document.body.appendChild(mochaDiv) 8 | 9 | window.$ = window.jQuery = require('jquery') 10 | window.Tether = require('tether') 11 | require('bootstrap') 12 | 13 | import 'mocha/mocha.js' 14 | import chai from 'chai' 15 | import chaiDOM from 'chai-dom' 16 | window.mocha.setup('bdd') 17 | chai.use(chaiDOM) 18 | chai.should() 19 | window.expect = chai.expect 20 | 21 | beforeEach(function () { 22 | this.DOM = document.createElement('div') 23 | this.DOM.id = `test-${Math.floor(Math.random() * 1000000000)}` 24 | 25 | document.body.appendChild(this.DOM) 26 | }) 27 | 28 | afterEach(function () { 29 | const tests = document.querySelectorAll('.test') 30 | const test = tests[tests.length - 1] 31 | 32 | if (!test) return 33 | 34 | const el = document.querySelector(`#${this.DOM.id}`) 35 | 36 | test.appendChild(el) 37 | }) 38 | 39 | const specsContext = require.context('./specs', true) 40 | specsContext.keys().forEach(specsContext) 41 | 42 | // window.mocha.checkLeaks() 43 | window.mocha.run() 44 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | export function isArray (any) { 2 | return Array.isArray(any) 3 | } 4 | 5 | export function isObject (any) { 6 | return any !== null && typeof (any) === 'object' 7 | } 8 | 9 | export function mapObject (source, take) { 10 | const target = {} 11 | 12 | if (isArray(take)) { 13 | take.forEach((key) => { 14 | if (key in source) { 15 | target[key] = source[key] 16 | } 17 | }) 18 | } else { 19 | Object.keys(take).forEach((sourceKey) => { 20 | const targetKey = take[sourceKey] 21 | 22 | if (sourceKey in source) { 23 | target[targetKey] = source[sourceKey] 24 | } 25 | }) 26 | } 27 | 28 | return target 29 | } 30 | 31 | export function each (obj, callback) { 32 | Object.keys(obj).forEach(key => callback(obj[key], key)) 33 | } 34 | 35 | export class ErrorBag { 36 | constructor (errors) { 37 | this._errors = typeof (errors) === 'object' ? errors : {} 38 | } 39 | 40 | has (key) { 41 | return this._errors.hasOwnProperty(key) 42 | } 43 | 44 | get (key) { 45 | const value = this._errors[key] 46 | 47 | if (isArray(value)) { 48 | return value.join(' ') 49 | } 50 | 51 | return value 52 | } 53 | 54 | unset (key) { 55 | delete this._errors[key] 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/components/InputText.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 32 | 33 | -------------------------------------------------------------------------------- /docs/mixins.md: -------------------------------------------------------------------------------- 1 | ## Mixins 2 | Checkout out [Vue docs](https://vuejs.org/v2/guide/mixins.html) to know about mixins. 3 | 4 | #### formHelper 5 | Provides error context to descendant `input-*` components. 6 | 7 | ##### Examples 8 | ````vue 9 |
10 | 11 | 12 | Clear Errors 13 |
14 | 15 | 38 | ```` 39 | 40 | ##### API 41 | - `setFormErrors` – Set validation errors/messages for `input-*` components. 42 | ```ts 43 | setFormError (errors: Object) : void 44 | ``` 45 | 46 | - `clearFormErrors` – Clear errors/messages on `input-*` components. 47 | ```ts 48 | clearFormErrors (name: ?string): void 49 | ``` 50 | -------------------------------------------------------------------------------- /src/components/CustomBox.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 17 | 18 | 47 | -------------------------------------------------------------------------------- /test/unit/specs/components/Modal.spec.js: -------------------------------------------------------------------------------- 1 | import { render } from '../../util' 2 | import Modal from 'src/components/Modal' 3 | 4 | function getComponent (ctx, template, data) { 5 | return render(ctx, { 6 | template, 7 | components: { Modal }, 8 | data () { 9 | return data || { open: false, title: 'A Modal' } 10 | } 11 | }) 12 | } 13 | 14 | describe('Modal.js', function () { 15 | it('should render correctly', function () { 16 | const vm = getComponent( 17 | this, 18 | '
This is a modal.
' 19 | ) 20 | }) 21 | 22 | it('should render small modal correctly', function () { 23 | const vm = getComponent( 24 | this, 25 | '
This is a modal.
' 26 | ) 27 | }) 28 | 29 | it('should render custom modal', function () { 30 | const vm = getComponent( 31 | this, 32 | `
33 | 34 | 35 |
This is a modal.
36 |
37 |
` 38 | ) 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /src/components.js: -------------------------------------------------------------------------------- 1 | // Breaks specs. 2 | export { default as Alert, default as VbAlert } from './components/Alert.vue' 3 | export { default as Breadcrumb, default as VbBreadcrumb } from './components/Breadcrumb.vue' 4 | export { default as Dropdown, default as VbDropdown } from './components/Dropdown.vue' 5 | export { default as Icon, default as VbIcon } from './components/Icon.vue' 6 | export { default as Modal, default as VbModel } from './components/Modal.vue' 7 | export { default as Search, default as VbSearch } from './components/Search.vue' 8 | export { default as Typeahead, default as VbTypeahead } from './components/Typeahead.vue' 9 | 10 | // Components 11 | export { default as CheckboxWrapper } from './components/CheckboxWrapper.vue' 12 | export { default as SmartForm, default as VbForm } from './components/SmartForm.vue' 13 | 14 | // Input Components 15 | export { default as InputBox } from './components/InputBox' 16 | export { default as InputButton } from './components/InputButton.vue' 17 | export { default as InputSearch } from './components/InputSearch.vue' 18 | export { default as InputSelect } from './components/InputSelect.vue' 19 | export { default as InputSwitch } from './components/InputSwitch.vue' 20 | export { default as InputText } from './components/InputText.vue' 21 | export { default as InputTextarea } from './components/InputTextarea.vue' 22 | export { default as InputTypeahead } from './components/InputTypeahead.vue' 23 | -------------------------------------------------------------------------------- /docs/config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | repo: 'znck/bootstrap-for-vue', 3 | 4 | 'edit-link': 'https://github.com/znck/bootstrap-for-vue/edit/master/docs', 5 | 6 | // landing: '', 7 | 8 | nav: [ 9 | { title: 'Home', path: '/' }, 10 | { title: 'Components', path: '/components' }, 11 | { title: 'Directives', path: '/directives' }, 12 | { title: 'Mixins', path: '/mixins' }, 13 | { title: 'Styles', path: '/styles' }, 14 | // { title: 'Helpers', path: '/helpers' }, 15 | ], 16 | 17 | plugins: [ 18 | docsearch({ 19 | apiKey: '4f8ad9afa2cf0f5f101852754018a29a', 20 | indexName: 'bootstrap-for-vue', 21 | tags: ['en'] 22 | }), 23 | docuteIframe({ 24 | prepend: ` 25 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ` 38 | }) 39 | ], 40 | 41 | url: 'https://bootstrap-for-vue.znck.me' 42 | } 43 | 44 | docute.init(config) 45 | -------------------------------------------------------------------------------- /test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge') 2 | const baseConfig = require('../../build/webpack.test.config.js') 3 | 4 | const webpackConfig = merge(baseConfig, { 5 | // use inline sourcemap for karma-sourcemap-loader 6 | devtool: '#inline-source-map' 7 | }) 8 | 9 | webpackConfig.plugins = [] 10 | 11 | const vueRule = webpackConfig.module.rules.find(rule => rule.loader === 'vue-loader') 12 | vueRule.options = vueRule.options || {} 13 | vueRule.options.loaders = vueRule.options.loaders || {} 14 | vueRule.options.loaders.js = 'babel-loader' 15 | 16 | // no need for app entry during tests 17 | delete webpackConfig.entry 18 | 19 | module.exports = function (config) { 20 | config.set({ 21 | // to run in additional browsers: 22 | // 1. install corresponding karma launcher 23 | // http://karma-runner.github.io/0.13/config/browsers.html 24 | // 2. add it to the `browsers` array below. 25 | browsers: ['PhantomJS'], 26 | frameworks: ['mocha', 'chai-dom', 'sinon-chai'], 27 | reporters: ['spec', 'coverage'], 28 | files: ['./index.js'], 29 | preprocessors: { 30 | './index.js': ['webpack', 'sourcemap'] 31 | }, 32 | webpack: webpackConfig, 33 | webpackMiddleware: { 34 | noInfo: true 35 | }, 36 | coverageReporter: { 37 | dir: './coverage', 38 | reporters: [ 39 | { type: 'lcov', subdir: '.' }, 40 | { type: 'text-summary' } 41 | ] 42 | } 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /test/unit/helpers/style.scss: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: 14px; 3 | } 4 | 5 | 6 | @import '~bootstrap/scss/variables'; 7 | @import '~bootstrap/scss/mixins'; 8 | @import '~bootstrap/scss/modal'; 9 | @import '~bootstrap/scss/tooltip'; 10 | 11 | $fa-font-path: '~font-awesome/fonts'; 12 | 13 | #mocha li.test { 14 | overflow: visible !important; 15 | } 16 | 17 | .test-component-container { 18 | position: relative; 19 | display: block; 20 | clear: both; 21 | 22 | > button { 23 | position: absolute; 24 | cursor: pointer; 25 | top: -18px; 26 | left: -24px; 27 | } 28 | 29 | > .test-output { 30 | margin: 16px; 31 | 32 | &:hover{ 33 | margin: 15px; 34 | border: dashed 1px red; 35 | } 36 | } 37 | } 38 | 39 | .test-component-container { 40 | > .test-output { 41 | 42 | font-size: 14px; 43 | font-family: sans-serif; // 1 44 | line-height: 1.15; // 2 45 | -ms-text-size-adjust: 100%; // 3 46 | -webkit-text-size-adjust: 100%; // 3 47 | 48 | box-sizing: border-box; 49 | 50 | font-family: $font-family-base; 51 | font-size: $font-size-base; 52 | font-weight: $font-weight-base; 53 | line-height: $line-height-base; 54 | // Go easy on the eyes and use something other than `#000` for text 55 | color: $body-color; 56 | // By default, `` has no `background-color` so we set one as a best practice. 57 | background-color: $body-bg; 58 | 59 | @import '~bootstrap/scss/bootstrap'; 60 | //@import '~font-awesome/scss/font-awesome'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test/unit/specs/components/InputButton.spec.js: -------------------------------------------------------------------------------- 1 | import { render } from '../../util' 2 | import InputButton from 'src/components/InputButton.vue' 3 | 4 | function getComponent (ctx, template) { 5 | return render(ctx, { 6 | template, 7 | components: { InputButton } 8 | }) 9 | } 10 | 11 | describe('InputButton.vue', function () { 12 | it('should render correctly', function () { 13 | const vm = getComponent(this, 'Foo') 14 | 15 | vm.$el.should.exist 16 | .and.have.class('btn') 17 | .and.have.attr('role', 'button') 18 | }) 19 | 20 | it('should accept icon', function () { 21 | const vm = getComponent(this, 'Foo') 22 | 23 | vm.$el.should.contain('i.fa.fa-circle') 24 | }) 25 | 26 | it('should accept button value', function () { 27 | const vm = getComponent(this, '') 28 | 29 | vm.$el.should.text(' Foo') 30 | }) 31 | 32 | it('should accept class', function () { 33 | const vm = getComponent(this, 'Foo') 34 | 35 | vm.$el.should.have.class('btn') 36 | .and.have.class('btn-secondary') 37 | }) 38 | 39 | it('should render button groups correctly', function () { 40 | const vm = getComponent(this, '
FooBar
') 41 | 42 | vm.$('.btn').should.exist 43 | .and.have.class('btn') 44 | .and.have.attr('role', 'button') 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Bootstrap + Vue 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 31 | 32 | 33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Bootstrap + Vue 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 31 | 32 | 33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /test/unit/specs/components/BootstrapBox.spec.js: -------------------------------------------------------------------------------- 1 | import { render, makeErrors } from '../../util' 2 | import InputBox from 'src/components/BootstrapBox.vue' 3 | 4 | function getComponent (ctx, template) { 5 | return render(ctx, { 6 | template, 7 | components: { InputBox }, 8 | data () { 9 | return { 10 | test: false, 11 | errors: makeErrors({ test: 'Name is required.' }) 12 | } 13 | } 14 | }) 15 | } 16 | 17 | describe('BootstrapBox.vue', function () { 18 | it('should render correctly', function () { 19 | const vm = getComponent(this, ``) 20 | 21 | vm.$el.should.exist 22 | vm.$('input').should.exist 23 | .and.have.class('custom-control-input') 24 | }) 25 | 26 | it('should have helper text', function () { 27 | const vm = getComponent(this, ``) 28 | 29 | vm.$('.custom-control-description').should.exist 30 | .and.have.text('Yes') 31 | }) 32 | 33 | it('should provide feedback on error', function (done) { 34 | const vm = getComponent(this, ``) 35 | const group = vm.$el 36 | 37 | vm.$nextTick(function () { 38 | group.should.have.class('has-danger') 39 | 40 | done() 41 | }) 42 | }) 43 | 44 | it('should be radio type', function () { 45 | const vm = getComponent(this, ``) 46 | 47 | const input = vm.$('input') 48 | input.should.have.attr('type', 'radio') 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /test/unit/specs/components/CustomBox.spec.js: -------------------------------------------------------------------------------- 1 | import { render, makeErrors } from '../../util' 2 | import InputBox from 'src/components/CustomBox.vue' 3 | 4 | function getComponent (ctx, template) { 5 | return render(ctx, { 6 | template, 7 | components: { InputBox }, 8 | data () { 9 | return { 10 | test: false, 11 | errors: makeErrors({ test: 'Name is required.' }) 12 | } 13 | } 14 | }) 15 | } 16 | 17 | describe('CustomBox.vue', function () { 18 | it('should render correctly', function () { 19 | const vm = getComponent(this, ``) 20 | 21 | // vm.$el.should.exist 22 | // vm.$('input').should.exist 23 | // .and.have.class('custom-control-input') 24 | }) 25 | 26 | it('should have helper text', function () { 27 | const vm = getComponent(this, ``) 28 | 29 | // vm.$('.custom-control-description').should.exist 30 | // .and.have.text('Yes') 31 | }) 32 | 33 | it('should provide feedback on error', function (done) { 34 | const vm = getComponent(this, ``) 35 | const group = vm.$el 36 | 37 | vm.$nextTick(function () { 38 | group.should.have.class('has-danger') 39 | 40 | done() 41 | }) 42 | }) 43 | 44 | it('should be radio type', function () { 45 | const vm = getComponent(this, ``) 46 | 47 | // const input = vm.$('input') 48 | // input.should.have.attr('type', 'radio') 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /test/unit/specs/components/InputSwitch.spec.js: -------------------------------------------------------------------------------- 1 | import { render, makeErrors } from '../../util' 2 | import InputBox from 'src/components/InputSwitch.vue' 3 | 4 | function getComponent (ctx, template) { 5 | return render(ctx, { 6 | template, 7 | components: { InputBox }, 8 | data () { 9 | return { 10 | test: false, 11 | errors: makeErrors({ test: 'Name is required.' }) 12 | } 13 | } 14 | }) 15 | } 16 | 17 | describe('InputSwitch.vue', function () { 18 | it('should render correctly', function () { 19 | const vm = getComponent(this, ``) 20 | 21 | // vm.$el.should.exist 22 | // vm.$('input').should.exist 23 | // .and.have.class('custom-control-input') 24 | }) 25 | 26 | it('should have helper text', function () { 27 | const vm = getComponent(this, ``) 28 | 29 | // vm.$('.custom-control-description').should.exist 30 | // .and.have.text('Yes') 31 | }) 32 | 33 | it('should provide feedback on error', function (done) { 34 | const vm = getComponent(this, ``) 35 | const group = vm.$el 36 | 37 | vm.$nextTick(function () { 38 | group.should.have.class('has-danger') 39 | 40 | done() 41 | }) 42 | }) 43 | 44 | it('should be radio type', function () { 45 | const vm = getComponent(this, ``) 46 | 47 | // const input = vm.$('input') 48 | // input.should.have.attr('type', 'radio') 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /src/mixins/formHelper.js: -------------------------------------------------------------------------------- 1 | import { ErrorBag, isObject } from '../utils' 2 | 3 | export default { 4 | 5 | provide () { 6 | const form = {} 7 | 8 | Object.defineProperties(form, { 9 | status: { 10 | enumerable: true, 11 | get: () => this.$data._formStatus 12 | }, 13 | 14 | errors: { 15 | enumerable: true, 16 | get: () => this.$data._formErrors 17 | } 18 | }) 19 | 20 | return { form } 21 | }, 22 | 23 | data: () => ({ _formErrors: null, _formStatus: null }), 24 | 25 | methods: { 26 | 27 | /** 28 | * @deprecated 29 | */ 30 | setErrors (payload) { 31 | return this.setFormErrors(payload) 32 | }, 33 | 34 | /** 35 | * @deprecated 36 | */ 37 | clearError (key) { 38 | return this.clearFormErrors(key) 39 | }, 40 | 41 | setFormErrors (payload) { 42 | this.clearFormErrors() 43 | 44 | if (!isObject(payload)) return 45 | 46 | this.$data._formErrors = new ErrorBag(payload) 47 | if ('$message' in payload) { 48 | this.$data._formStatus = payload.$message 49 | } 50 | }, 51 | 52 | setFormStatus (status) { 53 | this.$data._formStatus = status 54 | }, 55 | 56 | clearFormErrors (key) { 57 | if (key !== undefined) { 58 | this.$data._formErrors.unset(key) 59 | } else { 60 | this.$data._formErrors = new ErrorBag({}) 61 | this.$data._formStatus = null 62 | } 63 | } 64 | 65 | }, 66 | 67 | created () { 68 | this.$data._formErrors = new ErrorBag({}) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /test/unit/specs/directives/tooltip.spec.js: -------------------------------------------------------------------------------- 1 | import { render } from '../../util' 2 | import tooltip from 'src/directives/tooltip' 3 | 4 | function getComponent (ctx, template) { 5 | return render(ctx, { 6 | template, 7 | data () { 8 | return { tip: 'A tooltip' } 9 | }, 10 | directives: { tooltip } 11 | }) 12 | } 13 | 14 | describe('Tooltip.js', function () { 15 | it('should render correctly', function () { 16 | const vm = getComponent(this, ` 17 |
18 |
Top
19 |
Top Left
20 |
Top Right
21 |
Bottom
22 |
Bottom Right
23 |
Bottom Left
24 |
Left
25 |
Left Top
26 |
Left Bottom
27 |
Right
28 |
Right Top
29 |
Right Bottom
30 |
31 | `) 32 | 33 | // document.querySelector() 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /src/components/InputTextarea.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 51 | 52 | 66 | -------------------------------------------------------------------------------- /src/components/Dropdown.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 65 | 66 | 87 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar: false 3 | --- 4 | 5 |
6 | 7 |
8 | 9 |
10 | [![npm](https://img.shields.io/npm/v/bootstrap-for-vue.svg)](https://www.npmjs.com/package/bootstrap-for-vue) 11 | [![vue2](https://img.shields.io/badge/vue-2.x-brightgreen.svg)](https://vuejs.org/) 12 |
13 | 14 | ## Introduction 15 | **Bootstrap for Vue** is collection of bootstrap 4 based reusable components, (no jquery, no tether, no bootstrap.js). It is used by 16 | [Vue Issue](https://new-issue.vuejs.org) and [Zero](https://zero.institute). 17 | 18 | It would always support [Vue InterOp](https://github.com/znck/vue-interop) standards. 19 | 20 | ## Installation 21 | ```bash 22 | # Using yarn 23 | yarn add bootstrap-for-vue 24 | 25 | # or using npm 26 | npm install bootstrap-for-vue 27 | 28 | ``` 29 | 30 | ## Usage 31 | 32 |

Bootstrap CSS is required and it is not included.

33 | 34 | #### In browser 35 | 36 | ```html 37 | 38 | 39 | 40 | 41 | 42 | ``` 43 | 44 | #### With webpack/rollup 45 | 46 | ```js 47 | import Vue from 'vue' 48 | import VueBootstrap from 'bootstrap-for-vue' 49 | // You can import styles in js too. 50 | import 'bootstrap-for-vue/dist/bootstrap-for-vue.scss' 51 | // or 52 | import 'bootstrap-for-vue/dist/bootstrap-for-vue.css' 53 | 54 | Vue.use(VueBootstrap) 55 | ``` 56 | 57 | **Including styles** using scss. You can override all variables too. 58 | ```scss 59 | @import '~bootstrap-for-vue/dist/bootstrap-for-vue'; 60 | // or 61 | @import './node_modules/bootstrap-for-vue/dist/bootstrap-for-vue'; 62 | ``` 63 | 64 | **Including styles** with style tag in single file component (.vue file). 65 | ```vue 66 | 67 | ``` 68 | -------------------------------------------------------------------------------- /src/mixins/inputHelper.js: -------------------------------------------------------------------------------- 1 | import { ErrorBag } from '../utils' 2 | 3 | export default { 4 | inject: ['form'], 5 | 6 | props: { 7 | value: { required: true }, 8 | title: { type: String, default: null }, 9 | subtitle: { type: String, default: null }, 10 | name: { type: String, default: null }, 11 | inputName: { type: String, default: null }, 12 | errors: { 13 | validator (errors) { 14 | return !errors || errors instanceof ErrorBag 15 | }, 16 | 17 | default: null 18 | }, 19 | inputClass: String, 20 | placeholder: String, 21 | autofill: [String, Boolean], 22 | autocomplete: [String, Boolean], 23 | autofocus: [Boolean], 24 | min: {}, 25 | max: {} 26 | }, 27 | 28 | data: () => ({ 29 | expression: null, 30 | required: null 31 | }), 32 | 33 | computed: { 34 | 35 | id () { 36 | return `text${this._uid}` 37 | }, 38 | 39 | nameKey () { 40 | const inputName = this.inputName 41 | const expression = this.expression 42 | 43 | if (inputName) return inputName 44 | 45 | return expression 46 | }, 47 | 48 | feedback () { 49 | const errors = this.errors 50 | const form = this.form 51 | const name = this.nameKey 52 | 53 | if (errors) { 54 | return errors.get(name) 55 | } 56 | 57 | if (form && form.errors) { 58 | return form.errors.get(name) 59 | } 60 | 61 | return null 62 | } 63 | }, 64 | 65 | methods: { 66 | /** 67 | * Mirror attributes from root element. 68 | * 69 | * @return {void} 70 | */ 71 | updateAttributes () { 72 | if (!this.$vnode || !this.$vnode.data || !this.$vnode.data.attrs) return 73 | 74 | this.required = this.$vnode.data.attrs.hasOwnProperty('required') 75 | } 76 | }, 77 | 78 | mounted () { 79 | const model = this.$vnode.data.model 80 | 81 | if (model) { 82 | this.expression = model.expression.split('.').pop() 83 | } 84 | 85 | this.updateAttributes() 86 | 87 | if (this.autofocus !== false) { 88 | this.$nextTick(() => { 89 | const el = this.$el.querySelector('[autofocus]') 90 | if (el) el.focus() 91 | }) 92 | } 93 | }, 94 | 95 | beforeUpdate () { 96 | this.updateAttributes() 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /test/unit/specs/components/Alert.spec.js: -------------------------------------------------------------------------------- 1 | import { render } from '../../util' 2 | import Alert from 'src/components/Alert.vue' 3 | 4 | function getComponent (ctx, template) { 5 | return render(ctx, { 6 | template, 7 | components: { Alert } 8 | }) 9 | } 10 | 11 | describe('Alert.vue', function () { 12 | it('should render correctly', function () { 13 | const vm = getComponent( 14 | this, 15 | 'This is an alert.' 16 | ) 17 | 18 | vm.$el.should.exist 19 | .and.have.text('× This is an alert.') 20 | }) 21 | 22 | it('should render error', function () { 23 | const vm = getComponent( 24 | this, 25 | 'This is an alert.' 26 | ) 27 | 28 | vm.$el.should.exist 29 | .and.have.text('× This is an alert.') 30 | }) 31 | 32 | it('should render warning', function () { 33 | const vm = getComponent( 34 | this, 35 | 'This is an alert.' 36 | ) 37 | 38 | vm.$el.should.exist 39 | .and.have.text('× This is an alert.') 40 | }) 41 | 42 | it('should render info', function () { 43 | const vm = getComponent( 44 | this, 45 | 'This is an alert.' 46 | ) 47 | 48 | vm.$el.should.exist 49 | .and.have.text('× This is an alert.') 50 | }) 51 | 52 | it('should render with message', function () { 53 | const vm = getComponent( 54 | this, 55 | '' 56 | ) 57 | 58 | vm.$el.should.exist 59 | .and.have.text('× This is an alert.') 60 | }) 61 | 62 | it('should not be dismissible', function () { 63 | const vm = getComponent( 64 | this, 65 | '' 66 | ) 67 | 68 | vm.$el.should.exist 69 | expect(vm.$('button')).to.be.null 70 | }) 71 | 72 | it('should be dismissible', function () { 73 | const vm = getComponent( 74 | this, 75 | '' 76 | ) 77 | 78 | vm.$el.should.exist 79 | vm.$('button').click() 80 | 81 | return vm.tick().then(() => { 82 | expect(vm.$el.nodeType).to.be.equal(8) 83 | }) 84 | }) 85 | 86 | it('should render custom html', function () { 87 | const vm = getComponent( 88 | this, 89 | '

Alert

This is an alert.

' 90 | ) 91 | 92 | vm.$el.should.exist 93 | }) 94 | }) 95 | -------------------------------------------------------------------------------- /src/components/InputSelect.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 87 | -------------------------------------------------------------------------------- /src/components/InputTypeahead.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 82 | -------------------------------------------------------------------------------- /test/unit/specs/components/CheckboxWrapper.spec.js: -------------------------------------------------------------------------------- 1 | import { render } from '../../util' 2 | import CheckboxWrapper from 'src/components/CheckboxWrapper.vue' 3 | import InputBox from 'src/components/InputBox' 4 | 5 | function getComponent (ctx, template) { 6 | return render(ctx, { 7 | template, 8 | data () { 9 | return { 10 | ok: false 11 | } 12 | }, 13 | components: { CheckboxWrapper, InputBox } 14 | }) 15 | } 16 | 17 | describe('CheckboxWrapper.vue', function () { 18 | it('should render correctly', function () { 19 | const vm = getComponent(this, ` 20 | 21 | 22 | 23 | 24 | `) 25 | 26 | vm.$el.should.exist 27 | .and.have.class('form-group') 28 | 29 | vm.$('legend').should.exist 30 | .and.have.text('Are you foo?') 31 | }) 32 | 33 | it('should render correctly for inline', function () { 34 | const vm = getComponent(this, ` 35 | 36 | 37 | 38 | 39 | `) 40 | 41 | vm.$el.should.exist 42 | .and.have.class('form-group') 43 | 44 | vm.$('legend').should.exist 45 | .and.have.text('Are you foo?') 46 | }) 47 | 48 | it('should render correctly', function () { 49 | const vm = getComponent(this, ` 50 | 51 | 52 | 53 | 54 | `) 55 | 56 | // vm.$el.should.exist 57 | // .and.have.class('form-group') 58 | // 59 | // vm.$('legend').should.exist 60 | // .and.have.text('Are you foo?') 61 | }) 62 | 63 | it('should render correctly for inline', function () { 64 | const vm = getComponent(this, ` 65 | 66 | 67 | 68 | 69 | `) 70 | 71 | // vm.$el.should.exist 72 | // .and.have.class('form-group') 73 | // 74 | // vm.$('legend').should.exist 75 | // .and.have.text('Are you foo?') 76 | }) 77 | }) 78 | -------------------------------------------------------------------------------- /test/unit/specs/components/InputText.spec.js: -------------------------------------------------------------------------------- 1 | import { render, makeErrors } from '../../util' 2 | import InputText from 'src/components/InputText.vue' 3 | 4 | function getComponent (ctx, template, data = {}) { 5 | if (!('value' in data)) data.value = '' 6 | 7 | return render(ctx, { 8 | template, 9 | data () { return data }, 10 | components: { InputText } 11 | }) 12 | } 13 | 14 | describe('InputText.vue', function () { 15 | it('should render correct contents', function () { 16 | const vm = getComponent(this, ``) 17 | 18 | const group = vm.$el 19 | const input = vm.$('input') 20 | 21 | group.should.exist 22 | .and.have.class('form-group') 23 | .and.have.length(1) 24 | 25 | input.should.exist 26 | .and.have.class('form-control') 27 | .and.have.attr('type', 'text') 28 | }) 29 | 30 | it('should have label', function (done) { 31 | const vm = getComponent(this, ``) 32 | 33 | const label = vm.$('label') 34 | const id = vm.$('input').getAttribute('id') 35 | 36 | label.should.exist 37 | .and.have.class('form-control-label') 38 | .and.have.attr('for', id) 39 | 40 | vm.tick().then(function () { 41 | label.should.have.text('Name Field') 42 | 43 | done() 44 | }) 45 | }) 46 | 47 | it('should have helper text', function (done) { 48 | const vm = getComponent(this, ``) 49 | 50 | const helper = vm.$('small') 51 | 52 | helper.should.exist 53 | .and.have.class('form-text') 54 | .and.have.class('text-muted') 55 | 56 | vm.tick().then(function () { 57 | helper.should.have.text('Your name here.') 58 | 59 | done() 60 | }) 61 | }) 62 | 63 | it('should provide feedback on error', function () { 64 | const vm = getComponent(this, ``, { name: 'name', errors: makeErrors({ name: 'Name is required.' }) }) 65 | 66 | const group = vm.$el 67 | const feedback = vm.$('.form-control-feedback') 68 | 69 | return vm.tick().then(function () { 70 | group.should.have.class('has-danger') 71 | 72 | feedback.should.exist 73 | .and.have.text('Name is required.') 74 | }) 75 | }) 76 | 77 | it('should have required, autofocus and placeholder attributes', function () { 78 | const vm = getComponent(this, ``) 79 | 80 | const input = vm.$('input') 81 | 82 | input.should.have.attr('autofocus', 'autofocus') 83 | input.should.have.attr('placeholder', 'Name') 84 | 85 | return vm.tick().then(function () { 86 | input.should.have.attr('required', 'required') 87 | }) 88 | }) 89 | }) 90 | -------------------------------------------------------------------------------- /src/components/InputSwitch.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 21 | 22 | -------------------------------------------------------------------------------- /src/mixins/checkbox.js: -------------------------------------------------------------------------------- 1 | import { isArray } from '../utils' 2 | import inputHelper from '../mixins/inputHelper' 3 | 4 | export default { 5 | name: 'InputBox', 6 | 7 | props: { 8 | value: { 9 | type: [Array, String, Number, Boolean], 10 | required: true 11 | }, 12 | 13 | radio: { 14 | default: undefined 15 | }, 16 | 17 | checkbox: { 18 | default: true 19 | }, 20 | 21 | inline: { 22 | type: Boolean, 23 | default: false 24 | } 25 | }, 26 | 27 | computed: { 28 | /** 29 | * Type of field (radio/checkbox) 30 | * 31 | * @type {String} 32 | */ 33 | type () { 34 | const radio = this.radio 35 | 36 | if (radio !== undefined) return 'radio' 37 | 38 | return 'checkbox' 39 | }, 40 | 41 | /** 42 | * Value of the checkbox (If checked, value of the model would be set to this). 43 | * 44 | * @type {number|string|bool} 45 | */ 46 | localValue () { 47 | const radio = this.radio 48 | const checkbox = this.checkbox 49 | 50 | if (radio !== undefined) return radio 51 | 52 | return checkbox 53 | }, 54 | 55 | /** 56 | * Current state of the box/radio. 57 | * 58 | * @type {bool} 59 | */ 60 | state () { 61 | const value = this.value 62 | const localValue = this.localValue 63 | 64 | if (isArray(value)) { 65 | return value.indexOf(localValue) > -1 66 | } 67 | 68 | return value === localValue 69 | } 70 | }, 71 | 72 | methods: { 73 | /** 74 | * Handle check/uncheck event. 75 | * 76 | * @return {void} 77 | */ 78 | onChange () { 79 | if (this.state) { 80 | this.uncheck() 81 | } else { 82 | this.check() 83 | } 84 | }, 85 | 86 | /** 87 | * Mark box/radio as checked. 88 | * 89 | * @return {void} 90 | */ 91 | check () { 92 | if (this.type === 'checkbox' && isArray(this.value)) { 93 | const value = this.value.slice(0) // Copy Array. 94 | 95 | value.push(this.localValue) 96 | this.$emit('input', value) 97 | 98 | return 99 | } 100 | 101 | this.$emit('input', this.localValue) 102 | }, 103 | 104 | /** 105 | * Mark box/radio as unchecked. 106 | */ 107 | uncheck () { 108 | if (this.type === 'radio') return 109 | 110 | if (this.value instanceof Array) { 111 | const value = this.value.slice(0) 112 | 113 | value.splice(value.indexOf(this.localValue), 1) 114 | this.$emit('input', value) 115 | 116 | return 117 | } 118 | 119 | if (this.localValue === true) { 120 | this.$emit('input', false) 121 | 122 | return 123 | } 124 | 125 | this.$emit('input', null) 126 | } 127 | }, 128 | 129 | mixins: [inputHelper] 130 | } 131 | -------------------------------------------------------------------------------- /test/unit/specs/components/InputSelect.spec.js: -------------------------------------------------------------------------------- 1 | import { render, makeErrors } from '../../util' 2 | import InputSelect from 'src/components/InputSelect.vue' 3 | 4 | function getComponent (ctx, template, value = 1) { 5 | return render(ctx, { 6 | template, 7 | data () { 8 | return { 9 | value, 10 | options: [ 11 | { id: 1, name: 'one' }, 12 | { id: 2, name: 'two' }, 13 | { id: 3, name: 'three' }, 14 | { id: 4, name: 'four' } 15 | ], 16 | errors: makeErrors({ value: 'This field is required.' }) 17 | } 18 | }, 19 | components: { InputSelect } 20 | }) 21 | } 22 | 23 | describe('InputSelect.vue', function () { 24 | it('should render correct contents', function () { 25 | const vm = getComponent(this, '') 26 | 27 | const group = vm.$el 28 | const input = vm.$('select') 29 | 30 | group.should.exist 31 | .and.have.class('form-group') 32 | 33 | input.should.exist 34 | .and.have.class('form-control') 35 | .and.have.class('custom-select') 36 | }) 37 | 38 | it('should have label', function () { 39 | const vm = getComponent(this, '') 40 | 41 | const id = vm.$('select').getAttribute('id') 42 | 43 | vm.$('label').should.exist 44 | .and.have.class('form-control-label') 45 | .and.have.text('Select a number') 46 | .and.have.attr('for', id) 47 | }) 48 | 49 | it('should have helper text', function () { 50 | const vm = getComponent(this, '') 51 | 52 | const helper = vm.$('small') 53 | 54 | helper.should.exist 55 | .and.have.class('form-text') 56 | .and.have.class('text-muted') 57 | .and.have.text('Eg. 1, 2, 3, 4') 58 | }) 59 | 60 | it('should have required and autofocus attributes & placeholder', function () { 61 | const vm = getComponent(this, '', '') 62 | 63 | const input = vm.$('select') 64 | 65 | return vm.tick().then(function () { 66 | const placeholder = vm.$('option') 67 | 68 | input.should.have.attr('autofocus', 'autofocus') 69 | input.should.have.attr('required', 'required') 70 | placeholder.should.have.text('Number') 71 | }) 72 | }) 73 | 74 | it('should provide feedback on error', function () { 75 | const vm = getComponent(this, ``) 77 | 78 | return vm.tick().then(function () { 79 | const group = vm.$el 80 | const feedback = vm.$('.form-control-feedback') 81 | 82 | group.should.have.class('has-danger') 83 | 84 | feedback.should.exist 85 | .and.have.text('This field is required.') 86 | }) 87 | }) 88 | }) 89 | -------------------------------------------------------------------------------- /src/components/Search.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 89 | 90 | 125 | -------------------------------------------------------------------------------- /src/components/Modal.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 86 | 87 | 172 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap-for-vue", 3 | "version": "0.3.14", 4 | "description": "Bootstrap UI for Vue", 5 | "author": "Rahul Kadyan ", 6 | "main": "dist/bootstrap-for-vue.js", 7 | "module": "dist/bootstrap-for-vue.esm.js", 8 | "style": "dist/bootstrap-for-vue.css", 9 | "scripts": { 10 | "build": "node build/build.js", 11 | "build:dll": "webpack --progress --config build/webpack.dll.config.js", 12 | "lint": "yarn run lint:js && yarn run lint:css", 13 | "lint:js": "eslint --fix --ext js,vue src test/**/*.spec.js test/unit/*.js build", 14 | "lint:css": "stylelint src/**/*.{vue,css}", 15 | "lint:staged": "lint-staged", 16 | "test": "yarn run lint && yarn run test:unit", 17 | "test:unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run", 18 | "dev": "webpack-dev-server --config build/webpack.test.config.js --open", 19 | "dev:coverage": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js", 20 | "prepublish": "yarn run build" 21 | }, 22 | "lint-staged": { 23 | "*.{vue,js}": [ 24 | "eslint --fix", 25 | "git add" 26 | ], 27 | "*.{vue,css}": [ 28 | "stylefmt", 29 | "stylelint" 30 | ] 31 | }, 32 | "pre-commit": "lint:staged", 33 | "dependencies": { 34 | "lodash.debounce": "^4.0.8", 35 | "lodash.throttle": "^4.1.1", 36 | "sifter": "^0.5.2", 37 | "vue-clickaway": "^2.1.0" 38 | }, 39 | "peerDependencies": { 40 | "vue": "^2.1.8" 41 | }, 42 | "devDependencies": { 43 | "add-asset-html-webpack-plugin": "^1.0.2", 44 | "babel-core": "^6.24.0", 45 | "babel-eslint": "^7.1.1", 46 | "babel-helper-vue-jsx-merge-props": "^2.0.2", 47 | "babel-loader": "^6.4.0", 48 | "babel-plugin-istanbul": "^4.0.0", 49 | "babel-plugin-syntax-jsx": "^6.18.0", 50 | "babel-plugin-transform-runtime": "^6.23.0", 51 | "babel-plugin-transform-vue-jsx": "^3.4.2", 52 | "babel-preset-es2015": "^6.24.0", 53 | "babel-preset-stage-2": "^6.18.0", 54 | "bootstrap": "next", 55 | "buble": "^0.15.2", 56 | "chai": "^3.5.0", 57 | "chai-dom": "^1.4.3", 58 | "clean-css": "^4.0.8", 59 | "cross-env": "^3.2.4", 60 | "css-loader": "^0.27.3", 61 | "eslint": "^3.17.1", 62 | "eslint-config-vue": "^2.0.1", 63 | "eslint-plugin-vue": "^2.0.1", 64 | "file-loader": "^0.10.1", 65 | "font-awesome": "^4.7.0", 66 | "function-bind": "^1.1.0", 67 | "html-webpack-plugin": "^2.26.0", 68 | "jquery": "^3.1.1", 69 | "karma": "^1.5.0", 70 | "karma-chai-dom": "^1.1.0", 71 | "karma-coverage": "^1.1.1", 72 | "karma-mocha": "^1.3.0", 73 | "karma-phantomjs-launcher": "^1.0.4", 74 | "karma-sinon-chai": "^1.2.4", 75 | "karma-sourcemap-loader": "^0.3.7", 76 | "karma-spec-reporter": "^0.0.30", 77 | "karma-webpack": "^2.0.3", 78 | "lint-staged": "^3.4.0", 79 | "mocha": "^3.2.0", 80 | "mocha-css": "^1.0.1", 81 | "node-sass": "^4.4.0", 82 | "object-assign": "^4.1.0", 83 | "postcss": "^5.2.16", 84 | "postcss-cssnext": "^2.9.0", 85 | "pre-commit": "^1.2.2", 86 | "rollup": "^0.41.5", 87 | "rollup-plugin-buble": "^0.15.0", 88 | "rollup-plugin-commonjs": "^8.0.2", 89 | "rollup-plugin-json": "^2.1.0", 90 | "rollup-plugin-jsx": "^1.0.3", 91 | "rollup-plugin-node-resolve": "^2.0.0", 92 | "rollup-plugin-postcss": "^0.2.0", 93 | "rollup-plugin-replace": "^1.1.1", 94 | "rollup-plugin-vue": "^2.2.21", 95 | "sass-loader": "^6.0.3", 96 | "sinon": "^2.0.0", 97 | "sinon-chai": "^2.8.0", 98 | "style-loader": "^0.14.0", 99 | "stylefmt": "^5.2.0", 100 | "stylelint": "^7.9.0", 101 | "stylelint-config-standard": "^16.0.0", 102 | "stylelint-processor-html": "^1.0.0", 103 | "tether": "^1.4.0", 104 | "uglify-js": "^2.8.12", 105 | "vue": "^2.2.4", 106 | "vue-loader": "^11.1.4", 107 | "vue-router": "^2.3.0", 108 | "vue-template-compiler": "^2.2.4", 109 | "webpack": "^2.2.1", 110 | "webpack-dashboard": "^0.3.0", 111 | "webpack-dev-server": "^2.4.2", 112 | "webpack-merge": "^4.0.0" 113 | }, 114 | "dllPlugin": { 115 | "name": "vuePluginTemplateDeps", 116 | "include": [ 117 | "mocha/mocha.js", 118 | "vue/dist/vue.js", 119 | "chai", 120 | "core-js/library", 121 | "url", 122 | "sockjs-client", 123 | "vue-style-loader/addStyles.js", 124 | "style-loader/addStyles.js", 125 | "css-loader!mocha-css" 126 | ] 127 | }, 128 | "repository": { 129 | "type": "git", 130 | "url": "git+https://github.com/znck/bootstrap-for-vue.git" 131 | }, 132 | "bugs": { 133 | "url": "https://github.com/znck/bootstrap-for-vue/issues" 134 | }, 135 | "homepage": "https://github.com/znck/bootstrap-for-vue#readme", 136 | "license": { 137 | "type": "MIT", 138 | "url": "http://www.opensource.org/licenses/mit-license.php" 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/components/Typeahead.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 270 | 271 | 292 | -------------------------------------------------------------------------------- /src/components/Tooltip.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 484 | -------------------------------------------------------------------------------- /docs/components.md: -------------------------------------------------------------------------------- 1 | # Components 2 | Checkout out [Vue docs](https://vuejs.org/v2/guide/components.html#What-are-Components) to know about components. 3 | 4 | ## Input Components 5 | 6 | #### InputBox 7 | It uses [bootstrap checkbox & radios](https://v4-alpha.getbootstrap.com/components/forms/#checkboxes-and-radios) to render a choice/box input. 8 | 9 | ##### Examples 10 | 11 | **Checkbox:** A standard checkbox. 12 | ````vue 13 |
14 | Value: {{ checkbox }}
15 | 16 | 17 |
18 | 19 | 26 | ```` 27 | 28 | **Checkbox:** An alternate checkbox. 29 | ````vue 30 |
31 | Value: {{ checkbox }}
32 | 33 | 34 |
35 | 36 | 43 | ```` 44 | 45 | **Radio:** A box input can be formatted as a radio element. This means it is an exclusive option. A standard radio input. 46 | 47 | ````vue 48 |
49 | Value: {{ radio }}
50 | 51 | 52 | 53 |
54 | 55 | 62 | ```` 63 | 64 | **Radio:** An alternate radio input. 65 | ````vue 66 |
67 | Value: {{ radio }}
68 | 69 | 70 | 71 |
72 | 73 | 80 | ```` 81 | 82 | **Checkbox:** Inline checkbox & radio. 83 | ````vue 84 |
85 | Checkbox: {{ checkbox }}
86 | Radio: {{ radio }}
87 | 88 | 89 | 90 | 91 | 92 | 93 |
94 | 95 | 102 | ```` 103 | 104 | **Checkbox:** Multiple options. Set initial value to an `Array`. 105 | ````vue 106 |
107 | Value: {{ checkbox }}
108 | 109 | 110 | 111 | 112 |
113 | 114 | 121 | ```` 122 | 123 | ##### Model 124 | | Name | Value | 125 | | ------ | ------- | 126 | | value | value | 127 | | event | input | 128 | 129 | ##### Props 130 | | Name | Type | Default | Required | Remarks 131 | | :-------------------- | :---------------------- | --------------------- |----------| -------------- 132 | | autocomplete | Boolean, String | - | - | Same as HTML attribute 133 | | autofocus | Boolean | `false` | - | Same as HTML attribute 134 | | checkbox | Boolean, String | `true` | - | `radio` cannot be used. 135 | | inline | Boolean | `false` | - | 136 | | radio | Boolean, String | `undefined` | - | `checkbox` cannot be used. 137 | | value | Boolean, Array | - | yes | 138 | 139 | #### InputButton 140 | It uses [bootstrap button](https://v4-alpha.getbootstrap.com/components/buttons/) to render a form 141 | button. 142 | 143 | ##### Examples 144 | ````vue 145 |
146 | Primary Button 147 | Secondary Button 148 | Info Button 149 | Success Button 150 | Warning Button 151 | Danger Button

152 | Submit Button 153 | 154 | 155 |
156 | 157 | 163 | ```` 164 | 165 | ##### Model 166 | Name | Value 167 | --------|--------- 168 | value | value 169 | event | - 170 | 171 | ##### Props 172 | Name | Type | Default | Remarks 173 | ------- | ------ | --------- | ------- 174 | icon | String | - | Use FontAwesome icon class without `fa` prefix 175 | theme | String | `primary` | Bootstrap button themes, e.g. `success` would add `btn-success` class on the button 176 | type | String | `button` | Possible values: `button` or `submit` 177 | value | String | `null` | Button text 178 | 179 | ##### Slots 180 | Name | Description 181 | ------ | ------------- 182 | default | Button text 183 | 184 | #### InputSearch 185 | A search field with typeahead suggestion that respects bootstrap input style. 186 | 187 | ##### Examples 188 | 189 | A simple search field. 190 | ````vue 191 |
192 | 193 |
194 | 195 | 201 | ```` 202 | 203 | With placeholder & typeahead suggestion. 204 | ````vue 205 |
206 | 208 |
209 | 210 | 220 | ```` 221 | 222 | With title & subtitle and required. 223 | ````vue 224 |
225 | 227 |
228 | 229 | 235 | ```` 236 | 237 | With title & subtitle using slots. 238 | ````vue 239 |
240 | 241 | Search a foo 242 | 243 | 246 | 247 |
248 | 249 | 255 | ```` 256 | 257 | ##### Model 258 | | Name | Value | 259 | | ------ | ------- | 260 | | value | value | 261 | | event | input | 262 | 263 | ##### Props 264 | | Name | Type | Default | Required | Remarks 265 | | :-------------------- | :---------------------- | --------------------- | -------- | -------------- 266 | | autocomplete | Boolean, String | - | - | Same as HTML attribute 267 | | autofocus | Boolean | `false` | - | Same as HTML attribute 268 | | errors | [ErrorBag](/helpers#ErrorBag) | Injected by [formHelper](/mixins#formHelper) | - | It is automatically set to correct value using `provide/inject`. In most cases you won't have to care about it. 269 | | name | String | `null` | - | Same as HTML attribute 270 | | input-class | String | `null` | - | Add CSS `class` to underlying `` element. 271 | | input-name | String | `null` | - | Used for accessing validation errors.
If not provided, `v-model` expression is used.
e.g. `... v-model="foo" ...` then `input-name` is `'foo'`
e.g. `... v-model="attributes.bar" ...` then `input-name` is `'bar'` 272 | | placeholder | String | - | - | Same as HTML attribute 273 | | required | Boolean | - | - | Same as HTML attribute 274 | | subtitle | String | `null` | - | Input field helper text 275 | | suggestion | String | - | - | Typeahead shadow text 276 | | title | String | `null` | - | Input field cue 277 | | value | Boolean, Array | - | yes | 278 | 279 | ##### Slots 280 | Name | Description 281 | -----|------------- 282 | default | Input field title 283 | subtitle | Input field subtitle 284 | 285 | #### InputSelect 286 | A native `` element. 414 | | input-name | String | `null` | - | Used for accessing validation errors.
If not provided, `v-model` expression is used.
e.g. `... v-model="foo" ...` then `input-name` is `'foo'`
e.g. `... v-model="attributes.bar" ...` then `input-name` is `'bar'` 415 | | options | Array | - | yes | Options for select dropdown. An array of objects where each object is an option. 416 | | placeholder | String | - | - | Same as HTML attribute 417 | | required | Boolean | - | - | Same as HTML attribute 418 | | subtitle | String | `null` | - | Input field helper text 419 | | title | String | `null` | - | Input field cue 420 | | value | Boolean, Array | - | yes | 421 | | value-key | String | `'id'` | - | Actual value of the option 422 | 423 | ##### Slots 424 | Name | Description 425 | -----|------------- 426 | default | Input field title 427 | subtitle | Input field subtitle 428 | 429 | 430 | #### InputSwitch 431 | Toggle switches are missing from bootstrap. It provides an alternate UI for [InputBox](#InputBox) with same [API](#props). 432 | 433 | 434 | ````vue 435 |
436 | Value: {{ checkbox }}
437 | 438 | 439 |
440 | 441 | 448 | ```` 449 | 450 | #### InputText 451 | It wraps default `` element with bootstrap styles & Vue magic. 452 | 453 | ##### Examples 454 | A simple input field. 455 | ````vue 456 |
457 | 458 |
459 | 460 | 467 | ```` 468 | 469 | An input field with title, subtitle & placeholder. 470 | ````vue 471 |
472 | 473 |
474 | 475 | 482 | ```` 483 | 484 | An input field with custom title & subtitle. 485 | ````vue 486 |
487 | 488 | Your Name 489 | 490 | 493 | 494 |
495 | 496 | 503 | ```` 504 | 505 | A numeric input field with min & max limits. 506 | ````vue 507 |
508 | 509 |
510 | 511 | 518 | ```` 519 | 520 | A phone number input field with autocomplete. 521 | ````vue 522 |
523 | 524 |
525 | 526 | 533 | ```` 534 | 535 | An email number input field with autocomplete. 536 | ````vue 537 |
538 | 539 |
540 | 541 | 548 | ```` 549 | 550 | ##### Model 551 | | Name | Value | 552 | | ------ | ------- | 553 | | value | value | 554 | | event | input | 555 | 556 | ##### Props 557 | | Name | Type | Default | Required | Remarks 558 | | :-------------------- | :---------------------- | --------------------- | -------- | -------------- 559 | | autocomplete | Boolean, String | - | - | Same as HTML attribute 560 | | autofocus | Boolean | `false` | - | Same as HTML attribute 561 | | errors | [ErrorBag](/helpers#ErrorBag) | Injected by [formHelper](/mixins#formHelper) | - | It is automatically set to correct value using `provide/inject`. In most cases you won't have to care about it. 562 | | name | String | `null` | - | Same as HTML attribute 563 | | input-class | String | `null` | - | Add CSS `class` to underlying `` element. 564 | | input-name | String | `null` | - | Used for accessing validation errors.
If not provided, `v-model` expression is used.
e.g. `... v-model="foo" ...` then `input-name` is `'foo'`
e.g. `... v-model="attributes.bar" ...` then `input-name` is `'bar'` 565 | | placeholder | String | - | - | Same as HTML attribute 566 | | required | Boolean | - | - | Same as HTML attribute 567 | | subtitle | String | `null` | - | Input field helper text 568 | | title | String | `null` | - | Input field cue 569 | | type | String | `'text'` | - | Same as HTML attribute on `` element 570 | | value | Boolean, Array | - | yes | 571 | 572 | ##### Slots 573 | Name | Description 574 | -----|------------- 575 | default | Input field title 576 | subtitle | Input field subtitle 577 | 578 | #### InputTextarea 579 | A textarea field, styled with bootstrap. 580 | 581 | ##### Examples 582 | A simple textarea field 583 | ````vue 584 |
585 | 586 |
587 | 588 | 595 | ```` 596 | 597 | A non-resizeable textarea field 598 | ````vue 599 |
600 | 601 |
602 | 603 | 610 | ```` 611 | 612 | With rows 613 | ````vue 614 |
615 | 616 |
617 | 618 | 625 | ```` 626 | 627 | It supports title, subtitle & placeholder too. 628 | ````vue 629 |
630 | 634 |
635 | 636 | 643 | ```` 644 | 645 | And slots too. 646 | ````vue 647 |
648 | 651 | Write something 652 | You should write at least 200 words. 653 | 654 |
655 | 656 | 663 | ```` 664 | 665 | ##### Model 666 | | Name | Value | 667 | | ------ | ------- | 668 | | value | value | 669 | | event | input | 670 | 671 | ##### Props 672 | | Name | Type | Default | Required | Remarks 673 | | :-------------------- | :---------------------- | --------------------- | -------- | -------------- 674 | | autocomplete | Boolean, String | - | - | Same as HTML attribute 675 | | autofocus | Boolean | `false` | - | Same as HTML attribute 676 | | errors | [ErrorBag](/helpers#ErrorBag) | Injected by [formHelper](/mixins#formHelper) | - | It is automatically set to correct value using `provide/inject`. In most cases you won't have to care about it. 677 | | name | String | `null` | - | Same as HTML attribute 678 | | input-class | String | `null` | - | Add CSS `class` to underlying `` element. 679 | | input-name | String | `null` | - | Used for accessing validation errors.
If not provided, `v-model` expression is used.
e.g. `... v-model="foo" ...` then `input-name` is `'foo'`
e.g. `... v-model="attributes.bar" ...` then `input-name` is `'bar'` 680 | | placeholder | String | - | - | Same as HTML attribute 681 | | required | Boolean | - | - | Same as HTML attribute 682 | | resizeable | Boolean | - | - | Vertically resizeable textarea 683 | | rows | Number | - | - | Height of textarea in number of text rows 684 | | subtitle | String | `null` | - | Input field helper text 685 | | title | String | `null` | - | Input field cue 686 | | value | Boolean, Array | - | yes | 687 | 688 | ##### Slots 689 | Name | Description 690 | -----|------------- 691 | default | Input field title 692 | subtitle | Input field subtitle 693 | 694 | #### InputTypeahead 695 | An advanced select input inspired from select2. 696 | 697 | ##### Examples 698 | A simple typeahead input field 699 | ````vue 700 |
701 | 702 |
703 | 704 | 718 | ```` 719 | 720 | A typeahead input field with custom props. 721 | ````vue 722 |
723 | 726 |
727 | 728 | 742 | ```` 743 | 744 | Multiple selections 745 | ````vue 746 |
747 | 748 |
749 | 750 | 764 | ```` 765 | 766 |

Dropdown closes when clicked away. Though it may not work here if you click outside of `