uikit
Version:
UIkit is a lightweight and modular front-end framework for developing fast and powerful web interfaces.
180 lines (142 loc) • 4.71 kB
JavaScript
import {
$,
addClass,
append,
css,
endsWith,
hasClass,
height,
isVisible,
parent,
removeClass,
unwrap,
wrapAll,
} from 'uikit-util';
import { swipe } from '../api/observables';
import Modal from '../mixin/modal';
export default {
mixins: [Modal],
args: 'mode',
props: {
mode: String,
flip: Boolean,
overlay: Boolean,
swiping: Boolean,
},
data: {
mode: 'slide',
flip: false,
overlay: false,
clsPage: 'uk-offcanvas-page',
clsContainer: 'uk-offcanvas-container',
selPanel: '.uk-offcanvas-bar',
clsFlip: 'uk-offcanvas-flip',
clsContainerAnimation: 'uk-offcanvas-container-animation',
clsSidebarAnimation: 'uk-offcanvas-bar-animation',
clsMode: 'uk-offcanvas',
clsOverlay: 'uk-offcanvas-overlay',
selClose: '.uk-offcanvas-close',
container: false,
swiping: true,
},
computed: {
clsFlip: ({ flip, clsFlip }) => (flip ? clsFlip : ''),
clsOverlay: ({ overlay, clsOverlay }) => (overlay ? clsOverlay : ''),
clsMode: ({ mode, clsMode }) => `${clsMode}-${mode}`,
clsSidebarAnimation: ({ mode, clsSidebarAnimation }) =>
mode === 'none' || mode === 'reveal' ? '' : clsSidebarAnimation,
clsContainerAnimation: ({ mode, clsContainerAnimation }) =>
mode !== 'push' && mode !== 'reveal' ? '' : clsContainerAnimation,
transitionElement({ mode }) {
return mode === 'reveal' ? parent(this.panel) : this.panel;
},
},
observe: swipe({ filter: ({ swiping }) => swiping }),
update: {
read() {
if (this.isToggled() && !isVisible(this.$el)) {
this.hide();
}
},
events: ['resize'],
},
events: [
{
name: 'touchmove',
self: true,
passive: false,
filter: ({ overlay }) => overlay,
handler(e) {
e.cancelable && e.preventDefault();
},
},
{
name: 'show',
self: true,
handler() {
if (this.mode === 'reveal' && !hasClass(parent(this.panel), this.clsMode)) {
wrapAll(this.panel, '<div>');
addClass(parent(this.panel), this.clsMode);
}
const { body, scrollingElement } = document;
addClass(body, this.clsContainer, this.clsFlip);
css(body, 'touch-action', 'pan-y pinch-zoom');
css(this.$el, 'display', 'block');
css(this.panel, 'maxWidth', scrollingElement.clientWidth);
addClass(this.$el, this.clsOverlay);
addClass(
this.panel,
this.clsSidebarAnimation,
this.mode === 'reveal' ? '' : this.clsMode,
);
height(body); // force reflow
addClass(body, this.clsContainerAnimation);
this.clsContainerAnimation && suppressUserScale();
},
},
{
name: 'hide',
self: true,
handler() {
removeClass(document.body, this.clsContainerAnimation);
css(document.body, 'touch-action', '');
},
},
{
name: 'hidden',
self: true,
handler() {
this.clsContainerAnimation && resumeUserScale();
if (this.mode === 'reveal') {
unwrap(this.panel);
}
removeClass(this.panel, this.clsSidebarAnimation, this.clsMode);
removeClass(this.$el, this.clsOverlay);
css(this.$el, 'display', '');
css(this.panel, 'maxWidth', '');
removeClass(document.body, this.clsContainer, this.clsFlip);
},
},
{
name: 'swipeLeft swipeRight',
handler(e) {
if (this.isToggled() && endsWith(e.type, 'Left') ^ this.flip) {
this.hide();
}
},
},
],
};
// Chrome in responsive mode zooms page upon opening offcanvas
function suppressUserScale() {
getViewport().content += ',user-scalable=0';
}
function resumeUserScale() {
const viewport = getViewport();
viewport.content = viewport.content.replace(/,user-scalable=0$/, '');
}
function getViewport() {
return (
$('meta[name="viewport"]', document.head) || append(document.head, '<meta name="viewport">')
);
}