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

274 lines (241 loc) 10.1 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 } from '../../constants/components'; import { CLASS_NAME_SHOW } from '../../constants/classes'; import { IS_BROWSER } from '../../constants/env'; import { EVENT_NAME_HIDDEN, EVENT_NAME_HIDE, EVENT_NAME_SHOW, EVENT_NAME_SHOWN, EVENT_OPTIONS_NO_CAPTURE } from '../../constants/events'; import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props'; import { SLOT_NAME_DEFAULT } from '../../constants/slots'; import { addClass, hasClass, removeClass, closest, matches, getCS } from '../../utils/dom'; import { getRootActionEventName, getRootEventName, eventOnOff } from '../../utils/events'; import { makeModelMixin } from '../../utils/model'; import { sortKeys } from '../../utils/object'; import { makeProp, makePropsConfigurable } from '../../utils/props'; import { idMixin, props as idProps } from '../../mixins/id'; import { listenOnRootMixin } from '../../mixins/listen-on-root'; import { normalizeSlotMixin } from '../../mixins/normalize-slot'; import { BVCollapse } from './helpers/bv-collapse'; // --- Constants --- var ROOT_ACTION_EVENT_NAME_TOGGLE = getRootActionEventName(NAME_COLLAPSE, 'toggle'); var ROOT_ACTION_EVENT_NAME_REQUEST_STATE = getRootActionEventName(NAME_COLLAPSE, 'request-state'); var ROOT_EVENT_NAME_ACCORDION = getRootEventName(NAME_COLLAPSE, 'accordion'); 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 }), 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), {}, { // If `true` (and `visible` is `true` on mount), animate initially visible accordion: makeProp(PROP_TYPE_STRING), appear: makeProp(PROP_TYPE_BOOLEAN, false), isNav: makeProp(PROP_TYPE_BOOLEAN, false), tag: makeProp(PROP_TYPE_STRING, 'div') })), NAME_COLLAPSE); // --- Main component --- // @vue/component export var BCollapse = /*#__PURE__*/extend({ name: NAME_COLLAPSE, mixins: [idMixin, modelMixin, normalizeSlotMixin, listenOnRootMixin], props: props, data: function data() { return { show: this[MODEL_PROP_NAME], transitioning: false }; }, computed: { classObject: function classObject() { var transitioning = this.transitioning; return { 'navbar-collapse': this.isNav, collapse: !transitioning, show: this.show && !transitioning }; }, slotScope: function slotScope() { var _this = this; return { visible: this.show, close: function close() { _this.show = false; } }; } }, watch: (_watch = {}, _defineProperty(_watch, MODEL_PROP_NAME, function (newValue) { if (newValue !== this.show) { this.show = newValue; } }), _defineProperty(_watch, "show", function show(newValue, oldValue) { if (newValue !== oldValue) { this.emitState(); } }), _watch), created: function created() { this.show = this[MODEL_PROP_NAME]; }, mounted: function mounted() { var _this2 = this; this.show = this[MODEL_PROP_NAME]; // Listen for toggle events to open/close us this.listenOnRoot(ROOT_ACTION_EVENT_NAME_TOGGLE, this.handleToggleEvent); // Listen to other collapses for accordion events this.listenOnRoot(ROOT_EVENT_NAME_ACCORDION, this.handleAccordionEvent); if (this.isNav) { // Set up handlers this.setWindowEvents(true); this.handleResize(); } this.$nextTick(function () { _this2.emitState(); }); // Listen for "Sync state" requests from `v-b-toggle` this.listenOnRoot(ROOT_ACTION_EVENT_NAME_REQUEST_STATE, function (id) { if (id === _this2.safeId()) { _this2.$nextTick(_this2.emitSync); } }); }, updated: function updated() { // Emit a private event every time this component updates to ensure // the toggle button is in sync with the collapse's state // It is emitted regardless if the visible state changes this.emitSync(); }, /* istanbul ignore next */ deactivated: function deactivated() { if (this.isNav) { this.setWindowEvents(false); } }, /* istanbul ignore next */ activated: function activated() { if (this.isNav) { this.setWindowEvents(true); } this.emitSync(); }, beforeDestroy: function beforeDestroy() { // Trigger state emit if needed this.show = false; if (this.isNav && IS_BROWSER) { this.setWindowEvents(false); } }, methods: { setWindowEvents: function setWindowEvents(on) { eventOnOff(on, window, 'resize', this.handleResize, EVENT_OPTIONS_NO_CAPTURE); eventOnOff(on, window, 'orientationchange', this.handleResize, EVENT_OPTIONS_NO_CAPTURE); }, toggle: function toggle() { this.show = !this.show; }, onEnter: function onEnter() { this.transitioning = true; // This should be moved out so we can add cancellable events this.$emit(EVENT_NAME_SHOW); }, onAfterEnter: function onAfterEnter() { this.transitioning = false; this.$emit(EVENT_NAME_SHOWN); }, onLeave: function onLeave() { this.transitioning = true; // This should be moved out so we can add cancellable events this.$emit(EVENT_NAME_HIDE); }, onAfterLeave: function onAfterLeave() { this.transitioning = false; this.$emit(EVENT_NAME_HIDDEN); }, emitState: function emitState() { var show = this.show, accordion = this.accordion; var id = this.safeId(); this.$emit(MODEL_EVENT_NAME, show); // Let `v-b-toggle` know the state of this collapse this.emitOnRoot(ROOT_EVENT_NAME_STATE, id, show); if (accordion && show) { // Tell the other collapses in this accordion to close this.emitOnRoot(ROOT_EVENT_NAME_ACCORDION, id, accordion); } }, emitSync: function emitSync() { // Emit a private event every time this component updates to ensure // the toggle button is in sync with the collapse's state // It is emitted regardless if the visible state changes this.emitOnRoot(ROOT_EVENT_NAME_SYNC_STATE, this.safeId(), this.show); }, checkDisplayBlock: function checkDisplayBlock() { // Check to see if the collapse has `display: block !important` set // We can't set `display: none` directly on `this.$el`, as it would // trigger a new transition to start (or cancel a current one) var $el = this.$el; var restore = hasClass($el, CLASS_NAME_SHOW); removeClass($el, CLASS_NAME_SHOW); var isBlock = getCS($el).display === 'block'; if (restore) { addClass($el, CLASS_NAME_SHOW); } return isBlock; }, clickHandler: function clickHandler(event) { var el = event.target; // If we are in a nav/navbar, close the collapse when non-disabled link clicked /* istanbul ignore next: can't test `getComputedStyle()` in JSDOM */ if (!this.isNav || !el || getCS(this.$el).display !== 'block') { return; } // Only close the collapse if it is not forced to be `display: block !important` if ((matches(el, '.nav-link,.dropdown-item') || closest('.nav-link,.dropdown-item', el)) && !this.checkDisplayBlock()) { this.show = false; } }, handleToggleEvent: function handleToggleEvent(id) { if (id === this.safeId()) { this.toggle(); } }, handleAccordionEvent: function handleAccordionEvent(openedId, openAccordion) { var accordion = this.accordion, show = this.show; if (!accordion || accordion !== openAccordion) { return; } var isThis = openedId === this.safeId(); // Open this collapse if not shown or // close this collapse if shown if (isThis && !show || !isThis && show) { this.toggle(); } }, handleResize: function handleResize() { // Handler for orientation/resize to set collapsed state in nav/navbar this.show = getCS(this.$el).display === 'block'; } }, render: function render(h) { var appear = this.appear; var $content = h(this.tag, { class: this.classObject, directives: [{ name: 'show', value: this.show }], attrs: { id: this.safeId() }, on: { click: this.clickHandler } }, this.normalizeSlot(SLOT_NAME_DEFAULT, this.slotScope)); return h(BVCollapse, { props: { appear: appear }, on: { enter: this.onEnter, afterEnter: this.onAfterEnter, leave: this.onLeave, afterLeave: this.onAfterLeave } }, [$content]); } });