UNPKG

bootstrap-vue

Version:

With more than 85 components, over 45 available plugins, several directives, and 1000+ icons, BootstrapVue provides one of the most comprehensive implementations of the Bootstrap v4 component and grid system available for Vue.js v2.6, complete with extens

439 lines (397 loc) 15 kB
var _watch; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } import { extend } from '../../vue'; import { NAME_COLLAPSE, NAME_SIDEBAR } from '../../constants/components'; import { IS_BROWSER } from '../../constants/env'; import { EVENT_NAME_CHANGE, EVENT_NAME_HIDDEN, EVENT_NAME_SHOWN } from '../../constants/events'; import { CODE_ESC } from '../../constants/key-codes'; import { PROP_TYPE_ARRAY_OBJECT_STRING, PROP_TYPE_BOOLEAN, PROP_TYPE_BOOLEAN_STRING, PROP_TYPE_NUMBER_STRING, PROP_TYPE_STRING } from '../../constants/props'; import { SLOT_NAME_DEFAULT, SLOT_NAME_FOOTER, SLOT_NAME_HEADER, SLOT_NAME_HEADER_CLOSE, SLOT_NAME_TITLE } from '../../constants/slots'; import { attemptFocus, contains, getActiveElement, getTabables } from '../../utils/dom'; import { getRootActionEventName, getRootEventName } from '../../utils/events'; import { makeModelMixin } from '../../utils/model'; import { sortKeys } from '../../utils/object'; import { makeProp, makePropsConfigurable } from '../../utils/props'; import { attrsMixin } from '../../mixins/attrs'; import { idMixin, props as idProps } from '../../mixins/id'; import { listenOnRootMixin } from '../../mixins/listen-on-root'; import { normalizeSlotMixin } from '../../mixins/normalize-slot'; import { BIconX } from '../../icons/icons'; import { BButtonClose } from '../button/button-close'; import { BVTransition } from '../transition/bv-transition'; // --- Constants --- var CLASS_NAME = 'b-sidebar'; var ROOT_ACTION_EVENT_NAME_REQUEST_STATE = getRootActionEventName(NAME_COLLAPSE, 'request-state'); var ROOT_ACTION_EVENT_NAME_TOGGLE = getRootActionEventName(NAME_COLLAPSE, 'toggle'); var ROOT_EVENT_NAME_STATE = getRootEventName(NAME_COLLAPSE, 'state'); var ROOT_EVENT_NAME_SYNC_STATE = getRootEventName(NAME_COLLAPSE, 'sync-state'); var _makeModelMixin = makeModelMixin('visible', { type: PROP_TYPE_BOOLEAN, defaultValue: false, event: EVENT_NAME_CHANGE }), modelMixin = _makeModelMixin.mixin, modelProps = _makeModelMixin.props, MODEL_PROP_NAME = _makeModelMixin.prop, MODEL_EVENT_NAME = _makeModelMixin.event; // --- Props --- export var props = makePropsConfigurable(sortKeys(_objectSpread(_objectSpread(_objectSpread({}, idProps), modelProps), {}, { ariaLabel: makeProp(PROP_TYPE_STRING), ariaLabelledby: makeProp(PROP_TYPE_STRING), // If `true`, shows a basic backdrop backdrop: makeProp(PROP_TYPE_BOOLEAN, false), backdropVariant: makeProp(PROP_TYPE_STRING, 'dark'), bgVariant: makeProp(PROP_TYPE_STRING, 'light'), bodyClass: makeProp(PROP_TYPE_ARRAY_OBJECT_STRING), // `aria-label` for close button closeLabel: makeProp(PROP_TYPE_STRING), footerClass: makeProp(PROP_TYPE_ARRAY_OBJECT_STRING), footerTag: makeProp(PROP_TYPE_STRING, 'footer'), headerClass: makeProp(PROP_TYPE_ARRAY_OBJECT_STRING), headerTag: makeProp(PROP_TYPE_STRING, 'header'), lazy: makeProp(PROP_TYPE_BOOLEAN, false), noCloseOnBackdrop: makeProp(PROP_TYPE_BOOLEAN, false), noCloseOnEsc: makeProp(PROP_TYPE_BOOLEAN, false), noCloseOnRouteChange: makeProp(PROP_TYPE_BOOLEAN, false), noEnforceFocus: makeProp(PROP_TYPE_BOOLEAN, false), noHeader: makeProp(PROP_TYPE_BOOLEAN, false), noHeaderClose: makeProp(PROP_TYPE_BOOLEAN, false), noSlide: makeProp(PROP_TYPE_BOOLEAN, false), right: makeProp(PROP_TYPE_BOOLEAN, false), shadow: makeProp(PROP_TYPE_BOOLEAN_STRING, false), sidebarClass: makeProp(PROP_TYPE_ARRAY_OBJECT_STRING), tag: makeProp(PROP_TYPE_STRING, 'div'), textVariant: makeProp(PROP_TYPE_STRING, 'dark'), title: makeProp(PROP_TYPE_STRING), width: makeProp(PROP_TYPE_STRING), zIndex: makeProp(PROP_TYPE_NUMBER_STRING) })), NAME_SIDEBAR); // --- Render methods --- var renderHeaderTitle = function renderHeaderTitle(h, ctx) { // Render a empty `<span>` when to title was provided var title = ctx.normalizeSlot(SLOT_NAME_TITLE, ctx.slotScope) || ctx.title; if (!title) { return h('span'); } return h('strong', { attrs: { id: ctx.safeId('__title__') } }, [title]); }; var renderHeaderClose = function renderHeaderClose(h, ctx) { if (ctx.noHeaderClose) { return h(); } var closeLabel = ctx.closeLabel, textVariant = ctx.textVariant, hide = ctx.hide; return h(BButtonClose, { props: { ariaLabel: closeLabel, textVariant: textVariant }, on: { click: hide }, ref: 'close-button' }, [ctx.normalizeSlot(SLOT_NAME_HEADER_CLOSE) || h(BIconX)]); }; var renderHeader = function renderHeader(h, ctx) { if (ctx.noHeader) { return h(); } var $content = ctx.normalizeSlot(SLOT_NAME_HEADER, ctx.slotScope); if (!$content) { var $title = renderHeaderTitle(h, ctx); var $close = renderHeaderClose(h, ctx); $content = ctx.right ? [$close, $title] : [$title, $close]; } return h(ctx.headerTag, { staticClass: "".concat(CLASS_NAME, "-header"), class: ctx.headerClass, key: 'header' }, $content); }; var renderBody = function renderBody(h, ctx) { return h('div', { staticClass: "".concat(CLASS_NAME, "-body"), class: ctx.bodyClass, key: 'body' }, [ctx.normalizeSlot(SLOT_NAME_DEFAULT, ctx.slotScope)]); }; var renderFooter = function renderFooter(h, ctx) { var $footer = ctx.normalizeSlot(SLOT_NAME_FOOTER, ctx.slotScope); if (!$footer) { return h(); } return h(ctx.footerTag, { staticClass: "".concat(CLASS_NAME, "-footer"), class: ctx.footerClass, key: 'footer' }, [$footer]); }; var renderContent = function renderContent(h, ctx) { // We render the header even if `lazy` is enabled as it // acts as the accessible label for the sidebar var $header = renderHeader(h, ctx); if (ctx.lazy && !ctx.isOpen) { return $header; } return [$header, renderBody(h, ctx), renderFooter(h, ctx)]; }; var renderBackdrop = function renderBackdrop(h, ctx) { if (!ctx.backdrop) { return h(); } var backdropVariant = ctx.backdropVariant; return h('div', { directives: [{ name: 'show', value: ctx.localShow }], staticClass: 'b-sidebar-backdrop', class: _defineProperty({}, "bg-".concat(backdropVariant), backdropVariant), on: { click: ctx.onBackdropClick } }); }; // --- Main component --- // @vue/component export var BSidebar = /*#__PURE__*/extend({ name: NAME_SIDEBAR, mixins: [attrsMixin, idMixin, modelMixin, listenOnRootMixin, normalizeSlotMixin], inheritAttrs: false, props: props, data: function data() { var visible = !!this[MODEL_PROP_NAME]; return { // Internal `v-model` state localShow: visible, // For lazy render triggering isOpen: visible }; }, computed: { transitionProps: function transitionProps() { return this.noSlide ? /* istanbul ignore next */ { css: true } : { css: true, enterClass: '', enterActiveClass: 'slide', enterToClass: 'show', leaveClass: 'show', leaveActiveClass: 'slide', leaveToClass: '' }; }, slotScope: function slotScope() { var hide = this.hide, right = this.right, visible = this.localShow; return { hide: hide, right: right, visible: visible }; }, hasTitle: function hasTitle() { var $scopedSlots = this.$scopedSlots, $slots = this.$slots; return !this.noHeader && !this.hasNormalizedSlot(SLOT_NAME_HEADER) && !!(this.normalizeSlot(SLOT_NAME_TITLE, this.slotScope, $scopedSlots, $slots) || this.title); }, titleId: function titleId() { return this.hasTitle ? this.safeId('__title__') : null; }, computedAttrs: function computedAttrs() { return _objectSpread(_objectSpread({}, this.bvAttrs), {}, { id: this.safeId(), tabindex: '-1', role: 'dialog', 'aria-modal': this.backdrop ? 'true' : 'false', 'aria-hidden': this.localShow ? null : 'true', 'aria-label': this.ariaLabel || null, 'aria-labelledby': this.ariaLabelledby || this.titleId || null }); } }, watch: (_watch = {}, _defineProperty(_watch, MODEL_PROP_NAME, function (newValue, oldValue) { if (newValue !== oldValue) { this.localShow = newValue; } }), _defineProperty(_watch, "localShow", function localShow(newValue, oldValue) { if (newValue !== oldValue) { this.emitState(newValue); this.$emit(MODEL_EVENT_NAME, newValue); } }), _defineProperty(_watch, "$route", function $route() { var newValue = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oldValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (!this.noCloseOnRouteChange && newValue.fullPath !== oldValue.fullPath) { this.hide(); } }), _watch), created: function created() { // Define non-reactive properties this.$_returnFocusEl = null; }, mounted: function mounted() { var _this = this; // Add `$root` listeners this.listenOnRoot(ROOT_ACTION_EVENT_NAME_TOGGLE, this.handleToggle); this.listenOnRoot(ROOT_ACTION_EVENT_NAME_REQUEST_STATE, this.handleSync); // Send out a gratuitous state event to ensure toggle button is synced this.$nextTick(function () { _this.emitState(_this.localShow); }); }, /* istanbul ignore next */ activated: function activated() { this.emitSync(); }, beforeDestroy: function beforeDestroy() { this.localShow = false; this.$_returnFocusEl = null; }, methods: { hide: function hide() { this.localShow = false; }, emitState: function emitState() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.localShow; this.emitOnRoot(ROOT_EVENT_NAME_STATE, this.safeId(), state); }, emitSync: function emitSync() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.localShow; this.emitOnRoot(ROOT_EVENT_NAME_SYNC_STATE, this.safeId(), state); }, handleToggle: function handleToggle(id) { // Note `safeId()` can be null until after mount if (id && id === this.safeId()) { this.localShow = !this.localShow; } }, handleSync: function handleSync(id) { var _this2 = this; // Note `safeId()` can be null until after mount if (id && id === this.safeId()) { this.$nextTick(function () { _this2.emitSync(_this2.localShow); }); } }, onKeydown: function onKeydown(event) { var keyCode = event.keyCode; if (!this.noCloseOnEsc && keyCode === CODE_ESC && this.localShow) { this.hide(); } }, onBackdropClick: function onBackdropClick() { if (this.localShow && !this.noCloseOnBackdrop) { this.hide(); } }, /* istanbul ignore next */ onTopTrapFocus: function onTopTrapFocus() { var tabables = getTabables(this.$refs.content); this.enforceFocus(tabables.reverse()[0]); }, /* istanbul ignore next */ onBottomTrapFocus: function onBottomTrapFocus() { var tabables = getTabables(this.$refs.content); this.enforceFocus(tabables[0]); }, onBeforeEnter: function onBeforeEnter() { // Returning focus to `document.body` may cause unwanted scrolls, // so we exclude setting focus on body this.$_returnFocusEl = getActiveElement(IS_BROWSER ? [document.body] : []); // Trigger lazy render this.isOpen = true; }, onAfterEnter: function onAfterEnter(el) { if (!contains(el, getActiveElement())) { this.enforceFocus(el); } this.$emit(EVENT_NAME_SHOWN); }, onAfterLeave: function onAfterLeave() { this.enforceFocus(this.$_returnFocusEl); this.$_returnFocusEl = null; // Trigger lazy render this.isOpen = false; this.$emit(EVENT_NAME_HIDDEN); }, enforceFocus: function enforceFocus(el) { if (!this.noEnforceFocus) { attemptFocus(el); } } }, render: function render(h) { var _ref; var bgVariant = this.bgVariant, width = this.width, textVariant = this.textVariant, localShow = this.localShow; var shadow = this.shadow === '' ? true : this.shadow; var $sidebar = h(this.tag, { staticClass: CLASS_NAME, class: [(_ref = { shadow: shadow === true }, _defineProperty(_ref, "shadow-".concat(shadow), shadow && shadow !== true), _defineProperty(_ref, "".concat(CLASS_NAME, "-right"), this.right), _defineProperty(_ref, "bg-".concat(bgVariant), bgVariant), _defineProperty(_ref, "text-".concat(textVariant), textVariant), _ref), this.sidebarClass], style: { width: width }, attrs: this.computedAttrs, directives: [{ name: 'show', value: localShow }], ref: 'content' }, [renderContent(h, this)]); $sidebar = h('transition', { props: this.transitionProps, on: { beforeEnter: this.onBeforeEnter, afterEnter: this.onAfterEnter, afterLeave: this.onAfterLeave } }, [$sidebar]); var $backdrop = h(BVTransition, { props: { noFade: this.noSlide } }, [renderBackdrop(h, this)]); var $tabTrapTop = h(); var $tabTrapBottom = h(); if (this.backdrop && localShow) { $tabTrapTop = h('div', { attrs: { tabindex: '0' }, on: { focus: this.onTopTrapFocus } }); $tabTrapBottom = h('div', { attrs: { tabindex: '0' }, on: { focus: this.onBottomTrapFocus } }); } return h('div', { staticClass: 'b-sidebar-outer', style: { zIndex: this.zIndex }, attrs: { tabindex: '-1' }, on: { keydown: this.onKeydown } }, [$tabTrapTop, $sidebar, $tabTrapBottom, $backdrop]); } });