UNPKG

vuetify

Version:

Vue.js 2 Semantic Component Framework

360 lines (326 loc) 8.58 kB
import '../../stylus/components/_navigation-drawer.styl' // Mixins import Applicationable from '../../mixins/applicationable' import Dependent from '../../mixins/dependent' import Overlayable from '../../mixins/overlayable' import SSRBootable from '../../mixins/ssr-bootable' import Themeable from '../../mixins/themeable' // Directives import ClickOutside from '../../directives/click-outside' import Resize from '../../directives/resize' import Touch from '../../directives/touch' // Helpers import { convertToUnit } from '../../util/helpers' /* @vue/component */ export default { name: 'v-navigation-drawer', directives: { ClickOutside, Resize, Touch }, mixins: [ Applicationable(null, [ 'miniVariant', 'right', 'width' ]), Dependent, Overlayable, SSRBootable, Themeable ], props: { clipped: Boolean, disableRouteWatcher: Boolean, disableResizeWatcher: Boolean, height: { type: [Number, String], default: '100%' }, floating: Boolean, miniVariant: Boolean, miniVariantWidth: { type: [Number, String], default: 80 }, mobileBreakPoint: { type: [Number, String], default: 1264 }, permanent: Boolean, right: Boolean, stateless: Boolean, temporary: Boolean, touchless: Boolean, width: { type: [Number, String], default: 300 }, value: { required: false } }, data: () => ({ isActive: false, touchArea: { left: 0, right: 0 } }), computed: { /** * Used for setting an app * value from a dynamic * property. Called from * applicationable.js * * @return {string} */ applicationProperty () { return this.right ? 'right' : 'left' }, calculatedTransform () { if (this.isActive) return 0 return this.right ? this.calculatedWidth : -this.calculatedWidth }, calculatedWidth () { return this.miniVariant ? this.miniVariantWidth : this.width }, classes () { return { 'v-navigation-drawer': true, 'v-navigation-drawer--absolute': this.absolute, 'v-navigation-drawer--clipped': this.clipped, 'v-navigation-drawer--close': !this.isActive, 'v-navigation-drawer--fixed': !this.absolute && (this.app || this.fixed), 'v-navigation-drawer--floating': this.floating, 'v-navigation-drawer--is-mobile': this.isMobile, 'v-navigation-drawer--mini-variant': this.miniVariant, 'v-navigation-drawer--open': this.isActive, 'v-navigation-drawer--right': this.right, 'v-navigation-drawer--temporary': this.temporary, ...this.themeClasses } }, hasApp () { return this.app && (!this.isMobile && !this.temporary) }, isMobile () { return !this.stateless && !this.permanent && !this.temporary && this.$vuetify.breakpoint.width < parseInt(this.mobileBreakPoint, 10) }, marginTop () { if (!this.hasApp) return 0 let marginTop = this.$vuetify.application.bar marginTop += this.clipped ? this.$vuetify.application.top : 0 return marginTop }, maxHeight () { if (!this.hasApp) return null const maxHeight = ( this.$vuetify.application.bottom + this.$vuetify.application.footer + this.$vuetify.application.bar ) if (!this.clipped) return maxHeight return maxHeight + this.$vuetify.application.top }, reactsToClick () { return !this.stateless && !this.permanent && (this.isMobile || this.temporary) }, reactsToMobile () { return !this.disableResizeWatcher && !this.stateless && !this.permanent && !this.temporary }, reactsToRoute () { return !this.disableRouteWatcher && !this.stateless && (this.temporary || this.isMobile) }, resizeIsDisabled () { return this.disableResizeWatcher || this.stateless }, showOverlay () { return this.isActive && (this.isMobile || this.temporary) }, styles () { const styles = { height: convertToUnit(this.height), marginTop: `${this.marginTop}px`, maxHeight: `calc(100% - ${+this.maxHeight}px)`, transform: `translateX(${this.calculatedTransform}px)`, width: `${this.calculatedWidth}px` } return styles } }, watch: { $route () { if (this.reactsToRoute && this.closeConditional()) { this.isActive = false } }, isActive (val) { this.$emit('input', val) this.callUpdate() }, /** * When mobile changes, adjust * the active state only when * there has been a previous * value */ isMobile (val, prev) { !val && this.isActive && !this.temporary && this.removeOverlay() if (prev == null || this.resizeIsDisabled || !this.reactsToMobile ) return this.isActive = !val this.callUpdate() }, permanent (val) { // If enabling prop // enable the drawer if (val) { this.isActive = true } this.callUpdate() }, showOverlay (val) { if (val) this.genOverlay() else this.removeOverlay() }, temporary () { this.callUpdate() }, value (val) { if (this.permanent) return if (val == null) return this.init() if (val !== this.isActive) this.isActive = val } }, beforeMount () { this.init() }, methods: { calculateTouchArea () { if (!this.$el.parentNode) return const parentRect = this.$el.parentNode.getBoundingClientRect() this.touchArea = { left: parentRect.left + 50, right: parentRect.right - 50 } }, closeConditional () { return this.isActive && this.reactsToClick }, genDirectives () { const directives = [{ name: 'click-outside', value: () => (this.isActive = false), args: { closeConditional: this.closeConditional, include: this.getOpenDependentElements } }] !this.touchless && directives.push({ name: 'touch', value: { parent: true, left: this.swipeLeft, right: this.swipeRight } }) return directives }, /** * Sets state before mount to avoid * entry transitions in SSR * * @return {void} */ init () { if (this.permanent) { this.isActive = true } else if (this.stateless || this.value != null ) { this.isActive = this.value } else if (!this.temporary) { this.isActive = !this.isMobile } }, swipeRight (e) { if (this.isActive && !this.right) return this.calculateTouchArea() if (Math.abs(e.touchendX - e.touchstartX) < 100) return if (!this.right && e.touchstartX <= this.touchArea.left ) this.isActive = true else if (this.right && this.isActive) this.isActive = false }, swipeLeft (e) { if (this.isActive && this.right) return this.calculateTouchArea() if (Math.abs(e.touchendX - e.touchstartX) < 100) return if (this.right && e.touchstartX >= this.touchArea.right ) this.isActive = true else if (!this.right && this.isActive) this.isActive = false }, /** * Update the application layout * * @return {number} */ updateApplication () { return !this.isActive || this.temporary || this.isMobile ? 0 : this.calculatedWidth } }, render (h) { const data = { 'class': this.classes, style: this.styles, directives: this.genDirectives(), on: { click: () => { if (!this.miniVariant) return this.$emit('update:miniVariant', false) }, transitionend: e => { if (e.target !== e.currentTarget) return this.$emit('transitionend', e) // IE11 does not support new Event('resize') const resizeEvent = document.createEvent('UIEvents') resizeEvent.initUIEvent('resize', true, false, window, 0) window.dispatchEvent(resizeEvent) } } } return h('aside', data, [ this.$slots.default, h('div', { 'class': 'v-navigation-drawer__border' }) ]) } }