├── 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 |
2 |
3 |
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 |
2 |
3 | {{ title }}
4 |
5 |
6 |
7 |
8 |
20 |
21 |
26 |
--------------------------------------------------------------------------------
/src/components/SmartForm.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
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 |
7 | [](https://www.npmjs.com/package/bootstrap-for-vue)
8 | [](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 | ) : 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 |
2 |
3 |
4 |
5 | {{ title || ' ' }}
6 |
7 |
8 |
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 | 'Toggle This is a modal.
'
19 | )
20 | })
21 |
22 | it('should render small modal correctly', function () {
23 | const vm = getComponent(
24 | this,
25 | 'Toggle This is a modal.
'
26 | )
27 | })
28 |
29 | it('should render custom modal', function () {
30 | const vm = getComponent(
31 | this,
32 | `
33 |
Toggle
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, 'Foo Bar
')
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 |
2 |
14 |
15 |
16 |
51 |
52 |
66 |
--------------------------------------------------------------------------------
/src/components/Dropdown.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
18 |
19 |
20 |
21 |
65 |
66 |
87 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar: false
3 | ---
4 |
5 |
6 |
7 |
8 |
9 |
10 | [](https://www.npmjs.com/package/bootstrap-for-vue)
11 | [](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 |
2 |
18 |
19 |
20 |
87 |
--------------------------------------------------------------------------------
/src/components/InputTypeahead.vue:
--------------------------------------------------------------------------------
1 |
2 |
18 |
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 |
2 |
5 |
8 | {{ title }}
9 |
10 |
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 |
2 |
16 |
17 |
18 |
89 |
90 |
125 |
--------------------------------------------------------------------------------
/src/components/Modal.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
×
10 |
11 |
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 |
2 |
5 | $emit('search', val)"
8 | @keydown.down="onDown" @keydown.up="onUp"
9 | @keydown.enter.prevent="onEnter"
10 | @keydown.tab="onTab"
11 | @focus="show = true" @blur="onBlur" ref="search">
12 |
13 |
14 |
15 | No results :(
16 |
17 |
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 |
244 | This would search some foo s.
245 |
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 with bootstrap custom input style.
287 |
288 | ##### Examples
289 | A simple select field.
290 |
291 | ````vue
292 |
293 |
294 |
295 |
296 |
312 | ````
313 |
314 | A select field with title, subtitle & placeholder.
315 | ````vue
316 |
317 |
321 |
322 |
323 |
339 | ````
340 |
341 | With title & subtitle using slots.
342 | ````vue
343 |
344 |
345 | Select a foo
346 |
347 |
348 | You can choose some foo s.
349 |
350 |
351 |
352 |
353 |
369 | ````
370 |
371 | A select field with custom value, display & disabled props.
372 | ````vue
373 |
374 |
377 |
378 |
379 |
395 | ````
396 |
397 |
398 | ##### Model
399 | | Name | Value |
400 | | ------ | ------- |
401 | | value | value |
402 | | event | input |
403 |
404 | ##### Props
405 | | Name | Type | Default | Required | Remarks
406 | | :-------------------- | :---------------------- | --------------------- | -------- | --------------
407 | | autocomplete | Boolean, String | - | - | Same as HTML attribute
408 | | autofocus | Boolean | `false` | - | Same as HTML attribute
409 | | disabled-key | String | `'disabled'` | - | Disables option in select dropdown
410 | | display-key | String | `'name'` | - | Visible value of the option
411 | | 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.
412 | | name | String | `null` | - | Same as HTML attribute
413 | | input-class | String | `null` | - | Add CSS `class` to underlying ` ` 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 |
491 | Give you full name e.g. John Doe
492 |
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 `
767 |
768 | Custom filter logic
769 | ````vue
770 |
771 |
772 |
774 |
775 |
776 |
797 | ````
798 |
799 | Custom searchable fields
800 | ````vue
801 |
802 |
803 |
806 |
807 |
808 |
822 | ````
823 |
824 | Configure search: It uses [sifter](https://github.com/brianreavis/sifter.js) for search. You can provide `search` prop to provide sifter config.
825 | ````vue
826 |
827 |
828 |
830 |
831 |
832 |
847 | ````
848 |
849 |
850 | Default sifter config:
851 |
852 | `{ fields: [], sort: [{ field: , direction: 'asc' }], limit: 25 }`
853 |
854 |
855 |
856 | Custom suggestion render
857 | ````vue
858 |
859 |
860 |
861 |
862 |
863 |
891 | ````
892 |
893 | ##### Model
894 | | Name | Value |
895 | | ------ | ------- |
896 | | value | value |
897 | | event | input |
898 |
899 | ##### Props
900 | | Name | Type | Default | Required | Remarks
901 | | :-------------------- | :---------------------- | --------------------- | -------- | --------------
902 | | autocomplete | Boolean, String | - | - | Same as HTML attribute
903 | | autofocus | Boolean | `false` | - | Same as HTML attribute
904 | | component | String | - | - | Custom component for rendering suggestions. **This should be globally registered.**
905 | | 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.
906 | | filter | Function | - | - | Custom filter logic. It should return a `Boolean`.
907 | | name | String | `null` | - | Same as HTML attribute
908 | | input-class | String | `null` | - | Add CSS `class` to underlying ` ` element.
909 | | 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'`
910 | | placeholder | String | - | - | Same as HTML attribute
911 | | required | Boolean | - | - | Same as HTML attribute
912 | | search | Array, Object | - | - | List of searchable fields or sifter config
913 | | show-empty | Boolean | `true` | - | Show not found component if empty
914 | | subtitle | String | `null` | - | Input field helper text
915 | | suggestions | String | - | - | Typeahead shadow text
916 | | suggestion-key | String | `'id'` | - | Value of the suggestion
917 | | suggestion-value | String | `'id'` | - | Display text for the suggestion
918 | | title | String | `null` | - | Input field cue
919 | | value | Boolean, Array | - | yes |
920 |
921 | ##### Slots
922 | Name | Description
923 | -----|-------------
924 | default | Input field title
925 | subtitle | Input field subtitle
926 |
927 | ##### Events
928 | Name | Payload | Description
929 | ------|---------|------------
930 | search | String | Value in typeahead search field
931 | enter | Event | Native `keypress` event
932 | blur | Event | Native `blur` event
933 |
934 | ## Other Components
935 |
936 | #### Alert
937 | Provide contextual feedback messages for typical user actions with the handful of available and flexible alert messages.
938 | This component is available as `` or `` (web component spec complaint).
939 |
940 | ##### Examples
941 | A simple alert message
942 | ````vue
943 |
944 |
This is an alert message.
945 |
946 |
947 |
950 | ````
951 |
952 | Types of alerts: `info`, `success`, `danger` & `warning`
953 | ````vue
954 |
955 |
This is an info alert message.
956 |
This is a success alert message.
957 |
This is a warning alert message.
958 |
This is a danger alert message.
959 |
960 |
961 |
964 | ````
965 |
966 | Non-dismissible alert
967 | ````vue
968 |
969 |
This is a non-dismissible alert message.
970 |
971 |
972 |
975 | ````
976 |
977 | Alert with `message` prop.
978 | ````vue
979 |
982 |
983 |
990 | ````
991 |
992 | ##### Props
993 | Name | Type | Default | Required | Remarks
994 | ----------------------|-------------------------|-----------------------|----------|-----------
995 | dismissible | Boolean | `true` | - | Is alert dismissible?
996 | message | String | - | - | Contents of the alert message.
997 | type | String | `'info'` | - | One of `info`, `success`, `danger` & `warning`
998 |
999 | ##### Slots
1000 | Name | Description
1001 | -----|------------
1002 | Default | Body of alert
1003 |
1004 | ##### Events
1005 | Name | Payload | Description
1006 | -----|---------|-------------
1007 | dismiss | - | Emitted on dismiss button click
1008 |
1009 | #### Breadcrumb
1010 | Indicate the current page’s location within a navigational hierarchy.
1011 |
1012 | ##### Examples
1013 | ````vue
1014 |
1015 |
1016 |
1017 |
1018 |
1031 | ````
1032 |
1033 | ##### Props
1034 | Name | Type | Default | Required | Remarks
1035 | ----------------------|-------------------------|-----------------------|----------|-----------
1036 | links | Array | - | Yes | List of items/links.
1037 | component | String | `'router-link'` | - | Name of local or global component.
1038 |
1039 | #### Modal
1040 | Modals are streamlined, but flexible dialog prompts.
1041 |
1042 | ##### Examples
1043 | ````vue
1044 |
1045 |
Open Modal
1046 |
1047 |
1048 |
1049 | Hello World!
1050 |
1051 | This is a modal.
1052 |
1053 |
1054 |
1055 |
1056 |
1063 | ````
1064 |
1065 | ## Internals
1066 |
1067 | #### Dropdown
1068 | Used in ``.
1069 |
1070 | #### Icon
1071 | FontAwesome icon wrapper. Used in ``.
1072 |
1073 | #### Search
1074 | Used in `` & ``.
1075 |
1076 | #### Typeahead
1077 | Used in ``.
1078 |
--------------------------------------------------------------------------------