393 lines
10 KiB
JavaScript
393 lines
10 KiB
JavaScript
|
// Styles
|
||
|
import "../../../src/components/VNavigationDrawer/VNavigationDrawer.sass"; // Components
|
||
|
|
||
|
import VImg from '../VImg/VImg'; // Mixins
|
||
|
|
||
|
import Applicationable from '../../mixins/applicationable';
|
||
|
import Colorable from '../../mixins/colorable';
|
||
|
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'; // Utilities
|
||
|
|
||
|
import { convertToUnit, getSlot } from '../../util/helpers';
|
||
|
import mixins from '../../util/mixins';
|
||
|
const baseMixins = mixins(Applicationable('left', ['isActive', 'isMobile', 'miniVariant', 'expandOnHover', 'permanent', 'right', 'temporary', 'width']), Colorable, Dependent, Overlayable, SSRBootable, Themeable);
|
||
|
/* @vue/component */
|
||
|
|
||
|
export default baseMixins.extend({
|
||
|
name: 'v-navigation-drawer',
|
||
|
|
||
|
provide() {
|
||
|
return {
|
||
|
isInNav: this.tag === 'nav'
|
||
|
};
|
||
|
},
|
||
|
|
||
|
directives: {
|
||
|
ClickOutside,
|
||
|
Resize,
|
||
|
Touch
|
||
|
},
|
||
|
props: {
|
||
|
bottom: Boolean,
|
||
|
clipped: Boolean,
|
||
|
disableResizeWatcher: Boolean,
|
||
|
disableRouteWatcher: Boolean,
|
||
|
expandOnHover: Boolean,
|
||
|
floating: Boolean,
|
||
|
height: {
|
||
|
type: [Number, String],
|
||
|
|
||
|
default() {
|
||
|
return this.app ? '100vh' : '100%';
|
||
|
}
|
||
|
|
||
|
},
|
||
|
miniVariant: Boolean,
|
||
|
miniVariantWidth: {
|
||
|
type: [Number, String],
|
||
|
default: 80
|
||
|
},
|
||
|
mobileBreakPoint: {
|
||
|
type: [Number, String],
|
||
|
default: 1264
|
||
|
},
|
||
|
permanent: Boolean,
|
||
|
right: Boolean,
|
||
|
src: {
|
||
|
type: [String, Object],
|
||
|
default: ''
|
||
|
},
|
||
|
stateless: Boolean,
|
||
|
tag: {
|
||
|
type: String,
|
||
|
|
||
|
default() {
|
||
|
return this.app ? 'nav' : 'aside';
|
||
|
}
|
||
|
|
||
|
},
|
||
|
temporary: Boolean,
|
||
|
touchless: Boolean,
|
||
|
width: {
|
||
|
type: [Number, String],
|
||
|
default: 256
|
||
|
},
|
||
|
value: {
|
||
|
required: false
|
||
|
}
|
||
|
},
|
||
|
data: () => ({
|
||
|
isMouseover: false,
|
||
|
touchArea: {
|
||
|
left: 0,
|
||
|
right: 0
|
||
|
}
|
||
|
}),
|
||
|
computed: {
|
||
|
/**
|
||
|
* Used for setting an app value from a dynamic
|
||
|
* property. Called from applicationable.js
|
||
|
*/
|
||
|
applicationProperty() {
|
||
|
return this.right ? 'right' : 'left';
|
||
|
},
|
||
|
|
||
|
classes() {
|
||
|
return {
|
||
|
'v-navigation-drawer': true,
|
||
|
'v-navigation-drawer--absolute': this.absolute,
|
||
|
'v-navigation-drawer--bottom': this.bottom,
|
||
|
'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--is-mouseover': this.isMouseover,
|
||
|
'v-navigation-drawer--mini-variant': this.miniVariant || this.expandOnHover && !this.isMouseover,
|
||
|
'v-navigation-drawer--open': this.isActive,
|
||
|
'v-navigation-drawer--open-on-hover': this.expandOnHover,
|
||
|
'v-navigation-drawer--right': this.right,
|
||
|
'v-navigation-drawer--temporary': this.temporary,
|
||
|
...this.themeClasses
|
||
|
};
|
||
|
},
|
||
|
|
||
|
computedMaxHeight() {
|
||
|
if (!this.hasApp) return null;
|
||
|
const computedMaxHeight = this.$vuetify.application.bottom + this.$vuetify.application.footer + this.$vuetify.application.bar;
|
||
|
if (!this.clipped) return computedMaxHeight;
|
||
|
return computedMaxHeight + this.$vuetify.application.top;
|
||
|
},
|
||
|
|
||
|
computedTop() {
|
||
|
if (!this.hasApp) return 0;
|
||
|
let computedTop = this.$vuetify.application.bar;
|
||
|
computedTop += this.clipped ? this.$vuetify.application.top : 0;
|
||
|
return computedTop;
|
||
|
},
|
||
|
|
||
|
computedTransform() {
|
||
|
if (this.isActive) return 0;
|
||
|
if (this.isBottom) return 100;
|
||
|
return this.right ? 100 : -100;
|
||
|
},
|
||
|
|
||
|
computedWidth() {
|
||
|
if (this.expandOnHover && !this.isMouseover || this.miniVariant) return this.miniVariantWidth;
|
||
|
return this.width;
|
||
|
},
|
||
|
|
||
|
hasApp() {
|
||
|
return this.app && !this.isMobile && !this.temporary;
|
||
|
},
|
||
|
|
||
|
isBottom() {
|
||
|
return this.bottom && this.isMobile;
|
||
|
},
|
||
|
|
||
|
isMobile() {
|
||
|
return !this.stateless && !this.permanent && this.$vuetify.breakpoint.width < parseInt(this.mobileBreakPoint, 10);
|
||
|
},
|
||
|
|
||
|
reactsToClick() {
|
||
|
return !this.stateless && !this.permanent && (this.isMobile || this.temporary);
|
||
|
},
|
||
|
|
||
|
reactsToMobile() {
|
||
|
return this.app && !this.disableResizeWatcher && !this.permanent && !this.stateless && !this.temporary;
|
||
|
},
|
||
|
|
||
|
reactsToResize() {
|
||
|
return !this.disableResizeWatcher && !this.stateless;
|
||
|
},
|
||
|
|
||
|
reactsToRoute() {
|
||
|
return !this.disableRouteWatcher && !this.stateless && (this.temporary || this.isMobile);
|
||
|
},
|
||
|
|
||
|
showOverlay() {
|
||
|
return this.isActive && (this.isMobile || this.temporary);
|
||
|
},
|
||
|
|
||
|
styles() {
|
||
|
const translate = this.isBottom ? 'translateY' : 'translateX';
|
||
|
const styles = {
|
||
|
height: convertToUnit(this.height),
|
||
|
top: !this.isBottom ? convertToUnit(this.computedTop) : 'auto',
|
||
|
maxHeight: this.computedMaxHeight != null ? `calc(100% - ${convertToUnit(this.computedMaxHeight)})` : undefined,
|
||
|
transform: `${translate}(${convertToUnit(this.computedTransform, '%')})`,
|
||
|
width: convertToUnit(this.computedWidth)
|
||
|
};
|
||
|
return styles;
|
||
|
}
|
||
|
|
||
|
},
|
||
|
watch: {
|
||
|
$route: 'onRouteChange',
|
||
|
|
||
|
isActive(val) {
|
||
|
this.$emit('input', val);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 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.reactsToResize || !this.reactsToMobile) return;
|
||
|
this.isActive = !val;
|
||
|
},
|
||
|
|
||
|
permanent(val) {
|
||
|
// If enabling prop enable the drawer
|
||
|
if (val) this.isActive = true;
|
||
|
},
|
||
|
|
||
|
showOverlay(val) {
|
||
|
if (val) this.genOverlay();else this.removeOverlay();
|
||
|
},
|
||
|
|
||
|
value(val) {
|
||
|
if (this.permanent) return;
|
||
|
|
||
|
if (val == null) {
|
||
|
this.init();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (val !== this.isActive) this.isActive = val;
|
||
|
}
|
||
|
|
||
|
},
|
||
|
|
||
|
beforeMount() {
|
||
|
this.init();
|
||
|
},
|
||
|
|
||
|
methods: {
|
||
|
calculateTouchArea() {
|
||
|
const parent = this.$el.parentNode;
|
||
|
if (!parent) return;
|
||
|
const parentRect = parent.getBoundingClientRect();
|
||
|
this.touchArea = {
|
||
|
left: parentRect.left + 50,
|
||
|
right: parentRect.right - 50
|
||
|
};
|
||
|
},
|
||
|
|
||
|
closeConditional() {
|
||
|
return this.isActive && this.reactsToClick;
|
||
|
},
|
||
|
|
||
|
genAppend() {
|
||
|
return this.genPosition('append');
|
||
|
},
|
||
|
|
||
|
genBackground() {
|
||
|
const props = {
|
||
|
height: '100%',
|
||
|
width: '100%',
|
||
|
src: this.src
|
||
|
};
|
||
|
const image = this.$scopedSlots.img ? this.$scopedSlots.img(props) : this.$createElement(VImg, {
|
||
|
props
|
||
|
});
|
||
|
return this.$createElement('div', {
|
||
|
staticClass: 'v-navigation-drawer__image'
|
||
|
}, [image]);
|
||
|
},
|
||
|
|
||
|
genDirectives() {
|
||
|
const directives = [{
|
||
|
name: 'click-outside',
|
||
|
value: () => this.isActive = false,
|
||
|
args: {
|
||
|
closeConditional: this.closeConditional,
|
||
|
include: this.getOpenDependentElements
|
||
|
}
|
||
|
}];
|
||
|
|
||
|
if (!this.touchless && !this.stateless) {
|
||
|
directives.push({
|
||
|
name: 'touch',
|
||
|
value: {
|
||
|
parent: true,
|
||
|
left: this.swipeLeft,
|
||
|
right: this.swipeRight
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
return directives;
|
||
|
},
|
||
|
|
||
|
genListeners() {
|
||
|
const on = {
|
||
|
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);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
if (this.miniVariant) {
|
||
|
on.click = () => this.$emit('update:mini-variant', false);
|
||
|
}
|
||
|
|
||
|
if (this.expandOnHover) {
|
||
|
on.mouseenter = () => this.isMouseover = true;
|
||
|
|
||
|
on.mouseleave = () => this.isMouseover = false;
|
||
|
}
|
||
|
|
||
|
return on;
|
||
|
},
|
||
|
|
||
|
genPosition(name) {
|
||
|
const slot = getSlot(this, name);
|
||
|
if (!slot) return slot;
|
||
|
return this.$createElement('div', {
|
||
|
staticClass: `v-navigation-drawer__${name}`
|
||
|
}, slot);
|
||
|
},
|
||
|
|
||
|
genPrepend() {
|
||
|
return this.genPosition('prepend');
|
||
|
},
|
||
|
|
||
|
genContent() {
|
||
|
return this.$createElement('div', {
|
||
|
staticClass: 'v-navigation-drawer__content'
|
||
|
}, this.$slots.default);
|
||
|
},
|
||
|
|
||
|
genBorder() {
|
||
|
return this.$createElement('div', {
|
||
|
staticClass: 'v-navigation-drawer__border'
|
||
|
});
|
||
|
},
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
onRouteChange() {
|
||
|
if (this.reactsToRoute && this.closeConditional()) {
|
||
|
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;
|
||
|
},
|
||
|
|
||
|
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;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Update the application layout
|
||
|
*/
|
||
|
updateApplication() {
|
||
|
if (!this.isActive || this.isMobile || this.temporary || !this.$el) return 0;
|
||
|
const width = Number(this.computedWidth);
|
||
|
return isNaN(width) ? this.$el.clientWidth : width;
|
||
|
}
|
||
|
|
||
|
},
|
||
|
|
||
|
render(h) {
|
||
|
const children = [this.genPrepend(), this.genContent(), this.genAppend(), this.genBorder()];
|
||
|
if (this.src || getSlot(this, 'img')) children.unshift(this.genBackground());
|
||
|
return h(this.tag, this.setBackgroundColor(this.color, {
|
||
|
class: this.classes,
|
||
|
style: this.styles,
|
||
|
directives: this.genDirectives(),
|
||
|
on: this.genListeners()
|
||
|
}), children);
|
||
|
}
|
||
|
|
||
|
});
|
||
|
//# sourceMappingURL=VNavigationDrawer.js.map
|