├── docs ├── static │ ├── CNAME │ ├── icon.png │ └── logo.svg ├── markdown │ ├── reference │ │ ├── changelog │ │ │ ├── README.md │ │ │ └── meta.json │ │ ├── contributing │ │ │ ├── README.md │ │ │ └── meta.json │ │ ├── settings │ │ │ └── meta.json │ │ ├── accessibility │ │ │ └── meta.json │ │ ├── color-variants │ │ │ └── meta.json │ │ ├── images │ │ │ └── meta.json │ │ ├── spacing-classes │ │ │ └── meta.json │ │ ├── starter-templates │ │ │ └── meta.json │ │ ├── router-links │ │ │ └── meta.json │ │ ├── third-party │ │ │ └── meta.json │ │ ├── size-props │ │ │ └── meta.json │ │ ├── utility-classes │ │ │ └── meta.json │ │ ├── theming │ │ │ └── meta.json │ │ └── validation │ │ │ └── meta.json │ └── intro │ │ └── meta.json ├── plugins │ ├── docs.js │ └── bootstrap-vue.js ├── pages │ └── docs │ │ ├── misc │ │ ├── index.js │ │ ├── changelog.js │ │ ├── settings.js │ │ ├── contributing.js │ │ └── third-party.js │ │ ├── layout.js │ │ ├── components │ │ └── index.js │ │ ├── directives │ │ └── index.js │ │ └── reference │ │ ├── index.js │ │ └── _slug.js ├── components │ ├── main.js │ ├── reload.js │ ├── anchored-heading.js │ └── breadcrumbs.js ├── content │ └── themes │ │ ├── dexam-startup-and-product-landing-page.yaml │ │ ├── gull-admin-dashboard.yaml │ │ ├── argon-dashboard.yaml │ │ └── argon-dashboard-pro.yaml ├── utils │ ├── code-mirror.js │ ├── marked-loader.js │ ├── hljs.js │ └── needs-transpiler.js ├── constants.js ├── assets │ └── vercel.svg └── layouts │ └── default.js ├── src ├── icons │ ├── index.scss │ ├── index.d.ts │ ├── index.js │ └── iconstack.js ├── components │ ├── media │ │ ├── index.scss │ │ ├── _media.scss │ │ ├── index.js │ │ ├── index.d.ts │ │ ├── media-body.js │ │ └── media-aside.js │ ├── modal │ │ ├── index.scss │ │ ├── _modal.scss │ │ ├── index.js │ │ └── helpers │ │ │ └── bv-modal-event.class.js │ ├── table │ │ ├── index.scss │ │ ├── helpers │ │ │ ├── stringify-record-values.js │ │ │ ├── text-selection-active.js │ │ │ ├── mixin-colgroup.js │ │ │ ├── mixin-stacked.js │ │ │ ├── constants.js │ │ │ ├── mixin-pagination.js │ │ │ └── mixin-top-row.js │ │ ├── th.js │ │ └── index.js │ ├── time │ │ ├── index.scss │ │ ├── index.js │ │ ├── index.d.ts │ │ └── _time.scss │ ├── avatar │ │ ├── index.scss │ │ ├── index.js │ │ └── index.d.ts │ ├── calendar │ │ ├── index.scss │ │ ├── index.js │ │ └── index.d.ts │ ├── card │ │ ├── index.scss │ │ ├── _card-img.scss │ │ ├── card-text.js │ │ ├── card-title.js │ │ ├── card-group.js │ │ ├── index.js │ │ ├── index.d.ts │ │ ├── card-sub-title.js │ │ ├── card-title.spec.js │ │ └── card-text.spec.js │ ├── navbar │ │ ├── index.scss │ │ ├── _navbar.scss │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── navbar-brand.js │ │ └── navbar-nav.js │ ├── popover │ │ ├── index.scss │ │ ├── index.d.ts │ │ ├── index.js │ │ └── helpers │ │ │ └── bv-popover.js │ ├── sidebar │ │ ├── index.scss │ │ ├── index.d.ts │ │ └── index.js │ ├── skeleton │ │ ├── index.scss │ │ ├── index.js │ │ ├── index.d.ts │ │ ├── skeleton-icon.js │ │ └── skeleton.js │ ├── tooltip │ │ ├── index.scss │ │ ├── index.d.ts │ │ └── index.js │ ├── form-file │ │ ├── index.scss │ │ ├── index.js │ │ └── index.d.ts │ ├── form-input │ │ ├── index.scss │ │ ├── index.js │ │ └── index.d.ts │ ├── form-tags │ │ ├── index.scss │ │ ├── index.js │ │ └── index.d.ts │ ├── nav │ │ ├── index.scss │ │ ├── _nav-item-dropdown.scss │ │ ├── nav-text.js │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── nav-text.spec.js │ │ └── nav-form.js │ ├── pagination │ │ ├── index.scss │ │ ├── index.js │ │ ├── index.d.ts │ │ └── _pagination.scss │ ├── form-rating │ │ ├── index.scss │ │ ├── index.js │ │ ├── index.d.ts │ │ └── _form-rating.scss │ ├── form-spinbutton │ │ ├── index.scss │ │ ├── index.js │ │ └── index.d.ts │ ├── input-group │ │ ├── index.scss │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── input-group-text.js │ │ ├── input-group-append.js │ │ ├── input-group-prepend.js │ │ ├── _input-group.scss │ │ └── input-group-addon.js │ ├── form-datepicker │ │ ├── index.scss │ │ ├── _form-datepicker.scss │ │ ├── index.js │ │ └── index.d.ts │ ├── form-timepicker │ │ ├── index.scss │ │ ├── _form-timepicker.scss │ │ ├── index.js │ │ └── index.d.ts │ ├── pagination-nav │ │ ├── index.scss │ │ ├── _pagination-nav.scss │ │ ├── index.js │ │ └── index.d.ts │ ├── form-btn-label-control │ │ ├── index.scss │ │ └── package.json │ ├── form-radio │ │ ├── index.scss │ │ ├── _form-radio-group.scss │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── form-radio-group.js │ │ └── form-radio.js │ ├── form-checkbox │ │ ├── index.scss │ │ ├── _form-checkbox-group.scss │ │ ├── index.d.ts │ │ ├── index.js │ │ └── form-checkbox-group.js │ ├── toast │ │ ├── index.scss │ │ ├── index.js │ │ └── _toaster-transition.scss │ ├── dropdown │ │ ├── index.scss │ │ ├── _dropdown-text.scss │ │ ├── index.d.ts │ │ ├── dropdown-divider.js │ │ ├── dropdown-text.js │ │ ├── index.js │ │ └── _dropdown-form.scss │ ├── transition │ │ └── package.json │ ├── transporter │ │ └── package.json │ ├── link │ │ ├── index.js │ │ └── index.d.ts │ ├── alert │ │ ├── index.js │ │ └── index.d.ts │ ├── badge │ │ ├── index.js │ │ ├── index.d.ts │ │ └── package.json │ ├── embed │ │ ├── index.js │ │ ├── index.d.ts │ │ └── package.json │ ├── aspect │ │ ├── index.js │ │ ├── index.d.ts │ │ └── package.json │ ├── overlay │ │ ├── index.js │ │ └── index.d.ts │ ├── spinner │ │ ├── index.js │ │ ├── index.d.ts │ │ └── package.json │ ├── jumbotron │ │ ├── index.js │ │ └── index.d.ts │ ├── form-group │ │ ├── index.d.ts │ │ └── index.js │ ├── button-group │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── package.json │ │ └── button-group.js │ ├── tabs │ │ ├── index.js │ │ └── index.d.ts │ ├── button-toolbar │ │ ├── index.d.ts │ │ ├── index.js │ │ └── package.json │ ├── collapse │ │ ├── index.d.ts │ │ └── index.js │ ├── image │ │ ├── index.js │ │ └── index.d.ts │ ├── form-textarea │ │ ├── index.d.ts │ │ └── index.js │ ├── progress │ │ ├── index.js │ │ └── index.d.ts │ ├── carousel │ │ ├── index.js │ │ └── index.d.ts │ ├── button │ │ ├── index.d.ts │ │ └── index.js │ ├── list-group │ │ ├── index.js │ │ ├── index.d.ts │ │ └── list-group.js │ ├── breadcrumb │ │ ├── index.js │ │ ├── index.d.ts │ │ └── breadcrumb-item.js │ ├── layout │ │ ├── index.js │ │ ├── index.d.ts │ │ ├── form-row.js │ │ ├── container.js │ │ └── form-row.spec.js │ ├── form-select │ │ ├── index.d.ts │ │ ├── index.js │ │ └── form-select-option.js │ ├── form │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── form-text.js │ │ ├── form.js │ │ └── form-datalist.js │ └── index.scss ├── utils │ ├── noop.js │ ├── identity.js │ ├── memoize.js │ ├── loose-index-of.js │ ├── math.js │ ├── get-scope-id.js │ ├── html.js │ ├── env.js │ ├── clone-deep.js │ ├── model.js │ ├── array.js │ ├── number.js │ ├── locale.js │ ├── cache.js │ ├── stringify-object-values.js │ └── stable-sort.js ├── constants │ ├── classes.js │ ├── config.js │ ├── date.js │ ├── popper.js │ ├── key-codes.js │ └── safe-types.js ├── mixins │ ├── attrs.js │ ├── listeners.js │ ├── model.js │ ├── scoped-style.js │ ├── form-custom.js │ ├── form-size.js │ ├── card.js │ ├── has-listener.js │ ├── normalize-slot.js │ └── focus-in.js ├── bv-config.d.ts ├── bv-config.js ├── vue.js ├── icons.scss ├── directives │ ├── hover │ │ ├── index.js │ │ ├── index.d.ts │ │ ├── package.json │ │ └── hover.spec.js │ ├── modal │ │ ├── index.js │ │ └── index.d.ts │ ├── toggle │ │ ├── index.js │ │ ├── index.d.ts │ │ └── package.json │ ├── popover │ │ ├── index.js │ │ └── index.d.ts │ ├── tooltip │ │ ├── index.js │ │ └── index.d.ts │ ├── visible │ │ ├── index.js │ │ ├── index.d.ts │ │ └── package.json │ ├── scrollspy │ │ ├── index.js │ │ └── index.d.ts │ ├── index.d.ts │ └── index.js ├── browser.js ├── browser-icons.js ├── vue-injections.d.ts ├── index.scss ├── _utilities.scss ├── icons-only.js └── _custom-controls.scss ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── FEATURE_REQUEST_TEMPLATE.md │ ├── DOCS_ISSUE_TEMPLATE.md │ └── BUG_REPORT_TEMPLATE.md └── dependabot.yml ├── .codesandbox └── ci.json ├── static ├── logo.png ├── avatar.png ├── banner.png ├── logo-padded.png ├── logo.svg └── logo-padded.svg ├── tests ├── README.md ├── components │ ├── index.js │ └── TransitionGroupStub.js ├── setup.js └── utils.js ├── .eslintignore ├── now.json ├── .editorconfig ├── .prettierignore ├── scripts ├── postcss.config.js ├── index.scss ├── icons.scss └── banner.js ├── .gitignore ├── .browserslistrc ├── jest.config.js ├── .prettierrc ├── babel.config.js ├── SECURITY.md ├── .versionrc ├── LICENSE ├── .eslintrc.js └── nuxt └── plugin.template.js /docs/static/CNAME: -------------------------------------------------------------------------------- 1 | bootstrap-vue.js.org -------------------------------------------------------------------------------- /src/icons/index.scss: -------------------------------------------------------------------------------- 1 | @import "icons"; 2 | -------------------------------------------------------------------------------- /src/components/media/index.scss: -------------------------------------------------------------------------------- 1 | @import "media"; 2 | -------------------------------------------------------------------------------- /src/components/modal/index.scss: -------------------------------------------------------------------------------- 1 | @import "modal"; 2 | -------------------------------------------------------------------------------- /src/components/table/index.scss: -------------------------------------------------------------------------------- 1 | @import "table"; 2 | -------------------------------------------------------------------------------- /src/components/time/index.scss: -------------------------------------------------------------------------------- 1 | @import "time"; 2 | -------------------------------------------------------------------------------- /src/utils/noop.js: -------------------------------------------------------------------------------- 1 | export const noop = () => {} 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: bootstrap-vue 2 | -------------------------------------------------------------------------------- /src/components/avatar/index.scss: -------------------------------------------------------------------------------- 1 | @import "avatar"; 2 | -------------------------------------------------------------------------------- /src/components/calendar/index.scss: -------------------------------------------------------------------------------- 1 | @import "calendar"; 2 | -------------------------------------------------------------------------------- /src/components/card/index.scss: -------------------------------------------------------------------------------- 1 | @import "card-img"; 2 | -------------------------------------------------------------------------------- /src/components/navbar/index.scss: -------------------------------------------------------------------------------- 1 | @import "navbar"; 2 | -------------------------------------------------------------------------------- /src/components/popover/index.scss: -------------------------------------------------------------------------------- 1 | @import "popover"; 2 | -------------------------------------------------------------------------------- /src/components/sidebar/index.scss: -------------------------------------------------------------------------------- 1 | @import "sidebar"; 2 | -------------------------------------------------------------------------------- /src/components/skeleton/index.scss: -------------------------------------------------------------------------------- 1 | @import "skeleton"; 2 | -------------------------------------------------------------------------------- /src/components/tooltip/index.scss: -------------------------------------------------------------------------------- 1 | @import "tooltip"; 2 | -------------------------------------------------------------------------------- /src/utils/identity.js: -------------------------------------------------------------------------------- 1 | export const identity = x => x 2 | -------------------------------------------------------------------------------- /src/components/form-file/index.scss: -------------------------------------------------------------------------------- 1 | @import "form-file"; 2 | -------------------------------------------------------------------------------- /src/components/form-input/index.scss: -------------------------------------------------------------------------------- 1 | @import "form-input"; 2 | -------------------------------------------------------------------------------- /src/components/form-tags/index.scss: -------------------------------------------------------------------------------- 1 | @import "form-tags"; 2 | -------------------------------------------------------------------------------- /src/components/nav/index.scss: -------------------------------------------------------------------------------- 1 | @import "nav-item-dropdown"; 2 | -------------------------------------------------------------------------------- /src/components/pagination/index.scss: -------------------------------------------------------------------------------- 1 | @import "pagination"; 2 | -------------------------------------------------------------------------------- /docs/markdown/reference/changelog/README.md: -------------------------------------------------------------------------------- 1 | ../../../../CHANGELOG.md -------------------------------------------------------------------------------- /src/components/form-rating/index.scss: -------------------------------------------------------------------------------- 1 | @import "form-rating"; 2 | -------------------------------------------------------------------------------- /src/components/form-spinbutton/index.scss: -------------------------------------------------------------------------------- 1 | @import "spinbutton"; 2 | -------------------------------------------------------------------------------- /src/components/input-group/index.scss: -------------------------------------------------------------------------------- 1 | @import "input-group"; 2 | -------------------------------------------------------------------------------- /src/components/navbar/_navbar.scss: -------------------------------------------------------------------------------- 1 | @import "../dropdown/index"; 2 | -------------------------------------------------------------------------------- /.codesandbox/ci.json: -------------------------------------------------------------------------------- 1 | { 2 | "sandboxes": ["qeu9j", "xblbj"] 3 | } 4 | -------------------------------------------------------------------------------- /docs/markdown/reference/contributing/README.md: -------------------------------------------------------------------------------- 1 | ../../../../CONTRIBUTING.md -------------------------------------------------------------------------------- /src/components/form-datepicker/index.scss: -------------------------------------------------------------------------------- 1 | @import "form-datepicker"; 2 | -------------------------------------------------------------------------------- /src/components/form-timepicker/index.scss: -------------------------------------------------------------------------------- 1 | @import "form-timepicker"; 2 | -------------------------------------------------------------------------------- /src/components/pagination-nav/index.scss: -------------------------------------------------------------------------------- 1 | @import "pagination-nav"; 2 | -------------------------------------------------------------------------------- /src/components/nav/_nav-item-dropdown.scss: -------------------------------------------------------------------------------- 1 | @import "../dropdown/index"; 2 | -------------------------------------------------------------------------------- /src/components/form-btn-label-control/index.scss: -------------------------------------------------------------------------------- 1 | @import "form-btn-label-control"; 2 | -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tweichart/bootstrap-vue/dev/static/logo.png -------------------------------------------------------------------------------- /static/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tweichart/bootstrap-vue/dev/static/avatar.png -------------------------------------------------------------------------------- /static/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tweichart/bootstrap-vue/dev/static/banner.png -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # BootstrapVue tests 2 | 3 | Tests are moved into `src/**/*.spec` files. 4 | -------------------------------------------------------------------------------- /docs/static/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tweichart/bootstrap-vue/dev/docs/static/icon.png -------------------------------------------------------------------------------- /src/components/form-radio/index.scss: -------------------------------------------------------------------------------- 1 | @import "form-radio"; 2 | @import "form-radio-group"; 3 | -------------------------------------------------------------------------------- /src/components/form-checkbox/index.scss: -------------------------------------------------------------------------------- 1 | @import "form-checkbox"; 2 | @import "form-checkbox-group"; 3 | -------------------------------------------------------------------------------- /src/components/form-datepicker/_form-datepicker.scss: -------------------------------------------------------------------------------- 1 | @import "../form-btn-label-control/index"; 2 | -------------------------------------------------------------------------------- /src/components/form-timepicker/_form-timepicker.scss: -------------------------------------------------------------------------------- 1 | @import "../form-btn-label-control/index"; 2 | -------------------------------------------------------------------------------- /static/logo-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tweichart/bootstrap-vue/dev/static/logo-padded.png -------------------------------------------------------------------------------- /src/constants/classes.js: -------------------------------------------------------------------------------- 1 | export const CLASS_NAME_SHOW = 'show' 2 | export const CLASS_NAME_FADE = 'fade' 3 | -------------------------------------------------------------------------------- /src/components/toast/index.scss: -------------------------------------------------------------------------------- 1 | @import "toast"; 2 | @import "toaster"; 3 | @import "toaster-transition"; 4 | -------------------------------------------------------------------------------- /src/components/dropdown/index.scss: -------------------------------------------------------------------------------- 1 | @import "dropdown"; 2 | @import "dropdown-form"; 3 | @import "dropdown-text"; 4 | -------------------------------------------------------------------------------- /src/components/form-checkbox/_form-checkbox-group.scss: -------------------------------------------------------------------------------- 1 | @import "../../utilities"; 2 | @import "../input-group/index"; 3 | -------------------------------------------------------------------------------- /src/components/pagination-nav/_pagination-nav.scss: -------------------------------------------------------------------------------- 1 | // shares SCSS 2 | @import "../pagination/index"; 3 | -------------------------------------------------------------------------------- /src/components/transition/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bootstrap-vue/transition", 3 | "version": "1.0.0", 4 | "private": true 5 | } 6 | -------------------------------------------------------------------------------- /src/components/transporter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bootstrap-vue/transporter", 3 | "version": "1.0.0", 4 | "private": true 5 | } 6 | -------------------------------------------------------------------------------- /src/mixins/attrs.js: -------------------------------------------------------------------------------- 1 | import { makePropCacheMixin } from '../utils/cache' 2 | 3 | export const attrsMixin = makePropCacheMixin('$attrs', 'bvAttrs') 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .now/ 2 | .nuxt/ 3 | .vercel/ 4 | coverage/ 5 | dist/ 6 | docs-dist/ 7 | esm/ 8 | node_modules/ 9 | nuxt/plugin.*.js 10 | sw.js 11 | -------------------------------------------------------------------------------- /now.json: -------------------------------------------------------------------------------- 1 | { 2 | "headers": [ 3 | { 4 | "source": "/(.*)", 5 | "headers": [{ "key": "X-Robots-Tag", "value": "" }] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/bv-config.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { PluginFunction, PluginObject } from 'vue' 2 | import { BvPlugin } from './' 3 | 4 | export declare const BVConfigPlugin: BvPlugin 5 | -------------------------------------------------------------------------------- /src/components/form-btn-label-control/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bootstrap-vue/form-btn-label-control", 3 | "version": "1.0.0", 4 | "private": true 5 | } 6 | -------------------------------------------------------------------------------- /src/mixins/listeners.js: -------------------------------------------------------------------------------- 1 | import { makePropCacheMixin } from '../utils/cache' 2 | 3 | export const listenersMixin = makePropCacheMixin('$listeners', 'bvListeners') 4 | -------------------------------------------------------------------------------- /docs/markdown/reference/changelog/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Changelog", 3 | "description": "All notable changes to BootstrapVue are documented in this page." 4 | } 5 | -------------------------------------------------------------------------------- /docs/markdown/reference/contributing/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Contributing", 3 | "description": "Information on contributing to the BootstrapVue project." 4 | } 5 | -------------------------------------------------------------------------------- /src/components/form-radio/_form-radio-group.scss: -------------------------------------------------------------------------------- 1 | @import "../../utilities"; 2 | // Needed when radio-groups are inside an input group 3 | @import "../input-group/index"; 4 | -------------------------------------------------------------------------------- /src/constants/config.js: -------------------------------------------------------------------------------- 1 | export const NAME = 'BvConfig' 2 | export const PROP_NAME = '$bvConfig' 3 | 4 | export const DEFAULT_BREAKPOINT = ['xs', 'sm', 'md', 'lg', 'xl'] 5 | -------------------------------------------------------------------------------- /docs/plugins/docs.js: -------------------------------------------------------------------------------- 1 | // Disable vue global error handler 2 | import Vue from 'vue' 3 | 4 | export default function() { 5 | Vue.config.errorHandler = console.error 6 | } 7 | -------------------------------------------------------------------------------- /docs/plugins/bootstrap-vue.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { BootstrapVue, BootstrapVueIcons } from '../../src' 3 | 4 | Vue.use(BootstrapVue) 5 | Vue.use(BootstrapVueIcons) 6 | -------------------------------------------------------------------------------- /docs/markdown/reference/settings/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Settings", 3 | "description": "BootstrapVue provides a few options for customizing component default values, and more." 4 | } 5 | -------------------------------------------------------------------------------- /tests/components/index.js: -------------------------------------------------------------------------------- 1 | import TransitionGroupStub from './TransitionGroupStub' 2 | import TransitionStub from './TransitionStub' 3 | 4 | export { TransitionGroupStub, TransitionStub } 5 | -------------------------------------------------------------------------------- /docs/pages/docs/misc/index.js: -------------------------------------------------------------------------------- 1 | // Add redirect from old URL to new one 2 | // @vue/component 3 | export default { 4 | fetch({ redirect }) { 5 | redirect('/docs/reference') 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/components/media/_media.scss: -------------------------------------------------------------------------------- 1 | .media-aside { 2 | display: flex; 3 | margin-right: 1rem; 4 | } 5 | 6 | .media-aside-right { 7 | margin-right: 0; 8 | margin-left: 1rem; 9 | } 10 | -------------------------------------------------------------------------------- /docs/pages/docs/layout.js: -------------------------------------------------------------------------------- 1 | // Add redirect from old URL to new one 2 | // @vue/component 3 | export default { 4 | fetch({ redirect }) { 5 | redirect(301, '/docs/components/layout') 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/bv-config.js: -------------------------------------------------------------------------------- 1 | // 2 | // Utility Plugin for setting the configuration 3 | // 4 | import { pluginFactory } from './utils/plugins' 5 | 6 | export const BVConfigPlugin = /*#__PURE__*/ pluginFactory() 7 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/markdown/reference/accessibility/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Accessibility", 3 | "description": "A brief overview of BootstrapVue's features and limitations for the creation of accessible content." 4 | } 5 | -------------------------------------------------------------------------------- /docs/markdown/reference/color-variants/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Color Variants", 3 | "description": "Color variants available when using the default Bootstrap v4 CSS and their mappings to CSS classes." 4 | } 5 | -------------------------------------------------------------------------------- /src/components/card/_card-img.scss: -------------------------------------------------------------------------------- 1 | .card-img-left { 2 | @include border-left-radius($card-inner-border-radius); 3 | } 4 | 5 | .card-img-right { 6 | @include border-right-radius($card-inner-border-radius); 7 | } 8 | -------------------------------------------------------------------------------- /src/vue.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { mergeData } from 'vue-functional-data-merge' 3 | 4 | // --- Constants --- 5 | 6 | const COMPONENT_UID_KEY = '_uid' 7 | 8 | export { COMPONENT_UID_KEY, Vue, mergeData } 9 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .now/ 2 | .nuxt/ 3 | .vercel/ 4 | coverage/ 5 | dist/ 6 | docs-dist/ 7 | esm/ 8 | node_modules/ 9 | nuxt/plugin.template.js 10 | nuxt/plugin.prod.js 11 | nuxt/plugin.dev.js 12 | src/icons/icons.js 13 | -------------------------------------------------------------------------------- /src/components/modal/_modal.scss: -------------------------------------------------------------------------------- 1 | // Needed to allow Vue transition system to work with Bootstrap v4 .modal-backdrop 2 | // as modal opacity is 1 by default 3 | .modal-backdrop { 4 | opacity: $modal-backdrop-opacity; 5 | } 6 | -------------------------------------------------------------------------------- /docs/markdown/reference/images/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Component img src resolving", 3 | "description": "Using project relative image URLs for BootstrapVue custom components with vue-loader", 4 | "slug": "images" 5 | } 6 | -------------------------------------------------------------------------------- /src/icons.scss: -------------------------------------------------------------------------------- 1 | // --- BootstrapVue Icons Custom SCSS --- 2 | 3 | // Include variables and utilities first 4 | @import "variables"; 5 | @import "utilities"; 6 | 7 | // Include custom SCSS for icons 8 | @import "icons/index"; 9 | -------------------------------------------------------------------------------- /src/mixins/model.js: -------------------------------------------------------------------------------- 1 | import { makeModelMixin } from '../utils/model' 2 | 3 | const { mixin, props, prop, event } = makeModelMixin('value') 4 | 5 | export { mixin as modelMixin, props, prop as MODEL_PROP_NAME, event as MODEL_EVENT_NAME } 6 | -------------------------------------------------------------------------------- /docs/markdown/reference/spacing-classes/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Spacing classes", 3 | "description": "Bootstrap v4 CSS includes a wide range of shorthand responsive margin and padding utility classes to modify an element's appearance." 4 | } 5 | -------------------------------------------------------------------------------- /docs/markdown/reference/starter-templates/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Starter Templates", 3 | "description": "There are several ways you can create your app, from basic client side HTML all the way up to using a build system and compilers." 4 | } 5 | -------------------------------------------------------------------------------- /src/components/link/index.js: -------------------------------------------------------------------------------- 1 | import { BLink } from './link' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const LinkPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { BLink } 6 | }) 7 | 8 | export { LinkPlugin, BLink } 9 | -------------------------------------------------------------------------------- /src/components/time/index.js: -------------------------------------------------------------------------------- 1 | import { BTime } from './time' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const TimePlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { BTime } 6 | }) 7 | 8 | export { TimePlugin, BTime } 9 | -------------------------------------------------------------------------------- /src/components/alert/index.js: -------------------------------------------------------------------------------- 1 | import { BAlert } from './alert' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const AlertPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { BAlert } 6 | }) 7 | 8 | export { AlertPlugin, BAlert } 9 | -------------------------------------------------------------------------------- /src/components/badge/index.js: -------------------------------------------------------------------------------- 1 | import { BBadge } from './badge' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const BadgePlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { BBadge } 6 | }) 7 | 8 | export { BadgePlugin, BBadge } 9 | -------------------------------------------------------------------------------- /src/components/embed/index.js: -------------------------------------------------------------------------------- 1 | import { BEmbed } from './embed' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const EmbedPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { BEmbed } 6 | }) 7 | 8 | export { EmbedPlugin, BEmbed } 9 | -------------------------------------------------------------------------------- /docs/markdown/reference/router-links/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Router support", 3 | "description": "Several BootstrapVue components support rendering components compatible with Vue Router and Nuxt.js.", 4 | "slug": "router-links" 5 | } 6 | -------------------------------------------------------------------------------- /scripts/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = () => ({ 2 | map: { 3 | inline: false, 4 | annotation: true, 5 | sourcesContent: true 6 | }, 7 | plugins: { 8 | autoprefixer: { 9 | cascade: false 10 | } 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .now/ 3 | .nuxt/ 4 | .vercel/ 5 | .vscode/ 6 | coverage/ 7 | dist/ 8 | docs-dist/ 9 | esm/ 10 | node_modules/ 11 | *.iml 12 | *.log 13 | *.swp 14 | .DS_Store 15 | RELEASE-NOTES.md 16 | package-lock.json 17 | sw.js 18 | workbox*.js* 19 | -------------------------------------------------------------------------------- /src/components/aspect/index.js: -------------------------------------------------------------------------------- 1 | import { BAspect } from './aspect' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const AspectPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { BAspect } 6 | }) 7 | 8 | export { AspectPlugin, BAspect } 9 | -------------------------------------------------------------------------------- /src/directives/hover/index.js: -------------------------------------------------------------------------------- 1 | import { VBHover } from './hover' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const VBHoverPlugin = /*#__PURE__*/ pluginFactory({ 5 | directives: { VBHover } 6 | }) 7 | 8 | export { VBHoverPlugin, VBHover } 9 | -------------------------------------------------------------------------------- /src/directives/modal/index.js: -------------------------------------------------------------------------------- 1 | import { VBModal } from './modal' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const VBModalPlugin = /*#__PURE__*/ pluginFactory({ 5 | directives: { VBModal } 6 | }) 7 | 8 | export { VBModalPlugin, VBModal } 9 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | # https://github.com/browserslist/browserslist#readme 2 | 3 | >= 1% 4 | last 1 major version 5 | not dead 6 | Chrome >= 45 7 | Firefox >= 38 8 | Edge >= 12 9 | Explorer >= 11 10 | iOS >= 9 11 | Safari >= 9 12 | Android >= 4.4 13 | Opera >= 30 14 | -------------------------------------------------------------------------------- /docs/markdown/intro/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Intro", 3 | "title": "Getting Started", 4 | "description": "Get started with BootstrapVue, based on the world's most popular framework - Bootstrap v4, for building responsive, mobile-first sites using Vue.js" 5 | } 6 | -------------------------------------------------------------------------------- /src/components/overlay/index.js: -------------------------------------------------------------------------------- 1 | import { BOverlay } from './overlay' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const OverlayPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { BOverlay } 6 | }) 7 | 8 | export { OverlayPlugin, BOverlay } 9 | -------------------------------------------------------------------------------- /src/components/spinner/index.js: -------------------------------------------------------------------------------- 1 | import { BSpinner } from './spinner' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const SpinnerPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { BSpinner } 6 | }) 7 | 8 | export { SpinnerPlugin, BSpinner } 9 | -------------------------------------------------------------------------------- /src/directives/toggle/index.js: -------------------------------------------------------------------------------- 1 | import { VBToggle } from './toggle' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const VBTogglePlugin = /*#__PURE__*/ pluginFactory({ 5 | directives: { VBToggle } 6 | }) 7 | 8 | export { VBTogglePlugin, VBToggle } 9 | -------------------------------------------------------------------------------- /tests/setup.js: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom' 2 | import { config as vtuConfig } from '@vue/test-utils' 3 | 4 | // Don't stub `` and `` components 5 | vtuConfig.stubs.transition = false 6 | vtuConfig.stubs['transition-group'] = false 7 | -------------------------------------------------------------------------------- /src/components/calendar/index.js: -------------------------------------------------------------------------------- 1 | import { BCalendar } from './calendar' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const CalendarPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { BCalendar } 6 | }) 7 | 8 | export { CalendarPlugin, BCalendar } 9 | -------------------------------------------------------------------------------- /src/directives/popover/index.js: -------------------------------------------------------------------------------- 1 | import { VBPopover } from './popover' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const VBPopoverPlugin = /*#__PURE__*/ pluginFactory({ 5 | directives: { VBPopover } 6 | }) 7 | 8 | export { VBPopoverPlugin, VBPopover } 9 | -------------------------------------------------------------------------------- /src/directives/tooltip/index.js: -------------------------------------------------------------------------------- 1 | import { VBTooltip } from './tooltip' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const VBTooltipPlugin = /*#__PURE__*/ pluginFactory({ 5 | directives: { VBTooltip } 6 | }) 7 | 8 | export { VBTooltipPlugin, VBTooltip } 9 | -------------------------------------------------------------------------------- /src/directives/visible/index.js: -------------------------------------------------------------------------------- 1 | import { VBVisible } from './visible' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const VBVisiblePlugin = /*#__PURE__*/ pluginFactory({ 5 | directives: { VBVisible } 6 | }) 7 | 8 | export { VBVisiblePlugin, VBVisible } 9 | -------------------------------------------------------------------------------- /docs/markdown/reference/third-party/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Third party libraries", 3 | "slug": "third-party", 4 | "description": "There are several 3rd party libraries that you can use to add additional functionality and features to your BootstrapVue project." 5 | } 6 | -------------------------------------------------------------------------------- /src/browser.js: -------------------------------------------------------------------------------- 1 | // Main entry point for the browser build 2 | import { vueUse } from './utils/plugins' 3 | 4 | import { BootstrapVue } from './index' 5 | 6 | // Auto installation only occurs if window.Vue exists 7 | vueUse(BootstrapVue) 8 | 9 | export default BootstrapVue 10 | -------------------------------------------------------------------------------- /src/components/jumbotron/index.js: -------------------------------------------------------------------------------- 1 | import { BJumbotron } from './jumbotron' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const JumbotronPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { BJumbotron } 6 | }) 7 | 8 | export { JumbotronPlugin, BJumbotron } 9 | -------------------------------------------------------------------------------- /tests/components/TransitionGroupStub.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore file */ 2 | export default { 3 | render(h) { 4 | const tag = this.tag || this.$vnode.data.tag || 'span' 5 | const children = this.$slots.default || [] 6 | 7 | return h(tag, children) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /docs/pages/docs/components/index.js: -------------------------------------------------------------------------------- 1 | import SectionToc from '~/components/section-toc' 2 | import docsMixin from '~/plugins/docs-mixin' 3 | 4 | // @vue/component 5 | export default { 6 | name: 'BVDocsComponentsIndex', 7 | extends: SectionToc, 8 | mixins: [docsMixin] 9 | } 10 | -------------------------------------------------------------------------------- /docs/pages/docs/directives/index.js: -------------------------------------------------------------------------------- 1 | import SectionToc from '~/components/section-toc' 2 | import docsMixin from '~/plugins/docs-mixin' 3 | 4 | // @vue/component 5 | export default { 6 | name: 'BVDocsDirectivesIndex', 7 | extends: SectionToc, 8 | mixins: [docsMixin] 9 | } 10 | -------------------------------------------------------------------------------- /docs/pages/docs/reference/index.js: -------------------------------------------------------------------------------- 1 | import SectionToc from '~/components/section-toc' 2 | import docsMixin from '~/plugins/docs-mixin' 3 | 4 | // @vue/component 5 | export default { 6 | name: 'BVDocsReferenceIndex', 7 | extends: SectionToc, 8 | mixins: [docsMixin] 9 | } 10 | -------------------------------------------------------------------------------- /src/components/pagination/index.js: -------------------------------------------------------------------------------- 1 | import { BPagination } from './pagination' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const PaginationPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { BPagination } 6 | }) 7 | 8 | export { PaginationPlugin, BPagination } 9 | -------------------------------------------------------------------------------- /src/directives/scrollspy/index.js: -------------------------------------------------------------------------------- 1 | import { VBScrollspy } from './scrollspy' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const VBScrollspyPlugin = /*#__PURE__*/ pluginFactory({ 5 | directives: { VBScrollspy } 6 | }) 7 | 8 | export { VBScrollspyPlugin, VBScrollspy } 9 | -------------------------------------------------------------------------------- /docs/markdown/reference/size-props/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Size props and classes", 3 | "description": "Bootstrap v4 CSS provides several classes that control the sizing of elements, of which some of these have been translated into props on components.", 4 | "slug": "size-props" 5 | } 6 | -------------------------------------------------------------------------------- /src/constants/date.js: -------------------------------------------------------------------------------- 1 | export const CALENDAR_GREGORY = 'gregory' 2 | export const CALENDAR_LONG = 'long' 3 | export const CALENDAR_NARROW = 'narrow' 4 | export const CALENDAR_SHORT = 'short' 5 | 6 | export const DATE_FORMAT_2_DIGIT = '2-digit' 7 | export const DATE_FORMAT_NUMERIC = 'numeric' 8 | -------------------------------------------------------------------------------- /src/components/badge/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Badge 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const BadgePlugin: BvPlugin 9 | 10 | // Component: b-badge 11 | export declare class BBadge extends BvComponent {} 12 | -------------------------------------------------------------------------------- /src/components/embed/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Embed 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const EmbedPlugin: BvPlugin 9 | 10 | // Component: b-embed 11 | export declare class BEmbed extends BvComponent {} 12 | -------------------------------------------------------------------------------- /src/components/aspect/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Aspect 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const AspectPlugin: BvPlugin 9 | 10 | // Component: b-aspect 11 | export declare class BAspect extends BvComponent {} 12 | -------------------------------------------------------------------------------- /src/components/spinner/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Spinner 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const SpinnerPlugin: BvPlugin 9 | 10 | // Component: b-alert 11 | export declare class BSpinner extends BvComponent {} 12 | -------------------------------------------------------------------------------- /src/utils/memoize.js: -------------------------------------------------------------------------------- 1 | import { create } from './object' 2 | 3 | export const memoize = fn => { 4 | const cache = create(null) 5 | 6 | return (...args) => { 7 | const argsKey = JSON.stringify(args) 8 | return (cache[argsKey] = cache[argsKey] || fn.apply(null, args)) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/components/overlay/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Overlay 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const OverlayPlugin: BvPlugin 9 | 10 | // Component: b-overlay 11 | export declare class BOverlay extends BvComponent {} 12 | -------------------------------------------------------------------------------- /src/components/popover/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Popover 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const PopoverPlugin: BvPlugin 9 | 10 | // Component: b-popover 11 | export declare class BPopover extends BvComponent {} 12 | -------------------------------------------------------------------------------- /src/components/sidebar/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Sidebar 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const SidebarPlugin: BvPlugin 9 | 10 | // Component: b-sidebar 11 | export declare class BSidebar extends BvComponent {} 12 | -------------------------------------------------------------------------------- /src/components/tooltip/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Tooltip 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const TooltipPlugin: BvPlugin 9 | 10 | // Component: b-tooltip 11 | export declare class BTooltip extends BvComponent {} 12 | -------------------------------------------------------------------------------- /src/directives/hover/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // VBHover 3 | // 4 | import Vue, { DirectiveOptions } from 'vue' 5 | import { BvPlugin } from '../../' 6 | 7 | // Plugin 8 | export declare const VBHoverPlugin: BvPlugin 9 | 10 | // directive: v-b-hover 11 | export declare const VBHover: DirectiveOptions 12 | -------------------------------------------------------------------------------- /src/directives/modal/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // VBModal 3 | // 4 | import Vue, { DirectiveOptions } from 'vue' 5 | import { BvPlugin } from '../../' 6 | 7 | // Plugin 8 | export declare const VBModalPlugin: BvPlugin 9 | 10 | // directive: v-b-modal 11 | export declare const VBModal: DirectiveOptions 12 | -------------------------------------------------------------------------------- /docs/markdown/reference/utility-classes/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Utility Classes", 3 | "description": "Bootstrap v4 CSS provides various utility classes to control color, spacing, flex-box, text alignment, floating, position, responsive display/hiding and much more.", 4 | "slug": "utility-classes" 5 | } 6 | -------------------------------------------------------------------------------- /docs/pages/docs/misc/changelog.js: -------------------------------------------------------------------------------- 1 | // Add redirect from old URL to new one 2 | // @vue/component 3 | export default { 4 | fetch({ redirect, route = {} }) { 5 | // Use a 301 (permanent) redirect instead of default 302 (found) 6 | redirect(301, `/docs/reference/changelog${route.hash || ''}`) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/pages/docs/misc/settings.js: -------------------------------------------------------------------------------- 1 | // Add redirect from old URL to new one 2 | // @vue/component 3 | export default { 4 | fetch({ redirect, route = {} }) { 5 | // Use a 301 (permanent) redirect instead of default 302 (found) 6 | redirect(301, `/docs/reference/settings${route.hash || ''}`) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/components/form-rating/index.js: -------------------------------------------------------------------------------- 1 | import { BFormRating } from './form-rating' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const FormRatingPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { BFormRating, BRating: BFormRating } 6 | }) 7 | 8 | export { FormRatingPlugin, BFormRating } 9 | -------------------------------------------------------------------------------- /src/components/pagination-nav/index.js: -------------------------------------------------------------------------------- 1 | import { BPaginationNav } from './pagination-nav' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const PaginationNavPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { BPaginationNav } 6 | }) 7 | 8 | export { PaginationNavPlugin, BPaginationNav } 9 | -------------------------------------------------------------------------------- /src/directives/toggle/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // VBToggle 3 | // 4 | import Vue, { DirectiveOptions } from 'vue' 5 | import { BvPlugin } from '../../' 6 | 7 | // Plugin 8 | export declare const VBTogglePlugin: BvPlugin 9 | 10 | // directive: v-b-toggle 11 | export declare const VBToggle: DirectiveOptions 12 | -------------------------------------------------------------------------------- /docs/pages/docs/misc/contributing.js: -------------------------------------------------------------------------------- 1 | // Add redirect from old URL to new one 2 | // @vue/component 3 | export default { 4 | fetch({ redirect, route = {} }) { 5 | // Use a 301 (permanent) redirect instead of default 302 (found) 6 | redirect(301, `/docs/reference/contributing${route.hash || ''}`) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/pages/docs/misc/third-party.js: -------------------------------------------------------------------------------- 1 | // Add redirect from old URL to new one 2 | // @vue/component 3 | export default { 4 | fetch({ redirect, route = {} }) { 5 | // Use a 301 (permanent) redirect instead of default 302 (found) 6 | redirect(301, `/docs/reference/third-party${route.hash || ''}`) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/components/form-group/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Form Group 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const FormGroupPlugin: BvPlugin 9 | 10 | // Component: b-form-group 11 | export declare class BFormGroup extends BvComponent {} 12 | -------------------------------------------------------------------------------- /src/components/jumbotron/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Jumbotron 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const JumbotronPlugin: BvPlugin 9 | 10 | // Component: b-jumbotron 11 | export declare class BJumbotron extends BvComponent {} 12 | -------------------------------------------------------------------------------- /src/directives/popover/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // VBPopover 3 | // 4 | import Vue, { DirectiveOptions } from 'vue' 5 | import { BvPlugin } from '../../' 6 | 7 | // Plugin 8 | export declare const VBPopoverPlugin: BvPlugin 9 | 10 | // directive: v-b-popover 11 | export declare const VBPopover: DirectiveOptions 12 | -------------------------------------------------------------------------------- /src/directives/tooltip/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // VBTooltip 3 | // 4 | import Vue, { DirectiveOptions } from 'vue' 5 | import { BvPlugin } from '../../' 6 | 7 | // Plugin 8 | export declare const VBTooltipPlugin: BvPlugin 9 | 10 | // directive: v-b-tooltip 11 | export declare const VBTooltip: DirectiveOptions 12 | -------------------------------------------------------------------------------- /src/directives/visible/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // VBVisible 3 | // 4 | import Vue, { DirectiveOptions } from 'vue' 5 | import { BvPlugin } from '../../' 6 | 7 | // Plugin 8 | export declare const VBVisiblePlugin: BvPlugin 9 | 10 | // Directive: v-b-visible 11 | export declare const VBVisible: DirectiveOptions 12 | -------------------------------------------------------------------------------- /docs/markdown/reference/theming/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Theming Bootstrap", 3 | "description": "Theming is accomplished by SASS variables, SASS maps, and custom CSS. There's no dedicated theme stylesheet; instead, you can enable the built-in theme to add gradients, shadows, and more.", 4 | "slug": "theming" 5 | } 6 | -------------------------------------------------------------------------------- /src/browser-icons.js: -------------------------------------------------------------------------------- 1 | // Main entry point for the browser icons-only build 2 | import { vueUse } from './utils/plugins' 3 | 4 | import { BootstrapVueIcons } from './icons-only' 5 | 6 | // Auto installation only occurs if window.Vue exists 7 | vueUse(BootstrapVueIcons) 8 | 9 | export default BootstrapVueIcons 10 | -------------------------------------------------------------------------------- /src/components/pagination/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Pagination 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const PaginationPlugin: BvPlugin 9 | 10 | // Component: b-pagination 11 | export declare class BPagination extends BvComponent {} 12 | -------------------------------------------------------------------------------- /src/components/alert/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Alert 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const AlertPlugin: BvPlugin 9 | 10 | // Component: b-alert 11 | export declare class BAlert extends BvComponent { 12 | dismiss: () => void 13 | } 14 | -------------------------------------------------------------------------------- /src/components/button-group/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Button Group 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const ButtonGroupPlugin: BvPlugin 9 | 10 | // Component: b-button-group 11 | export declare class BButtonGroup extends BvComponent {} 12 | -------------------------------------------------------------------------------- /src/components/form-file/index.js: -------------------------------------------------------------------------------- 1 | import { BFormFile } from './form-file' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const FormFilePlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { 6 | BFormFile, 7 | BFile: BFormFile 8 | } 9 | }) 10 | 11 | export { FormFilePlugin, BFormFile } 12 | -------------------------------------------------------------------------------- /src/components/tabs/index.js: -------------------------------------------------------------------------------- 1 | import { BTabs } from './tabs' 2 | import { BTab } from './tab' 3 | import { pluginFactory } from '../../utils/plugins' 4 | 5 | const TabsPlugin = /*#__PURE__*/ pluginFactory({ 6 | components: { 7 | BTabs, 8 | BTab 9 | } 10 | }) 11 | 12 | export { TabsPlugin, BTabs, BTab } 13 | -------------------------------------------------------------------------------- /src/directives/scrollspy/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // VBScrollspy 3 | // 4 | import Vue, { DirectiveOptions } from 'vue' 5 | import { BvPlugin } from '../../' 6 | 7 | // Plugin 8 | export declare const VBScrollspyPlugin: BvPlugin 9 | 10 | // directive: v-b-scrollspy 11 | export declare const VBScrollspy: DirectiveOptions 12 | -------------------------------------------------------------------------------- /src/components/button-toolbar/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Button Toolbar 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const ButtonToolbarPlugin: BvPlugin 9 | 10 | // Component: b-button-toolbar 11 | export declare class BButtonToolbar extends BvComponent {} 12 | -------------------------------------------------------------------------------- /src/components/form-input/index.js: -------------------------------------------------------------------------------- 1 | import { BFormInput } from './form-input' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const FormInputPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { 6 | BFormInput, 7 | BInput: BFormInput 8 | } 9 | }) 10 | 11 | export { FormInputPlugin, BFormInput } 12 | -------------------------------------------------------------------------------- /src/components/pagination-nav/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // PaginationNav 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const PaginationNavPlugin: BvPlugin 9 | 10 | // Component: b-pagination-nav 11 | export declare class BPaginationNav extends BvComponent {} 12 | -------------------------------------------------------------------------------- /src/utils/loose-index-of.js: -------------------------------------------------------------------------------- 1 | import { looseEqual } from './loose-equal' 2 | 3 | // Assumes that the first argument is an array 4 | export const looseIndexOf = (array, value) => { 5 | for (let i = 0; i < array.length; i++) { 6 | if (looseEqual(array[i], value)) { 7 | return i 8 | } 9 | } 10 | return -1 11 | } 12 | -------------------------------------------------------------------------------- /docs/markdown/reference/validation/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Form Validation", 3 | "description": "BootstrapVue does not include form validation by default; we leave that up to the many existing form validation plugins. Included here are some examples of validation plugins and how they may be integrated.", 4 | "slug": "validation" 5 | } 6 | -------------------------------------------------------------------------------- /src/components/collapse/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Collapse 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const CollapsePlugin: BvPlugin 9 | 10 | // Component: b-collapse 11 | export declare class BCollapse extends BvComponent { 12 | toggle: () => void 13 | } 14 | -------------------------------------------------------------------------------- /src/components/form-file/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Form File 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const FormFilePlugin: BvPlugin 9 | 10 | // Component: b-form-file 11 | export declare class BFormFile extends BvComponent { 12 | reset: () => void 13 | } 14 | -------------------------------------------------------------------------------- /src/components/form-group/index.js: -------------------------------------------------------------------------------- 1 | import { BFormGroup } from './form-group' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const FormGroupPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { 6 | BFormGroup, 7 | BFormFieldset: BFormGroup 8 | } 9 | }) 10 | 11 | export { FormGroupPlugin, BFormGroup } 12 | -------------------------------------------------------------------------------- /src/components/image/index.js: -------------------------------------------------------------------------------- 1 | import { BImg } from './img' 2 | import { BImgLazy } from './img-lazy' 3 | import { pluginFactory } from '../../utils/plugins' 4 | 5 | const ImagePlugin = /*#__PURE__*/ pluginFactory({ 6 | components: { 7 | BImg, 8 | BImgLazy 9 | } 10 | }) 11 | 12 | export { ImagePlugin, BImg, BImgLazy } 13 | -------------------------------------------------------------------------------- /src/components/avatar/index.js: -------------------------------------------------------------------------------- 1 | import { BAvatar } from './avatar' 2 | import { BAvatarGroup } from './avatar-group' 3 | import { pluginFactory } from '../../utils/plugins' 4 | 5 | const AvatarPlugin = /*#__PURE__*/ pluginFactory({ 6 | components: { BAvatar, BAvatarGroup } 7 | }) 8 | 9 | export { AvatarPlugin, BAvatar, BAvatarGroup } 10 | -------------------------------------------------------------------------------- /src/components/form-input/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Form Input 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const FormInputPlugin: BvPlugin 9 | 10 | // Component: b-form-input 11 | export declare class BFormInput extends BvComponent { 12 | focus: () => void 13 | } 14 | -------------------------------------------------------------------------------- /src/components/link/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Link 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const LinkPlugin: BvPlugin 9 | 10 | // Component: b-link 11 | export declare class BLink extends BvComponent { 12 | focus: () => void 13 | blur: () => void 14 | } 15 | -------------------------------------------------------------------------------- /scripts/index.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * BootstrapVue Custom CSS (https://bootstrap-vue.org) 3 | */ 4 | 5 | // Include Bootstrap functions, variables, and mixins 6 | @import "bootstrap/scss/functions"; 7 | @import "bootstrap/scss/variables"; 8 | @import "bootstrap/scss/mixins"; 9 | 10 | // Import BootstrapVue custom SCSS 11 | @import "../src/index.scss"; 12 | -------------------------------------------------------------------------------- /src/components/button-group/index.js: -------------------------------------------------------------------------------- 1 | import { BButtonGroup } from './button-group' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const ButtonGroupPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { 6 | BButtonGroup, 7 | BBtnGroup: BButtonGroup 8 | } 9 | }) 10 | 11 | export { ButtonGroupPlugin, BButtonGroup } 12 | -------------------------------------------------------------------------------- /src/utils/math.js: -------------------------------------------------------------------------------- 1 | // Math utilty functions 2 | 3 | export const mathMin = Math.min 4 | 5 | export const mathMax = Math.max 6 | 7 | export const mathAbs = Math.abs 8 | 9 | export const mathCeil = Math.ceil 10 | 11 | export const mathFloor = Math.floor 12 | 13 | export const mathPow = Math.pow 14 | 15 | export const mathRound = Math.round 16 | -------------------------------------------------------------------------------- /src/vue-injections.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Augment the typings of Vue.js 3 | */ 4 | import Vue from 'vue' 5 | import { BvModal } from './components/modal' 6 | import { BvToast } from './components/toast' 7 | 8 | declare module 'vue/types/vue' { 9 | interface Vue { 10 | readonly $bvModal: BvModal 11 | readonly $bvToast: BvToast 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/components/form-textarea/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Form Textarea 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const FormTextareaPlugin: BvPlugin 9 | 10 | // Component: b-form-textarea 11 | export declare class BFormTextarea extends BvComponent { 12 | focus: () => void 13 | } 14 | -------------------------------------------------------------------------------- /src/components/form-textarea/index.js: -------------------------------------------------------------------------------- 1 | import { BFormTextarea } from './form-textarea' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const FormTextareaPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { 6 | BFormTextarea, 7 | BTextarea: BFormTextarea 8 | } 9 | }) 10 | 11 | export { FormTextareaPlugin, BFormTextarea } 12 | -------------------------------------------------------------------------------- /src/components/form-rating/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Alert 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const FormRatingPlugin: BvPlugin 9 | 10 | // Component: b-alert 11 | export declare class BFormRating extends BvComponent { 12 | focus: () => void 13 | blur: () => void 14 | } 15 | -------------------------------------------------------------------------------- /scripts/icons.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * BootstrapVue Icons Custom CSS (https://bootstrap-vue.org) 3 | */ 4 | 5 | // Include Bootstrap functions, variables, and mixins 6 | @import "bootstrap/scss/functions"; 7 | @import "bootstrap/scss/variables"; 8 | @import "bootstrap/scss/mixins"; 9 | 10 | // Import BootstrapVue Icons custom SCSS 11 | @import "../src/icons.scss"; 12 | -------------------------------------------------------------------------------- /src/components/button-toolbar/index.js: -------------------------------------------------------------------------------- 1 | import { BButtonToolbar } from './button-toolbar' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const ButtonToolbarPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { 6 | BButtonToolbar, 7 | BBtnToolbar: BButtonToolbar 8 | } 9 | }) 10 | 11 | export { ButtonToolbarPlugin, BButtonToolbar } 12 | -------------------------------------------------------------------------------- /src/components/sidebar/index.js: -------------------------------------------------------------------------------- 1 | import { BSidebar } from './sidebar' 2 | import { VBTogglePlugin } from '../../directives/toggle' 3 | import { pluginFactory } from '../../utils/plugins' 4 | 5 | const SidebarPlugin = /*#__PURE__*/ pluginFactory({ 6 | components: { BSidebar }, 7 | plugins: { VBTogglePlugin } 8 | }) 9 | 10 | export { SidebarPlugin, BSidebar } 11 | -------------------------------------------------------------------------------- /src/mixins/scoped-style.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../vue' 2 | import { getScopeId } from '../utils/get-scope-id' 3 | 4 | // @vue/component 5 | export const scopedStyleMixin = Vue.extend({ 6 | computed: { 7 | scopedStyleAttrs() { 8 | const scopeId = getScopeId(this.$parent) 9 | return scopeId ? { [scopeId]: '' } : {} 10 | } 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/components/popover/index.js: -------------------------------------------------------------------------------- 1 | import { BPopover } from './popover' 2 | import { VBPopoverPlugin } from '../../directives/popover' 3 | import { pluginFactory } from '../../utils/plugins' 4 | 5 | const PopoverPlugin = /*#__PURE__*/ pluginFactory({ 6 | components: { BPopover }, 7 | plugins: { VBPopoverPlugin } 8 | }) 9 | 10 | export { PopoverPlugin, BPopover } 11 | -------------------------------------------------------------------------------- /src/components/tooltip/index.js: -------------------------------------------------------------------------------- 1 | import { BTooltip } from './tooltip' 2 | import { VBTooltipPlugin } from '../../directives/tooltip' 3 | import { pluginFactory } from '../../utils/plugins' 4 | 5 | const TooltipPlugin = /*#__PURE__*/ pluginFactory({ 6 | components: { BTooltip }, 7 | plugins: { VBTooltipPlugin } 8 | }) 9 | 10 | export { TooltipPlugin, BTooltip } 11 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testRegex: 'spec.js$', 3 | moduleFileExtensions: ['js', 'vue'], 4 | transform: { 5 | '^.+\\.js$': 'babel-jest', 6 | '.*\\.(vue)$': 'vue-jest' 7 | }, 8 | coverageDirectory: './coverage/', 9 | testEnvironmentOptions: { 10 | pretendToBeVisual: true 11 | }, 12 | setupFilesAfterEnv: ['./tests/setup.js'] 13 | } 14 | -------------------------------------------------------------------------------- /src/components/collapse/index.js: -------------------------------------------------------------------------------- 1 | import { BCollapse } from './collapse' 2 | import { VBTogglePlugin } from '../../directives/toggle' 3 | import { pluginFactory } from '../../utils/plugins' 4 | 5 | const CollapsePlugin = /*#__PURE__*/ pluginFactory({ 6 | components: { BCollapse }, 7 | plugins: { VBTogglePlugin } 8 | }) 9 | 10 | export { CollapsePlugin, BCollapse } 11 | -------------------------------------------------------------------------------- /src/components/form-datepicker/index.js: -------------------------------------------------------------------------------- 1 | import { BFormDatepicker } from './form-datepicker' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const FormDatepickerPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { 6 | BFormDatepicker, 7 | BDatepicker: BFormDatepicker 8 | } 9 | }) 10 | 11 | export { FormDatepickerPlugin, BFormDatepicker } 12 | -------------------------------------------------------------------------------- /src/components/form-spinbutton/index.js: -------------------------------------------------------------------------------- 1 | import { BFormSpinbutton } from './form-spinbutton' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const FormSpinbuttonPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { 6 | BFormSpinbutton, 7 | BSpinbutton: BFormSpinbutton 8 | } 9 | }) 10 | 11 | export { FormSpinbuttonPlugin, BFormSpinbutton } 12 | -------------------------------------------------------------------------------- /src/components/form-timepicker/index.js: -------------------------------------------------------------------------------- 1 | import { BFormTimepicker } from './form-timepicker' 2 | import { pluginFactory } from '../../utils/plugins' 3 | 4 | const FormTimepickerPlugin = /*#__PURE__*/ pluginFactory({ 5 | components: { 6 | BFormTimepicker, 7 | BTimepicker: BFormTimepicker 8 | } 9 | }) 10 | 11 | export { FormTimepickerPlugin, BFormTimepicker } 12 | -------------------------------------------------------------------------------- /src/components/image/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Image 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const ImagePlugin: BvPlugin 9 | 10 | // Component: b-img 11 | export declare class BImg extends BvComponent {} 12 | 13 | // Component: b-img-lazy 14 | export declare class BImgLazy extends BvComponent {} 15 | -------------------------------------------------------------------------------- /src/components/progress/index.js: -------------------------------------------------------------------------------- 1 | import { BProgress } from './progress' 2 | import { BProgressBar } from './progress-bar' 3 | import { pluginFactory } from '../../utils/plugins' 4 | 5 | const ProgressPlugin = /*#__PURE__*/ pluginFactory({ 6 | components: { 7 | BProgress, 8 | BProgressBar 9 | } 10 | }) 11 | 12 | export { ProgressPlugin, BProgress, BProgressBar } 13 | -------------------------------------------------------------------------------- /src/components/carousel/index.js: -------------------------------------------------------------------------------- 1 | import { BCarousel } from './carousel' 2 | import { BCarouselSlide } from './carousel-slide' 3 | import { pluginFactory } from '../../utils/plugins' 4 | 5 | const CarouselPlugin = /*#__PURE*/ pluginFactory({ 6 | components: { 7 | BCarousel, 8 | BCarouselSlide 9 | } 10 | }) 11 | 12 | export { CarouselPlugin, BCarousel, BCarouselSlide } 13 | -------------------------------------------------------------------------------- /src/components/form-datepicker/index.d.ts: -------------------------------------------------------------------------------- 1 | // --- Form Datepicker --- 2 | import Vue from 'vue' 3 | import { BvPlugin, BvComponent } from '../../' 4 | 5 | // Plugin 6 | export declare const FormDatepickerPlugin: BvPlugin 7 | 8 | // Component: b-form-datepicker 9 | export declare class BFormDatepicker extends BvComponent { 10 | focus: () => void 11 | blur: () => void 12 | } 13 | -------------------------------------------------------------------------------- /src/components/form-spinbutton/index.d.ts: -------------------------------------------------------------------------------- 1 | // --- Form Spinbutton --- 2 | import Vue from 'vue' 3 | import { BvPlugin, BvComponent } from '../../' 4 | 5 | // Plugin 6 | export declare const FormSpinbuttonPlugin: BvPlugin 7 | 8 | // Component: b-form-spinbutton 9 | export declare class BFormSpinbutton extends BvComponent { 10 | focus: () => void 11 | blur: () => void 12 | } 13 | -------------------------------------------------------------------------------- /src/components/form-timepicker/index.d.ts: -------------------------------------------------------------------------------- 1 | // --- Form Timepicker --- 2 | import Vue from 'vue' 3 | import { BvPlugin, BvComponent } from '../../' 4 | 5 | // Plugin 6 | export declare const FormTimepickerPlugin: BvPlugin 7 | 8 | // Component: b-form-timepicker 9 | export declare class BFormTimepicker extends BvComponent { 10 | focus: () => void 11 | blur: () => void 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/get-scope-id.js: -------------------------------------------------------------------------------- 1 | // This method returns a component's scoped style attribute name: `data-v-xxxxxxx` 2 | // The `_scopeId` options property is added by vue-loader when using scoped styles 3 | // and will be `undefined` if no scoped styles are in use 4 | export const getScopeId = (vm, defaultValue = null) => { 5 | return vm ? vm.$options._scopeId || defaultValue : defaultValue 6 | } 7 | -------------------------------------------------------------------------------- /src/components/avatar/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Avatar 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const AvatarPlugin: BvPlugin 9 | 10 | // Component: b-avatar 11 | export declare class BAvatar extends BvComponent {} 12 | 13 | // Component: b-avatar-group 14 | export declare class BAvatarGroup extends BvComponent {} 15 | -------------------------------------------------------------------------------- /src/components/button/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Buttons 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const ButtonPlugin: BvPlugin 9 | 10 | // Component: b-button 11 | export declare class BButton extends BvComponent {} 12 | 13 | // Component: b-button-close 14 | export declare class BButtonClose extends BvComponent {} 15 | -------------------------------------------------------------------------------- /src/components/list-group/index.js: -------------------------------------------------------------------------------- 1 | import { BListGroup } from './list-group' 2 | import { BListGroupItem } from './list-group-item' 3 | import { pluginFactory } from '../../utils/plugins' 4 | 5 | const ListGroupPlugin = /*#__PURE__*/ pluginFactory({ 6 | components: { 7 | BListGroup, 8 | BListGroupItem 9 | } 10 | }) 11 | 12 | export { ListGroupPlugin, BListGroup, BListGroupItem } 13 | -------------------------------------------------------------------------------- /src/components/progress/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Progress 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const ProgressPlugin: BvPlugin 9 | 10 | // Component: b-progress 11 | export declare class BProgress extends BvComponent {} 12 | 13 | // Component: b-progress-bar 14 | export declare class BProgressBar extends BvComponent {} 15 | -------------------------------------------------------------------------------- /tests/utils.js: -------------------------------------------------------------------------------- 1 | // --- Utils for testing --- 2 | 3 | export const createContainer = (tag = 'div') => { 4 | const container = document.createElement(tag) 5 | document.body.appendChild(container) 6 | return container 7 | } 8 | 9 | export const waitNT = ctx => new Promise(resolve => ctx.$nextTick(resolve)) 10 | export const waitRAF = () => new Promise(resolve => requestAnimationFrame(resolve)) 11 | -------------------------------------------------------------------------------- /src/directives/hover/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bootstrap-vue/v-b-hover", 3 | "version": "1.0.0", 4 | "meta": { 5 | "title": "Hover", 6 | "description": "A lightweight directive that allows you to react when an element either becomes hovered or unhovered", 7 | "directive": "VBHover", 8 | "version": "2.5.0", 9 | "expression": [ 10 | "Function" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/directives/index.d.ts: -------------------------------------------------------------------------------- 1 | import { BvPlugin } from '../' 2 | 3 | // Plugin that installs all plugins 4 | export declare const directivesPlugin: BvPlugin 5 | 6 | // Named exports of all directives 7 | export * from './hover' 8 | export * from './modal' 9 | export * from './popover' 10 | export * from './scrollspy' 11 | export * from './toggle' 12 | export * from './tooltip' 13 | export * from './visible' 14 | -------------------------------------------------------------------------------- /src/components/list-group/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // ListGroup 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const ListGroupPlugin: BvPlugin 9 | 10 | // Component: b-list-group 11 | export declare class BListGroup extends BvComponent {} 12 | 13 | // Component: b-list-group-item 14 | export declare class BListGroupItem extends BvComponent {} 15 | -------------------------------------------------------------------------------- /src/components/form-radio/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Form Radio 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const FormRadioPlugin: BvPlugin 9 | 10 | // Component: b-form-radio 11 | export declare class BFormRadio extends BvComponent {} 12 | 13 | // Component: b-form-radio-group 14 | export declare class BFormRadioGroup extends BvComponent {} 15 | -------------------------------------------------------------------------------- /src/components/form-tags/index.js: -------------------------------------------------------------------------------- 1 | import { BFormTags } from './form-tags' 2 | import { BFormTag } from './form-tag' 3 | import { pluginFactory } from '../../utils/plugins' 4 | 5 | const FormTagsPlugin = /*#__PURE__*/ pluginFactory({ 6 | components: { 7 | BFormTags, 8 | BTags: BFormTags, 9 | BFormTag, 10 | BTag: BFormTag 11 | } 12 | }) 13 | 14 | export { FormTagsPlugin, BFormTags, BFormTag } 15 | -------------------------------------------------------------------------------- /docs/components/main.js: -------------------------------------------------------------------------------- 1 | import { mergeData } from 'vue-functional-data-merge' 2 | 3 | // @vue/component 4 | export default { 5 | name: 'BVMain', 6 | functional: true, 7 | props: { 8 | tag: { 9 | type: String, 10 | default: 'main' 11 | } 12 | }, 13 | render(h, { props, data, children }) { 14 | return h(props.tag, mergeData(data, { staticClass: 'bd-main' }), [children]) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/components/button/index.js: -------------------------------------------------------------------------------- 1 | import { BButton } from './button' 2 | import { BButtonClose } from './button-close' 3 | import { pluginFactory } from '../../utils/plugins' 4 | 5 | const ButtonPlugin = /*#__PURE__*/ pluginFactory({ 6 | components: { 7 | BButton, 8 | BBtn: BButton, 9 | BButtonClose, 10 | BBtnClose: BButtonClose 11 | } 12 | }) 13 | 14 | export { ButtonPlugin, BButton, BButtonClose } 15 | -------------------------------------------------------------------------------- /src/constants/popper.js: -------------------------------------------------------------------------------- 1 | export const PLACEMENT_TOP_START = 'top-start' 2 | export const PLACEMENT_TOP_END = 'top-end' 3 | export const PLACEMENT_BOTTOM_START = 'bottom-start' 4 | export const PLACEMENT_BOTTOM_END = 'bottom-end' 5 | export const PLACEMENT_RIGHT_START = 'right-start' 6 | export const PLACEMENT_RIGHT_END = 'right-end' 7 | export const PLACEMENT_LEFT_START = 'left-start' 8 | export const PLACEMENT_LEFT_END = 'left-end' 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "proseWrap": "always", 4 | "semi": false, 5 | "singleQuote": true, 6 | "tabWidth": 2, 7 | "overrides": [ 8 | { 9 | "files": ".github/**/*.md", 10 | "options": { 11 | "proseWrap": "preserve" 12 | } 13 | }, 14 | { 15 | "files": "*.scss", 16 | "options": { 17 | "singleQuote": false 18 | } 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /src/components/media/index.js: -------------------------------------------------------------------------------- 1 | import { BMedia } from './media' 2 | import { BMediaAside } from './media-aside' 3 | import { BMediaBody } from './media-body' 4 | import { pluginFactory } from '../../utils/plugins' 5 | 6 | const MediaPlugin = /*#__PURE__*/ pluginFactory({ 7 | components: { 8 | BMedia, 9 | BMediaAside, 10 | BMediaBody 11 | } 12 | }) 13 | 14 | export { MediaPlugin, BMedia, BMediaAside, BMediaBody } 15 | -------------------------------------------------------------------------------- /src/components/toast/index.js: -------------------------------------------------------------------------------- 1 | import { BVToastPlugin } from './helpers/bv-toast' 2 | import { BToast } from './toast' 3 | import { BToaster } from './toaster' 4 | import { pluginFactory } from '../../utils/plugins' 5 | 6 | const ToastPlugin = /*#__PURE__*/ pluginFactory({ 7 | components: { BToast, BToaster }, 8 | // $bvToast injection 9 | plugins: { BVToastPlugin } 10 | }) 11 | 12 | export { ToastPlugin, BToast, BToaster } 13 | -------------------------------------------------------------------------------- /src/components/form-checkbox/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Form Checkbox 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const FormCheckboxPlugin: BvPlugin 9 | 10 | // Component: b-form-checkbox 11 | export declare class BFormCheckbox extends BvComponent {} 12 | 13 | // Component: b-form-checkbox-group 14 | export declare class BFormCheckboxGroup extends BvComponent {} 15 | -------------------------------------------------------------------------------- /src/components/tabs/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Tabs 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const TabsPlugin: BvPlugin 9 | 10 | // Component: b-tabs 11 | export declare class BTabs extends BvComponent {} 12 | 13 | // Component: b-tab 14 | export declare class BTab extends BvComponent { 15 | activate: () => boolean 16 | deactivate: () => boolean 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/html.js: -------------------------------------------------------------------------------- 1 | import { RX_HTML_TAGS } from '../constants/regex' 2 | 3 | // Removes anything that looks like an HTML tag from the supplied string 4 | export const stripTags = (text = '') => String(text).replace(RX_HTML_TAGS, '') 5 | 6 | // Generate a `domProps` object for either `innerHTML`, `textContent` or an empty object 7 | export const htmlOrText = (innerHTML, textContent) => 8 | innerHTML ? { innerHTML } : textContent ? { textContent } : {} 9 | -------------------------------------------------------------------------------- /src/components/modal/index.js: -------------------------------------------------------------------------------- 1 | import { BModal } from './modal' 2 | import { VBModal } from '../../directives/modal/modal' 3 | import { BVModalPlugin } from './helpers/bv-modal' 4 | import { pluginFactory } from '../../utils/plugins' 5 | 6 | const ModalPlugin = /*#__PURE__*/ pluginFactory({ 7 | components: { BModal }, 8 | directives: { VBModal }, 9 | // $bvModal injection 10 | plugins: { BVModalPlugin } 11 | }) 12 | 13 | export { ModalPlugin, BModal } 14 | -------------------------------------------------------------------------------- /scripts/banner.js: -------------------------------------------------------------------------------- 1 | const pkg = require('../package.json') 2 | const year = new Date().getFullYear() 3 | 4 | const banner = `/*! 5 | * BootstrapVue ${pkg.version} 6 | * 7 | * @link ${pkg.homepage} 8 | * @source https://github.com/bootstrap-vue/bootstrap-vue 9 | * @copyright (c) 2016-${year} BootstrapVue 10 | * @license ${pkg.license} 11 | * https://github.com/bootstrap-vue/bootstrap-vue/blob/master/LICENSE 12 | */ 13 | ` 14 | 15 | module.exports = banner 16 | -------------------------------------------------------------------------------- /src/components/breadcrumb/index.js: -------------------------------------------------------------------------------- 1 | import { BBreadcrumb } from './breadcrumb' 2 | import { BBreadcrumbItem } from './breadcrumb-item' 3 | import { BBreadcrumbLink } from './breadcrumb-link' 4 | import { pluginFactory } from '../../utils/plugins' 5 | 6 | const BreadcrumbPlugin = /*#__PURE__*/ pluginFactory({ 7 | components: { BBreadcrumb, BBreadcrumbItem, BBreadcrumbLink } 8 | }) 9 | 10 | export { BreadcrumbPlugin, BBreadcrumb, BBreadcrumbItem, BBreadcrumbLink } 11 | -------------------------------------------------------------------------------- /src/components/form-radio/index.js: -------------------------------------------------------------------------------- 1 | import { BFormRadio } from './form-radio' 2 | import { BFormRadioGroup } from './form-radio-group' 3 | import { pluginFactory } from '../../utils/plugins' 4 | 5 | const FormRadioPlugin = /*#__PURE__*/ pluginFactory({ 6 | components: { 7 | BFormRadio, 8 | BRadio: BFormRadio, 9 | BFormRadioGroup, 10 | BRadioGroup: BFormRadioGroup 11 | } 12 | }) 13 | 14 | export { FormRadioPlugin, BFormRadio, BFormRadioGroup } 15 | -------------------------------------------------------------------------------- /src/components/form-tags/index.d.ts: -------------------------------------------------------------------------------- 1 | // --- Form Tags --- 2 | import Vue from 'vue' 3 | import { BvPlugin, BvComponent } from '../../' 4 | 5 | // Plugin 6 | export declare const FormTagsPlugin: BvPlugin 7 | 8 | // Component: b-form-tags 9 | export declare class BFormTags extends BvComponent { 10 | focus: () => void 11 | blur: () => void 12 | reset: () => void 13 | } 14 | 15 | // Component: b-form-tag 16 | export declare class BFormTag extends BvComponent {} 17 | -------------------------------------------------------------------------------- /src/components/layout/index.js: -------------------------------------------------------------------------------- 1 | import { BContainer } from './container' 2 | import { BRow } from './row' 3 | import { BCol } from './col' 4 | import { BFormRow } from './form-row' 5 | import { pluginFactory } from '../../utils/plugins' 6 | 7 | const LayoutPlugin = /*#__PURE__*/ pluginFactory({ 8 | components: { 9 | BContainer, 10 | BRow, 11 | BCol, 12 | BFormRow 13 | } 14 | }) 15 | 16 | export { LayoutPlugin, BContainer, BRow, BCol, BFormRow } 17 | -------------------------------------------------------------------------------- /docs/content/themes/dexam-startup-and-product-landing-page.yaml: -------------------------------------------------------------------------------- 1 | title: 'Dexam - Startup & Product Landing Page' 2 | type: 'landing page' 3 | category: 'Landing Page' 4 | img: 'https://i.ibb.co/1MyVr6S/preview-dexam.png' 5 | href: 'https://1.envato.market/rVP4d' 6 | description: 'Dexam is a clean startup and product landing page built with Vue Cli and BootstrapVue. It comes with 10+ home and colors variations. A HTML version is included.' 7 | provider: 'UI Lib' 8 | price: '$22.00' 9 | -------------------------------------------------------------------------------- /src/components/media/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Media 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const MediaPlugin: BvPlugin 9 | 10 | // Component: b-media 11 | export declare class BMedia extends BvComponent {} 12 | 13 | // Component: b-media-aside 14 | export declare class BMediaAside extends BvComponent {} 15 | 16 | // Component: b-media-body 17 | export declare class BMediaBody extends BvComponent {} 18 | -------------------------------------------------------------------------------- /src/utils/env.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Utilities to get information about the current environment 3 | */ 4 | 5 | export const getEnv = (key, fallback = null) => { 6 | const env = typeof process !== 'undefined' && process ? process.env || {} : {} 7 | if (!key) { 8 | /* istanbul ignore next */ 9 | return env 10 | } 11 | return env[key] || fallback 12 | } 13 | 14 | export const getNoWarn = () => 15 | getEnv('BOOTSTRAP_VUE_NO_WARN') || getEnv('NODE_ENV') === 'production' 16 | -------------------------------------------------------------------------------- /src/constants/key-codes.js: -------------------------------------------------------------------------------- 1 | export const CODE_BACKSPACE = 8 2 | export const CODE_BREAK = 19 3 | export const CODE_DELETE = 46 4 | export const CODE_DOWN = 40 5 | export const CODE_END = 35 6 | export const CODE_ENTER = 13 7 | export const CODE_ESC = 27 8 | export const CODE_HOME = 36 9 | export const CODE_LEFT = 37 10 | export const CODE_PAGEDOWN = 34 11 | export const CODE_PAGEUP = 33 12 | export const CODE_RIGHT = 39 13 | export const CODE_SPACE = 32 14 | export const CODE_UP = 38 15 | -------------------------------------------------------------------------------- /src/icons/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Icons 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../' 6 | 7 | // Plugin 8 | export declare const IconsPlugin: BvPlugin 9 | export declare const BootstrapVueIcons: BvPlugin 10 | 11 | // Component: b-icon 12 | export declare class BIcon extends BvComponent {} 13 | 14 | // Component: b-iconstack 15 | export declare class BIconstack extends BvComponent {} 16 | 17 | // Components: b-icon-{icon-name} 18 | export * from './icons' 19 | -------------------------------------------------------------------------------- /docs/content/themes/gull-admin-dashboard.yaml: -------------------------------------------------------------------------------- 1 | title: 'Gull - Admin Dashboard' 2 | type: 'dashboard' 3 | category: 'Admin & Dashboard' 4 | img: 'https://i.ibb.co/bRH1NN5/preview-gull.png' 5 | href: 'https://1.envato.market/ayXRq' 6 | description: 'Gull is a modern, next-generation Vue.js Admin Dashboard. It is feature-rich, responsive and built on top of Vue CLI, Vuex, Vue Router and BootstrapVue. If you want to create a Vue.js Admin Dashboard, Gull is best option.' 7 | provider: 'UI Lib' 8 | price: '$24.00' 9 | -------------------------------------------------------------------------------- /src/index.scss: -------------------------------------------------------------------------------- 1 | // --- BootstrapVue Custom SCSS --- 2 | 3 | // Requires at least the Bootstrap functions, variables and 4 | // mixins to be imported first 5 | 6 | // Include variables and utilities first 7 | @import "variables"; 8 | @import "utilities"; 9 | 10 | // General styling needed for special form controls 11 | @import "custom-controls"; 12 | 13 | // Include custom SCSS for components 14 | @import "components/index"; 15 | 16 | // Include custom SCSS for icons 17 | @import "icons/index"; 18 | -------------------------------------------------------------------------------- /src/utils/clone-deep.js: -------------------------------------------------------------------------------- 1 | import { isArray, isPlainObject } from './inspect' 2 | import { keys } from './object' 3 | 4 | export const cloneDeep = (obj, defaultValue = obj) => { 5 | if (isArray(obj)) { 6 | return obj.reduce((result, val) => [...result, cloneDeep(val, val)], []) 7 | } 8 | if (isPlainObject(obj)) { 9 | return keys(obj).reduce( 10 | (result, key) => ({ ...result, [key]: cloneDeep(obj[key], obj[key]) }), 11 | {} 12 | ) 13 | } 14 | return defaultValue 15 | } 16 | -------------------------------------------------------------------------------- /docs/utils/code-mirror.js: -------------------------------------------------------------------------------- 1 | let CodeMirror 2 | if (typeof window !== 'undefined') { 3 | CodeMirror = require('codemirror') 4 | require('codemirror/mode/javascript/javascript') 5 | // require('codemirror/mode/shell/shell') 6 | require('codemirror/mode/vue/vue') 7 | require('codemirror/mode/htmlmixed/htmlmixed') 8 | require('codemirror/addon/edit/closetag') 9 | require('codemirror/addon/edit/closebrackets') 10 | require('codemirror/addon/fold/xml-fold') 11 | } 12 | 13 | export default CodeMirror 14 | -------------------------------------------------------------------------------- /src/components/nav/nav-text.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_NAV_TEXT } from '../../constants/components' 3 | 4 | // --- Props --- 5 | 6 | export const props = {} 7 | 8 | // --- Main component --- 9 | 10 | // @vue/component 11 | export const BNavText = /*#__PURE__*/ Vue.extend({ 12 | name: NAME_NAV_TEXT, 13 | functional: true, 14 | props, 15 | render(h, { data, children }) { 16 | return h('li', mergeData(data, { staticClass: 'navbar-text' }), children) 17 | } 18 | }) 19 | -------------------------------------------------------------------------------- /src/components/breadcrumb/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Breadcrumb 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const BreadcrumbPlugin: BvPlugin 9 | 10 | // Component: b-breadcrumb 11 | export declare class BBreadcrumb extends BvComponent {} 12 | 13 | // Component: b-breadcrumb-item 14 | export declare class BBreadcrumbItem extends BvComponent {} 15 | 16 | // Component: b-breadcrumb-link 17 | export declare class BBreadcrumbLink extends BvComponent {} 18 | -------------------------------------------------------------------------------- /src/components/carousel/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Carousel 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const CarouselPlugin: BvPlugin 9 | 10 | // Component: b-carousel 11 | export declare class BCarousel extends BvComponent { 12 | setSlide: (slide: number) => void 13 | prev: () => void 14 | next: () => void 15 | start: () => void 16 | pause: () => void 17 | } 18 | 19 | // Component: b-carousel-slide 20 | export declare class BCarouselSlide extends BvComponent {} 21 | -------------------------------------------------------------------------------- /src/components/form-select/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Form Select 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const FormSelectPlugin: BvPlugin 9 | 10 | // Component: b-form-select 11 | export declare class BFormSelect extends BvComponent {} 12 | 13 | // Component: b-form-select-option 14 | export declare class BFormSelectOption extends BvComponent {} 15 | 16 | // Component: b-form-select-option-group 17 | export declare class BFormSelectOptionGroup extends BvComponent {} 18 | -------------------------------------------------------------------------------- /src/components/layout/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Layout 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const LayoutPlugin: BvPlugin 9 | 10 | // Component: b-container 11 | export declare class BContainer extends BvComponent {} 12 | 13 | // Component: b-row 14 | export declare class BRow extends BvComponent {} 15 | 16 | // Component: b-col 17 | export declare class BCol extends BvComponent {} 18 | 19 | // Component: b-form-row 20 | export declare class BFormRow extends BvComponent {} 21 | -------------------------------------------------------------------------------- /src/icons/index.js: -------------------------------------------------------------------------------- 1 | // Icons Plugin 2 | 3 | // These re-exports are not currently used, as Webpack 4 has 4 | // issues with tree shaking re-exports of re-exports 5 | // `/src/index.js` does a single level re-export `* from './icons/icons/icons'` 6 | // Export all icons 7 | export * from './icons' 8 | 9 | // Export helper component 10 | export { BIcon } from './icon' 11 | 12 | // Export stacking component 13 | export { BIconstack } from './iconstack' 14 | 15 | // Plugin (an iconNames for docs) 16 | export { IconsPlugin, BootstrapVueIcons, iconNames } from './plugin' 17 | -------------------------------------------------------------------------------- /src/mixins/form-custom.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../vue' 2 | import { PROP_TYPE_BOOLEAN } from '../constants/props' 3 | import { makeProp, makePropsConfigurable } from '../utils/props' 4 | 5 | // --- Props --- 6 | 7 | export const props = makePropsConfigurable( 8 | { 9 | plain: makeProp(PROP_TYPE_BOOLEAN, false) 10 | }, 11 | 'formControls' 12 | ) 13 | 14 | // --- Mixin --- 15 | 16 | // @vue/component 17 | export const formCustomMixin = Vue.extend({ 18 | props, 19 | computed: { 20 | custom() { 21 | return !this.plain 22 | } 23 | } 24 | }) 25 | -------------------------------------------------------------------------------- /src/components/dropdown/_dropdown-text.scss: -------------------------------------------------------------------------------- 1 | $bv-dropdown-text-defined: false !default; 2 | 3 | @if $bv-dropdown-text-defined == false { 4 | // This test will only include these style definitions once 5 | $bv-dropdown-text-defined: true; 6 | 7 | // Custom styles for 8 | // Based on class `.dropdown-item` 9 | .b-dropdown-text { 10 | display: inline-block; 11 | padding: $dropdown-item-padding-y $dropdown-item-padding-x; 12 | margin-bottom: 0; 13 | width: 100%; 14 | clear: both; 15 | font-weight: $font-weight-lighter; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/components/form-checkbox/index.js: -------------------------------------------------------------------------------- 1 | import { BFormCheckbox } from './form-checkbox' 2 | import { BFormCheckboxGroup } from './form-checkbox-group' 3 | import { pluginFactory } from '../../utils/plugins' 4 | 5 | const FormCheckboxPlugin = /*#__PURE__*/ pluginFactory({ 6 | components: { 7 | BFormCheckbox, 8 | BCheckbox: BFormCheckbox, 9 | BCheck: BFormCheckbox, 10 | BFormCheckboxGroup, 11 | BCheckboxGroup: BFormCheckboxGroup, 12 | BCheckGroup: BFormCheckboxGroup 13 | } 14 | }) 15 | 16 | export { FormCheckboxPlugin, BFormCheckbox, BFormCheckboxGroup } 17 | -------------------------------------------------------------------------------- /src/components/navbar/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Navbar 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const NavbarPlugin: BvPlugin 9 | // Component: b-navbar 10 | export declare class BNavbar extends BvComponent {} 11 | 12 | // Component: b-navbar-brand 13 | export declare class BNavbarBrand extends BvComponent {} 14 | 15 | // Component: b-navbar-nav 16 | export declare class BNavbarNav extends BvComponent {} 17 | 18 | // Component: b-navbar-toggle 19 | export declare class BNavbarToggle extends BvComponent {} 20 | -------------------------------------------------------------------------------- /src/mixins/form-size.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../vue' 2 | import { PROP_TYPE_STRING } from '../constants/props' 3 | import { makeProp, makePropsConfigurable } from '../utils/props' 4 | 5 | // --- Props --- 6 | 7 | export const props = makePropsConfigurable( 8 | { 9 | size: makeProp(PROP_TYPE_STRING) 10 | }, 11 | 'formControls' 12 | ) 13 | 14 | // --- Mixin --- 15 | 16 | // @vue/component 17 | export const formSizeMixin = Vue.extend({ 18 | props, 19 | computed: { 20 | sizeFormClass() { 21 | return [this.size ? `form-control-${this.size}` : null] 22 | } 23 | } 24 | }) 25 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = api => { 2 | const isDocs = api.env('docs') 3 | 4 | const presets = [] 5 | if (!isDocs) { 6 | presets.push(['@babel/env', { useBuiltIns: 'entry', corejs: { version: 3 } }]) 7 | } 8 | 9 | return { 10 | presets, 11 | env: { 12 | es: { 13 | plugins: [['@babel/plugin-transform-modules-commonjs', { loose: true }]] 14 | }, 15 | esm: { 16 | presets: [['@babel/env', { modules: false }]] 17 | }, 18 | test: { 19 | presets: [['@babel/env', { targets: { node: 'current' } }]] 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/components/table/helpers/stringify-record-values.js: -------------------------------------------------------------------------------- 1 | import { isObject } from '../../../utils/inspect' 2 | import { stringifyObjectValues } from '../../../utils/stringify-object-values' 3 | import { sanitizeRow } from './sanitize-row' 4 | 5 | // Stringifies the values of a record, ignoring any special top level field keys 6 | // TODO: Add option to stringify `scopedSlot` items 7 | export const stringifyRecordValues = (row, ignoreFields, includeFields, fieldsObj) => { 8 | return isObject(row) 9 | ? stringifyObjectValues(sanitizeRow(row, ignoreFields, includeFields, fieldsObj)) 10 | : /* istanbul ignore next */ '' 11 | } 12 | -------------------------------------------------------------------------------- /docs/content/themes/argon-dashboard.yaml: -------------------------------------------------------------------------------- 1 | title: 'BootstrapVue Argon Dashboard' 2 | type: 'dashboard' 3 | category: 'Admin & Dashboard' 4 | img: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/bootstrap-vue-argon-dashboard/opt_ad_bootstrapvue_thumbnail.jpg' 5 | href: 'https://www.creative-tim.com/product/bootstrap-vue-argon-dashboard?partner=134895' 6 | description: 'BootstrapVue Argon Dashboard is built with over 100 individual components, giving you the freedom of choosing and combining. All components can take variations in color, that you can easily modify using SASS files.' 7 | provider: 'Creative Tim' 8 | price: 'FREE' 9 | -------------------------------------------------------------------------------- /src/constants/safe-types.js: -------------------------------------------------------------------------------- 1 | import { HAS_WINDOW_SUPPORT, WINDOW } from './env' 2 | 3 | /* istanbul ignore next */ 4 | export const Element = HAS_WINDOW_SUPPORT ? WINDOW.Element : class Element extends Object {} 5 | 6 | /* istanbul ignore next */ 7 | export const HTMLElement = HAS_WINDOW_SUPPORT 8 | ? WINDOW.HTMLElement 9 | : class HTMLElement extends Element {} 10 | 11 | /* istanbul ignore next */ 12 | export const SVGElement = HAS_WINDOW_SUPPORT 13 | ? WINDOW.SVGElement 14 | : class SVGElement extends Element {} 15 | 16 | /* istanbul ignore next */ 17 | export const File = HAS_WINDOW_SUPPORT ? WINDOW.File : class File extends Object {} 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature request 3 | about: Suggest an idea for this project. 4 | --- 5 | 6 | ### Is your feature request related to a problem? Please describe... 7 | 8 | A clear and concise description of what the problem is. 9 | 10 | ### Describe the solution you'd like 11 | 12 | A clear and concise description of what you want to happen. 13 | 14 | ### Describe alternatives you've considered 15 | 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | ### Additional context 19 | 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/components/modal/helpers/bv-modal-event.class.js: -------------------------------------------------------------------------------- 1 | import { BvEvent } from '../../../utils/bv-event.class' 2 | import { defineProperties, readonlyDescriptor } from '../../../utils/object' 3 | 4 | class BvModalEvent extends BvEvent { 5 | constructor(type, eventInit = {}) { 6 | super(type, eventInit) 7 | // Freeze our new props as readonly, but leave them enumerable 8 | defineProperties(this, { 9 | trigger: readonlyDescriptor() 10 | }) 11 | } 12 | 13 | static get Defaults() { 14 | return { 15 | ...super.Defaults, 16 | trigger: null 17 | } 18 | } 19 | } 20 | 21 | // Named exports 22 | export { BvModalEvent } 23 | -------------------------------------------------------------------------------- /src/components/skeleton/index.js: -------------------------------------------------------------------------------- 1 | import { pluginFactory } from '../../utils/plugins' 2 | import { BSkeleton } from './skeleton' 3 | import { BSkeletonIcon } from './skeleton-icon' 4 | import { BSkeletonImg } from './skeleton-img' 5 | import { BSkeletonTable } from './skeleton-table' 6 | import { BSkeletonWrapper } from './skeleton-wrapper' 7 | 8 | const SkeletonPlugin = /*#__PURE__*/ pluginFactory({ 9 | components: { 10 | BSkeleton, 11 | BSkeletonIcon, 12 | BSkeletonImg, 13 | BSkeletonTable, 14 | BSkeletonWrapper 15 | } 16 | }) 17 | 18 | export { SkeletonPlugin, BSkeleton, BSkeletonIcon, BSkeletonImg, BSkeletonTable, BSkeletonWrapper } 19 | -------------------------------------------------------------------------------- /src/components/form-select/index.js: -------------------------------------------------------------------------------- 1 | import { BFormSelect } from './form-select' 2 | import { BFormSelectOption } from './form-select-option' 3 | import { BFormSelectOptionGroup } from './form-select-option-group' 4 | import { pluginFactory } from '../../utils/plugins' 5 | 6 | const FormSelectPlugin = /*#__PURE__*/ pluginFactory({ 7 | components: { 8 | BFormSelect, 9 | BFormSelectOption, 10 | BFormSelectOptionGroup, 11 | BSelect: BFormSelect, 12 | BSelectOption: BFormSelectOption, 13 | BSelectOptionGroup: BFormSelectOptionGroup 14 | } 15 | }) 16 | 17 | export { FormSelectPlugin, BFormSelect, BFormSelectOption, BFormSelectOptionGroup } 18 | -------------------------------------------------------------------------------- /src/components/table/helpers/text-selection-active.js: -------------------------------------------------------------------------------- 1 | import { getSel, isElement } from '../../../utils/dom' 2 | 3 | // Helper to determine if a there is an active text selection on the document page 4 | // Used to filter out click events caused by the mouse up at end of selection 5 | // 6 | // Accepts an element as only argument to test to see if selection overlaps or is 7 | // contained within the element 8 | export const textSelectionActive = (el = document) => { 9 | const sel = getSel() 10 | return sel && sel.toString().trim() !== '' && sel.containsNode && isElement(el) 11 | ? /* istanbul ignore next */ sel.containsNode(el, true) 12 | : false 13 | } 14 | -------------------------------------------------------------------------------- /src/mixins/card.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../vue' 2 | import { NAME_CARD } from '../constants/components' 3 | import { PROP_TYPE_STRING } from '../constants/props' 4 | import { makeProp, makePropsConfigurable } from '../utils/props' 5 | 6 | // --- Props --- 7 | 8 | export const props = makePropsConfigurable( 9 | { 10 | bgVariant: makeProp(PROP_TYPE_STRING), 11 | borderVariant: makeProp(PROP_TYPE_STRING), 12 | tag: makeProp(PROP_TYPE_STRING, 'div'), 13 | textVariant: makeProp(PROP_TYPE_STRING) 14 | }, 15 | NAME_CARD 16 | ) 17 | 18 | // --- Mixin --- 19 | 20 | // @vue/component 21 | export const cardMixin = Vue.extend({ 22 | props 23 | }) 24 | -------------------------------------------------------------------------------- /src/_utilities.scss: -------------------------------------------------------------------------------- 1 | // --- BootstrapVue utility / helper classes --- 2 | 3 | $bv-utility-classes-defined: false !default; 4 | 5 | // Make sure to include these style definitions only once 6 | @if $bv-utility-classes-defined == false { 7 | $bv-utility-classes-defined: true; 8 | 9 | // No focus outline helper (use sparingly) 10 | .bv-no-focus-ring:focus { 11 | outline: none; 12 | } 13 | 14 | // Create `.bv-d--down-none` helper classes 15 | @each $breakpoint in map-keys($grid-breakpoints) { 16 | @include media-breakpoint-down($breakpoint) { 17 | .bv-d-#{$breakpoint}-down-none { 18 | display: none !important; 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/components/form/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Form 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const FormPlugin: BvPlugin 9 | 10 | // Component: b-form 11 | export declare class BForm extends BvComponent {} 12 | 13 | // Component: b-form-text 14 | export declare class BFormText extends BvComponent {} 15 | 16 | // Component: b-form-invalid-feedback 17 | export declare class BFormInvalidFeedback extends BvComponent {} 18 | 19 | // Component: b-form-valid-feedback 20 | export declare class BFormValidFeedback extends BvComponent {} 21 | 22 | // Component: b-form-datalist 23 | export declare class BFormDatalist extends BvComponent {} 24 | -------------------------------------------------------------------------------- /src/utils/model.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../vue' 2 | import { EVENT_NAME_INPUT } from '../constants/events' 3 | import { PROP_TYPE_ANY } from '../constants/props' 4 | import { makeProp } from './props' 5 | 6 | export const makeModelMixin = ( 7 | prop, 8 | { 9 | type = PROP_TYPE_ANY, 10 | defaultValue = undefined, 11 | validator = undefined, 12 | event = EVENT_NAME_INPUT 13 | } = {} 14 | ) => { 15 | const props = { 16 | [prop]: makeProp(type, defaultValue, validator) 17 | } 18 | 19 | // @vue/component 20 | const mixin = Vue.extend({ 21 | model: { 22 | prop, 23 | event 24 | }, 25 | props 26 | }) 27 | 28 | return { mixin, props, prop, event } 29 | } 30 | -------------------------------------------------------------------------------- /src/components/skeleton/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Skeleton 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const SkeletonPlugin: BvPlugin 9 | 10 | // Component: b-skeleton 11 | export declare class BSkeleton extends BvComponent {} 12 | 13 | // Component: b-skeleton-icon 14 | export declare class BSkeletonIcon extends BvComponent {} 15 | 16 | // Component: b-skeleton-img 17 | export declare class BSkeletonImg extends BvComponent {} 18 | 19 | // Component: b-skeleton-table 20 | export declare class BSkeletonTable extends BvComponent {} 21 | 22 | // Component: b-skeleton-wrapper 23 | export declare class BSkeletonWrapper extends BvComponent {} 24 | -------------------------------------------------------------------------------- /src/components/pagination/_pagination.scss: -------------------------------------------------------------------------------- 1 | // and require the helper utility classes 2 | @import "../../utilities"; 3 | 4 | $bv-pagination-classes-defined: false !default; 5 | 6 | // Make sure to include these style definitions only once 7 | @if $bv-pagination-classes-defined == false { 8 | $bv-pagination-classes-defined: true; 9 | 10 | // Pagination pill style 11 | .b-pagination-pills { 12 | .page-item { 13 | .page-link { 14 | border-radius: 50rem !important; 15 | margin-left: 0.25rem; 16 | line-height: 1; 17 | } 18 | 19 | &:first-child { 20 | .page-link { 21 | margin-left: 0; 22 | } 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /docs/static/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/components/time/index.d.ts: -------------------------------------------------------------------------------- 1 | // --- Time --- 2 | import Vue from 'vue' 3 | import { BvPlugin, BvComponent } from '../../' 4 | 5 | // Plugin 6 | export declare const TimePlugin: BvPlugin 7 | 8 | // Component: b-time 9 | export declare class BTime extends BvComponent { 10 | focus: () => void 11 | blur: () => void 12 | } 13 | 14 | // --- Interfaces --- 15 | 16 | // Time context event object 17 | export interface BvTimeCtxEvent { 18 | readonly formatted: string 19 | readonly value: string 20 | readonly hours: number | null 21 | readonly minutes: number | null 22 | readonly seconds: number | null 23 | readonly hourCycle: string 24 | readonly hour12: boolean 25 | readonly locale: string 26 | readonly isRtl: boolean 27 | } 28 | -------------------------------------------------------------------------------- /src/directives/index.js: -------------------------------------------------------------------------------- 1 | import { pluginFactory } from '../utils/plugins' 2 | 3 | import { VBHoverPlugin } from './hover' 4 | import { VBModalPlugin } from './modal' 5 | import { VBPopoverPlugin } from './popover' 6 | import { VBScrollspyPlugin } from './scrollspy' 7 | import { VBTogglePlugin } from './toggle' 8 | import { VBTooltipPlugin } from './tooltip' 9 | import { VBVisiblePlugin } from './visible' 10 | 11 | // Main plugin for installing all directive plugins 12 | export const directivesPlugin = /*#__PURE__*/ pluginFactory({ 13 | plugins: { 14 | VBHoverPlugin, 15 | VBModalPlugin, 16 | VBPopoverPlugin, 17 | VBScrollspyPlugin, 18 | VBTogglePlugin, 19 | VBTooltipPlugin, 20 | VBVisiblePlugin 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /src/components/input-group/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // InputGroup 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const InputGroupPlugin: BvPlugin 9 | 10 | // Component: b-input-group 11 | export declare class BInputGroup extends BvComponent {} 12 | 13 | // Component: b-input-group-append 14 | export declare class BInputGroupAppend extends BvComponent {} 15 | 16 | // Component: b-input-group-prepend 17 | export declare class BInputGroupPrepend extends BvComponent {} 18 | 19 | // Component: b-input-group-text 20 | export declare class BInputGroupText extends BvComponent {} 21 | 22 | // Component: b-input-group-addon 23 | export declare class BInputGroupAddon extends BvComponent {} 24 | -------------------------------------------------------------------------------- /src/components/nav/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Nav 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const NavPlugin: BvPlugin 9 | 10 | // Component: b-nav 11 | export declare class BNav extends BvComponent {} 12 | 13 | // Component: b-nav-form 14 | export declare class BNavForm extends BvComponent {} 15 | 16 | // Component: b-nav-item 17 | export declare class BNavItem extends BvComponent {} 18 | 19 | // Component: b-nav-item-dropdown 20 | export declare class BNavItemDropdown extends BvComponent { 21 | // Public methods 22 | show: () => void 23 | hide: (refocus?: boolean) => void 24 | } 25 | 26 | // Component: b-nav-text 27 | export declare class BNavText extends BvComponent {} 28 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | | Version | Supported | 6 | | ------- | ------------------ | 7 | | 2.x | :white_check_mark: | 8 | | 1.x | :x: | 9 | | < 1.0 | :x: | 10 | 11 | ## Reporting a Vulnerability 12 | 13 | The BootstrapVue team takes security issues very seriously. We appreciate your efforts to 14 | responsibly disclose your findings, and will make every effort to acknowledge your contributions. 15 | 16 | To report a security issue, email 17 | [bootstrapvue.js@gmail.com](mailto:security@bootstrapvue.js@gmail.com) and include the word 18 | "SECURITY" in the subject line. 19 | 20 | We'll endeavor to respond quickly, and will keep you updated throughout the process. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/DOCS_ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 📖 Documentation issue 3 | about: Help improve our docs. 4 | --- 5 | 6 | ### Documentation issue 7 | 8 | 9 | 10 | - [ ] Reporting a typo 11 | - [ ] Reporting a documentation bug 12 | - [ ] Documentation improvement 13 | - [ ] Documentation feedback 14 | 15 | 19 | 20 | ### Is there a specific documentation page you are reporting? 21 | 22 | Enter the URL or documentation section here. 23 | 24 | ### Additional context or description 25 | 26 | Provide any additional details here as needed. 27 | -------------------------------------------------------------------------------- /src/components/table/th.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../../vue' 2 | import { NAME_TH } from '../../constants/components' 3 | import { makePropsConfigurable } from '../../utils/props' 4 | import { BTd, props as BTdProps } from './td' 5 | 6 | // --- Props --- 7 | 8 | export const props = makePropsConfigurable(BTdProps, NAME_TH) 9 | 10 | // --- Main component --- 11 | 12 | // TODO: 13 | // In Bootstrap v5, we won't need "sniffing" as table element variants properly inherit 14 | // to the child elements, so this can be converted to a functional component 15 | // @vue/component 16 | export const BTh = /*#__PURE__*/ Vue.extend({ 17 | name: NAME_TH, 18 | extends: BTd, 19 | props, 20 | computed: { 21 | tag() { 22 | return 'th' 23 | } 24 | } 25 | }) 26 | -------------------------------------------------------------------------------- /docs/content/themes/argon-dashboard-pro.yaml: -------------------------------------------------------------------------------- 1 | title: 'BootstrapVue Argon Dashboard PRO' 2 | type: 'dashboard' 3 | category: 'Admin & Dashboard' 4 | img: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/bootstrap-vue-argon-dashboard-pro/opt_badp_thumbnail.jpg' 5 | href: 'https://www.creative-tim.com/product/bootstrap-vue-argon-dashboard-pro?partner=134895' 6 | description: 'BootstrapVue Argon Dashboard PRO is a completely new product built on our newest re-built from scratch framework structure that is meant to make our products more intuitive, more adaptive and, needless to say, so much easier to customize. Let Argon amaze you with its cool features and build tools and get your project to a whole new level.' 7 | provider: 'Creative Tim' 8 | price: '$89.00' 9 | -------------------------------------------------------------------------------- /src/components/input-group/index.js: -------------------------------------------------------------------------------- 1 | import { BInputGroup } from './input-group' 2 | import { BInputGroupAddon } from './input-group-addon' 3 | import { BInputGroupPrepend } from './input-group-prepend' 4 | import { BInputGroupAppend } from './input-group-append' 5 | import { BInputGroupText } from './input-group-text' 6 | import { pluginFactory } from '../../utils/plugins' 7 | 8 | const InputGroupPlugin = /*#__PURE__*/ pluginFactory({ 9 | components: { 10 | BInputGroup, 11 | BInputGroupAddon, 12 | BInputGroupPrepend, 13 | BInputGroupAppend, 14 | BInputGroupText 15 | } 16 | }) 17 | 18 | export { 19 | InputGroupPlugin, 20 | BInputGroup, 21 | BInputGroupAddon, 22 | BInputGroupPrepend, 23 | BInputGroupAppend, 24 | BInputGroupText 25 | } 26 | -------------------------------------------------------------------------------- /docs/components/reload.js: -------------------------------------------------------------------------------- 1 | import Section from './section' 2 | 3 | // @vue/component 4 | export default { 5 | name: 'BVReload', 6 | render(h) { 7 | const $heading = h('h1', [ 8 | h('span', { staticClass: 'bd-content-title' }, 'Updated documentation') 9 | ]) 10 | const $lead = h( 11 | 'p', 12 | { staticClass: 'lead' }, 13 | 'Updated documentation is available. Please reload.' 14 | ) 15 | const $button = h( 16 | 'b-button', 17 | { 18 | props: { variant: 'primary' }, 19 | on: { 20 | click: () => { 21 | window.location.reload(true) 22 | } 23 | } 24 | }, 25 | 'Reload page' 26 | ) 27 | return h(Section, [$heading, $lead, h('p', [$button])]) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/components/card/card-text.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_CARD_TEXT } from '../../constants/components' 3 | import { PROP_TYPE_STRING } from '../../constants/props' 4 | import { makeProp, makePropsConfigurable } from '../../utils/props' 5 | 6 | // --- Props --- 7 | 8 | export const props = makePropsConfigurable( 9 | { 10 | textTag: makeProp(PROP_TYPE_STRING, 'p') 11 | }, 12 | NAME_CARD_TEXT 13 | ) 14 | 15 | // --- Main component --- 16 | 17 | // @vue/component 18 | export const BCardText = /*#__PURE__*/ Vue.extend({ 19 | name: NAME_CARD_TEXT, 20 | functional: true, 21 | props, 22 | render(h, { props, data, children }) { 23 | return h(props.textTag, mergeData(data, { staticClass: 'card-text' }), children) 24 | } 25 | }) 26 | -------------------------------------------------------------------------------- /src/components/media/media-body.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_MEDIA_BODY } from '../../constants/components' 3 | import { PROP_TYPE_STRING } from '../../constants/props' 4 | import { makeProp, makePropsConfigurable } from '../../utils/props' 5 | 6 | // --- Props --- 7 | 8 | export const props = makePropsConfigurable( 9 | { 10 | tag: makeProp(PROP_TYPE_STRING, 'div') 11 | }, 12 | NAME_MEDIA_BODY 13 | ) 14 | 15 | // --- Main component --- 16 | 17 | // @vue/component 18 | export const BMediaBody = /*#__PURE__*/ Vue.extend({ 19 | name: NAME_MEDIA_BODY, 20 | functional: true, 21 | props, 22 | render(h, { props, data, children }) { 23 | return h(props.tag, mergeData(data, { staticClass: 'media-body' }), children) 24 | } 25 | }) 26 | -------------------------------------------------------------------------------- /src/components/nav/index.js: -------------------------------------------------------------------------------- 1 | import { BNav } from './nav' 2 | import { BNavItem } from './nav-item' 3 | import { BNavText } from './nav-text' 4 | import { BNavForm } from './nav-form' 5 | import { BNavItemDropdown } from './nav-item-dropdown' 6 | import { DropdownPlugin } from '../dropdown' 7 | import { pluginFactory } from '../../utils/plugins' 8 | 9 | const NavPlugin = /*#__PURE__*/ pluginFactory({ 10 | components: { 11 | BNav, 12 | BNavItem, 13 | BNavText, 14 | BNavForm, 15 | BNavItemDropdown, 16 | BNavItemDd: BNavItemDropdown, 17 | BNavDropdown: BNavItemDropdown, 18 | BNavDd: BNavItemDropdown 19 | }, 20 | plugins: { 21 | DropdownPlugin 22 | } 23 | }) 24 | 25 | export { NavPlugin, BNav, BNavItem, BNavText, BNavForm, BNavItemDropdown } 26 | -------------------------------------------------------------------------------- /src/components/table/helpers/mixin-colgroup.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../../../vue' 2 | import { SLOT_NAME_TABLE_COLGROUP } from '../../../constants/slots' 3 | 4 | // --- Props --- 5 | 6 | export const props = {} 7 | 8 | // --- Mixin --- 9 | 10 | // @vue/component 11 | export const colgroupMixin = Vue.extend({ 12 | methods: { 13 | renderColgroup() { 14 | const { computedFields: fields } = this 15 | const h = this.$createElement 16 | 17 | let $colgroup = h() 18 | if (this.hasNormalizedSlot(SLOT_NAME_TABLE_COLGROUP)) { 19 | $colgroup = h('colgroup', { key: 'colgroup' }, [ 20 | this.normalizeSlot(SLOT_NAME_TABLE_COLGROUP, { columns: fields.length, fields }) 21 | ]) 22 | } 23 | 24 | return $colgroup 25 | } 26 | } 27 | }) 28 | -------------------------------------------------------------------------------- /src/components/navbar/index.js: -------------------------------------------------------------------------------- 1 | import { BNavbar } from './navbar' 2 | import { BNavbarNav } from './navbar-nav' 3 | import { BNavbarBrand } from './navbar-brand' 4 | import { BNavbarToggle } from './navbar-toggle' 5 | import { NavPlugin } from '../nav' 6 | import { CollapsePlugin } from '../collapse' 7 | import { DropdownPlugin } from '../dropdown' 8 | import { pluginFactory } from '../../utils/plugins' 9 | 10 | const NavbarPlugin = /*#__PURE__*/ pluginFactory({ 11 | components: { 12 | BNavbar, 13 | BNavbarNav, 14 | BNavbarBrand, 15 | BNavbarToggle, 16 | BNavToggle: BNavbarToggle 17 | }, 18 | plugins: { 19 | NavPlugin, 20 | CollapsePlugin, 21 | DropdownPlugin 22 | } 23 | }) 24 | 25 | export { NavbarPlugin, BNavbar, BNavbarNav, BNavbarBrand, BNavbarToggle } 26 | -------------------------------------------------------------------------------- /src/utils/array.js: -------------------------------------------------------------------------------- 1 | import { isFunction } from './inspect' 2 | 3 | // --- Static --- 4 | 5 | export const from = (...args) => Array.from(...args) 6 | 7 | // --- Instance --- 8 | 9 | export const arrayIncludes = (array, value) => array.indexOf(value) !== -1 10 | export const concat = (...args) => Array.prototype.concat.apply([], args) 11 | 12 | // --- Utilities --- 13 | 14 | export const createArray = (length, fillFn) => { 15 | const mapFn = isFunction(fillFn) ? fillFn : () => fillFn 16 | return Array.apply(null, { length }).map(mapFn) 17 | } 18 | 19 | export const flatten = array => array.reduce((result, item) => concat(result, item), []) 20 | 21 | export const flattenDeep = array => 22 | array.reduce((result, item) => concat(result, Array.isArray(item) ? flattenDeep(item) : item), []) 23 | -------------------------------------------------------------------------------- /src/utils/number.js: -------------------------------------------------------------------------------- 1 | // Number utilities 2 | 3 | // Converts a value (string, number, etc.) to an integer number 4 | // Assumes radix base 10 5 | export const toInteger = (value, defaultValue = NaN) => { 6 | const integer = parseInt(value, 10) 7 | return isNaN(integer) ? defaultValue : integer 8 | } 9 | 10 | // Converts a value (string, number, etc.) to a number 11 | export const toFloat = (value, defaultValue = NaN) => { 12 | const float = parseFloat(value) 13 | return isNaN(float) ? defaultValue : float 14 | } 15 | 16 | // Converts a value (string, number, etc.) to a string 17 | // representation with `precision` digits after the decimal 18 | // Returns the string 'NaN' if the value cannot be converted 19 | export const toFixed = (val, precision) => toFloat(val).toFixed(toInteger(precision, 0)) 20 | -------------------------------------------------------------------------------- /docs/utils/marked-loader.js: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/peerigon/markdown-loader/ 2 | // Converts markdown files into HTML format 3 | // Config options are passed directly to marked 4 | // - https://marked.js.org/#/USING_ADVANCED.md#options 5 | 6 | const marked = require('marked') 7 | const { getOptions } = require('loader-utils') 8 | 9 | module.exports = function(markdown) { 10 | // merge params and default config 11 | const options = getOptions(this) 12 | // Make results cacheable 13 | this.cacheable() 14 | // Pass our options 15 | marked.setOptions(options) 16 | // Return the converted file as HTML 17 | const html = marked(markdown) || '' 18 | // Mark certain elements as translate="no" 19 | return html.replace(/<(kbd|code|samp)>/gi, '<$1 class="notranslate" translate="no">') 20 | } 21 | -------------------------------------------------------------------------------- /src/components/aspect/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bootstrap-vue/aspect", 3 | "version": "1.0.0", 4 | "meta": { 5 | "title": "Aspect", 6 | "version": "2.9.0", 7 | "description": "The `` component can be used to maintain a minimum responsive aspect ratio for content.", 8 | "components": [ 9 | { 10 | "component": "BAspect", 11 | "props": [ 12 | { 13 | "prop": "aspect", 14 | "description": "Aspect as a width to height numeric ratio (such as `1.5`) or `width:height` string (such as '16:9')" 15 | } 16 | ], 17 | "slots": [ 18 | { 19 | "name": "default", 20 | "description": "Content to place in the aspect" 21 | } 22 | ] 23 | } 24 | ] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/components/calendar/index.d.ts: -------------------------------------------------------------------------------- 1 | // --- Calendar --- 2 | import Vue from 'vue' 3 | import { BvPlugin, BvComponent } from '../../' 4 | 5 | // Plugin 6 | export declare const CalendarPlugin: BvPlugin 7 | 8 | // Component: b-calendar 9 | export declare class BCalendar extends BvComponent { 10 | focus: () => void 11 | blur: () => void 12 | } 13 | 14 | // --- Interfaces --- 15 | 16 | // Calendar context event object 17 | export interface BcCalendarCtxObject { 18 | readonly selectedFormatted: string 19 | readonly selectedYMD: string 20 | readonly selectedDate: Date | null 21 | readonly activeFormatted: string 22 | readonly activeYMD: string 23 | readonly activeDate: Date | null 24 | readonly disabled: boolean 25 | readonly locale: string 26 | readonly calendarLocale: string 27 | readonly rtl: boolean 28 | } 29 | -------------------------------------------------------------------------------- /src/components/layout/form-row.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_FORM_ROW } from '../../constants/components' 3 | import { PROP_TYPE_STRING } from '../../constants/props' 4 | import { makeProp, makePropsConfigurable } from '../../utils/props' 5 | 6 | // --- Props --- 7 | 8 | export const props = makePropsConfigurable( 9 | { 10 | tag: makeProp(PROP_TYPE_STRING, 'div') 11 | }, 12 | NAME_FORM_ROW 13 | ) 14 | 15 | // --- Main component --- 16 | 17 | // @vue/component 18 | export const BFormRow = /*#__PURE__*/ Vue.extend({ 19 | name: NAME_FORM_ROW, 20 | functional: true, 21 | props, 22 | render(h, { props, data, children }) { 23 | return h( 24 | props.tag, 25 | mergeData(data, { 26 | staticClass: 'form-row' 27 | }), 28 | children 29 | ) 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /.versionrc: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "postchangelog": "./node_modules/.bin/prettier --write CHANGELOG.md" 4 | }, 5 | "skip": { 6 | "commit": true, 7 | "tag": true 8 | }, 9 | "types": [ 10 | { 11 | "type": "fix", 12 | "section": "Bug Fixes" 13 | }, 14 | { 15 | "type": "feat", 16 | "section": "Features" 17 | }, 18 | { 19 | "type": "perf", 20 | "section": "Performance" 21 | }, 22 | { 23 | "type": "docs", 24 | "hidden": true 25 | }, 26 | { 27 | "type": "style", 28 | "hidden": true 29 | }, 30 | { 31 | "type": "refactor", 32 | "hidden": true 33 | }, 34 | { 35 | "type": "chore", 36 | "hidden": true 37 | }, 38 | { 39 | "type": "test", 40 | "hidden": true 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /src/components/index.scss: -------------------------------------------------------------------------------- 1 | @import "avatar/index"; 2 | @import "calendar/index"; 3 | @import "card/index"; 4 | @import "dropdown/index"; 5 | @import "form-checkbox/index"; 6 | @import "form-datepicker/index"; 7 | @import "form-file/index"; 8 | @import "form-input/index"; 9 | @import "form-radio/index"; 10 | @import "form-rating/index"; 11 | @import "form-spinbutton/index"; 12 | @import "form-tags/index"; 13 | @import "form-timepicker/index"; 14 | @import "input-group/index"; 15 | @import "media/index"; 16 | @import "modal/index"; 17 | @import "nav/index"; 18 | @import "navbar/index"; 19 | @import "pagination-nav/index"; 20 | @import "pagination/index"; 21 | @import "popover/index"; 22 | @import "sidebar/index"; 23 | @import "skeleton/index"; 24 | @import "table/index"; 25 | @import "time/index"; 26 | @import "toast/index"; 27 | @import "tooltip/index"; 28 | -------------------------------------------------------------------------------- /src/directives/visible/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bootstrap-vue/v-b-visible", 3 | "version": "0.0.0", 4 | "meta": { 5 | "title": "Visible", 6 | "description": "The `v-b-visible` directive allows you to react when an element becomes visible in the viewport.", 7 | "directive": "VBVisible", 8 | "version": "2.1.0", 9 | "expression": [ 10 | "Function" 11 | ], 12 | "modifiers": [ 13 | { 14 | "name": "once", 15 | "description": "Only calls the callback once when the element becomes visible in the viewport" 16 | }, 17 | { 18 | "name": "{###}", 19 | "pattern": "[0-9]+", 20 | "description": "An offset value in pixels (where `{###}` is the number of pixels) relative to the viewport, defaults to 0. Negative values allowed" 21 | } 22 | ] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/directives/toggle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bootstrap-vue/v-b-toggle", 3 | "version": "1.0.0", 4 | "meta": { 5 | "title": "Toggle", 6 | "description": "A light-weight directive for toggling visibility state for collapses and sidebars by ID. It automatically handles the accessibility attributes on the trigger element.", 7 | "directive": "VBToggle", 8 | "expression": [ 9 | "String", 10 | "Array" 11 | ], 12 | "arg": { 13 | "version": "2.14.0", 14 | "pattern": "[a-zA-Z][a-zA-Z0-9_\\-]*", 15 | "description": "ID of component to toggle", 16 | "required": false 17 | }, 18 | "modifiers": [ 19 | { 20 | "name": "{componentId}", 21 | "pattern": "[a-zA-Z][a-zA-Z0-9_\\-]*", 22 | "description": "ID of component to toggle" 23 | } 24 | ] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/components/popover/helpers/bv-popover.js: -------------------------------------------------------------------------------- 1 | // Popover "Class" (Built as a renderless Vue instance) 2 | // Inherits from BVTooltip 3 | // 4 | // Handles trigger events, etc. 5 | // Instantiates template on demand 6 | 7 | import { Vue } from '../../../vue' 8 | import { NAME_POPOVER_HELPER } from '../../../constants/components' 9 | import { BVTooltip } from '../../tooltip/helpers/bv-tooltip' 10 | import { BVPopoverTemplate } from './bv-popover-template' 11 | 12 | // @vue/component 13 | export const BVPopover = /*#__PURE__*/ Vue.extend({ 14 | name: NAME_POPOVER_HELPER, 15 | extends: BVTooltip, 16 | computed: { 17 | // Overwrites BVTooltip 18 | templateType() { 19 | return 'popover' 20 | } 21 | }, 22 | methods: { 23 | getTemplate() { 24 | // Overwrites BVTooltip 25 | return BVPopoverTemplate 26 | } 27 | } 28 | }) 29 | -------------------------------------------------------------------------------- /src/components/form-radio/form-radio-group.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../../vue' 2 | import { NAME_FORM_RADIO_GROUP } from '../../constants/components' 3 | import { makePropsConfigurable } from '../../utils/props' 4 | import { 5 | formRadioCheckGroupMixin, 6 | props as formRadioCheckGroupProps 7 | } from '../../mixins/form-radio-check-group' 8 | 9 | // --- Props --- 10 | 11 | export const props = makePropsConfigurable(formRadioCheckGroupProps, NAME_FORM_RADIO_GROUP) 12 | 13 | // --- Main component --- 14 | 15 | // @vue/component 16 | export const BFormRadioGroup = /*#__PURE__*/ Vue.extend({ 17 | name: NAME_FORM_RADIO_GROUP, 18 | mixins: [formRadioCheckGroupMixin], 19 | provide() { 20 | return { 21 | bvRadioGroup: this 22 | } 23 | }, 24 | props, 25 | computed: { 26 | isRadioGroup() { 27 | return true 28 | } 29 | } 30 | }) 31 | -------------------------------------------------------------------------------- /src/components/badge/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bootstrap-vue/badge", 3 | "version": "1.0.0", 4 | "meta": { 5 | "title": "Badge", 6 | "description": "Small and adaptive tag for adding context to just about any content.", 7 | "components": [ 8 | { 9 | "component": "BBadge", 10 | "props": [ 11 | { 12 | "prop": "pill", 13 | "description": "When set to 'true', renders the badge in pill style" 14 | }, 15 | { 16 | "prop": "variant", 17 | "description": "Applies one of the Bootstrap theme color variants to the component" 18 | } 19 | ], 20 | "slots": [ 21 | { 22 | "name": "default", 23 | "description": "Content to place in the badge" 24 | } 25 | ] 26 | } 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/icons/iconstack.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../vue' 2 | import { NAME_ICONSTACK } from '../constants/components' 3 | import { omit } from '../utils/object' 4 | import { makePropsConfigurable } from '../utils/props' 5 | import { BVIconBase, props as BVIconBaseProps } from './helpers/icon-base' 6 | 7 | // --- Props --- 8 | 9 | export const props = makePropsConfigurable( 10 | omit(BVIconBaseProps, ['content', 'stacked']), 11 | NAME_ICONSTACK 12 | ) 13 | 14 | // --- Main component --- 15 | 16 | // @vue/component 17 | export const BIconstack = /*#__PURE__*/ Vue.extend({ 18 | name: NAME_ICONSTACK, 19 | functional: true, 20 | props, 21 | render(h, { data, props, children }) { 22 | return h( 23 | BVIconBase, 24 | mergeData(data, { 25 | staticClass: 'b-iconstack', 26 | props 27 | }), 28 | children 29 | ) 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /src/components/input-group/input-group-text.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_INPUT_GROUP_TEXT } from '../../constants/components' 3 | import { PROP_TYPE_STRING } from '../../constants/props' 4 | import { makeProp, makePropsConfigurable } from '../../utils/props' 5 | 6 | // --- Props --- 7 | 8 | export const props = makePropsConfigurable( 9 | { 10 | tag: makeProp(PROP_TYPE_STRING, 'div') 11 | }, 12 | NAME_INPUT_GROUP_TEXT 13 | ) 14 | 15 | // --- Main component --- 16 | 17 | // @vue/component 18 | export const BInputGroupText = /*#__PURE__*/ Vue.extend({ 19 | name: NAME_INPUT_GROUP_TEXT, 20 | functional: true, 21 | props, 22 | render(h, { props, data, children }) { 23 | return h( 24 | props.tag, 25 | mergeData(data, { 26 | staticClass: 'input-group-text' 27 | }), 28 | children 29 | ) 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /src/components/time/_time.scss: -------------------------------------------------------------------------------- 1 | // BTime custom SCSS 2 | 3 | .b-time { 4 | min-width: 150px; 5 | 6 | &[aria-disabled="true"] output, 7 | &[aria-readonly="true"] output, 8 | output.disabled { 9 | background-color: $input-disabled-bg; 10 | opacity: 1; 11 | } 12 | 13 | &[aria-disabled="true"] output { 14 | pointer-events: none; 15 | } 16 | 17 | @at-root { 18 | // Prevent the spinbuttons from reversing order in RTL mode 19 | // as time is always read LTR 20 | [dir="rtl"] & > .d-flex:not(.flex-column) { 21 | flex-direction: row-reverse; 22 | } 23 | } 24 | 25 | .b-time-header { 26 | margin-bottom: 0.5rem; 27 | 28 | output { 29 | padding: 0.25rem; 30 | font-size: 80%; 31 | } 32 | } 33 | 34 | .b-time-footer { 35 | margin-top: 0.5rem; 36 | } 37 | 38 | .b-time-ampm { 39 | margin-left: 0.5rem; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/components/button-group/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bootstrap-vue/button-group", 3 | "version": "1.0.0", 4 | "meta": { 5 | "title": "Button Group", 6 | "description": "Group a series of buttons together on a single line with .", 7 | "components": [ 8 | { 9 | "component": "BButtonGroup", 10 | "description": "Group a series of buttons together on a single line", 11 | "aliases": [ 12 | "BBtnGroup" 13 | ], 14 | "props": [ 15 | { 16 | "prop": "vertical", 17 | "description": "When set, rendered the button group in vertical mode" 18 | } 19 | ], 20 | "slots": [ 21 | { 22 | "name": "default", 23 | "description": "Content (buttons) to place in the button group" 24 | } 25 | ] 26 | } 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/components/breadcrumb/breadcrumb-item.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_BREADCRUMB_ITEM } from '../../constants/components' 3 | import { makePropsConfigurable } from '../../utils/props' 4 | import { BBreadcrumbLink, props as BBreadcrumbLinkProps } from './breadcrumb-link' 5 | 6 | // --- Props --- 7 | 8 | export const props = makePropsConfigurable(BBreadcrumbLinkProps, NAME_BREADCRUMB_ITEM) 9 | 10 | // --- Main component --- 11 | 12 | // @vue/component 13 | export const BBreadcrumbItem = /*#__PURE__*/ Vue.extend({ 14 | name: NAME_BREADCRUMB_ITEM, 15 | functional: true, 16 | props, 17 | render(h, { props, data, children }) { 18 | return h( 19 | 'li', 20 | mergeData(data, { 21 | staticClass: 'breadcrumb-item', 22 | class: { active: props.active } 23 | }), 24 | [h(BBreadcrumbLink, { props }, children)] 25 | ) 26 | } 27 | }) 28 | -------------------------------------------------------------------------------- /src/components/form/index.js: -------------------------------------------------------------------------------- 1 | import { BForm } from './form' 2 | import { BFormDatalist } from './form-datalist' 3 | import { BFormText } from './form-text' 4 | import { BFormInvalidFeedback } from './form-invalid-feedback' 5 | import { BFormValidFeedback } from './form-valid-feedback' 6 | import { BFormRow } from '../layout/form-row' 7 | import { pluginFactory } from '../../utils/plugins' 8 | 9 | const FormPlugin = /*#__PURE__*/ pluginFactory({ 10 | components: { 11 | BForm, 12 | BFormDatalist, 13 | BDatalist: BFormDatalist, 14 | BFormText, 15 | BFormInvalidFeedback, 16 | BFormFeedback: BFormInvalidFeedback, 17 | BFormValidFeedback, 18 | // Added here for convenience 19 | BFormRow 20 | } 21 | }) 22 | 23 | // BFormRow is not exported here as a named export, as it is exported by Layout 24 | export { FormPlugin, BForm, BFormDatalist, BFormText, BFormInvalidFeedback, BFormValidFeedback } 25 | -------------------------------------------------------------------------------- /src/utils/locale.js: -------------------------------------------------------------------------------- 1 | // Localization utilities 2 | import { RX_STRIP_LOCALE_MODS } from '../constants/regex' 3 | import { arrayIncludes } from './array' 4 | import { toString } from './string' 5 | 6 | // Languages that are RTL 7 | const RTL_LANGS = [ 8 | 'ar', 9 | 'az', 10 | 'ckb', 11 | 'fa', 12 | 'he', 13 | 'ks', 14 | 'lrc', 15 | 'mzn', 16 | 'ps', 17 | 'sd', 18 | 'te', 19 | 'ug', 20 | 'ur', 21 | 'yi' 22 | ].map(locale => locale.toLowerCase()) 23 | 24 | // Returns true if the locale is RTL 25 | export const isLocaleRTL = locale => { 26 | // Determines if the locale is RTL (only single locale supported) 27 | const parts = toString(locale) 28 | .toLowerCase() 29 | .replace(RX_STRIP_LOCALE_MODS, '') 30 | .split('-') 31 | const locale1 = parts.slice(0, 2).join('-') 32 | const locale2 = parts[0] 33 | return arrayIncludes(RTL_LANGS, locale1) || arrayIncludes(RTL_LANGS, locale2) 34 | } 35 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | day: tuesday 8 | time: "12:00" 9 | timezone: Europe/Berlin 10 | reviewers: 11 | - jackmu95 12 | labels: 13 | - "Type: CI" 14 | - "Type: Dependencies" 15 | 16 | - package-ecosystem: npm 17 | directory: "/" 18 | schedule: 19 | interval: "daily" 20 | time: "12:00" 21 | timezone: Europe/Berlin 22 | ignore: 23 | - dependency-name: "bootstrap" 24 | versions: [">=5.0.0"] 25 | - dependency-name: "prettier" 26 | versions: [">1.14.3"] 27 | - dependency-name: "@vue/test-utils" 28 | versions: [">=2.0.0"] 29 | reviewers: 30 | - jackmu95 31 | labels: 32 | - "Type: Dependencies" 33 | versioning-strategy: increase 34 | rebase-strategy: disabled 35 | -------------------------------------------------------------------------------- /src/components/nav/nav-text.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import { BNavText } from './nav-text' 3 | 4 | describe('nav > nav-text', () => { 5 | it('has expected default structure', async () => { 6 | const wrapper = mount(BNavText) 7 | 8 | expect(wrapper.element.tagName).toBe('LI') 9 | expect(wrapper.classes()).toContain('navbar-text') 10 | expect(wrapper.classes().length).toBe(1) 11 | expect(wrapper.text()).toEqual('') 12 | 13 | wrapper.destroy() 14 | }) 15 | 16 | it('renders default slot content', async () => { 17 | const wrapper = mount(BNavText, { 18 | slots: { 19 | default: 'foobar' 20 | } 21 | }) 22 | 23 | expect(wrapper.element.tagName).toBe('LI') 24 | expect(wrapper.classes()).toContain('navbar-text') 25 | expect(wrapper.classes().length).toBe(1) 26 | expect(wrapper.text()).toEqual('foobar') 27 | 28 | wrapper.destroy() 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /src/components/card/card-title.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_CARD_TITLE } from '../../constants/components' 3 | import { PROP_TYPE_STRING } from '../../constants/props' 4 | import { makeProp, makePropsConfigurable } from '../../utils/props' 5 | import { toString } from '../../utils/string' 6 | 7 | // --- Props --- 8 | 9 | export const props = makePropsConfigurable( 10 | { 11 | title: makeProp(PROP_TYPE_STRING), 12 | titleTag: makeProp(PROP_TYPE_STRING, 'h4') 13 | }, 14 | NAME_CARD_TITLE 15 | ) 16 | 17 | // --- Main component --- 18 | 19 | // @vue/component 20 | export const BCardTitle = /*#__PURE__*/ Vue.extend({ 21 | name: NAME_CARD_TITLE, 22 | functional: true, 23 | props, 24 | render(h, { props, data, children }) { 25 | return h( 26 | props.titleTag, 27 | mergeData(data, { 28 | staticClass: 'card-title' 29 | }), 30 | children || toString(props.title) 31 | ) 32 | } 33 | }) 34 | -------------------------------------------------------------------------------- /src/components/table/helpers/mixin-stacked.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../../../vue' 2 | import { PROP_TYPE_BOOLEAN_STRING } from '../../../constants/props' 3 | import { makeProp } from '../../../utils/props' 4 | 5 | // --- Props --- 6 | 7 | export const props = { 8 | stacked: makeProp(PROP_TYPE_BOOLEAN_STRING, false) 9 | } 10 | 11 | // --- Mixin --- 12 | 13 | // @vue/component 14 | export const stackedMixin = Vue.extend({ 15 | props, 16 | computed: { 17 | isStacked() { 18 | const { stacked } = this 19 | // `true` when always stacked, or returns breakpoint specified 20 | return stacked === '' ? true : stacked 21 | }, 22 | isStackedAlways() { 23 | return this.isStacked === true 24 | }, 25 | stackedTableClasses() { 26 | const { isStackedAlways } = this 27 | return { 28 | 'b-table-stacked': isStackedAlways, 29 | [`b-table-stacked-${this.stacked}`]: !isStackedAlways && this.isStacked 30 | } 31 | } 32 | } 33 | }) 34 | -------------------------------------------------------------------------------- /docs/constants.js: -------------------------------------------------------------------------------- 1 | export const BASE_URL = 'https://bootstrap-vue.org' 2 | export const BASE_URL_DEV = 'https://dev.bootstrap-vue.org' 3 | export const NETLIFY_URL = 'https://bootstrap-vue.netlify.app' 4 | 5 | export const GA_TRACKING_ID = 'UA-89526435-1' 6 | 7 | export const TWITTER_HANDLE = '@BootstrapVue' 8 | 9 | // --- Google Webmaster tools google-site-verification codes --- 10 | // bootstrap-vue.js.org 11 | // 12 | export const GWT_JS_ORG = 'H9Mn7ie5Z5KJSjRfsn9nilLn5kFgn7BMEqL_sUhVpzg' 13 | // *.bootstrap-vue.org 14 | // 15 | export const GWT_BV_ORG = '5E0vyG9CXTfY7McIt2aQppkNA6FQ3b0JJRZzhQ16HW4' 16 | // bootstrap-vue.netlify.app (legacy dev site) 17 | // 18 | export const GWT_BV_NETLIFY = 'Ba1MOy9bRa-r-8eLhSTEkayGbGrT3HhbNuqNiY60uzo' 19 | -------------------------------------------------------------------------------- /src/components/card/card-group.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_CARD_GROUP } from '../../constants/components' 3 | import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' 4 | import { makeProp, makePropsConfigurable } from '../../utils/props' 5 | 6 | // --- Props --- 7 | 8 | export const props = makePropsConfigurable( 9 | { 10 | columns: makeProp(PROP_TYPE_BOOLEAN, false), 11 | deck: makeProp(PROP_TYPE_BOOLEAN, false), 12 | tag: makeProp(PROP_TYPE_STRING, 'div') 13 | }, 14 | NAME_CARD_GROUP 15 | ) 16 | 17 | // --- Main component --- 18 | 19 | // @vue/component 20 | export const BCardGroup = /*#__PURE__*/ Vue.extend({ 21 | name: NAME_CARD_GROUP, 22 | functional: true, 23 | props, 24 | render(h, { props, data, children }) { 25 | return h( 26 | props.tag, 27 | mergeData(data, { 28 | class: props.deck ? 'card-deck' : props.columns ? 'card-columns' : 'card-group' 29 | }), 30 | children 31 | ) 32 | } 33 | }) 34 | -------------------------------------------------------------------------------- /docs/assets/vercel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/input-group/input-group-append.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_INPUT_GROUP_APPEND } from '../../constants/components' 3 | import { omit } from '../../utils/object' 4 | import { makePropsConfigurable } from '../../utils/props' 5 | import { BInputGroupAddon, props as BInputGroupAddonProps } from './input-group-addon' 6 | 7 | // --- Props --- 8 | 9 | export const props = makePropsConfigurable( 10 | omit(BInputGroupAddonProps, ['append']), 11 | NAME_INPUT_GROUP_APPEND 12 | ) 13 | 14 | // --- Main component --- 15 | 16 | // @vue/component 17 | export const BInputGroupAppend = /*#__PURE__*/ Vue.extend({ 18 | name: NAME_INPUT_GROUP_APPEND, 19 | functional: true, 20 | props, 21 | render(h, { props, data, children }) { 22 | // Pass all our data down to child, and set `append` to `true` 23 | return h( 24 | BInputGroupAddon, 25 | mergeData(data, { 26 | props: { ...props, append: true } 27 | }), 28 | children 29 | ) 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /src/components/input-group/input-group-prepend.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_INPUT_GROUP_PREPEND } from '../../constants/components' 3 | import { omit } from '../../utils/object' 4 | import { makePropsConfigurable } from '../../utils/props' 5 | import { BInputGroupAddon, props as BInputGroupAddonProps } from './input-group-addon' 6 | 7 | // --- Props --- 8 | 9 | export const props = makePropsConfigurable( 10 | omit(BInputGroupAddonProps, ['append']), 11 | NAME_INPUT_GROUP_PREPEND 12 | ) 13 | 14 | // --- Main component --- 15 | 16 | // @vue/component 17 | export const BInputGroupPrepend = /*#__PURE__*/ Vue.extend({ 18 | name: NAME_INPUT_GROUP_PREPEND, 19 | functional: true, 20 | props, 21 | render(h, { props, data, children }) { 22 | // Pass all our data down to child, and set `append` to `true` 23 | return h( 24 | BInputGroupAddon, 25 | mergeData(data, { 26 | props: { ...props, append: false } 27 | }), 28 | children 29 | ) 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /src/components/form-radio/form-radio.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../../vue' 2 | import { NAME_FORM_RADIO } from '../../constants/components' 3 | import { looseEqual } from '../../utils/loose-equal' 4 | import { makePropsConfigurable } from '../../utils/props' 5 | import { 6 | MODEL_EVENT_NAME, 7 | formRadioCheckMixin, 8 | props as formRadioCheckProps 9 | } from '../../mixins/form-radio-check' 10 | 11 | // --- Props --- 12 | 13 | export const props = makePropsConfigurable(formRadioCheckProps, NAME_FORM_RADIO) 14 | 15 | // --- Main component --- 16 | 17 | // @vue/component 18 | export const BFormRadio = /*#__PURE__*/ Vue.extend({ 19 | name: NAME_FORM_RADIO, 20 | mixins: [formRadioCheckMixin], 21 | inject: { 22 | bvGroup: { 23 | from: 'bvRadioGroup', 24 | default: false 25 | } 26 | }, 27 | props, 28 | watch: { 29 | computedLocalChecked(newValue, oldValue) { 30 | if (!looseEqual(newValue, oldValue)) { 31 | this.$emit(MODEL_EVENT_NAME, newValue) 32 | } 33 | } 34 | } 35 | }) 36 | -------------------------------------------------------------------------------- /src/components/embed/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bootstrap-vue/embed", 3 | "version": "1.0.0", 4 | "meta": { 5 | "title": "Embed", 6 | "description": "Create responsive video or slideshow embeds based on the width of the parent by creating an intrinsic ratio that scales on any device.", 7 | "components": [ 8 | { 9 | "component": "BEmbed", 10 | "props": [ 11 | { 12 | "prop": "aspect", 13 | "description": "Aspect ratio of the embed. Supported values are '16by9', '21by9', '4by3', and '1by1' and are translated to CSS classes. Refer to the docs for more details" 14 | }, 15 | { 16 | "prop": "type", 17 | "description": "Type of embed. Possible values are 'iframe', 'video', 'embed' and 'object'" 18 | } 19 | ], 20 | "slots": [ 21 | { 22 | "name": "default", 23 | "description": "Content to place in the embed" 24 | } 25 | ] 26 | } 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/components/form-select/form-select-option.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_FORM_SELECT_OPTION } from '../../constants/components' 3 | import { PROP_TYPE_ANY, PROP_TYPE_BOOLEAN } from '../../constants/props' 4 | import { makeProp, makePropsConfigurable } from '../../utils/props' 5 | 6 | // --- Props --- 7 | 8 | export const props = makePropsConfigurable( 9 | { 10 | disabled: makeProp(PROP_TYPE_BOOLEAN, false), 11 | value: makeProp(PROP_TYPE_ANY, undefined, true) // Required 12 | }, 13 | NAME_FORM_SELECT_OPTION 14 | ) 15 | 16 | // --- Main component --- 17 | 18 | // @vue/component 19 | export const BFormSelectOption = /*#__PURE__*/ Vue.extend({ 20 | name: NAME_FORM_SELECT_OPTION, 21 | functional: true, 22 | props, 23 | render(h, { props, data, children }) { 24 | const { value, disabled } = props 25 | 26 | return h( 27 | 'option', 28 | mergeData(data, { 29 | attrs: { disabled }, 30 | domProps: { value } 31 | }), 32 | children 33 | ) 34 | } 35 | }) 36 | -------------------------------------------------------------------------------- /src/components/card/index.js: -------------------------------------------------------------------------------- 1 | import { BCard } from './card' 2 | import { BCardHeader } from './card-header' 3 | import { BCardBody } from './card-body' 4 | import { BCardTitle } from './card-title' 5 | import { BCardSubTitle } from './card-sub-title' 6 | import { BCardFooter } from './card-footer' 7 | import { BCardImg } from './card-img' 8 | import { BCardImgLazy } from './card-img-lazy' 9 | import { BCardText } from './card-text' 10 | import { BCardGroup } from './card-group' 11 | import { pluginFactory } from '../../utils/plugins' 12 | 13 | const CardPlugin = /*#__PURE__*/ pluginFactory({ 14 | components: { 15 | BCard, 16 | BCardHeader, 17 | BCardBody, 18 | BCardTitle, 19 | BCardSubTitle, 20 | BCardFooter, 21 | BCardImg, 22 | BCardImgLazy, 23 | BCardText, 24 | BCardGroup 25 | } 26 | }) 27 | 28 | export { 29 | CardPlugin, 30 | BCard, 31 | BCardHeader, 32 | BCardBody, 33 | BCardTitle, 34 | BCardSubTitle, 35 | BCardFooter, 36 | BCardImg, 37 | BCardImgLazy, 38 | BCardText, 39 | BCardGroup 40 | } 41 | -------------------------------------------------------------------------------- /src/mixins/has-listener.js: -------------------------------------------------------------------------------- 1 | // Mixin to determine if an event listener has been registered 2 | // either via `v-on:name` (in the parent) or programmatically 3 | // via `vm.$on('name', ...)` 4 | // See: https://github.com/vuejs/vue/issues/10825 5 | import { Vue } from '../vue' 6 | import { isArray, isUndefined } from '../utils/inspect' 7 | 8 | // @vue/component 9 | export const hasListenerMixin = Vue.extend({ 10 | methods: { 11 | hasListener(name) { 12 | // Only includes listeners registered via `v-on:name` 13 | const $listeners = this.$listeners || {} 14 | // Includes `v-on:name` and `this.$on('name')` registered listeners 15 | // Note this property is not part of the public Vue API, but it is 16 | // the only way to determine if a listener was added via `vm.$on` 17 | const $events = this._events || {} 18 | // Registered listeners in `this._events` are always an array, 19 | // but might be zero length 20 | return !isUndefined($listeners[name]) || (isArray($events[name]) && $events[name].length > 0) 21 | } 22 | } 23 | }) 24 | -------------------------------------------------------------------------------- /docs/components/anchored-heading.js: -------------------------------------------------------------------------------- 1 | import { mergeData } from 'vue-functional-data-merge' 2 | 3 | // @vue/component 4 | export default { 5 | name: 'BVAnchoredHeading', 6 | functional: true, 7 | props: { 8 | id: { 9 | type: String, 10 | default: '' 11 | }, 12 | level: { 13 | type: [Number, String], 14 | default: 2 15 | } 16 | }, 17 | render(h, { props, data, children }) { 18 | const $anchor = h( 19 | 'b-link', 20 | { 21 | staticClass: 'anchorjs-link', 22 | props: { to: { hash: `#${props.id}` } }, 23 | attrs: { 24 | 'aria-labelledby': props.id || null, 25 | 'aria-label': props.id ? null : 'Anchor' 26 | } 27 | }, 28 | [h()] 29 | ) 30 | const $content = h('span', { staticClass: 'bd-content-title' }, [children, $anchor]) 31 | return h( 32 | `h${props.level}`, 33 | mergeData(data, { 34 | staticClass: 'bv-no-focus-ring', 35 | attrs: { 36 | id: props.id, 37 | tabindex: '-1' 38 | } 39 | }), 40 | [$content] 41 | ) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /docs/utils/hljs.js: -------------------------------------------------------------------------------- 1 | import hljs from 'highlight.js/lib/core' 2 | // Import only the languages we need from "highlight.js" 3 | import bash from 'highlight.js/lib/languages/bash' // Includes sh 4 | import css from 'highlight.js/lib/languages/css' 5 | import javascript from 'highlight.js/lib/languages/javascript' 6 | import json from 'highlight.js/lib/languages/json' 7 | import plaintext from 'highlight.js/lib/languages/plaintext' 8 | import scss from 'highlight.js/lib/languages/scss' 9 | import shell from 'highlight.js/lib/languages/shell' 10 | import typescript from 'highlight.js/lib/languages/typescript' 11 | import xml from 'highlight.js/lib/languages/xml' // Includes HTML 12 | 13 | // Register languages 14 | hljs.registerLanguage('bash', bash) 15 | hljs.registerLanguage('css', css) 16 | hljs.registerLanguage('javascript', javascript) 17 | hljs.registerLanguage('json', json) 18 | hljs.registerLanguage('plaintext', plaintext) 19 | hljs.registerLanguage('scss', scss) 20 | hljs.registerLanguage('shell', shell) 21 | hljs.registerLanguage('typescript', typescript) 22 | hljs.registerLanguage('xml', xml) 23 | 24 | export default hljs 25 | -------------------------------------------------------------------------------- /src/components/dropdown/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Dropdown 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const DropdownPlugin: BvPlugin 9 | 10 | // Component: b-dropdown 11 | export declare class BDropdown extends BvComponent { 12 | // Public methods 13 | show: () => void 14 | hide: (refocus?: boolean) => void 15 | } 16 | 17 | // Component: b-dropdown-item 18 | export declare class BDropdownItem extends BvComponent {} 19 | 20 | // Component: b-dropdown-item-button 21 | export declare class BDropdownItemButton extends BvComponent {} 22 | 23 | // Component: b-dropdown-divider 24 | export declare class BDropdownDivider extends BvComponent {} 25 | 26 | // Component: b-dropdown-form 27 | export declare class BDropdownForm extends BvComponent {} 28 | 29 | // Component: b-dropdown-text 30 | export declare class BDropdownText extends BvComponent {} 31 | 32 | // Component: b-dropdown-group 33 | export declare class BDropdownGroup extends BvComponent {} 34 | 35 | // Component: b-dropdown-header 36 | export declare class BDropdownHeader extends BvComponent {} 37 | -------------------------------------------------------------------------------- /src/icons-only.js: -------------------------------------------------------------------------------- 1 | // Icons only build 2 | import { BootstrapVueIcons, IconsPlugin } from './icons' 3 | 4 | const install = BootstrapVueIcons.install 5 | const NAME = BootstrapVueIcons.NAME 6 | 7 | export { 8 | // Installer exported just in case the consumer does not import `default` 9 | // as the plugin in CommonJS build (or does not have interop enabled 10 | // for CommonJS). Both the following will work: 11 | // BootstrapVueIcons = require('bootstrap-vue/dist/bootstrap-vue-icons.common') 12 | // BootstrapVueIcons = require('bootstrap-vue/dist/bootstrap-vue-icons.common').default 13 | // Vue.use(BootstrapVueIcons) 14 | install, 15 | NAME, 16 | // Main BootstrapVueIcons Plugin 17 | BootstrapVueIcons, 18 | IconsPlugin 19 | } 20 | 21 | // 22 | // Export Icon components 23 | // 24 | // export * from './icons' 25 | 26 | export { BIcon } from './icons/icon' 27 | 28 | // This re-export is only a single level deep, which 29 | // Webpack 4 handles correctly when tree shaking 30 | export * from './icons/icons' 31 | 32 | // Default export is the BootstrapVueIcons plugin 33 | export default BootstrapVueIcons 34 | -------------------------------------------------------------------------------- /src/components/dropdown/dropdown-divider.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_DROPDOWN_DIVIDER } from '../../constants/components' 3 | import { PROP_TYPE_STRING } from '../../constants/props' 4 | import { makeProp, makePropsConfigurable } from '../../utils/props' 5 | import { omit } from '../../utils/object' 6 | 7 | // --- Props --- 8 | 9 | export const props = makePropsConfigurable( 10 | { 11 | tag: makeProp(PROP_TYPE_STRING, 'hr') 12 | }, 13 | NAME_DROPDOWN_DIVIDER 14 | ) 15 | 16 | // --- Main component --- 17 | 18 | // @vue/component 19 | export const BDropdownDivider = /*#__PURE__*/ Vue.extend({ 20 | name: NAME_DROPDOWN_DIVIDER, 21 | functional: true, 22 | props, 23 | render(h, { props, data }) { 24 | return h('li', mergeData(omit(data, ['attrs']), { attrs: { role: 'presentation' } }), [ 25 | h(props.tag, { 26 | staticClass: 'dropdown-divider', 27 | attrs: { 28 | ...(data.attrs || {}), 29 | role: 'separator', 30 | 'aria-orientation': 'horizontal' 31 | }, 32 | ref: 'divider' 33 | }) 34 | ]) 35 | } 36 | }) 37 | -------------------------------------------------------------------------------- /src/components/button-toolbar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bootstrap-vue/button-toolbar", 3 | "version": "1.0.0", 4 | "meta": { 5 | "title": "Button Toolbar", 6 | "description": "Group a series of and/or together on a single line, with optional keyboard navigation.", 7 | "components": [ 8 | { 9 | "component": "BButtonToolbar", 10 | "aliases": [ 11 | "BBtnToolbar" 12 | ], 13 | "props": [ 14 | { 15 | "prop": "justify", 16 | "description": "Make the toolbar span the maximum available width, by increasing spacing between the button groups, input groups and dropdowns" 17 | }, 18 | { 19 | "prop": "keyNav", 20 | "description": "When set, enabled keyboard navigation mode for the toolbar. Do not set this prop when the toolbar has inputs" 21 | } 22 | ], 23 | "slots": [ 24 | { 25 | "name": "default", 26 | "description": "Content to place in the button toolbar" 27 | } 28 | ] 29 | } 30 | ] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/components/card/index.d.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Card 3 | // 4 | import Vue from 'vue' 5 | import { BvPlugin, BvComponent } from '../../' 6 | 7 | // Plugin 8 | export declare const CardPlugin: BvPlugin 9 | 10 | // Component: b-card 11 | export declare class BCard extends BvComponent {} 12 | 13 | // Component: b-card-header 14 | export declare class BCardHeader extends BvComponent {} 15 | 16 | // Component: b-card-footer 17 | export declare class BCardFooter extends BvComponent {} 18 | 19 | // Component: b-card-body 20 | export declare class BCardBody extends BvComponent {} 21 | 22 | // Component: b-card-title 23 | export declare class BCardTitle extends BvComponent {} 24 | 25 | // Component: b-card-sub-title 26 | export declare class BCardSubTitle extends BvComponent {} 27 | 28 | // Component: b-card-img 29 | export declare class BCardImg extends BvComponent {} 30 | 31 | // Component: b-card-img-lazy 32 | export declare class BCardImgLazy extends BvComponent {} 33 | 34 | // Component: b-card-text 35 | export declare class BCardText extends BvComponent {} 36 | 37 | // Component: b-card-group 38 | export declare class BCardGroup extends BvComponent {} 39 | -------------------------------------------------------------------------------- /src/components/input-group/_input-group.scss: -------------------------------------------------------------------------------- 1 | $bv-input-group-dropdown-patched: false !default; 2 | 3 | // Make sure to include these style definitions only once 4 | @if $bv-input-group-dropdown-patched == false { 5 | $bv-input-group-dropdown-patched: true; 6 | 7 | // Workaround for https://github.com/bootstrap-vue/bootstrap-vue/issues/1560 8 | // Workaround for https://github.com/bootstrap-vue/bootstrap-vue/issues/2114 */ 9 | // Based on: `~bootstrap/scss/_input-group.scss` 10 | .input-group { 11 | > .input-group-prepend > .btn-group, 12 | > .input-group-append:not(:last-child) > .btn-group, 13 | > .input-group-append:last-child > .btn-group:not(:last-child):not(.dropdown-toggle) { 14 | > .btn { 15 | border-top-right-radius: 0; 16 | border-bottom-right-radius: 0; 17 | } 18 | } 19 | 20 | > .input-group-append > .btn-group, 21 | > .input-group-prepend:not(:first-child) > .btn-group, 22 | > .input-group-prepend:first-child > .btn-group:not(:first-child) { 23 | > .btn { 24 | border-top-left-radius: 0; 25 | border-bottom-left-radius: 0; 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/components/toast/_toaster-transition.scss: -------------------------------------------------------------------------------- 1 | // --- custom transition SCSS --- 2 | 3 | // PortalVue appears to have issues with transition classes on portaled items 4 | 5 | .b-toaster { 6 | &.b-toaster-top-right, 7 | &.b-toaster-top-left, 8 | &.b-toaster-bottom-right, 9 | &.b-toaster-bottom-left { 10 | .b-toast { 11 | &.b-toaster-enter-active, 12 | &.b-toaster-leave-active, 13 | &.b-toaster-move { 14 | transition: transform 0.175s; 15 | } 16 | 17 | &.b-toaster-enter { 18 | } 19 | 20 | &.b-toaster-enter-to, 21 | &.b-toaster-enter-active { 22 | .toast.fade { 23 | // Delay the appearance of the toast until 24 | // the move transition has completed 25 | transition-delay: 0.175s; 26 | } 27 | } 28 | 29 | &.b-toaster-enter-to { 30 | } 31 | 32 | &.b-toaster-leave-active { 33 | position: absolute; 34 | transition-delay: 0.175s; 35 | 36 | .toast.fade { 37 | transition-delay: 0s; 38 | } 39 | } 40 | 41 | &.b-toaster-leave-to { 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/components/card/card-sub-title.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_CARD_SUB_TITLE } from '../../constants/components' 3 | import { PROP_TYPE_STRING } from '../../constants/props' 4 | import { makeProp, makePropsConfigurable } from '../../utils/props' 5 | import { toString } from '../../utils/string' 6 | 7 | // --- Props --- 8 | 9 | export const props = makePropsConfigurable( 10 | { 11 | subTitle: makeProp(PROP_TYPE_STRING), 12 | subTitleTag: makeProp(PROP_TYPE_STRING, 'h6'), 13 | subTitleTextVariant: makeProp(PROP_TYPE_STRING, 'muted') 14 | }, 15 | NAME_CARD_SUB_TITLE 16 | ) 17 | 18 | // --- Main component --- 19 | 20 | // @vue/component 21 | export const BCardSubTitle = /*#__PURE__*/ Vue.extend({ 22 | name: NAME_CARD_SUB_TITLE, 23 | functional: true, 24 | props, 25 | render(h, { props, data, children }) { 26 | return h( 27 | props.subTitleTag, 28 | mergeData(data, { 29 | staticClass: 'card-subtitle', 30 | class: [props.subTitleTextVariant ? `text-${props.subTitleTextVariant}` : null] 31 | }), 32 | children || toString(props.subTitle) 33 | ) 34 | } 35 | }) 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2020 - BootstrapVue 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/components/card/card-title.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import { BCardTitle } from './card-title' 3 | 4 | describe('card-title', () => { 5 | it('default has tag "h4"', async () => { 6 | const wrapper = mount(BCardTitle) 7 | 8 | expect(wrapper.element.tagName).toBe('H4') 9 | 10 | wrapper.destroy() 11 | }) 12 | 13 | it('default has class "card-title"', async () => { 14 | const wrapper = mount(BCardTitle) 15 | 16 | expect(wrapper.classes()).toContain('card-title') 17 | expect(wrapper.classes().length).toBe(1) 18 | 19 | wrapper.destroy() 20 | }) 21 | 22 | it('renders custom tag', async () => { 23 | const wrapper = mount(BCardTitle, { 24 | context: { 25 | props: { titleTag: 'div' } 26 | } 27 | }) 28 | 29 | expect(wrapper.element.tagName).toBe('DIV') 30 | 31 | wrapper.destroy() 32 | }) 33 | 34 | it('has content from default slot', async () => { 35 | const wrapper = mount(BCardTitle, { 36 | slots: { 37 | default: 'bar' 38 | } 39 | }) 40 | 41 | expect(wrapper.text()).toContain('bar') 42 | 43 | wrapper.destroy() 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /src/components/table/helpers/constants.js: -------------------------------------------------------------------------------- 1 | // Constants used by table helpers 2 | 3 | export const FIELD_KEY_CELL_VARIANT = '_cellVariants' 4 | export const FIELD_KEY_ROW_VARIANT = '_rowVariant' 5 | export const FIELD_KEY_SHOW_DETAILS = '_showDetails' 6 | 7 | // Object of item keys that should be ignored for headers and 8 | // stringification and filter events 9 | export const IGNORED_FIELD_KEYS = [ 10 | FIELD_KEY_CELL_VARIANT, 11 | FIELD_KEY_ROW_VARIANT, 12 | FIELD_KEY_SHOW_DETAILS 13 | ].reduce((result, key) => ({ ...result, [key]: true }), {}) 14 | 15 | // Filter CSS selector for click/dblclick/etc. events 16 | // If any of these selectors match the clicked element, we ignore the event 17 | export const EVENT_FILTER = [ 18 | 'a', 19 | 'a *', // Include content inside links 20 | 'button', 21 | 'button *', // Include content inside buttons 22 | 'input:not(.disabled):not([disabled])', 23 | 'select:not(.disabled):not([disabled])', 24 | 'textarea:not(.disabled):not([disabled])', 25 | '[role="link"]', 26 | '[role="link"] *', 27 | '[role="button"]', 28 | '[role="button"] *', 29 | '[tabindex]:not(.disabled):not([disabled])' 30 | ].join(',') 31 | -------------------------------------------------------------------------------- /src/mixins/normalize-slot.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../vue' 2 | import { SLOT_NAME_DEFAULT } from '../constants/slots' 3 | import { hasNormalizedSlot, normalizeSlot } from '../utils/normalize-slot' 4 | import { concat } from '../utils/array' 5 | 6 | // @vue/component 7 | export const normalizeSlotMixin = Vue.extend({ 8 | methods: { 9 | // Returns `true` if the either a `$scopedSlot` or `$slot` exists with the specified name 10 | // `name` can be a string name or an array of names 11 | hasNormalizedSlot( 12 | name = SLOT_NAME_DEFAULT, 13 | scopedSlots = this.$scopedSlots, 14 | slots = this.$slots 15 | ) { 16 | return hasNormalizedSlot(name, scopedSlots, slots) 17 | }, 18 | // Returns an array of rendered VNodes if slot found, otherwise `undefined` 19 | // `name` can be a string name or an array of names 20 | normalizeSlot( 21 | name = SLOT_NAME_DEFAULT, 22 | scope = {}, 23 | scopedSlots = this.$scopedSlots, 24 | slots = this.$slots 25 | ) { 26 | const vNodes = normalizeSlot(name, scope, scopedSlots, slots) 27 | return vNodes ? concat(vNodes) : vNodes 28 | } 29 | } 30 | }) 31 | -------------------------------------------------------------------------------- /src/components/form/form-text.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_FORM_TEXT } from '../../constants/components' 3 | import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' 4 | import { makeProp, makePropsConfigurable } from '../../utils/props' 5 | 6 | // --- Props --- 7 | 8 | export const props = makePropsConfigurable( 9 | { 10 | id: makeProp(PROP_TYPE_STRING), 11 | inline: makeProp(PROP_TYPE_BOOLEAN, false), 12 | tag: makeProp(PROP_TYPE_STRING, 'small'), 13 | textVariant: makeProp(PROP_TYPE_STRING, 'muted') 14 | }, 15 | NAME_FORM_TEXT 16 | ) 17 | 18 | // --- Main component --- 19 | 20 | // @vue/component 21 | export const BFormText = /*#__PURE__*/ Vue.extend({ 22 | name: NAME_FORM_TEXT, 23 | functional: true, 24 | props, 25 | render(h, { props, data, children }) { 26 | return h( 27 | props.tag, 28 | mergeData(data, { 29 | class: { 30 | 'form-text': !props.inline, 31 | [`text-${props.textVariant}`]: props.textVariant 32 | }, 33 | attrs: { 34 | id: props.id 35 | } 36 | }), 37 | children 38 | ) 39 | } 40 | }) 41 | -------------------------------------------------------------------------------- /src/components/form/form.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_FORM } from '../../constants/components' 3 | import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' 4 | import { makeProp, makePropsConfigurable } from '../../utils/props' 5 | 6 | // --- Props --- 7 | 8 | export const props = makePropsConfigurable( 9 | { 10 | id: makeProp(PROP_TYPE_STRING), 11 | inline: makeProp(PROP_TYPE_BOOLEAN, false), 12 | novalidate: makeProp(PROP_TYPE_BOOLEAN, false), 13 | validated: makeProp(PROP_TYPE_BOOLEAN, false) 14 | }, 15 | NAME_FORM 16 | ) 17 | 18 | // --- Main component --- 19 | 20 | // @vue/component 21 | export const BForm = /*#__PURE__*/ Vue.extend({ 22 | name: NAME_FORM, 23 | functional: true, 24 | props, 25 | render(h, { props, data, children }) { 26 | return h( 27 | 'form', 28 | mergeData(data, { 29 | class: { 30 | 'form-inline': props.inline, 31 | 'was-validated': props.validated 32 | }, 33 | attrs: { 34 | id: props.id, 35 | novalidate: props.novalidate 36 | } 37 | }), 38 | children 39 | ) 40 | } 41 | }) 42 | -------------------------------------------------------------------------------- /src/utils/cache.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../vue' 2 | import { cloneDeep } from './clone-deep' 3 | import { looseEqual } from './loose-equal' 4 | import { hasOwnProperty, keys } from './object' 5 | 6 | const isEmpty = value => !value || keys(value).length === 0 7 | 8 | export const makePropWatcher = propName => ({ 9 | handler(newValue, oldValue) { 10 | if (looseEqual(newValue, oldValue)) { 11 | return 12 | } 13 | if (isEmpty(newValue) || isEmpty(oldValue)) { 14 | this[propName] = cloneDeep(newValue) 15 | return 16 | } 17 | for (const key in oldValue) { 18 | if (!hasOwnProperty(newValue, key)) { 19 | this.$delete(this.$data[propName], key) 20 | } 21 | } 22 | for (const key in newValue) { 23 | this.$set(this.$data[propName], key, newValue[key]) 24 | } 25 | } 26 | }) 27 | 28 | export const makePropCacheMixin = (propName, proxyPropName) => 29 | Vue.extend({ 30 | data() { 31 | return { [proxyPropName]: cloneDeep(this[propName]) } 32 | }, 33 | watch: { 34 | // Work around unwanted re-renders: https://github.com/vuejs/vue/issues/10115 35 | [propName]: makePropWatcher(proxyPropName) 36 | } 37 | }) 38 | -------------------------------------------------------------------------------- /static/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/_custom-controls.scss: -------------------------------------------------------------------------------- 1 | // Special styling for some BootstrapVue custom form controls that do 2 | // not have a native HTML input type root element (or tabindex) 3 | // Used by BFormSpinbutton, BFormDatepicker, BFormTimepicker, BTime, BCalendar 4 | .form-control { 5 | // Adds focus styling to the form-control class (via the focus class) 6 | // Specifically when we are using non focusable elements, or when true focus 7 | // is within the `.form-control` element. 8 | // Mimics the `.form-control:focus` styling 9 | &.focus { 10 | color: $input-focus-color; 11 | background-color: $input-focus-bg; 12 | border-color: $input-focus-border-color; 13 | outline: 0; 14 | @if $enable-shadows { 15 | box-shadow: $input-box-shadow, $input-focus-box-shadow; 16 | } @else { 17 | box-shadow: $input-focus-box-shadow; 18 | } 19 | 20 | &.is-valid { 21 | border-color: $form-feedback-valid-color; 22 | box-shadow: 0 0 0 $input-focus-width rgba($form-feedback-valid-color, 0.25); 23 | } 24 | 25 | &.is-invalid { 26 | border-color: $form-feedback-invalid-color; 27 | box-shadow: 0 0 0 $input-focus-width rgba($form-feedback-invalid-color, 0.25); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/utils/stringify-object-values.js: -------------------------------------------------------------------------------- 1 | import { isDate, isObject, isUndefinedOrNull } from './inspect' 2 | import { keys } from './object' 3 | import { toString } from './string' 4 | 5 | // Recursively stringifies the values of an object, space separated, in an 6 | // SSR safe deterministic way (keys are sorted before stringification) 7 | // 8 | // ex: 9 | // { b: 3, c: { z: 'zzz', d: null, e: 2 }, d: [10, 12, 11], a: 'one' } 10 | // becomes 11 | // 'one 3 2 zzz 10 12 11' 12 | // 13 | // Strings are returned as-is 14 | // Numbers get converted to string 15 | // `null` and `undefined` values are filtered out 16 | // Dates are converted to their native string format 17 | export const stringifyObjectValues = value => { 18 | if (isUndefinedOrNull(value)) { 19 | return '' 20 | } 21 | // Arrays are also object, and keys just returns the array indexes 22 | // Date objects we convert to strings 23 | if (isObject(value) && !isDate(value)) { 24 | return keys(value) 25 | .sort() // Sort to prevent SSR issues on pre-rendered sorted tables 26 | .map(k => stringifyObjectValues(value[k])) 27 | .filter(v => !!v) // Ignore empty strings 28 | .join(' ') 29 | } 30 | return toString(value) 31 | } 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG_REPORT_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug report 3 | about: Create a bug report to help us improve. 4 | --- 5 | 6 | ### Describe the bug 7 | 8 | A clear and concise description of what the bug is. 9 | 10 | ### Steps to reproduce the bug 11 | 12 | 1. Go to [...] 13 | 2. Click on [...] 14 | 3. Scroll down to [...] 15 | 4. See error 16 | 17 | ### Expected behavior 18 | 19 | A clear and concise description of what you expected to happen. 20 | 21 | ### Versions 22 | 23 | **Libraries:** 24 | 25 | - BootstrapVue: 2.#.# 26 | - Bootstrap: 4.#.# 27 | - Vue: 2.#.# 28 | 29 | **Environment:** 30 | 31 | - Device: [e.g. Mac or iPhone X] 32 | - OS: [e.g. macOS Mojave or iOS 12] 33 | - Browser: [e.g. Chrome] 34 | - Version: [e.g. 70] 35 | 36 | ### Demo link 37 | 38 | If applicable, add a minimal demo link to help explain your problem. Some options for that are [CodePen](https://codepen.io/), [CodeSandbox](https://codesandbox.io/), [JS Bin](https://jsbin.com/) or [JSFiddle](https://jsfiddle.net/). 39 | 40 | You can also export markup from the [Online Playground](https://bootstrap-vue.org/play) to _CodePen_, _CodeSandbox_ or _JSFiddle_. 41 | 42 | ### Additional context 43 | 44 | Add any other context about the bug here. 45 | -------------------------------------------------------------------------------- /src/components/table/index.js: -------------------------------------------------------------------------------- 1 | import { BTable } from './table' 2 | import { BTableLite } from './table-lite' 3 | import { BTableSimple } from './table-simple' 4 | import { BTbody } from './tbody' 5 | import { BThead } from './thead' 6 | import { BTfoot } from './tfoot' 7 | import { BTr } from './tr' 8 | import { BTd } from './td' 9 | import { BTh } from './th' 10 | import { pluginFactory } from '../../utils/plugins' 11 | 12 | const TableLitePlugin = /*#__PURE__*/ pluginFactory({ 13 | components: { 14 | BTableLite 15 | } 16 | }) 17 | 18 | const TableSimplePlugin = /*#__PURE__*/ pluginFactory({ 19 | components: { 20 | BTableSimple, 21 | BTbody, 22 | BThead, 23 | BTfoot, 24 | BTr, 25 | BTd, 26 | BTh 27 | } 28 | }) 29 | 30 | const TablePlugin = /*#__PURE__*/ pluginFactory({ 31 | components: { 32 | BTable 33 | }, 34 | plugins: { 35 | TableLitePlugin, 36 | TableSimplePlugin 37 | } 38 | }) 39 | 40 | export { 41 | // Plugins 42 | TablePlugin, 43 | TableLitePlugin, 44 | TableSimplePlugin, 45 | // Table components 46 | BTable, 47 | BTableLite, 48 | BTableSimple, 49 | // Helper components 50 | BTbody, 51 | BThead, 52 | BTfoot, 53 | BTr, 54 | BTd, 55 | BTh 56 | } 57 | -------------------------------------------------------------------------------- /src/components/layout/container.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_CONTAINER } from '../../constants/components' 3 | import { PROP_TYPE_BOOLEAN_STRING, PROP_TYPE_STRING } from '../../constants/props' 4 | import { makeProp, makePropsConfigurable } from '../../utils/props' 5 | 6 | // --- Props --- 7 | 8 | export const props = makePropsConfigurable( 9 | { 10 | // String breakpoint name new in Bootstrap v4.4.x 11 | fluid: makeProp(PROP_TYPE_BOOLEAN_STRING, false), 12 | tag: makeProp(PROP_TYPE_STRING, 'div') 13 | }, 14 | NAME_CONTAINER 15 | ) 16 | 17 | // --- Main component --- 18 | 19 | // @vue/component 20 | export const BContainer = /*#__PURE__*/ Vue.extend({ 21 | name: NAME_CONTAINER, 22 | functional: true, 23 | props, 24 | render(h, { props, data, children }) { 25 | const { fluid } = props 26 | 27 | return h( 28 | props.tag, 29 | mergeData(data, { 30 | class: { 31 | container: !(fluid || fluid === ''), 32 | 'container-fluid': fluid === true || fluid === '', 33 | // Bootstrap v4.4+ responsive containers 34 | [`container-${fluid}`]: fluid && fluid !== true 35 | } 36 | }), 37 | children 38 | ) 39 | } 40 | }) 41 | -------------------------------------------------------------------------------- /docs/pages/docs/reference/_slug.js: -------------------------------------------------------------------------------- 1 | import MainDocs from '~/components/main-docs' 2 | import docsMixin from '~/plugins/docs-mixin' 3 | import { reference as referenceMeta } from '~/content' 4 | 5 | const getReadmeData = name => { 6 | try { 7 | return import(`~/markdown/reference/${name}/README.md` /* webpackChunkName: "docs/reference" */) 8 | } catch { 9 | return { default: { loadError: true } } 10 | } 11 | } 12 | 13 | // @vue/component 14 | export default { 15 | name: 'BDVReference', 16 | mixins: [docsMixin], 17 | layout: 'docs', 18 | validate({ params }) { 19 | return Boolean(referenceMeta[params.slug]) 20 | }, 21 | async asyncData({ params }) { 22 | const name = params.slug 23 | const meta = referenceMeta[name] 24 | const readmeData = (await getReadmeData(name)).default 25 | const { titleLead = '', body = '', baseTOC = {}, loadError = false } = readmeData 26 | 27 | return { meta, titleLead, body, baseTOC, loadError } 28 | }, 29 | render(h) { 30 | return h(MainDocs, { 31 | staticClass: 'bd-components', 32 | props: { 33 | meta: this.meta, 34 | titleLead: this.titleLead, 35 | body: this.body, 36 | loadError: this.loadError 37 | } 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/components/card/card-text.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import { BCardText } from './card-text' 3 | 4 | describe('card-text', () => { 5 | it('has root element "p"', async () => { 6 | const wrapper = mount(BCardText) 7 | 8 | expect(wrapper.element.tagName).toBe('P') 9 | 10 | wrapper.destroy() 11 | }) 12 | 13 | it('has class card-text', async () => { 14 | const wrapper = mount(BCardText) 15 | 16 | expect(wrapper.classes()).toContain('card-text') 17 | 18 | wrapper.destroy() 19 | }) 20 | 21 | it('has custom root element "div" when prop text-tag=div', async () => { 22 | const wrapper = mount(BCardText, { 23 | context: { 24 | props: { 25 | textTag: 'div' 26 | } 27 | } 28 | }) 29 | 30 | expect(wrapper.element.tagName).toBe('DIV') 31 | expect(wrapper.classes()).toContain('card-text') 32 | 33 | wrapper.destroy() 34 | }) 35 | 36 | it('accepts custom classes', async () => { 37 | const wrapper = mount(BCardText, { 38 | context: { 39 | class: ['foobar'] 40 | } 41 | }) 42 | 43 | expect(wrapper.classes()).toContain('card-text') 44 | expect(wrapper.classes()).toContain('foobar') 45 | 46 | wrapper.destroy() 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /src/components/form-checkbox/form-checkbox-group.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../../vue' 2 | import { NAME_FORM_CHECKBOX_GROUP } from '../../constants/components' 3 | import { PROP_TYPE_ARRAY, PROP_TYPE_BOOLEAN } from '../../constants/props' 4 | import { sortKeys } from '../../utils/object' 5 | import { makeProp, makePropsConfigurable } from '../../utils/props' 6 | import { 7 | MODEL_PROP_NAME, 8 | formRadioCheckGroupMixin, 9 | props as formRadioCheckGroupProps 10 | } from '../../mixins/form-radio-check-group' 11 | 12 | // --- Props --- 13 | 14 | export const props = makePropsConfigurable( 15 | sortKeys({ 16 | ...formRadioCheckGroupProps, 17 | [MODEL_PROP_NAME]: makeProp(PROP_TYPE_ARRAY, []), 18 | // Custom switch styling 19 | switches: makeProp(PROP_TYPE_BOOLEAN, false) 20 | }), 21 | NAME_FORM_CHECKBOX_GROUP 22 | ) 23 | 24 | // --- Main component --- 25 | 26 | // @vue/component 27 | export const BFormCheckboxGroup = /*#__PURE__*/ Vue.extend({ 28 | name: NAME_FORM_CHECKBOX_GROUP, 29 | // Includes render function 30 | mixins: [formRadioCheckGroupMixin], 31 | provide() { 32 | return { 33 | bvCheckGroup: this 34 | } 35 | }, 36 | props, 37 | computed: { 38 | isRadioGroup() { 39 | return false 40 | } 41 | } 42 | }) 43 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['standard', 'plugin:vue/recommended', 'plugin:prettier/recommended'], 3 | plugins: ['jest', 'markdown', 'node', 'promise'], 4 | parserOptions: { 5 | parser: 'babel-eslint', 6 | sourceType: 'module' 7 | }, 8 | env: { 9 | browser: true, 10 | es6: true, 11 | 'jest/globals': true 12 | }, 13 | globals: { 14 | Vue: true 15 | }, 16 | rules: { 17 | 'no-unused-vars': [ 18 | 'error', 19 | { 20 | vars: 'all', 21 | args: 'after-used', 22 | ignoreRestSiblings: false 23 | } 24 | ], 25 | 'object-shorthand': ['error', 'properties'], 26 | 'spaced-comment': 'off', // needed to ignore `/*#__PURE__*/` comments 27 | 'vue/custom-event-name-casing': 'off', 28 | 'vue/html-self-closing': [ 29 | 'error', 30 | { 31 | html: { 32 | void: 'never', 33 | normal: 'never', 34 | component: 'never' 35 | } 36 | } 37 | ], 38 | 'vue/max-attributes-per-line': ['error', { singleline: 4 }], 39 | 'vue/no-v-html': 'off', 40 | 'vue/one-component-per-file': 'off', 41 | 'vue/require-default-prop': 'off', 42 | 'vue/require-prop-types': 'off', 43 | 'vue/singleline-html-element-content-newline': 'off' 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /static/logo-padded.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/utils/stable-sort.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Consistent and stable sort function across JavaScript platforms 3 | * 4 | * Inconsistent sorts can cause SSR problems between client and server 5 | * such as in if sortBy is applied to the data on server side render. 6 | * Chrome and V8 native sorts are inconsistent/unstable 7 | * 8 | * This function uses native sort with fallback to index compare when the a and b 9 | * compare returns 0 10 | * 11 | * Algorithm based on: 12 | * https://stackoverflow.com/questions/1427608/fast-stable-sorting-algorithm-implementation-in-javascript/45422645#45422645 13 | * 14 | * @param {array} array to sort 15 | * @param {function} sort compare function 16 | * @return {array} 17 | */ 18 | export const stableSort = (array, compareFn) => { 19 | // Using `.bind(compareFn)` on the wrapped anonymous function improves 20 | // performance by avoiding the function call setup. We don't use an arrow 21 | // function here as it binds `this` to the `stableSort` context rather than 22 | // the `compareFn` context, which wouldn't give us the performance increase. 23 | return array 24 | .map((a, index) => [index, a]) 25 | .sort( 26 | function(a, b) { 27 | return this(a[1], b[1]) || a[0] - b[0] 28 | }.bind(compareFn) 29 | ) 30 | .map(e => e[1]) 31 | } 32 | -------------------------------------------------------------------------------- /src/components/navbar/navbar-brand.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_NAVBAR_BRAND } from '../../constants/components' 3 | import { PROP_TYPE_STRING } from '../../constants/props' 4 | import { omit, sortKeys } from '../../utils/object' 5 | import { makeProp, makePropsConfigurable, pluckProps } from '../../utils/props' 6 | import { BLink, props as BLinkProps } from '../link/link' 7 | 8 | // --- Props --- 9 | 10 | const linkProps = omit(BLinkProps, ['event', 'routerTag']) 11 | linkProps.href.default = undefined 12 | linkProps.to.default = undefined 13 | 14 | export const props = makePropsConfigurable( 15 | sortKeys({ 16 | ...linkProps, 17 | tag: makeProp(PROP_TYPE_STRING, 'div') 18 | }), 19 | NAME_NAVBAR_BRAND 20 | ) 21 | 22 | // --- Main component --- 23 | 24 | // @vue/component 25 | export const BNavbarBrand = /*#__PURE__*/ Vue.extend({ 26 | name: NAME_NAVBAR_BRAND, 27 | functional: true, 28 | props, 29 | render(h, { props, data, children }) { 30 | const isLink = props.to || props.href 31 | const tag = isLink ? BLink : props.tag 32 | 33 | return h( 34 | tag, 35 | mergeData(data, { 36 | staticClass: 'navbar-brand', 37 | props: isLink ? pluckProps(linkProps, props) : {} 38 | }), 39 | children 40 | ) 41 | } 42 | }) 43 | -------------------------------------------------------------------------------- /src/components/skeleton/skeleton-icon.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../../vue' 2 | import { NAME_SKELETON_ICON } from '../../constants/components' 3 | import { PROP_TYPE_OBJECT, PROP_TYPE_STRING } from '../../constants/props' 4 | import { makeProp, makePropsConfigurable } from '../../utils/props' 5 | import { BIcon } from '../../icons' 6 | 7 | // --- Props --- 8 | 9 | export const props = makePropsConfigurable( 10 | { 11 | animation: makeProp(PROP_TYPE_STRING, 'wave'), 12 | icon: makeProp(PROP_TYPE_STRING), 13 | iconProps: makeProp(PROP_TYPE_OBJECT, {}) 14 | }, 15 | NAME_SKELETON_ICON 16 | ) 17 | 18 | // --- Main component --- 19 | 20 | // @vue/component 21 | export const BSkeletonIcon = /*#__PURE__*/ Vue.extend({ 22 | name: NAME_SKELETON_ICON, 23 | functional: true, 24 | props, 25 | render(h, { props }) { 26 | const { icon, animation } = props 27 | 28 | const $icon = h(BIcon, { 29 | staticClass: 'b-skeleton-icon', 30 | props: { 31 | ...props.iconProps, 32 | icon 33 | } 34 | }) 35 | 36 | return h( 37 | 'div', 38 | { 39 | staticClass: 'b-skeleton-icon-wrapper position-relative d-inline-block overflow-hidden', 40 | class: { [`b-skeleton-animate-${animation}`]: animation } 41 | }, 42 | [$icon] 43 | ) 44 | } 45 | }) 46 | -------------------------------------------------------------------------------- /src/components/navbar/navbar-nav.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_NAVBAR_NAV } from '../../constants/components' 3 | import { pick } from '../../utils/object' 4 | import { makePropsConfigurable } from '../../utils/props' 5 | import { props as BNavProps } from '../nav/nav' 6 | 7 | // --- Helper methods --- 8 | 9 | const computeJustifyContent = value => { 10 | value = value === 'left' ? 'start' : value === 'right' ? 'end' : value 11 | return `justify-content-${value}` 12 | } 13 | 14 | // --- Props --- 15 | 16 | export const props = makePropsConfigurable( 17 | pick(BNavProps, ['tag', 'fill', 'justified', 'align', 'small']), 18 | NAME_NAVBAR_NAV 19 | ) 20 | 21 | // --- Main component --- 22 | 23 | // @vue/component 24 | export const BNavbarNav = /*#__PURE__*/ Vue.extend({ 25 | name: NAME_NAVBAR_NAV, 26 | functional: true, 27 | props, 28 | render(h, { props, data, children }) { 29 | const { align } = props 30 | 31 | return h( 32 | props.tag, 33 | mergeData(data, { 34 | staticClass: 'navbar-nav', 35 | class: { 36 | 'nav-fill': props.fill, 37 | 'nav-justified': props.justified, 38 | [computeJustifyContent(align)]: align, 39 | small: props.small 40 | } 41 | }), 42 | children 43 | ) 44 | } 45 | }) 46 | -------------------------------------------------------------------------------- /src/components/table/helpers/mixin-pagination.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../../../vue' 2 | import { PROP_TYPE_NUMBER_STRING } from '../../../constants/props' 3 | import { mathMax } from '../../../utils/math' 4 | import { toInteger } from '../../../utils/number' 5 | import { makeProp } from '../../../utils/props' 6 | 7 | // --- Props --- 8 | 9 | export const props = { 10 | currentPage: makeProp(PROP_TYPE_NUMBER_STRING, 1), 11 | perPage: makeProp(PROP_TYPE_NUMBER_STRING, 0) 12 | } 13 | 14 | // --- Mixin --- 15 | 16 | // @vue/component 17 | export const paginationMixin = Vue.extend({ 18 | props, 19 | computed: { 20 | localPaging() { 21 | return this.hasProvider ? !!this.noProviderPaging : true 22 | }, 23 | paginatedItems() { 24 | let items = this.sortedItems || this.filteredItems || this.localItems || [] 25 | const currentPage = mathMax(toInteger(this.currentPage, 1), 1) 26 | const perPage = mathMax(toInteger(this.perPage, 0), 0) 27 | // Apply local pagination 28 | if (this.localPaging && perPage) { 29 | // Grab the current page of data (which may be past filtered items limit) 30 | items = items.slice((currentPage - 1) * perPage, currentPage * perPage) 31 | } 32 | // Return the items to display in the table 33 | return items 34 | } 35 | } 36 | }) 37 | -------------------------------------------------------------------------------- /src/components/table/helpers/mixin-top-row.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../../../vue' 2 | import { SLOT_NAME_TOP_ROW } from '../../../constants/slots' 3 | import { isFunction } from '../../../utils/inspect' 4 | import { BTr } from '../tr' 5 | 6 | // --- Props --- 7 | 8 | export const props = {} 9 | 10 | // --- Mixin --- 11 | 12 | // @vue/component 13 | export const topRowMixin = Vue.extend({ 14 | methods: { 15 | renderTopRow() { 16 | const { computedFields: fields, stacked, tbodyTrClass, tbodyTrAttr } = this 17 | const h = this.$createElement 18 | 19 | // Add static Top Row slot (hidden in visibly stacked mode as we can't control the data-label) 20 | // If in *always* stacked mode, we don't bother rendering the row 21 | if (!this.hasNormalizedSlot(SLOT_NAME_TOP_ROW) || stacked === true || stacked === '') { 22 | return h() 23 | } 24 | 25 | return h( 26 | BTr, 27 | { 28 | staticClass: 'b-table-top-row', 29 | class: [isFunction(tbodyTrClass) ? tbodyTrClass(null, 'row-top') : tbodyTrClass], 30 | attrs: isFunction(tbodyTrAttr) ? tbodyTrAttr(null, 'row-top') : tbodyTrAttr, 31 | key: 'b-top-row' 32 | }, 33 | [this.normalizeSlot(SLOT_NAME_TOP_ROW, { columns: fields.length, fields })] 34 | ) 35 | } 36 | } 37 | }) 38 | -------------------------------------------------------------------------------- /docs/components/breadcrumbs.js: -------------------------------------------------------------------------------- 1 | import { nav } from '~/content' 2 | 3 | const navLookup = nav.reduce( 4 | (obj, section) => ({ ...obj, [section.base.replace('/', '')]: section }), 5 | {} 6 | ) 7 | 8 | // @vue/component 9 | export default { 10 | name: 'BVBreadcrumbs', 11 | computed: { 12 | items() { 13 | const items = [{ text: 'Home', to: '/' }, { text: 'Docs', to: '/docs' }] 14 | 15 | const section = this.$route.name.split('-')[1] || '' 16 | if (section) { 17 | const sectionMeta = navLookup[section] || {} 18 | 19 | items.push({ 20 | text: sectionMeta.title || section, 21 | to: ['/docs', section].join('/') 22 | }) 23 | 24 | const slug = this.$route.params.slug || '' 25 | if (slug) { 26 | const pagesMeta = sectionMeta.pages || {} 27 | 28 | items.push({ 29 | text: (pagesMeta[slug] || {}).title || slug, 30 | to: ['/docs', section, slug].join('/') 31 | }) 32 | } 33 | } 34 | 35 | return items 36 | } 37 | }, 38 | render(h) { 39 | return h('nav', { attrs: { 'aria-label': 'Breadcrumbs' } }, [ 40 | h('b-breadcrumb', { 41 | staticClass: 'd-inline-flex my-0 px-2 py-1 bg-transparent', 42 | props: { items: this.items } 43 | }) 44 | ]) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/components/form-rating/_form-rating.scss: -------------------------------------------------------------------------------- 1 | .b-rating { 2 | text-align: center; 3 | 4 | &.d-inline-flex { 5 | width: auto; 6 | } 7 | 8 | .b-rating-star, 9 | .b-rating-value { 10 | padding: 0 0.25em; 11 | } 12 | 13 | .b-rating-value { 14 | // Keep the stars from moving when value changes (usually) 15 | min-width: 2.5em; 16 | } 17 | 18 | .b-rating-star { 19 | display: inline-flex; 20 | justify-content: center; 21 | outline: 0; 22 | 23 | .b-rating-icon { 24 | display: inline-flex; 25 | transition: all 0.15s ease-in-out; 26 | } 27 | } 28 | 29 | &.disabled, 30 | &:disabled { 31 | background-color: $input-disabled-bg; 32 | color: $text-muted; 33 | } 34 | 35 | &:not(.disabled):not(.readonly) { 36 | .b-rating-star { 37 | cursor: pointer; 38 | } 39 | 40 | // Zoom focused or hovered icons 41 | &:focus:not(:hover) .b-rating-star.focused, 42 | .b-rating-star:hover { 43 | .b-rating-icon { 44 | transform: scale(1.5); 45 | } 46 | } 47 | } 48 | 49 | // Flip the half icon if RTL mode 50 | // We transform the outer star wrapper so that we do not 51 | // interfere with the hover/focus transform above 52 | &[dir="rtl"] { 53 | .b-rating-star-half { 54 | transform: scale(-1, 1); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/components/input-group/input-group-addon.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_INPUT_GROUP_ADDON } from '../../constants/components' 3 | import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' 4 | import { makeProp, makePropsConfigurable } from '../../utils/props' 5 | import { BInputGroupText } from './input-group-text' 6 | 7 | // --- Props --- 8 | 9 | export const props = makePropsConfigurable( 10 | { 11 | append: makeProp(PROP_TYPE_BOOLEAN, false), 12 | id: makeProp(PROP_TYPE_STRING), 13 | isText: makeProp(PROP_TYPE_BOOLEAN, false), 14 | tag: makeProp(PROP_TYPE_STRING, 'div') 15 | }, 16 | NAME_INPUT_GROUP_ADDON 17 | ) 18 | 19 | // --- Main component --- 20 | 21 | // @vue/component 22 | export const BInputGroupAddon = /*#__PURE__*/ Vue.extend({ 23 | name: NAME_INPUT_GROUP_ADDON, 24 | functional: true, 25 | props, 26 | render(h, { props, data, children }) { 27 | const { append } = props 28 | 29 | return h( 30 | props.tag, 31 | mergeData(data, { 32 | class: { 33 | 'input-group-append': append, 34 | 'input-group-prepend': !append 35 | }, 36 | attrs: { 37 | id: props.id 38 | } 39 | }), 40 | props.isText ? [h(BInputGroupText, children)] : children 41 | ) 42 | } 43 | }) 44 | -------------------------------------------------------------------------------- /src/components/spinner/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bootstrap-vue/spinner", 3 | "version": "1.0.0", 4 | "meta": { 5 | "title": "Spinner", 6 | "description": "The component can be used to show the loading state in your projects. They're rendered only with basic HTML and CSS as a lightweight Vue functional component.", 7 | "slug": "spinner", 8 | "components": [ 9 | { 10 | "component": "BSpinner", 11 | "props": [ 12 | { 13 | "prop": "label", 14 | "description": "Text content to place in the sr-only label" 15 | }, 16 | { 17 | "prop": "small", 18 | "description": "When set, rendered a smaller spinner suitable for placing in buttons" 19 | }, 20 | { 21 | "prop": "type", 22 | "description": "Type of spinner to show. Current supported types are 'border' and 'grow'" 23 | }, 24 | { 25 | "prop": "variant", 26 | "description": "Applies one of the Bootstrap theme color variants to the component" 27 | } 28 | ], 29 | "slots": [ 30 | { 31 | "name": "label", 32 | "description": "Content to place in the sr-only label" 33 | } 34 | ] 35 | } 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/dropdown/dropdown-text.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_DROPDOWN_TEXT } from '../../constants/components' 3 | import { PROP_TYPE_ARRAY_OBJECT_STRING, PROP_TYPE_STRING } from '../../constants/props' 4 | import { omit } from '../../utils/object' 5 | import { makeProp, makePropsConfigurable } from '../../utils/props' 6 | 7 | // --- Props --- 8 | 9 | export const props = makePropsConfigurable( 10 | { 11 | tag: makeProp(PROP_TYPE_STRING, 'p'), 12 | textClass: makeProp(PROP_TYPE_ARRAY_OBJECT_STRING), 13 | variant: makeProp(PROP_TYPE_STRING) 14 | }, 15 | NAME_DROPDOWN_TEXT 16 | ) 17 | 18 | // --- Main component --- 19 | 20 | // @vue/component 21 | export const BDropdownText = /*#__PURE__*/ Vue.extend({ 22 | name: NAME_DROPDOWN_TEXT, 23 | functional: true, 24 | props, 25 | render(h, { props, data, children }) { 26 | const { tag, textClass, variant } = props 27 | 28 | return h('li', mergeData(omit(data, ['attrs']), { attrs: { role: 'presentation' } }), [ 29 | h( 30 | tag, 31 | { 32 | staticClass: 'b-dropdown-text', 33 | class: [textClass, { [`text-${variant}`]: variant }], 34 | props, 35 | attrs: data.attrs || {}, 36 | ref: 'text' 37 | }, 38 | children 39 | ) 40 | ]) 41 | } 42 | }) 43 | -------------------------------------------------------------------------------- /src/components/media/media-aside.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_MEDIA_ASIDE } from '../../constants/components' 3 | import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' 4 | import { makeProp, makePropsConfigurable } from '../../utils/props' 5 | 6 | // --- Props --- 7 | 8 | export const props = makePropsConfigurable( 9 | { 10 | right: makeProp(PROP_TYPE_BOOLEAN, false), 11 | tag: makeProp(PROP_TYPE_STRING, 'div'), 12 | verticalAlign: makeProp(PROP_TYPE_STRING, 'top') 13 | }, 14 | NAME_MEDIA_ASIDE 15 | ) 16 | 17 | // --- Main component --- 18 | 19 | // @vue/component 20 | export const BMediaAside = /*#__PURE__*/ Vue.extend({ 21 | name: NAME_MEDIA_ASIDE, 22 | functional: true, 23 | props, 24 | render(h, { props, data, children }) { 25 | const { verticalAlign } = props 26 | const align = 27 | verticalAlign === 'top' 28 | ? 'start' 29 | : verticalAlign === 'bottom' 30 | ? 'end' 31 | : /* istanbul ignore next */ verticalAlign 32 | 33 | return h( 34 | props.tag, 35 | mergeData(data, { 36 | staticClass: 'media-aside', 37 | class: { 38 | 'media-aside-right': props.right, 39 | [`align-self-${align}`]: align 40 | } 41 | }), 42 | children 43 | ) 44 | } 45 | }) 46 | -------------------------------------------------------------------------------- /src/components/layout/form-row.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import { BFormRow } from './form-row' 3 | 4 | describe('layout > form-row', () => { 5 | it('has expected default structure', async () => { 6 | const wrapper = mount(BFormRow) 7 | 8 | expect(wrapper.element.tagName).toBe('DIV') 9 | expect(wrapper.classes()).toContain('form-row') 10 | expect(wrapper.classes().length).toBe(1) 11 | expect(wrapper.text()).toEqual('') 12 | 13 | wrapper.destroy() 14 | }) 15 | 16 | it('custom root element when prop tag set', async () => { 17 | const wrapper = mount(BFormRow, { 18 | propsData: { 19 | tag: 'span' 20 | } 21 | }) 22 | 23 | expect(wrapper.element.tagName).toBe('SPAN') 24 | expect(wrapper.classes()).toContain('form-row') 25 | expect(wrapper.classes().length).toBe(1) 26 | expect(wrapper.text()).toEqual('') 27 | 28 | wrapper.destroy() 29 | }) 30 | 31 | it('renders default slot content', async () => { 32 | const wrapper = mount(BFormRow, { 33 | slots: { 34 | default: 'foobar' 35 | } 36 | }) 37 | 38 | expect(wrapper.element.tagName).toBe('DIV') 39 | expect(wrapper.classes()).toContain('form-row') 40 | expect(wrapper.classes().length).toBe(1) 41 | expect(wrapper.text()).toEqual('foobar') 42 | 43 | wrapper.destroy() 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /nuxt/plugin.template.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | <% if (!options.treeShake) { %> 4 | <% if (options.icons) { %> 5 | import { BootstrapVue, BootstrapVueIcons } from 'bootstrap-vue'; 6 | 7 | Vue.use(BootstrapVue, <%= JSON.stringify(options.config || {}, undefined, 2) %>); 8 | Vue.use(BootstrapVueIcons); 9 | <% } else { %> 10 | import { BootstrapVue } from 'bootstrap-vue'; 11 | 12 | Vue.use(BootstrapVue, <%= JSON.stringify(options.config || {}, undefined, 2) %>); 13 | <% } %> 14 | <% } %> 15 | 16 | <% if (options.treeShake) { %> 17 | import { 18 | <%= [].concat( 19 | options.config ? 'BVConfigPlugin' : null, 20 | options.componentPlugins, 21 | options.directivePlugins, 22 | options.components, 23 | options.directives 24 | ).filter(Boolean).join(',\n ') %> 25 | } from 'bootstrap-vue'; 26 | 27 | <% if (options.config) { %> 28 | Vue.use(BVConfigPlugin, <%= JSON.stringify(options.config, undefined, 2) %>); 29 | <% } %> 30 | 31 | <%= options.componentPlugins.reduce((acc, plugin) => (acc += `Vue.use(${plugin});\n` ), '') %> 32 | <%= options.directivePlugins.reduce((acc, plugin) => (acc += `Vue.use(${plugin});\n` ), '') %> 33 | <%= options.components.reduce((acc, c) => (acc += `Vue.component('${c}', ${c});\n` ), '') %> 34 | <%= options.directives.reduce((acc, d) => (acc += `Vue.directive('${d.replace(/^VB/, 'B')}', ${d});\n` ), '') %> 35 | <% } %> 36 | -------------------------------------------------------------------------------- /src/mixins/focus-in.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../vue' 2 | import { EVENT_OPTIONS_NO_CAPTURE } from '../constants/events' 3 | import { eventOn, eventOff } from '../utils/events' 4 | 5 | // @vue/component 6 | export const focusInMixin = Vue.extend({ 7 | data() { 8 | return { 9 | listenForFocusIn: false 10 | } 11 | }, 12 | watch: { 13 | listenForFocusIn(newValue, oldValue) { 14 | if (newValue !== oldValue) { 15 | eventOff(this.focusInElement, 'focusin', this._focusInHandler, EVENT_OPTIONS_NO_CAPTURE) 16 | if (newValue) { 17 | eventOn(this.focusInElement, 'focusin', this._focusInHandler, EVENT_OPTIONS_NO_CAPTURE) 18 | } 19 | } 20 | } 21 | }, 22 | beforeCreate() { 23 | // Declare non-reactive properties 24 | this.focusInElement = null 25 | }, 26 | mounted() { 27 | if (!this.focusInElement) { 28 | this.focusInElement = document 29 | } 30 | if (this.listenForFocusIn) { 31 | eventOn(this.focusInElement, 'focusin', this._focusInHandler, EVENT_OPTIONS_NO_CAPTURE) 32 | } 33 | }, 34 | beforeDestroy() { 35 | eventOff(this.focusInElement, 'focusin', this._focusInHandler, EVENT_OPTIONS_NO_CAPTURE) 36 | }, 37 | methods: { 38 | _focusInHandler(event) { 39 | if (this.focusInHandler) { 40 | this.focusInHandler(event) 41 | } 42 | } 43 | } 44 | }) 45 | -------------------------------------------------------------------------------- /src/components/button-group/button-group.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_BUTTON_GROUP } from '../../constants/components' 3 | import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' 4 | import { pick, sortKeys } from '../../utils/object' 5 | import { makeProp, makePropsConfigurable } from '../../utils/props' 6 | import { props as buttonProps } from '../button/button' 7 | 8 | // --- Props --- 9 | 10 | export const props = makePropsConfigurable( 11 | sortKeys({ 12 | ...pick(buttonProps, ['size']), 13 | ariaRole: makeProp(PROP_TYPE_STRING, 'group'), 14 | size: makeProp(PROP_TYPE_STRING), 15 | tag: makeProp(PROP_TYPE_STRING, 'div'), 16 | vertical: makeProp(PROP_TYPE_BOOLEAN, false) 17 | }), 18 | NAME_BUTTON_GROUP 19 | ) 20 | 21 | // --- Main component --- 22 | 23 | // @vue/component 24 | export const BButtonGroup = /*#__PURE__*/ Vue.extend({ 25 | name: NAME_BUTTON_GROUP, 26 | functional: true, 27 | props, 28 | render(h, { props, data, children }) { 29 | return h( 30 | props.tag, 31 | mergeData(data, { 32 | class: { 33 | 'btn-group': !props.vertical, 34 | 'btn-group-vertical': props.vertical, 35 | [`btn-group-${props.size}`]: props.size 36 | }, 37 | attrs: { role: props.ariaRole } 38 | }), 39 | children 40 | ) 41 | } 42 | }) 43 | -------------------------------------------------------------------------------- /src/components/dropdown/index.js: -------------------------------------------------------------------------------- 1 | import { BDropdown } from './dropdown' 2 | import { BDropdownItem } from './dropdown-item' 3 | import { BDropdownItemButton } from './dropdown-item-button' 4 | import { BDropdownHeader } from './dropdown-header' 5 | import { BDropdownDivider } from './dropdown-divider' 6 | import { BDropdownForm } from './dropdown-form' 7 | import { BDropdownText } from './dropdown-text' 8 | import { BDropdownGroup } from './dropdown-group' 9 | import { pluginFactory } from '../../utils/plugins' 10 | 11 | const DropdownPlugin = /*#__PURE__*/ pluginFactory({ 12 | components: { 13 | BDropdown, 14 | BDd: BDropdown, 15 | BDropdownItem, 16 | BDdItem: BDropdownItem, 17 | BDropdownItemButton, 18 | BDropdownItemBtn: BDropdownItemButton, 19 | BDdItemButton: BDropdownItemButton, 20 | BDdItemBtn: BDropdownItemButton, 21 | BDropdownHeader, 22 | BDdHeader: BDropdownHeader, 23 | BDropdownDivider, 24 | BDdDivider: BDropdownDivider, 25 | BDropdownForm, 26 | BDdForm: BDropdownForm, 27 | BDropdownText, 28 | BDdText: BDropdownText, 29 | BDropdownGroup, 30 | BDdGroup: BDropdownGroup 31 | } 32 | }) 33 | 34 | export { 35 | DropdownPlugin, 36 | BDropdown, 37 | BDropdownItem, 38 | BDropdownItemButton, 39 | BDropdownHeader, 40 | BDropdownDivider, 41 | BDropdownForm, 42 | BDropdownText, 43 | BDropdownGroup 44 | } 45 | -------------------------------------------------------------------------------- /src/components/dropdown/_dropdown-form.scss: -------------------------------------------------------------------------------- 1 | $bv-dropdown-form-defined: false !default; 2 | 3 | @if $bv-dropdown-form-defined == false { 4 | // This test will only include these style definitions once 5 | $bv-dropdown-form-defined: true; 6 | 7 | // Custom styles for 8 | // Based on class `.dropdown-item` 9 | .b-dropdown-form { 10 | display: inline-block; 11 | padding: $dropdown-item-padding-y $dropdown-item-padding-x; 12 | width: 100%; 13 | clear: both; 14 | font-weight: $font-weight-normal; 15 | 16 | &:focus { 17 | // From https://github.com/twbs/bootstrap/blob/master/scss/_reboot.scss 18 | // mimicking button:focus styling. 19 | // We add important here as anything with tabindex `-1` and focused will not 20 | // have a focus ring due to reboot.scss and its `!important` override. 21 | // Needed for keyboard navigation high-lighting 22 | outline: 1px dotted !important; 23 | outline: 5px auto -webkit-focus-ring-color !important; 24 | } 25 | 26 | &.disabled, 27 | &:disabled { 28 | outline: 0 !important; 29 | color: $dropdown-link-disabled-color; 30 | pointer-events: none; 31 | // background-color: transparent; 32 | // Remove CSS gradients if they're enabled 33 | // @if $enable-gradients { 34 | // background-image: none; 35 | // } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/skeleton/skeleton.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_SKELETON } from '../../constants/components' 3 | import { PROP_TYPE_STRING } from '../../constants/props' 4 | import { makeProp, makePropsConfigurable } from '../../utils/props' 5 | 6 | // --- Props --- 7 | 8 | export const props = makePropsConfigurable( 9 | { 10 | animation: makeProp(PROP_TYPE_STRING, 'wave'), 11 | height: makeProp(PROP_TYPE_STRING), 12 | size: makeProp(PROP_TYPE_STRING), 13 | type: makeProp(PROP_TYPE_STRING, 'text'), 14 | variant: makeProp(PROP_TYPE_STRING), 15 | width: makeProp(PROP_TYPE_STRING) 16 | }, 17 | NAME_SKELETON 18 | ) 19 | 20 | // --- Main component --- 21 | 22 | // @vue/component 23 | export const BSkeleton = /*#__PURE__*/ Vue.extend({ 24 | name: NAME_SKELETON, 25 | functional: true, 26 | props, 27 | render(h, { data, props }) { 28 | const { size, animation, variant } = props 29 | 30 | return h( 31 | 'div', 32 | mergeData(data, { 33 | staticClass: 'b-skeleton', 34 | style: { 35 | width: size || props.width, 36 | height: size || props.height 37 | }, 38 | class: { 39 | [`b-skeleton-${props.type}`]: true, 40 | [`b-skeleton-animate-${animation}`]: animation, 41 | [`bg-${variant}`]: variant 42 | } 43 | }) 44 | ) 45 | } 46 | }) 47 | -------------------------------------------------------------------------------- /docs/utils/needs-transpiler.js: -------------------------------------------------------------------------------- 1 | // Determine if the broser needs to use @babel/standalone compiler for v-play and playground 2 | 3 | let needsTranspiler = false 4 | 5 | // Tests to see if we need to compile ES6 to ES5. Tests for commonly used ES6 features. 6 | // If any test fails, then we need to transpile code with @babel/standalone. 7 | const tests = [ 8 | // Arrow functions 9 | 'const test1 = (a) => a', 10 | // Object function shortcut 11 | 'const test2 = { a: 1, b() { return 0 } }', 12 | // Object shortcut 13 | 'const test3a = { a: 1 }; const test3b = { test3a, b: 2 }', 14 | // Object spread 15 | 'const test4a = { a: 1, b: 2 }; const test4b = { c: 3, ...test4a }', 16 | // Array spread 17 | 'const test5a = [1, 2]; const test5b = [...test5a, 3, 4]', 18 | // String interpolation 19 | /* eslint-disable no-template-curly-in-string */ 20 | 'const test6a = "bar"; const test6b = `foo${test6a}`' 21 | /* eslint-enable no-template-curly-in-string */ 22 | ] 23 | 24 | // Run tests to see if transpilation is needed. Returns after first test that fails 25 | if (typeof window !== 'undefined') { 26 | /* eslint-disable no-eval */ 27 | for (let i = 0; i < tests.length && !needsTranspiler; i++) { 28 | try { 29 | eval(tests[i]) 30 | } catch (e) { 31 | needsTranspiler = true 32 | } 33 | } 34 | /* eslint-enable no-eval */ 35 | } 36 | 37 | export default needsTranspiler 38 | -------------------------------------------------------------------------------- /src/components/nav/nav-form.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_NAV_FORM } from '../../constants/components' 3 | import { PROP_TYPE_ARRAY_OBJECT_STRING } from '../../constants/props' 4 | import { omit, sortKeys } from '../../utils/object' 5 | import { makeProp, makePropsConfigurable, pluckProps } from '../../utils/props' 6 | import { BForm, props as BFormProps } from '../form/form' 7 | 8 | // --- Props --- 9 | 10 | const formProps = omit(BFormProps, ['inline']) 11 | 12 | export const props = makePropsConfigurable( 13 | sortKeys({ 14 | ...formProps, 15 | formClass: makeProp(PROP_TYPE_ARRAY_OBJECT_STRING) 16 | }), 17 | NAME_NAV_FORM 18 | ) 19 | 20 | // --- Main component --- 21 | 22 | // @vue/component 23 | export const BNavForm = /*#__PURE__*/ Vue.extend({ 24 | name: NAME_NAV_FORM, 25 | functional: true, 26 | props, 27 | render(h, { props, data, children, listeners }) { 28 | const $form = h( 29 | BForm, 30 | { 31 | class: props.formClass, 32 | props: { 33 | ...pluckProps(formProps, props), 34 | inline: true 35 | }, 36 | attrs: data.attrs, 37 | on: listeners 38 | }, 39 | children 40 | ) 41 | 42 | return h( 43 | 'li', 44 | mergeData(omit(data, ['attrs', 'on']), { 45 | staticClass: 'form-inline' 46 | }), 47 | [$form] 48 | ) 49 | } 50 | }) 51 | -------------------------------------------------------------------------------- /src/components/list-group/list-group.js: -------------------------------------------------------------------------------- 1 | import { Vue, mergeData } from '../../vue' 2 | import { NAME_LIST_GROUP } from '../../constants/components' 3 | import { 4 | PROP_TYPE_BOOLEAN, 5 | PROP_TYPE_BOOLEAN_STRING, 6 | PROP_TYPE_STRING 7 | } from '../../constants/props' 8 | import { isString } from '../../utils/inspect' 9 | import { makeProp, makePropsConfigurable } from '../../utils/props' 10 | 11 | // --- Props --- 12 | 13 | export const props = makePropsConfigurable( 14 | { 15 | flush: makeProp(PROP_TYPE_BOOLEAN, false), 16 | horizontal: makeProp(PROP_TYPE_BOOLEAN_STRING, false), 17 | tag: makeProp(PROP_TYPE_STRING, 'div') 18 | }, 19 | NAME_LIST_GROUP 20 | ) 21 | 22 | // --- Main component --- 23 | 24 | // @vue/component 25 | export const BListGroup = /*#__PURE__*/ Vue.extend({ 26 | name: NAME_LIST_GROUP, 27 | functional: true, 28 | props, 29 | render(h, { props, data, children }) { 30 | let horizontal = props.horizontal === '' ? true : props.horizontal 31 | horizontal = props.flush ? false : horizontal 32 | const componentData = { 33 | staticClass: 'list-group', 34 | class: { 35 | 'list-group-flush': props.flush, 36 | 'list-group-horizontal': horizontal === true, 37 | [`list-group-horizontal-${horizontal}`]: isString(horizontal) 38 | } 39 | } 40 | return h(props.tag, mergeData(data, componentData), children) 41 | } 42 | }) 43 | -------------------------------------------------------------------------------- /src/directives/hover/hover.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import { VBHover } from './hover' 3 | 4 | describe('v-b-hover directive', () => { 5 | it('works', async () => { 6 | let hovered1 = false 7 | let hovered2 = false 8 | const App = { 9 | data() { 10 | return { 11 | text: 'FOO', 12 | changeHandler: false 13 | } 14 | }, 15 | directives: { 16 | BHover: VBHover 17 | }, 18 | methods: { 19 | handleHover1(isHovered) { 20 | hovered1 = isHovered 21 | }, 22 | handleHover2(isHovered) { 23 | hovered2 = isHovered 24 | } 25 | }, 26 | template: `
{{ text }}
` 27 | } 28 | const wrapper = mount(App) 29 | 30 | expect(wrapper.vm).toBeDefined() 31 | expect(hovered1).toBe(false) 32 | 33 | await wrapper.trigger('mouseenter') 34 | expect(hovered1).toBe(true) 35 | 36 | await wrapper.trigger('mouseleave') 37 | expect(hovered1).toBe(false) 38 | 39 | await wrapper.setData({ text: 'BAR' }) 40 | await wrapper.trigger('mouseenter') 41 | expect(hovered1).toBe(true) 42 | 43 | await wrapper.setData({ changeHandler: true }) 44 | await wrapper.trigger('mouseenter') 45 | expect(hovered2).toBe(true) 46 | 47 | wrapper.destroy() 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /docs/layouts/default.js: -------------------------------------------------------------------------------- 1 | import { BASE_URL, GWT_BV_ORG, GWT_JS_ORG, GWT_BV_NETLIFY } from '~/constants' 2 | import Footer from '~/components/footer' 3 | import Header from '~/components/header' 4 | 5 | export default { 6 | name: 'BVDefaultLayout', 7 | render(h) { 8 | return h('div', [h(Header), h('nuxt'), h(Footer)]) 9 | }, 10 | head() { 11 | return { 12 | link: [ 13 | // Add canonical URL so all site variations are 14 | // indexed to the same primary URL 15 | { 16 | hid: 'canonical', 17 | rel: 'canonical', 18 | href: `${BASE_URL}${this.$route.path}` 19 | } 20 | ], 21 | meta: [ 22 | // Add GWT site verification for *.bootstrap-vue.org 23 | { 24 | hid: 'google-site-verification-bv-org', 25 | name: 'google-site-verification', 26 | content: GWT_BV_ORG 27 | }, 28 | // Add GWT site verification for bootstrap-vue.js.org (legacy) 29 | { 30 | hid: 'google-site-verification-js-org', 31 | name: 'google-site-verification', 32 | content: GWT_JS_ORG 33 | }, 34 | // Add GWT site verification for bootstrap-vue.netlify.app (legacy) 35 | { 36 | hid: 'google-site-verification-netlify', 37 | name: 'google-site-verification', 38 | content: GWT_BV_NETLIFY 39 | } 40 | ] 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/components/form/form-datalist.js: -------------------------------------------------------------------------------- 1 | import { Vue } from '../../vue' 2 | import { NAME_FORM_DATALIST } from '../../constants/components' 3 | import { PROP_TYPE_STRING } from '../../constants/props' 4 | import { htmlOrText } from '../../utils/html' 5 | import { sortKeys } from '../../utils/object' 6 | import { makeProp, makePropsConfigurable } from '../../utils/props' 7 | import { formOptionsMixin, props as formOptionsProps } from '../../mixins/form-options' 8 | import { normalizeSlotMixin } from '../../mixins/normalize-slot' 9 | 10 | // --- Props --- 11 | 12 | export const props = makePropsConfigurable( 13 | sortKeys({ 14 | ...formOptionsProps, 15 | id: makeProp(PROP_TYPE_STRING, undefined, true) // Required 16 | }), 17 | NAME_FORM_DATALIST 18 | ) 19 | 20 | // --- Main component --- 21 | 22 | // @vue/component 23 | export const BFormDatalist = /*#__PURE__*/ Vue.extend({ 24 | name: NAME_FORM_DATALIST, 25 | mixins: [formOptionsMixin, normalizeSlotMixin], 26 | props, 27 | render(h) { 28 | const { id } = this 29 | 30 | const $options = this.formOptions.map((option, index) => { 31 | const { value, text, html, disabled } = option 32 | 33 | return h('option', { 34 | attrs: { value, disabled }, 35 | domProps: htmlOrText(html, text), 36 | key: `option_${index}` 37 | }) 38 | }) 39 | 40 | return h('datalist', { attrs: { id } }, [$options, this.normalizeSlot()]) 41 | } 42 | }) 43 | --------------------------------------------------------------------------------