Initial commit

This commit is contained in:
Rick Dullaart 2022-11-18 21:38:41 +01:00
commit b82db0e393
369 changed files with 24255 additions and 0 deletions

47
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,47 @@
# Contributing to this project
Please take a moment to review this document in order to make the contribution
process easy and effective for everyone involved.
## Using the issue tracker
The issue tracker is the preferred channel for [bug reports](#bugs) and
[features requests](#features), but please respect the following restrictions:
* Please **do not** use the issue tracker for personal support requests.
* Please keep the discussion **on topic** and respect the opinions of others.
<a name="bugs"></a>
## Bug reports
A bug is a _demonstrable problem_ that is caused by the code in the repository.
Good bug reports are extremely helpful - thank you!
Guidelines for bug reports:
1. **Use the GitHub issue search** &mdash; check if the issue has already been
reported.
2. **Check if the issue has been fixed** &mdash; try to reproduce it using the
latest branch in the repository.
3. **Isolate the problem** &mdash; create a [reduced test
case](http://css-tricks.com/reduced-test-cases/) and a live example.
A good bug report shouldn't leave others needing to chase you up for more
information. Please try to be as detailed as possible in your report. What is
your environment? What steps will reproduce the issue? What browser(s) and OS
experience the problem? What would you expect to be the outcome? All these
details will help people to fix any potential bugs.
<a name="features"></a>
## Feature requests
Feature requests are welcome. But take a moment to find out whether your idea
fits with the scope and aims of the project. It's up to *you* to make a strong
case to convince the project's developers of the merits of this feature. Please
provide as much detail and context as possible.

2
LICENSE.txt Normal file
View File

@ -0,0 +1,2 @@
This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License.
To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.

31
README.md Normal file
View File

@ -0,0 +1,31 @@
mmenu.js
================
The best javascript plugin for app look-alike on- and off-canvas menus with sliding submenus for your website and webapp. It is very customizable through a wide range of options, extensions and add-ons and it will always fit your needs.
Need help? Have a look at [the documentation](https://mmenujs.com) for demos, tutorials, documentation and support.<br />
Working on a WordPress site? Check out [the mmenu WordPress plugin](https://mmenujs.com/wordpress-plugin).
<img src="https://mmenujs.com/img/preview-mmenu.png" alt="mmenu.js" width="100%" />
### Licence
The mmenu javascript plugin is licensed under the [CC-BY-NC-4.0 license](http://creativecommons.org/licenses/by-nc/4.0/).<br />
You can [purchase a license](https://mmenujs.com/download.html) if you want to use it in a commercial project.
### Learn more
+ [Tutorial](https://mmenujs.com/tutorials/off-canvas/)
+ [Options](https://mmenujs.com/documentation/core/options.html)
+ [Extensions](https://mmenujs.com/documentation/extensions/)
+ [Add-ons](https://mmenujs.com/documentation/addons/)
+ [API](https://mmenujs.com/documentation/core/api.html)
### Browser support
As of version 8, the mmenu.js plugin only supports [ECMAScript 6 compliant browsers](https://kangax.github.io/compat-table/es6/).<br />
For Internet Explorer 10 and 11, you''ll need [some polyfills](https://polyfill.io/v3/polyfill.min.js?features=default%2CElement.prototype.matches%2CElement.prototype.prepend%2CElement.prototype.closest).<br />
[Version 7](https://github.com/FrDH/jQuery.mmenu/releases/tag/v7.2.2) should work to some degree in Internet Explorer 9, but you'll need a [matchMedia polyfill](https://polyfill.io/v3/polyfill.min.js?features=default%2CmatchMedia),
[version 6](https://github.com/FrDH/jQuery.mmenu/releases/tag/v6.1.8) should work in Internet Explorer 9 without any shortcomings.</p>
### Development
This project uses [Gulp(4)](http://gulpjs.com/) to compile, minify and concatenate the TS/JS and SCSS/CSS files.
If you are unfamiliar with Gulp, check [this tutorial](https://travismaynard.com/writing/getting-started-with-gulp) on how to get started.<br />
Run `gulp watch` in the command-line to put a watch on the files and run all scripts immediately after saving your changes.

27
composer.json Normal file
View File

@ -0,0 +1,27 @@
{
"name" : "frdh/mmenu.js",
"version" : "8.5.3",
"authors" : [{
"name" : "Fred Heusschen",
"email" : "info@frebsite.nl",
"homepage" : "http://www.frebsite.nl",
"role" : "King :)"
}],
"license" : "CC-BY-NC-4.0",
"description" : "The best javascript plugin for app look-alike on- and off-canvas menus with sliding submenus for your website and webapp.",
"keywords" : [
"app",
"list",
"listview",
"megamenu",
"menu",
"mmenu",
"mobile",
"navigation",
"off-canvas",
"on-canvas",
"curtain",
"panels",
"submenu"
]
}

162
demo/advanced.html Normal file
View File

@ -0,0 +1,162 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="author" content="www.frebsite.nl" />
<meta name="viewport" content="width=device-width minimum-scale=1.0 maximum-scale=1.0 user-scalable=no" />
<title>mmenu.js demo</title>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
<link rel="stylesheet" href="css/demo.css" />
<link rel="stylesheet" href="../dist/mmenu.css" />
<style>
:root {
--mm-sidebar-expanded-size: 300px;
}
.mm-menu {
--mm-listitem-size: 50px;
--mm-navbar-size: 50px;
}
@media (min-width: 992px) {
.header a {
display: none;
}
}
.mm-navbar_tabs span {
display: inline-block;
margin-left: 8px;
}
@media (max-width: 450px) {
.mm-navbar_tabs span {
display: none;
}
}
</style>
</head>
<body>
<div id="page">
<div class="header">
<a href="#menu"><span></span></a>
Demo
</div>
<div class="content">
<p><strong>This is an advanced demo.</strong><br />
Click the menu icon to open the menu.</p>
</div>
<nav id="menu">
<div id="panel-menu">
<ul>
<li><a href="#/">Home</a></li>
<li><span>About us</span>
<ul>
<li><a href="#/">History</a></li>
<li><span>The team</span>
<ul>
<li><a href="#/">Management</a></li>
<li><a href="#/">Sales</a></li>
<li><a href="#/">Development</a></li>
</ul>
</li>
<li><a href="#/">Our address</a></li>
</ul>
</li>
<li><a href="#/">Contact</a></li>
<li class="Divider">Other demos</li>
<li><a href="default.html">Default demo</a></li>
<li><a href="onepage.html">One page demo</a></li>
</ul>
</div>
<div id="panel-account">
<ul>
<li><a href="#/">My profile</a></li>
<li><a href="#/">Privacy settings</a></li>
<li><a href="#/">Activity</a></li>
<li><a href="#/">Sign out</a></li>
</ul>
</div>
<div id="panel-cart">
<p style="text-align: center; padding-top: 30px;">Your shoppingcart is empty.<br />
<a href="#/">Continue shopping.</a></p>
</div>
</nav>
</div>
<!-- mmenu scripts -->
<script src="../dist/mmenu.polyfills.js"></script>
<script src="../dist/mmenu.js"></script>
<script>
new Mmenu(
document.querySelector('#menu'),
{
extensions : [ 'theme-dark', 'shadow-page' ],
setSelected : true,
counters : true,
searchfield : {
placeholder : 'Search menu items'
},
iconbar : {
use : '(min-width: 450px)',
top : [
'<a href="#/"><span class="fa fa-home"></span></a>'
],
bottom : [
'<a href="#/"><span class="fa fa-twitter"></span></a>',
'<a href="#/"><span class="fa fa-facebook"></span></a>',
'<a href="#/"><span class="fa fa-youtube"></span></a>'
]
},
sidebar : {
collapsed : {
use : '(min-width: 450px)',
hideNavbar : false
},
expanded : {
use : '(min-width: 992px)'
}
},
navbars : [
{
content : [ 'searchfield' ]
}, {
type : 'tabs',
content : [
'<a href="#panel-menu"><i class="fa fa-bars"></i> <span>Menu</span></a>',
'<a href="#panel-account"><i class="fa fa-user"></i> <span>Account</span></a>',
'<a href="#panel-cart"><i class="fa fa-shopping-cart"></i> <span>Cart</span></a>'
]
}, {
content : [ 'prev', 'breadcrumbs', 'close' ]
}, {
position : 'bottom',
content : [ '<a href="http://mmenu.frebsite.nl/wordpress-plugin" target="_blank">WordPress plugin</a>' ]
}
]
}, {
searchfield : {
clear : true
},
navbars : {
breadcrumbs : {
removeFirst : true
}
}
}
);
document.addEventListener( 'click', function( evnt ) {
var anchor = evnt.target.closest( 'a[href^="#/"]' );
if ( anchor ) {
alert('Thank you for clicking, but that\'s a demo link.');
evnt.preventDefault();
}
});
</script>
</body>
</html>

92
demo/css/demo.css Normal file
View File

@ -0,0 +1,92 @@
html,
body {
padding: 0;
margin: 0;
}
body {
background-color: #fff;
font-family: Arial, Helvetica, Verdana;
font-size: 14px;
line-height: 22px;
color: #666;
position: relative;
-webkit-text-size-adjust: none;
}
h1, h2, h3, h4, h5, h6 {
line-height: 1;
font-weight: bold;
margin: 20px 0 10px 0;
}
h1, h2, h3 {
font-size: 18px;
}
h4, h5, h6 {
font-size: 16px;
}
p {
margin: 0 0 10px 0;
}
a, a:link, a:active, a:visited, a:hover {
color: inherit;
text-decoration: underline;
}
nav:not(.mm-menu) {
display: none;
}
.header,
.content,
.footer {
text-align: center;
}
.header,
.footer {
background: #4bb5ef;
font-size: 16px;
font-weight: bold;
color: #fff;
line-height: 44px;
-moz-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
height: 44px;
padding: 0 50px;
}
.header.fixed {
position: fixed;
top: 0;
left: 0;
}
.footer.fixed {
position: fixed;
bottom: 0;
left: 0;
}
.header a {
display: block;
width: 28px;
height: 18px;
padding: 11px;
margin: 2px;
position: absolute;
top: 0;
left: 0;
}
.header a:before,
.header a:after {
content: '';
display: block;
background: #fff;
height: 2px;
}
.header a span {
background: #fff;
display: block;
height: 2px;
margin: 6px 0;
}
.content {
padding: 150px 50px 50px 50px;
}

75
demo/css/site.css Normal file
View File

@ -0,0 +1,75 @@
html,
body {
padding: 0;
margin: 0;
height: 100%;
}
body {
background-color: #3ea7e1;
-webkit-text-size-adjust: none;
font-family: Arial, Helvetica, Verdana;
font-size: 18px;
line-height: 26px;
color: #fff;
position: relative;
}
h1 {
text-shadow: 8px 10px 1px rgba(0,0,0,.1);
text-transform: lowercase;
font-family: 'Pacifico', Arial, sans-serif;
font-weight: normal;
font-size: 150px;
line-height: 150px;
letter-spacing: -10px;
margin: 0 0 20px 0;
}
a,
a:hover
{
color: #fff;
text-decoration: underline;
}
.phone {
position: fixed;
top: 50%;
right: 50%;
height: 760px;
width: 430px;
margin-top: -380px;
background: url( ../img/iphonex-example-blue.png ) center top / 395px auto no-repeat transparent;
}
.phone:before {
content: '';
border-radius: 30px 30px 0 0;
background: url( ../img/iphonex-example-camera.png ) center top no-repeat #4bb5ef;
display: block;
width: 290px;
height: 25px;
position: absolute;
top: 62px;
left: calc( 50% - 290px / 2);
z-index: 1;
}
.phone iframe {
position: absolute;
top: 87px;
left: 70px;
z-index: 0;
width: 290px;
height: 600px;
border: none;
border-radius: 0 0 30px 30px;
}
#page {
width: 350px;
position: fixed;
margin-top: -20px;
margin-left: 0;
top: 50%;
left: 50%;
transform: translateY(-50%);
}

64
demo/default.html Normal file
View File

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="author" content="www.frebsite.nl" />
<meta name="viewport" content="width=device-width minimum-scale=1.0 maximum-scale=1.0 user-scalable=no" />
<title>mmenu.js demo</title>
<link type="text/css" rel="stylesheet" href="css/demo.css" />
<link type="text/css" rel="stylesheet" href="../dist/mmenu.css" />
</head>
<body>
<div id="page">
<div class="header">
<a href="#menu"><span></span></a>
Demo
</div>
<div class="content">
<p><strong>This is a demo.</strong><br />
Click the menu icon to open the menu.</p>
</div>
<nav id="menu">
<ul>
<li><a href="#">Home</a></li>
<li><span>About us</span>
<ul>
<li><a href="#about/history">History</a></li>
<li><span>The team</span>
<ul>
<li><a href="#about/team/management">Management</a></li>
<li><a href="#about/team/sales">Sales</a></li>
<li><a href="#about/team/development">Development</a></li>
</ul>
</li>
<li><a href="#about/address">Our address</a></li>
</ul>
</li>
<li><a href="#contact">Contact</a></li>
<li class="Divider">Other demos</li>
<li><a href="advanced.html">Advanced demo</a></li>
<li><a href="onepage.html">One page demo</a></li>
</ul>
</nav>
</div>
<!-- mmenu scripts -->
<script src="../dist/mmenu.polyfills.js"></script>
<script src="../dist/mmenu.js"></script>
<script>
new Mmenu( document.querySelector( '#menu' ));
document.addEventListener( 'click', function( evnt ) {
var anchor = evnt.target.closest( 'a[href^="#/"]' );
if ( anchor ) {
alert('Thank you for clicking, but that\'s a demo link.');
evnt.preventDefault();
}
});
</script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

128
demo/onepage.html Normal file
View File

@ -0,0 +1,128 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="author" content="www.frebsite.nl" />
<meta name="viewport" content="width=device-width minimum-scale=1.0 maximum-scale=1.0 user-scalable=no" />
<title>mmenu.js demo</title>
<link type="text/css" rel="stylesheet" href="css/demo.css" />
<link type="text/css" rel="stylesheet" href="../dist/mmenu.css" />
<!-- for the one page layout -->
<style type="text/css">
section
{
border-top: 1px solid #ccc;
padding: 150px 0 200px;
}
section:first-child
{
border-top: none;
padding-top: 0;
}
</style>
<!-- for the fixed header -->
<style type="text/css">
.header,
.footer
{
position: fixed;
left: 0;
right: 0;
}
.header
{
top: 0;
}
.footer
{
bottom: 0;
}
@media (min-width: 800px) {
.header a
{
display: none;
}
}
</style>
</head>
<body>
<div id="page">
<div class="header Fixed">
<a href="#menu"><span></span></a>
Demo
</div>
<div class="content" id="content">
<section id="intro">
<p><strong>This is a demo</strong><br />
Click the menu icon to open the menu.</p>
<p>Some of the links in the menu link to a section on this page.</p>
</section>
<section id="widescreen">
<p><strong>Widescreen extension</strong><br />
On wider screens, the menu will always be opened.</p>
<p><a href="#menu">Open the menu.</a></p>
</section>
<section id="drag">
<p><strong>Drag add-on</strong><br />
You can also drag the page to the right to open the menu.</p>
<p><a href="#menu">Open the menu.</a></p>
</section>
<section id="fixed">
<p><strong>Fixed elements</strong><br />
Notice how the fixed header and footer slide out along with the page.</p>
<p><a href="#menu">Open the menu.</a></p>
</section>
</div>
<div class="footer Fixed">
Fixed footer :-)
</div>
<nav id="menu">
<ul>
<li><a href="#content">Introduction</a></li>
<li><a href="#widescreen">Widescreen extension</a></li>
<li><a href="#drag">Drag add-on</a></li>
<li><a href="#fixed">Fixed elements</a></li>
<li class="Divider">Other demos</li>
<li><a href="default.html">Default demo</a></li>
<li><a href="advanced.html">Advanced demo</a></li>
</ul>
</nav>
</div>
<!-- Hammer.js for dragging -->
<script type="text/javascript" src="http://cdn.jsdelivr.net/hammerjs/2.0.8/hammer.min.js"></script>
<!-- mmenu scripts -->
<script src="../dist/mmenu.polyfills.js"></script>
<script src="../dist/mmenu.js"></script>
<script>
new Mmenu(
document.querySelector( '#menu' ),
{
drag : true,
pageScroll : {
scroll : true,
update : true
},
sidebar : {
expanded : 800
}
}
);
document.addEventListener( 'click', function( evnt ) {
var anchor = evnt.target.closest( 'a[href^="#/"]' );
if ( anchor ) {
alert('Thank you for clicking, but that\'s a demo link.');
evnt.preventDefault();
}
});
</script>
</body>
</html>

132
dist/_modules/dom.js vendored Normal file
View File

@ -0,0 +1,132 @@
/**
* Create an element with classname.
*
* @param {string} selector The nodeName and classnames for the element to create.
* @return {HTMLElement} The created element.
*/
export function create(selector) {
var args = selector.split('.');
var elem = document.createElement(args.shift());
// IE11:
args.forEach(function (classname) {
elem.classList.add(classname);
});
// Better browsers:
// elem.classList.add(...args);
return elem;
}
/**
* Find all elements matching the selector.
* Basically the same as element.querySelectorAll() but it returns an actuall array.
*
* @param {HTMLElement} element Element to search in.
* @param {string} filter The filter to match.
* @return {array} Array of elements that match the filter.
*/
export function find(element, filter) {
return Array.prototype.slice.call(element.querySelectorAll(filter));
}
/**
* Find all child elements matching the (optional) selector.
*
* @param {HTMLElement} element Element to search in.
* @param {string} filter The filter to match.
* @return {array} Array of child elements that match the filter.
*/
export function children(element, filter) {
var children = Array.prototype.slice.call(element.children);
return filter ? children.filter(function (child) { return child.matches(filter); }) : children;
}
/**
* Find text excluding text from within child elements.
* @param {HTMLElement} element Element to search in.
* @return {string} The text.
*/
export function text(element) {
return Array.prototype.slice
.call(element.childNodes)
.filter(function (child) { return child.nodeType == 3; })
.map(function (child) { return child.textContent; })
.join(' ');
}
/**
* Find all preceding elements matching the selector.
*
* @param {HTMLElement} element Element to start searching from.
* @param {string} filter The filter to match.
* @return {array} Array of preceding elements that match the selector.
*/
export function parents(element, filter) {
/** Array of preceding elements that match the selector. */
var parents = [];
/** Array of preceding elements that match the selector. */
var parent = element.parentElement;
while (parent) {
parents.push(parent);
parent = parent.parentElement;
}
return filter ? parents.filter(function (parent) { return parent.matches(filter); }) : parents;
}
/**
* Find all previous siblings matching the selecotr.
*
* @param {HTMLElement} element Element to start searching from.
* @param {string} filter The filter to match.
* @return {array} Array of previous siblings that match the selector.
*/
export function prevAll(element, filter) {
/** Array of previous siblings that match the selector. */
var previous = [];
/** Current element in the loop */
var current = element.previousElementSibling;
while (current) {
if (!filter || current.matches(filter)) {
previous.push(current);
}
current = current.previousElementSibling;
}
return previous;
}
/**
* Get an element offset relative to the document.
*
* @param {HTMLElement} element Element to start measuring from.
* @param {string} [direction=top] Offset top or left.
* @return {number} The element offset relative to the document.
*/
export function offset(element, direction) {
return (element.getBoundingClientRect()[direction] +
document.body[direction === 'left' ? 'scrollLeft' : 'scrollTop']);
}
/**
* Filter out non-listitem listitems.
* @param {array} listitems Elements to filter.
* @return {array} The filtered set of listitems.
*/
export function filterLI(listitems) {
return listitems.filter(function (listitem) { return !listitem.matches('.mm-hidden'); });
}
/**
* Find anchors in listitems (excluding anchor that open a sub-panel).
* @param {array} listitems Elements to filter.
* @return {array} The found set of anchors.
*/
export function filterLIA(listitems) {
var anchors = [];
filterLI(listitems).forEach(function (listitem) {
anchors.push.apply(anchors, children(listitem, 'a.mm-listitem__text'));
});
return anchors.filter(function (anchor) { return !anchor.matches('.mm-btn_next'); });
}
/**
* Refactor a classname on multiple elements.
* @param {HTMLElement} element Element to refactor.
* @param {string} oldClass Classname to remove.
* @param {string} newClass Classname to add.
*/
export function reClass(element, oldClass, newClass) {
if (element.matches('.' + oldClass)) {
element.classList.remove(oldClass);
element.classList.add(newClass);
}
}

12
dist/_modules/dragevents/_defaults.js vendored Normal file
View File

@ -0,0 +1,12 @@
/** How far from the sides the gesture can start. */
export var area = {
top: 0,
right: 0,
bottom: 0,
left: 0
};
/** Tresholds for gestures. */
export var treshold = {
start: 15,
swipe: 15
};

15
dist/_modules/dragevents/_helpers.js vendored Normal file
View File

@ -0,0 +1,15 @@
/**
* Calculate a distance from a percentage.
* @param {string|number} position The percentage (e.g. "75%").
* @param {number} size The available width or height in pixels.
* @return {number} The calculated distance.
*/
export var percentage2number = function (position, size) {
if (typeof position == 'string') {
if (position.slice(-1) == '%') {
position = parseInt(position.slice(0, -1), 10);
position = size * (position / 100);
}
}
return position;
};

11
dist/_modules/dragevents/_settings.js vendored Normal file
View File

@ -0,0 +1,11 @@
/** Names of the possible directions. */
export var directionNames = {
x: ['Right', 'Left'],
y: ['Down', 'Up']
};
/** States for the gesture. */
export var state = {
inactive: 0,
watching: 1,
dragging: 2
};

4
dist/_modules/dragevents/_support.js vendored Normal file
View File

@ -0,0 +1,4 @@
/** Whether or not touch gestures are supported by the browser. */
export var touch = 'ontouchstart' in window ||
(navigator.msMaxTouchPoints ? true : false) ||
false;

208
dist/_modules/dragevents/index.js vendored Normal file
View File

@ -0,0 +1,208 @@
import * as support from './_support';
import * as options from './_defaults';
import * as settings from './_settings';
import { percentage2number } from './_helpers';
import { extend } from '../helpers';
var DragEvents = /** @class */ (function () {
/**
* Create the gestures.
* @param {HTMLElement} surface The surface for the gesture.
* @param {object} area Restriction where on the surface the gesture can be started.
* @param {object} treshold Treshold for the gestures.
*/
function DragEvents(surface, area, treshold) {
this.surface = surface;
this.area = extend(area, options.area);
this.treshold = extend(treshold, options.treshold);
// Set the mouse/touch events.
if (!this.surface['mmHasDragEvents']) {
this.surface.addEventListener(support.touch ? 'touchstart' : 'mousedown', this.start.bind(this));
this.surface.addEventListener(support.touch ? 'touchend' : 'mouseup', this.stop.bind(this));
this.surface.addEventListener(support.touch ? 'touchleave' : 'mouseleave', this.stop.bind(this));
this.surface.addEventListener(support.touch ? 'touchmove' : 'mousemove', this.move.bind(this));
}
this.surface['mmHasDragEvents'] = true;
}
/**
* Starting the touch gesture.
* @param {Event} event The touch event.
*/
DragEvents.prototype.start = function (event) {
this.currentPosition = {
x: event.touches ? event.touches[0].pageX : event.pageX || 0,
y: event.touches ? event.touches[0].pageY : event.pageY || 0
};
/** The widht of the surface. */
var width = this.surface.clientWidth;
/** The height of the surface. */
var height = this.surface.clientHeight;
// Check if the gesture started below the area.top.
var top = percentage2number(this.area.top, height);
if (typeof top == 'number') {
if (this.currentPosition.y < top) {
return;
}
}
// Check if the gesture started before the area.right.
var right = percentage2number(this.area.right, width);
if (typeof right == 'number') {
right = width - right;
if (this.currentPosition.x > right) {
return;
}
}
// Check if the gesture started above the area.bottom.
var bottom = percentage2number(this.area.bottom, height);
if (typeof bottom == 'number') {
bottom = height - bottom;
if (this.currentPosition.y > bottom) {
return;
}
}
// Check if the gesture started after the area.left.
var left = percentage2number(this.area.left, width);
if (typeof left == 'number') {
if (this.currentPosition.x < left) {
return;
}
}
// Store the start x- and y-position.
this.startPosition = {
x: this.currentPosition.x,
y: this.currentPosition.y
};
// Set the state of the gesture to "watching".
this.state = settings.state.watching;
};
/**
* Stopping the touch gesture.
* @param {Event} event The touch event.
*/
DragEvents.prototype.stop = function (event) {
// Dispatch the "dragEnd" events.
if (this.state == settings.state.dragging) {
/** The direction. */
var dragDirection = this._dragDirection();
/** The event information. */
var detail = this._eventDetail(dragDirection);
this._dispatchEvents('drag*End', detail);
// Dispatch the "swipe" events.
if (Math.abs(this.movement[this.axis]) > this.treshold.swipe) {
/** The direction. */
var swipeDirection = this._swipeDirection();
detail.direction = swipeDirection;
this._dispatchEvents('swipe*', detail);
}
}
// Set the state of the gesture to "inactive".
this.state = settings.state.inactive;
};
/**
* Doing the touch gesture.
* @param {Event} event The touch event.
*/
DragEvents.prototype.move = function (event) {
switch (this.state) {
case settings.state.watching:
case settings.state.dragging:
var position = {
x: event.changedTouches
? event.touches[0].pageX
: event.pageX || 0,
y: event.changedTouches
? event.touches[0].pageY
: event.pageY || 0
};
this.movement = {
x: position.x - this.currentPosition.x,
y: position.y - this.currentPosition.y
};
this.distance = {
x: position.x - this.startPosition.x,
y: position.y - this.startPosition.y
};
this.currentPosition = {
x: position.x,
y: position.y
};
this.axis =
Math.abs(this.distance.x) > Math.abs(this.distance.y)
? 'x'
: 'y';
/** The direction. */
var dragDirection = this._dragDirection();
/** The event information. */
var detail = this._eventDetail(dragDirection);
// Watching for the gesture to go past the treshold.
if (this.state == settings.state.watching) {
if (Math.abs(this.distance[this.axis]) > this.treshold.start) {
this._dispatchEvents('drag*Start', detail);
// Set the state of the gesture to "inactive".
this.state = settings.state.dragging;
}
}
// Dispatch the "drag" events.
if (this.state == settings.state.dragging) {
this._dispatchEvents('drag*Move', detail);
}
break;
}
};
/**
* Get the event details.
* @param {string} direction Direction for the event (up, right, down, left).
* @return {object} The event details.
*/
DragEvents.prototype._eventDetail = function (direction) {
var distX = this.distance.x;
var distY = this.distance.y;
if (this.axis == 'x') {
distX -= distX > 0 ? this.treshold.start : 0 - this.treshold.start;
}
if (this.axis == 'y') {
distY -= distY > 0 ? this.treshold.start : 0 - this.treshold.start;
}
return {
axis: this.axis,
direction: direction,
movementX: this.movement.x,
movementY: this.movement.y,
distanceX: distX,
distanceY: distY
};
};
/**
* Dispatch the events
* @param {string} eventName The name for the events to dispatch.
* @param {object} detail The event details.
*/
DragEvents.prototype._dispatchEvents = function (eventName, detail) {
/** General event, e.g. "drag" */
var event = new CustomEvent(eventName.replace('*', ''), { detail: detail });
this.surface.dispatchEvent(event);
/** Axis event, e.g. "dragX" */
var axis = new CustomEvent(eventName.replace('*', this.axis.toUpperCase()), { detail: detail });
this.surface.dispatchEvent(axis);
/** Direction event, e.g. "dragLeft" */
var direction = new CustomEvent(eventName.replace('*', detail.direction), {
detail: detail
});
this.surface.dispatchEvent(direction);
};
/**
* Get the dragging direction.
* @return {string} The direction in which the user is dragging.
*/
DragEvents.prototype._dragDirection = function () {
return settings.directionNames[this.axis][this.distance[this.axis] > 0 ? 0 : 1];
};
/**
* Get the dragging direction.
* @return {string} The direction in which the user is dragging.
*/
DragEvents.prototype._swipeDirection = function () {
return settings.directionNames[this.axis][this.movement[this.axis] > 0 ? 0 : 1];
};
return DragEvents;
}());
export default DragEvents;

50
dist/_modules/eventlisteners.js vendored Normal file
View File

@ -0,0 +1,50 @@
/**
* Make the first letter in a word uppercase.
* @param {string} word The word.
*/
function ucFirst(word) {
if (!word) {
return '';
}
return word.charAt(0).toUpperCase() + word.slice(1);
}
/**
* Bind an event listener to an element.
* @param {HTMLElement} element The element to bind the event listener to.
* @param {string} evnt The event to listen to.
* @param {funcion} handler The function to invoke.
*/
export function on(element, evnt, handler) {
// Extract the event name and space from the event (the event can include a namespace (click.foo)).
var evntParts = evnt.split('.');
evnt = 'mmEvent' + ucFirst(evntParts[0]) + ucFirst(evntParts[1]);
element[evnt] = element[evnt] || [];
element[evnt].push(handler);
element.addEventListener(evntParts[0], handler);
}
/**
* Remove an event listener from an element.
* @param {HTMLElement} element The element to remove the event listeners from.
* @param {string} evnt The event to remove.
*/
export function off(element, evnt) {
// Extract the event name and space from the event (the event can include a namespace (click.foo)).
var evntParts = evnt.split('.');
evnt = 'mmEvent' + ucFirst(evntParts[0]) + ucFirst(evntParts[1]);
(element[evnt] || []).forEach(function (handler) {
element.removeEventListener(evntParts[0], handler);
});
}
/**
* Trigger the bound event listeners on an element.
* @param {HTMLElement} element The element of which to trigger the event listeners from.
* @param {string} evnt The event to trigger.
* @param {object} [options] Options to pass to the handler.
*/
export function trigger(element, evnt, options) {
var evntParts = evnt.split('.');
evnt = 'mmEvent' + ucFirst(evntParts[0]) + ucFirst(evntParts[1]);
(element[evnt] || []).forEach(function (handler) {
handler(options || {});
});
}

125
dist/_modules/helpers.js vendored Normal file
View File

@ -0,0 +1,125 @@
/**
* Deep extend an object with the given defaults.
* Note that the extended object is not a clone, meaning the original object will also be updated.
*
* @param {object} orignl The object to extend to.
* @param {object} dfault The object to extend from.
* @return {object} The extended "orignl" object.
*/
export function extend(orignl, dfault) {
if (type(orignl) != 'object') {
orignl = {};
}
if (type(dfault) != 'object') {
dfault = {};
}
for (var k in dfault) {
if (!dfault.hasOwnProperty(k)) {
continue;
}
if (typeof orignl[k] == 'undefined') {
orignl[k] = dfault[k];
}
else if (type(orignl[k]) == 'object') {
extend(orignl[k], dfault[k]);
}
}
return orignl;
}
/**
* Detect the touch / dragging direction on a touch device.
*
* @param {HTMLElement} surface The element to monitor for touch events.
* @return {object} Object with "get" function.
*/
export function touchDirection(surface) {
var direction = '';
surface.addEventListener('touchmove', function (evnt) {
direction = '';
if (evnt.movementY > 0) {
direction = 'down';
}
else if (evnt.movementY < 0) {
direction = 'up';
}
});
return {
get: function () { return direction; }
};
}
/**
* Get the type of any given variable. Improvement of "typeof".
*
* @param {any} variable The variable.
* @return {string} The type of the variable in lowercase.
*/
export function type(variable) {
return {}.toString
.call(variable)
.match(/\s([a-zA-Z]+)/)[1]
.toLowerCase();
}
/**
* Find the value from an option or function.
* @param {HTMLElement} element Scope for the function.
* @param {any} [option] Value or function.
* @param {any} [dfault] Default fallback value.
* @return {any} The given evaluation of the given option, or the default fallback value.
*/
export function valueOrFn(element, option, dfault) {
if (typeof option == 'function') {
var value = option.call(element);
if (typeof value != 'undefined') {
return value;
}
}
if ((option === null ||
typeof option == 'function' ||
typeof option == 'undefined') &&
typeof dfault != 'undefined') {
return dfault;
}
return option;
}
/**
* Set and invoke a (single) transition-end function with fallback.
*
* @param {HTMLElement} element Scope for the function.
* @param {function} func Function to invoke.
* @param {number} duration The duration of the animation (for the fallback).
*/
export function transitionend(element, func, duration) {
var _ended = false, _fn = function (evnt) {
if (typeof evnt !== 'undefined') {
if (evnt.target !== element) {
return;
}
}
if (!_ended) {
element.removeEventListener('transitionend', _fn);
element.removeEventListener('webkitTransitionEnd', _fn);
func.call(element);
}
_ended = true;
};
element.addEventListener('transitionend', _fn);
element.addEventListener('webkitTransitionEnd', _fn);
setTimeout(_fn, duration * 1.1);
}
/**
* Get a (page wide) unique ID.
*/
export function uniqueId() {
return 'mm-' + __id++;
}
var __id = 0;
/**
* Get the original ID from a possibly prefixed ID.
* @param id The possibly prefixed ID.
*/
export function originalId(id) {
if (id.slice(0, 3) == 'mm-') {
return id.slice(3);
}
return id;
}

34
dist/_modules/i18n.js vendored Normal file
View File

@ -0,0 +1,34 @@
import { extend } from './helpers';
var translations = {};
/**
* Add translations to a language.
* @param {object} text Object of key/value translations.
* @param {string} language The translated language.
*/
export function add(text, language) {
if (typeof translations[language] == 'undefined') {
translations[language] = {};
}
extend(translations[language], text);
}
/**
* Find a translated text in a language.
* @param {string} text The text to find the translation for.
* @param {string} language The language to search in.
* @return {string} The translated text.
*/
export function get(text, language) {
if (typeof language == 'string' &&
typeof translations[language] != 'undefined') {
return translations[language][text] || text;
}
return text;
}
/**
* Get all translated text in a language.
* @param {string} language The language to search for.
* @return {object} The translations.
*/
export function all(language) {
return translations;
}

43
dist/_modules/matchmedia.js vendored Normal file
View File

@ -0,0 +1,43 @@
/** Collection of callback functions for media querys. */
var listeners = {};
/**
* Bind functions to a matchMedia listener (subscriber).
*
* @param {string|number} query Media query to match or number for min-width.
* @param {function} yes Function to invoke when the media query matches.
* @param {function} no Function to invoke when the media query doesn't match.
*/
export function add(query, yes, no) {
if (typeof query == 'number') {
query = '(min-width: ' + query + 'px)';
}
listeners[query] = listeners[query] || [];
listeners[query].push({ yes: yes, no: no });
}
/**
* Initialize the matchMedia listener.
*/
export function watch() {
var _loop_1 = function (query) {
var mqlist = window.matchMedia(query);
fire(query, mqlist);
mqlist.onchange = function (evnt) {
fire(query, mqlist);
};
};
for (var query in listeners) {
_loop_1(query);
}
}
/**
* Invoke the "yes" or "no" function for a matchMedia listener (publisher).
*
* @param {string} query Media query to check for.
* @param {MediaQueryList} mqlist Media query list to check with.
*/
export function fire(query, mqlist) {
var fn = mqlist.matches ? 'yes' : 'no';
for (var m = 0; m < listeners[query].length; m++) {
listeners[query][m][fn]();
}
}

4
dist/_modules/support.js vendored Normal file
View File

@ -0,0 +1,4 @@
/** Whether or not touch gestures are supported by the browser. */
export var touch = 'ontouchstart' in window ||
(navigator.msMaxTouchPoints ? true : false) ||
false;

1
dist/_version.js vendored Normal file
View File

@ -0,0 +1 @@
export default '8.5.3';

27
dist/addons/autoheight/_options.js vendored Normal file
View File

@ -0,0 +1,27 @@
var opts = {
height: 'default'
};
export default opts;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'boolean' && options) {
options = {
height: 'auto'
};
}
if (typeof options == 'string') {
options = {
height: options
};
}
if (typeof options != 'object') {
options = {};
}
return options;
}
;

View File

@ -0,0 +1 @@
.mm-menu_autoheight:not(.mm-menu_offcanvas){position:relative}.mm-menu_autoheight.mm-menu_position-bottom,.mm-menu_autoheight.mm-menu_position-top{max-height:80%}.mm-menu_autoheight-measuring .mm-panel{display:block!important}.mm-menu_autoheight-measuring .mm-panels>.mm-panel{bottom:auto!important;height:auto!important}.mm-menu_autoheight-measuring .mm-listitem_vertical:not(.mm-listitem_opened) .mm-panel{display:none!important}

View File

@ -0,0 +1,84 @@
import Mmenu from './../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import { extend } from '../../_modules/helpers';
// Add the options.
Mmenu.options.autoHeight = options;
export default function () {
var _this = this;
var options = extendShorthandOptions(this.opts.autoHeight);
this.opts.autoHeight = extend(options, Mmenu.options.autoHeight);
if (options.height != 'auto' && options.height != 'highest') {
return;
}
var setHeight = (function () {
var getCurrent = function () {
var panel = DOM.children(_this.node.pnls, '.mm-panel_opened')[0];
if (panel) {
panel = measurablePanel(panel);
}
// Fallback, just to be sure we have a panel.
if (!panel) {
panel = DOM.children(_this.node.pnls, '.mm-panel')[0];
}
return panel.scrollHeight;
};
var getHighest = function () {
var highest = 0;
DOM.children(_this.node.pnls, '.mm-panel').forEach(function (panel) {
panel = measurablePanel(panel);
highest = Math.max(highest, panel.scrollHeight);
});
return highest;
};
var measurablePanel = function (panel) {
// If it's a vertically expanding panel...
if (panel.parentElement.matches('.mm-listitem_vertical')) {
// ...find the first parent panel that isn't.
panel = DOM.parents(panel, '.mm-panel').filter(function (panel) {
return !panel.parentElement.matches('.mm-listitem_vertical');
})[0];
}
return panel;
};
return function () {
if (_this.opts.offCanvas && !_this.vars.opened) {
return;
}
var _hgh = 0;
var _dif = _this.node.menu.offsetHeight - _this.node.pnls.offsetHeight;
// The "measuring" classname undoes some CSS to be able to measure the height.
_this.node.menu.classList.add('mm-menu_autoheight-measuring');
// Measure the height.
if (options.height == 'auto') {
_hgh = getCurrent();
}
else if (options.height == 'highest') {
_hgh = getHighest();
}
// Set the height.
_this.node.menu.style.height = _hgh + _dif + 'px';
// Remove the "measuring" classname.
_this.node.menu.classList.remove('mm-menu_autoheight-measuring');
};
})();
// Add the autoheight class to the menu.
this.bind('initMenu:after', function () {
_this.node.menu.classList.add('mm-menu_autoheight');
});
if (this.opts.offCanvas) {
// Measure the height when opening the off-canvas menu.
this.bind('open:start', setHeight);
}
if (options.height == 'highest') {
// Measure the height when initiating panels.
this.bind('initPanels:after', setHeight);
}
if (options.height == 'auto') {
// Measure the height when updating listviews.
this.bind('updateListview', setHeight);
// Measure the height when opening a panel.
this.bind('openPanel:start', setHeight);
}
}

23
dist/addons/backbutton/_options.js vendored Normal file
View File

@ -0,0 +1,23 @@
var options = {
close: false,
open: false
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'boolean') {
options = {
close: options
};
}
if (typeof options != 'object') {
options = {};
}
return options;
}
;

View File

@ -0,0 +1,58 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import { extend } from '../../_modules/helpers';
// Add the options.
Mmenu.options.backButton = options;
export default function () {
var _this = this;
if (!this.opts.offCanvas) {
return;
}
var options = extendShorthandOptions(this.opts.backButton);
this.opts.backButton = extend(options, Mmenu.options.backButton);
var _menu = '#' + this.node.menu.id;
// Close menu
if (options.close) {
var states = [];
var setStates = function () {
states = [_menu];
DOM.children(_this.node.pnls, '.mm-panel_opened, .mm-panel_opened-parent').forEach(function (panel) {
states.push('#' + panel.id);
});
};
this.bind('open:finish', function () {
history.pushState(null, document.title, _menu);
});
this.bind('open:finish', setStates);
this.bind('openPanel:finish', setStates);
this.bind('close:finish', function () {
states = [];
history.back();
history.pushState(null, document.title, location.pathname + location.search);
});
window.addEventListener('popstate', function (evnt) {
if (_this.vars.opened) {
if (states.length) {
states = states.slice(0, -1);
var hash = states[states.length - 1];
if (hash == _menu) {
_this.close();
}
else {
_this.openPanel(_this.node.menu.querySelector(hash));
history.pushState(null, document.title, _menu);
}
}
}
});
}
if (options.open) {
window.addEventListener('popstate', function (evnt) {
if (!_this.vars.opened && location.hash == _menu) {
_this.open();
}
});
}
}

38
dist/addons/columns/_options.js vendored Normal file
View File

@ -0,0 +1,38 @@
var options = {
add: false,
visible: {
min: 1,
max: 3
}
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'boolean') {
options = {
add: options
};
}
if (typeof options == 'number') {
options = {
add: true,
visible: options
};
}
if (typeof options != 'object') {
options = {};
}
if (typeof options.visible == 'number') {
options.visible = {
min: options.visible,
max: options.visible
};
}
return options;
}
;

1
dist/addons/columns/mmenu.columns.css vendored Normal file

File diff suppressed because one or more lines are too long

103
dist/addons/columns/mmenu.columns.js vendored Normal file
View File

@ -0,0 +1,103 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import { extend } from '../../_modules/helpers';
// Add the options.
Mmenu.options.columns = options;
export default function () {
var _this = this;
var options = extendShorthandOptions(this.opts.columns);
this.opts.columns = extend(options, Mmenu.options.columns);
// Add the columns
if (options.add) {
options.visible.min = Math.max(1, Math.min(6, options.visible.min));
options.visible.max = Math.max(options.visible.min, Math.min(6, options.visible.max));
/** Columns related clasnames for the menu. */
var colm = [];
/** Columns related clasnames for the panels. */
var colp = [];
/** Classnames to remove from panels in favor of showing columns. */
var rmvc = [
'mm-panel_opened',
'mm-panel_opened-parent',
'mm-panel_highest'
];
for (var i = 0; i <= options.visible.max; i++) {
colm.push('mm-menu_columns-' + i);
colp.push('mm-panel_columns-' + i);
}
rmvc.push.apply(rmvc, colp);
// Close all later opened panels
this.bind('openPanel:before', function (panel) {
/** The parent panel. */
var parent;
if (panel) {
parent = panel['mmParent'];
}
if (!parent) {
return;
}
parent = parent.closest('.mm-panel');
if (!parent) {
return;
}
var classname = parent.className;
if (!classname.length) {
return;
}
classname = classname.split('mm-panel_columns-')[1];
if (!classname) {
return;
}
var colnr = parseInt(classname.split(' ')[0], 10) + 1;
while (colnr > 0) {
panel = DOM.children(_this.node.pnls, '.mm-panel_columns-' + colnr)[0];
if (panel) {
colnr++;
panel.classList.add('mm-hidden');
// IE11:
rmvc.forEach(function (classname) {
panel.classList.remove(classname);
});
// Better browsers:
// panel.classList.remove(...rmvc);
}
else {
colnr = -1;
break;
}
}
});
this.bind('openPanel:start', function (panel) {
var columns = DOM.children(_this.node.pnls, '.mm-panel_opened-parent').length;
if (!panel.matches('.mm-panel_opened-parent')) {
columns++;
}
columns = Math.min(options.visible.max, Math.max(options.visible.min, columns));
// IE11:
colm.forEach(function (classname) {
_this.node.menu.classList.remove(classname);
});
// Better browsers:
// this.node.menu.classList.remove(...colm);
_this.node.menu.classList.add('mm-menu_columns-' + columns);
var panels = [];
DOM.children(_this.node.pnls, '.mm-panel').forEach(function (panel) {
// IE11:
colp.forEach(function (classname) {
panel.classList.remove(classname);
});
// Better browsers:
// panel.classList.remove(...colp);
if (panel.matches('.mm-panel_opened-parent')) {
panels.push(panel);
}
});
panels.push(panel);
panels.slice(-options.visible.max).forEach(function (panel, p) {
panel.classList.add('mm-panel_columns-' + p);
});
});
}
}

28
dist/addons/counters/_options.js vendored Normal file
View File

@ -0,0 +1,28 @@
var options = {
add: false,
addTo: 'panels',
count: false
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'boolean') {
options = {
add: options,
addTo: 'panels',
count: options
};
}
if (typeof options != 'object') {
options = {};
}
if (options.addTo == 'panels') {
options.addTo = '.mm-listview';
}
return options;
}

View File

@ -0,0 +1 @@
.mm-counter{color:rgba(0,0,0,.3);display:block;padding-left:20px;float:right;text-align:right;color:var(--mm-color-text-dimmed)}.mm-listitem_nosubitems>.mm-counter{display:none}[dir=rtl] .mm-counter{text-align:left;float:left;padding-left:0;padding-right:20px}

65
dist/addons/counters/mmenu.counters.js vendored Normal file
View File

@ -0,0 +1,65 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import { extend } from '../../_modules/helpers';
// Add the options.
Mmenu.options.counters = options;
// Add the classnames.
Mmenu.configs.classNames.counters = {
counter: 'Counter'
};
export default function () {
var _this = this;
var options = extendShorthandOptions(this.opts.counters);
this.opts.counters = extend(options, Mmenu.options.counters);
// Refactor counter class
this.bind('initListview:after', function (listview) {
var cntrclss = _this.conf.classNames.counters.counter, counters = DOM.find(listview, '.' + cntrclss);
counters.forEach(function (counter) {
DOM.reClass(counter, cntrclss, 'mm-counter');
});
});
// Add the counters after a listview is initiated.
if (options.add) {
this.bind('initListview:after', function (listview) {
if (!listview.matches(options.addTo)) {
return;
}
var parent = listview.closest('.mm-panel')['mmParent'];
if (parent) {
// Check if no counter already excists.
if (!DOM.find(parent, '.mm-counter').length) {
var btn = DOM.children(parent, '.mm-btn')[0];
if (btn) {
btn.prepend(DOM.create('span.mm-counter'));
}
}
}
});
}
if (options.count) {
var count = function (listview) {
var panels = listview
? [listview.closest('.mm-panel')]
: DOM.children(_this.node.pnls, '.mm-panel');
panels.forEach(function (panel) {
var parent = panel['mmParent'];
if (!parent) {
return;
}
var counter = DOM.find(parent, '.mm-counter')[0];
if (!counter) {
return;
}
var listitems = [];
DOM.children(panel, '.mm-listview').forEach(function (listview) {
listitems.push.apply(listitems, DOM.children(listview));
});
counter.innerHTML = DOM.filterLI(listitems).length.toString();
});
};
this.bind('initListview:after', count);
this.bind('updateListview', count);
}
}

25
dist/addons/dividers/_options.js vendored Normal file
View File

@ -0,0 +1,25 @@
var options = {
add: false,
addTo: 'panels'
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'boolean') {
options = {
add: options
};
}
if (typeof options != 'object') {
options = {};
}
if (options.addTo == 'panels') {
options.addTo = '.mm-listview';
}
return options;
}

View File

@ -0,0 +1 @@
.mm-divider{position:relative;min-height:20px;padding:4.3px;background:#f3f3f3;-o-text-overflow:ellipsis;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;min-height:var(--mm-line-height);padding:calc(((var(--mm-listitem-size) * .65) - var(--mm-line-height)) * .5);padding-right:10px;padding-left:20px;font-size:75%;text-transform:uppercase;background:var(--mm-color-background);opacity:1;-webkit-transition:opacity .4s ease;-o-transition:opacity .4s ease;transition:opacity .4s ease}.mm-divider:before{background:rgba(0,0,0,.05)}@supports ((position:-webkit-sticky) or (position:sticky)){.mm-divider{position:-webkit-sticky;position:sticky;z-index:2;top:0}.mm-navbar_sticky:not(.mm-hidden)~.mm-listview .mm-divider{top:var(--mm-navbar-size)}}.mm-divider:before{content:'';position:absolute;top:0;right:0;bottom:0;left:0;z-index:-1;background:var(--mm-color-background-highlight)}

46
dist/addons/dividers/mmenu.dividers.js vendored Normal file
View File

@ -0,0 +1,46 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import { extend } from '../../_modules/helpers';
// Add the options.
Mmenu.options.dividers = options;
// Add the classnames.
Mmenu.configs.classNames.divider = 'Divider';
export default function () {
var _this = this;
var options = extendShorthandOptions(this.opts.dividers);
this.opts.dividers = extend(options, Mmenu.options.dividers);
// Refactor divider classname
this.bind('initListview:after', function (listview) {
DOM.children(listview).forEach(function (listitem) {
DOM.reClass(listitem, _this.conf.classNames.divider, 'mm-divider');
if (listitem.matches('.mm-divider')) {
listitem.classList.remove('mm-listitem');
}
});
});
// Add dividers
if (options.add) {
this.bind('initListview:after', function (listview) {
if (!listview.matches(options.addTo)) {
return;
}
DOM.find(listview, '.mm-divider').forEach(function (divider) {
divider.remove();
});
var lastletter = '', listitems = DOM.children(listview);
DOM.filterLI(listitems).forEach(function (listitem) {
var letter = DOM.children(listitem, '.mm-listitem__text')[0]
.textContent.trim()
.toLowerCase()[0];
if (letter.length && letter != lastletter) {
lastletter = letter;
var divider = DOM.create('li.mm-divider');
divider.textContent = letter;
listview.insertBefore(divider, listitem);
}
});
});
}
}

202
dist/addons/drag/_drag.open.js vendored Normal file
View File

@ -0,0 +1,202 @@
import DragEvents from '../../_modules/dragevents/index';
import * as DOM from '../../_modules/dom';
import * as events from '../../_modules/eventlisteners';
import * as media from '../../_modules/matchmedia';
/** Instance of the DragEvents class. */
var dragInstance = null;
/** THe node that can be dragged. */
var dragNode = null;
/** How far the page (or menu) can be dragged. */
var maxDistance = 0;
export default function (page) {
var _this = this;
/** Variables that vary for each menu position (top, right, bottom, left. front, back). */
var vars = {};
/** Whether or not the page or menu is actually being moved. */
var moving = false;
/**
* Add the dragging events.
*/
var addEvents = function () {
if (dragNode) {
// Prepare the page or menu to be moved.
events.on(dragNode, 'dragStart', function (evnt) {
if (evnt['detail'].direction == vars.direction) {
moving = true;
// Class prevents interaction with the page.
_this.node.wrpr.classList.add('mm-wrapper_dragging');
// Prepare the menu to be opened.
_this._openSetup();
_this.trigger('open:start');
// Get the maximum distance to move out the page or menu.
maxDistance = _this.node.menu[vars.axis == 'x' ? 'clientWidth' : 'clientHeight'];
}
});
// Move the page or menu when dragging.
events.on(dragNode, 'dragMove', function (evnt) {
if (evnt['detail'].axis == vars.axis) {
if (moving) {
var distance = evnt['detail']['distance' + vars.axis.toUpperCase()];
switch (vars.position) {
case 'right':
case 'bottom':
distance = Math.min(Math.max(distance, -maxDistance), 0);
break;
default:
distance = Math.max(Math.min(distance, maxDistance), 0);
}
// Deviate for position front (the menu starts out of view).
if (vars.zposition == 'front') {
switch (vars.position) {
case 'right':
case 'bottom':
distance += maxDistance;
break;
default:
distance -= maxDistance;
break;
}
}
vars.slideOutNodes.forEach(function (node) {
node.style['transform'] =
'translate' +
vars.axis.toUpperCase() +
'(' +
distance +
'px)';
});
}
}
});
// Stop the page or menu from being moved.
events.on(dragNode, 'dragEnd', function (evnt) {
if (evnt['detail'].axis == vars.axis) {
if (moving) {
moving = false;
_this.node.wrpr.classList.remove('mm-wrapper_dragging');
vars.slideOutNodes.forEach(function (node) {
node.style['transform'] = '';
});
// Determine if the menu should open or close.
var open_1 = Math.abs(evnt['detail']['distance' + vars.axis.toUpperCase()]) >=
maxDistance * 0.75;
if (!open_1) {
var movement = evnt['detail']['movement' + vars.axis.toUpperCase()];
switch (vars.position) {
case 'right':
case 'bottom':
open_1 = movement <= 0;
break;
default:
open_1 = movement >= 0;
break;
}
}
if (open_1) {
_this._openStart();
}
else {
_this.close();
}
}
}
});
}
};
/**
* Remove the dragging events.
*/
var removeEvents = function () {
if (dragNode) {
events.off(dragNode, 'dragStart');
events.off(dragNode, 'dragMove');
events.off(dragNode, 'dragEnd');
}
};
var addMatchMedia = function () {
var queries = Object.keys(_this.opts.extensions);
if (queries.length) {
// A media query that'll match if any of the other media query matches:
// set the defaults if it doesn't match.
media.add(queries.join(', '), function () { }, function () {
vars = getPositionVars(vars, [], _this.node.menu);
});
// The other media queries.
queries.forEach(function (query) {
media.add(query, function () {
vars = getPositionVars(vars, _this.opts.extensions[query], _this.node.menu);
}, function () { });
});
// No extensions, just use the defaults.
}
else {
vars = getPositionVars(vars, [], _this.node.menu);
}
};
// Remove events from previous "page"
removeEvents();
// Store new "page"
dragNode = page;
// Initialize the drag events.
dragInstance = new DragEvents(dragNode);
addMatchMedia();
addMatchMedia = function () { };
addEvents();
}
var getPositionVars = function (vars, extensions, menu) {
// Default position and z-position.
vars.position = 'left';
vars.zposition = 'back';
// Find position.
['right', 'top', 'bottom'].forEach(function (pos) {
if (extensions.indexOf('position-' + pos) > -1) {
vars.position = pos;
}
});
// Find z-position.
['front', 'top', 'bottom'].forEach(function (pos) {
if (extensions.indexOf('position-' + pos) > -1) {
vars.zposition = 'front';
}
});
// Set the area where the dragging can start.
dragInstance.area = {
top: vars.position == 'bottom' ? '75%' : 0,
right: vars.position == 'left' ? '75%' : 0,
bottom: vars.position == 'top' ? '75%' : 0,
left: vars.position == 'right' ? '75%' : 0
};
// What side of the menu to measure (width or height).
// What axis to drag the menu along (x or y).
switch (vars.position) {
case 'top':
case 'bottom':
vars.axis = 'y';
break;
default:
vars.axis = 'x';
}
// What direction to drag in.
switch (vars.position) {
case 'top':
vars.direction = 'Down';
break;
case 'right':
vars.direction = 'Left';
break;
case 'bottom':
vars.direction = 'Up';
break;
default:
vars.direction = 'Right';
}
// What nodes to slide out while dragging.
switch (vars.zposition) {
case 'front':
vars.slideOutNodes = [menu];
break;
default:
vars.slideOutNodes = DOM.find(document.body, '.mm-slideout');
}
return vars;
};

22
dist/addons/drag/_options.js vendored Normal file
View File

@ -0,0 +1,22 @@
var options = {
open: false,
node: null
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'boolean') {
options = {
open: options
};
}
if (typeof options != 'object') {
options = {};
}
return options;
}

1
dist/addons/drag/mmenu.drag.css vendored Normal file
View File

@ -0,0 +1 @@
.mm-wrapper_dragging .mm-menu,.mm-wrapper_dragging .mm-slideout{-webkit-transition-duration:0s!important;-o-transition-duration:0s!important;transition-duration:0s!important;-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.mm-wrapper_dragging .mm-menu{pointer-events:none!important}.mm-wrapper_dragging .mm-wrapper__blocker{display:none!important}

21
dist/addons/drag/mmenu.drag.js vendored Normal file
View File

@ -0,0 +1,21 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import dragOpen from './_drag.open';
import { extend } from '../../_modules/helpers';
// Add the options and configs.
Mmenu.options.drag = options;
export default function () {
var _this = this;
if (!this.opts.offCanvas) {
return;
}
var options = extendShorthandOptions(this.opts.drag);
this.opts.drag = extend(options, Mmenu.options.drag);
// Drag open the menu
if (options.open) {
this.bind('setPage:after', function (page) {
dragOpen.call(_this, options.node || page);
});
}
}

19
dist/addons/dropdown/_configs.js vendored Normal file
View File

@ -0,0 +1,19 @@
var configs = {
offset: {
button: {
x: -5,
y: 5
},
viewport: {
x: 20,
y: 20
}
},
height: {
max: 880
},
width: {
max: 440
}
};
export default configs;

31
dist/addons/dropdown/_options.js vendored Normal file
View File

@ -0,0 +1,31 @@
var options = {
drop: false,
fitViewport: true,
event: 'click',
position: {},
tip: true
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'boolean' && options) {
options = {
drop: options
};
}
if (typeof options != 'object') {
options = {};
}
if (typeof options.position == 'string') {
options.position = {
of: options.position
};
}
return options;
}
;

View File

@ -0,0 +1 @@
.mm-menu_dropdown{-webkit-box-shadow:0 2px 10px rgba(0,0,0,.3);box-shadow:0 2px 10px rgba(0,0,0,.3);height:80%}.mm-wrapper_dropdown .mm-slideout{-webkit-transform:none!important;-ms-transform:none!important;transform:none!important;z-index:0}.mm-wrapper_dropdown .mm-wrapper__blocker{-webkit-transition-delay:0s!important;-o-transition-delay:0s!important;transition-delay:0s!important;z-index:1}.mm-wrapper_dropdown .mm-menu_dropdown{z-index:2}.mm-wrapper_dropdown.mm-wrapper_opened:not(.mm-wrapper_opening) .mm-menu_dropdown{display:none}.mm-menu_tip-bottom:before,.mm-menu_tip-left:before,.mm-menu_tip-right:before,.mm-menu_tip-top:before{content:'';background:inherit;-webkit-box-shadow:0 2px 10px rgba(0,0,0,.3);box-shadow:0 2px 10px rgba(0,0,0,.3);display:block;width:15px;height:15px;position:absolute;z-index:-1;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.mm-menu_tip-left:before{left:22px}.mm-menu_tip-right:before{right:22px}.mm-menu_tip-top:before{top:-8px}.mm-menu_tip-bottom:before{bottom:-8px}

159
dist/addons/dropdown/mmenu.dropdown.js vendored Normal file
View File

@ -0,0 +1,159 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import configs from './_configs';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import { extend, originalId } from '../../_modules/helpers';
// Add the options and configs.
Mmenu.options.dropdown = options;
Mmenu.configs.dropdown = configs;
export default function () {
var _this = this;
if (!this.opts.offCanvas) {
return;
}
var options = extendShorthandOptions(this.opts.dropdown);
this.opts.dropdown = extend(options, Mmenu.options.dropdown);
var configs = this.conf.dropdown;
if (!options.drop) {
return;
}
var button;
this.bind('initMenu:after', function () {
_this.node.menu.classList.add('mm-menu_dropdown');
if (typeof options.position.of != 'string') {
var id = originalId(_this.node.menu.id);
if (id) {
options.position.of = '[href="#' + id + '"]';
}
}
if (typeof options.position.of != 'string') {
return;
}
// Get the button to put the menu next to
button = DOM.find(document.body, options.position.of)[0];
// Emulate hover effect
var events = options.event.split(' ');
if (events.length == 1) {
events[1] = events[0];
}
if (events[0] == 'hover') {
button.addEventListener('mouseenter', function () {
_this.open();
}, { passive: true });
}
if (events[1] == 'hover') {
_this.node.menu.addEventListener('mouseleave', function () {
_this.close();
}, { passive: true });
}
});
// Add/remove classname and style when opening/closing the menu
this.bind('open:start', function () {
_this.node.menu['mmStyle'] = _this.node.menu.getAttribute('style');
_this.node.wrpr.classList.add('mm-wrapper_dropdown');
});
this.bind('close:finish', function () {
_this.node.menu.setAttribute('style', _this.node.menu['mmStyle']);
_this.node.wrpr.classList.remove('mm-wrapper_dropdown');
});
/**
* Find the position (x, y) and sizes (width, height) for the menu.
*
* @param {string} dir The direction to measure ("x" for horizontal, "y" for vertical)
* @param {object} obj The object where (previously) measured values are stored.
* @return {object} The object where measered values are stored.
*/
var getPosition = function (dir, obj) {
var css = obj[0], cls = obj[1];
var _outerSize = dir == 'x' ? 'offsetWidth' : 'offsetHeight', _startPos = dir == 'x' ? 'left' : 'top', _stopPos = dir == 'x' ? 'right' : 'bottom', _size = dir == 'x' ? 'width' : 'height', _winSize = dir == 'x' ? 'innerWidth' : 'innerHeight', _maxSize = dir == 'x' ? 'maxWidth' : 'maxHeight', _position = null;
var startPos = DOM.offset(button, _startPos), stopPos = startPos + button[_outerSize], windowSize = window[_winSize];
/** Offset for the menu relative to the button. */
var offs = configs.offset.button[dir] + configs.offset.viewport[dir];
// Position set in option
if (options.position[dir]) {
switch (options.position[dir]) {
case 'left':
case 'bottom':
_position = 'after';
break;
case 'right':
case 'top':
_position = 'before';
break;
}
}
// Position not set in option, find most space
if (_position === null) {
_position =
startPos + (stopPos - startPos) / 2 < windowSize / 2
? 'after'
: 'before';
}
// Set position and max
var val, max;
if (_position == 'after') {
val = dir == 'x' ? startPos : stopPos;
max = windowSize - (val + offs);
css[_startPos] = val + configs.offset.button[dir] + 'px';
css[_stopPos] = 'auto';
if (options.tip) {
cls.push('mm-menu_tip-' + (dir == 'x' ? 'left' : 'top'));
}
}
else {
val = dir == 'x' ? stopPos : startPos;
max = val - offs;
css[_stopPos] =
'calc( 100% - ' + (val - configs.offset.button[dir]) + 'px )';
css[_startPos] = 'auto';
if (options.tip) {
cls.push('mm-menu_tip-' + (dir == 'x' ? 'right' : 'bottom'));
}
}
if (options.fitViewport) {
css[_maxSize] = Math.min(configs[_size].max, max) + 'px';
}
return [css, cls];
};
function position() {
var _this = this;
if (!this.vars.opened) {
return;
}
this.node.menu.setAttribute('style', this.node.menu['mmStyle']);
var obj = [{}, []];
obj = getPosition.call(this, 'y', obj);
obj = getPosition.call(this, 'x', obj);
for (var s in obj[0]) {
this.node.menu.style[s] = obj[0][s];
}
if (options.tip) {
var classnames = [
'mm-menu_tip-left',
'mm-menu_tip-right',
'mm-menu_tip-top',
'mm-menu_tip-bottom'
];
// IE11:
classnames.forEach(function (classname) {
_this.node.menu.classList.remove(classname);
});
obj[1].forEach(function (classname) {
_this.node.menu.classList.add(classname);
});
// Better browsers:
// this.node.menu.classList.remove(...classnames);
// this.node.menu.classList.add(...obj[1]);
}
}
this.bind('open:start', position);
window.addEventListener('resize', function (evnt) {
position.call(_this);
}, { passive: true });
if (!this.opts.offCanvas.blockUI) {
window.addEventListener('scroll', function (evnt) {
position.call(_this);
}, { passive: true });
}
}

5
dist/addons/fixedelements/_configs.js vendored Normal file
View File

@ -0,0 +1,5 @@
var configs = {
insertMethod: 'append',
insertSelector: 'body'
};
export default configs;

View File

@ -0,0 +1,26 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import configs from './_configs';
import * as DOM from '../../_modules/dom';
// Add the configs.
Mmenu.configs.fixedElements = configs;
// Add the classnames.
Mmenu.configs.classNames.fixedElements = {
fixed: 'Fixed'
};
export default function () {
var _this = this;
if (!this.opts.offCanvas) {
return;
}
var configs = this.conf.fixedElements;
var _fixd, fixed, wrppr;
this.bind('setPage:after', function (page) {
_fixd = _this.conf.classNames.fixedElements.fixed;
wrppr = DOM.find(document, configs.insertSelector)[0];
fixed = DOM.find(page, '.' + _fixd);
fixed.forEach(function (fxd) {
DOM.reClass(fxd, _fixd, 'mm-slideout');
wrppr[configs.insertMethod](fxd);
});
});
}

33
dist/addons/iconbar/_options.js vendored Normal file
View File

@ -0,0 +1,33 @@
import { type } from '../../_modules/helpers';
var options = {
use: false,
top: [],
bottom: [],
position: 'left',
type: 'default'
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (type(options) == 'array') {
options = {
use: true,
top: options
};
}
if (type(options) != 'object') {
options = {};
}
if (typeof options.use == 'undefined') {
options.use = true;
}
if (typeof options.use == 'boolean' && options.use) {
options.use = true;
}
return options;
}

1
dist/addons/iconbar/mmenu.iconbar.css vendored Normal file
View File

@ -0,0 +1 @@
:root{--mm-iconbar-size:50px}.mm-menu_iconbar-left .mm-navbars_bottom,.mm-menu_iconbar-left .mm-navbars_top,.mm-menu_iconbar-left .mm-panels{margin-left:50px;margin-left:var(--mm-iconbar-size)}.mm-menu_iconbar-left .mm-iconbar{border-right-width:1px;display:block;left:0}.mm-menu_iconbar-right .mm-navbars_bottom,.mm-menu_iconbar-right .mm-navbars_top,.mm-menu_iconbar-right .mm-panels{margin-right:50px;margin-right:var(--mm-iconbar-size)}.mm-menu_iconbar-right .mm-iconbar{border-left-width:1px;display:block;right:0}.mm-iconbar{width:50px;border-color:rgba(0,0,0,.1);background:#f3f3f3;color:rgba(0,0,0,.3);display:none;width:var(--mm-iconbar-size);overflow:hidden;-webkit-box-sizing:border-box;box-sizing:border-box;position:absolute;top:0;bottom:0;z-index:2;border:0 solid;border-color:var(--mm-color-border);background:var(--mm-color-background);color:var(--mm-color-text-dimmed);text-align:center}.mm-iconbar__bottom,.mm-iconbar__top{width:inherit;position:absolute}.mm-iconbar__bottom>*,.mm-iconbar__top>*{-webkit-box-sizing:border-box;box-sizing:border-box;display:block;padding:12.5px 0}.mm-iconbar__bottom a,.mm-iconbar__bottom a:hover,.mm-iconbar__top a,.mm-iconbar__top a:hover{text-decoration:none}.mm-iconbar__top{top:0}.mm-iconbar__bottom{bottom:0}.mm-iconbar__tab_selected{background:rgba(255,255,255,.4);background:var(--mm-color-background-emphasis)}

103
dist/addons/iconbar/mmenu.iconbar.js vendored Normal file
View File

@ -0,0 +1,103 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import * as media from '../../_modules/matchmedia';
import { type, extend } from '../../_modules/helpers';
// Add the options.
Mmenu.options.iconbar = options;
export default function () {
var _this = this;
var options = extendShorthandOptions(this.opts.iconbar);
this.opts.iconbar = extend(options, Mmenu.options.iconbar);
if (!options.use) {
return;
}
var iconbar;
['top', 'bottom'].forEach(function (position, n) {
var ctnt = options[position];
// Extend shorthand options
if (type(ctnt) != 'array') {
ctnt = [ctnt];
}
// Create node
var part = DOM.create('div.mm-iconbar__' + position);
// Add content
for (var c = 0, l = ctnt.length; c < l; c++) {
if (typeof ctnt[c] == 'string') {
part.innerHTML += ctnt[c];
}
else {
part.append(ctnt[c]);
}
}
if (part.children.length) {
if (!iconbar) {
iconbar = DOM.create('div.mm-iconbar');
}
iconbar.append(part);
}
});
// Add to menu
if (iconbar) {
// Add the iconbar.
this.bind('initMenu:after', function () {
_this.node.menu.prepend(iconbar);
});
// En-/disable the iconbar.
var classname_1 = 'mm-menu_iconbar-' + options.position;
var enable = function () {
_this.node.menu.classList.add(classname_1);
Mmenu.sr_aria(iconbar, 'hidden', false);
};
var disable = function () {
_this.node.menu.classList.remove(classname_1);
Mmenu.sr_aria(iconbar, 'hidden', true);
};
if (typeof options.use == 'boolean') {
this.bind('initMenu:after', enable);
}
else {
media.add(options.use, enable, disable);
}
// Tabs
if (options.type == 'tabs') {
iconbar.classList.add('mm-iconbar_tabs');
iconbar.addEventListener('click', function (evnt) {
var anchor = evnt.target;
if (!anchor.matches('a')) {
return;
}
if (anchor.matches('.mm-iconbar__tab_selected')) {
evnt.stopImmediatePropagation();
return;
}
try {
var panel = _this.node.menu.querySelector(anchor.getAttribute('href'))[0];
if (panel && panel.matches('.mm-panel')) {
evnt.preventDefault();
evnt.stopImmediatePropagation();
_this.openPanel(panel, false);
}
}
catch (err) { }
});
var selectTab_1 = function (panel) {
DOM.find(iconbar, 'a').forEach(function (anchor) {
anchor.classList.remove('mm-iconbar__tab_selected');
});
var anchor = DOM.find(iconbar, '[href="#' + panel.id + '"]')[0];
if (anchor) {
anchor.classList.add('mm-iconbar__tab_selected');
}
else {
var parent_1 = panel['mmParent'];
if (parent_1) {
selectTab_1(parent_1.closest('.mm-panel'));
}
}
};
this.bind('openPanel:start', selectTab_1);
}
}
}

33
dist/addons/iconpanels/_options.js vendored Normal file
View File

@ -0,0 +1,33 @@
var options = {
add: false,
blockPanel: true,
hideDivider: false,
hideNavbar: true,
visible: 3
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'boolean') {
options = {
add: options
};
}
if (typeof options == 'number' ||
typeof options == 'string') {
options = {
add: true,
visible: options
};
}
if (typeof options != 'object') {
options = {};
}
return options;
}
;

View File

@ -0,0 +1 @@
:root{--mm-iconpanel-size:50px}.mm-panel_iconpanel-1{width:calc(100% - 50px);width:calc(100% - (var(--mm-iconpanel-size) * 1))}.mm-panel_iconpanel-2{width:calc(100% - 100px);width:calc(100% - (var(--mm-iconpanel-size) * 2))}.mm-panel_iconpanel-3{width:calc(100% - 150px);width:calc(100% - (var(--mm-iconpanel-size) * 3))}.mm-panel_iconpanel-first~.mm-panel{width:calc(100% - 50px);width:calc(100% - var(--mm-iconpanel-size))}.mm-menu_iconpanel .mm-panels>.mm-panel{left:auto;-webkit-transition-property:width,-webkit-transform;transition-property:width,-webkit-transform;-o-transition-property:transform,width;transition-property:transform,width;transition-property:transform,width,-webkit-transform}.mm-menu_iconpanel .mm-panels>.mm-panel_opened,.mm-menu_iconpanel .mm-panels>.mm-panel_opened-parent{display:block!important}.mm-menu_iconpanel .mm-panels>.mm-panel_opened-parent{overflow-y:hidden;-webkit-transform:unset;-ms-transform:unset;transform:unset}.mm-menu_iconpanel .mm-panels>.mm-panel:not(.mm-panel_iconpanel-first):not(.mm-panel_iconpanel-0){border-left-width:1px;border-left-style:solid}.mm-menu_hidedivider .mm-panel_opened-parent .mm-divider,.mm-menu_hidenavbar .mm-panel_opened-parent .mm-navbar{opacity:0}.mm-panel__blocker{background:inherit;opacity:0;display:block;position:absolute;top:0;right:0;left:0;z-index:3;-webkit-transition:opacity .4s ease;-o-transition:opacity .4s ease;transition:opacity .4s ease}.mm-panel_opened-parent .mm-panel__blocker{opacity:.6;bottom:-100000px}[dir=rtl] .mm-menu_iconpanel .mm-panels>.mm-panel{left:0;right:auto;-webkit-transition-property:width,-webkit-transform;transition-property:width,-webkit-transform;-o-transition-property:transform,width;transition-property:transform,width;transition-property:transform,width,-webkit-transform}[dir=rtl] .mm-menu_iconpanel .mm-panels>.mm-panel:not(.mm-panel_iconpanel-first):not(.mm-panel_iconpanel-0){border-left:none;border-right:1px solid;border-color:inherit}

View File

@ -0,0 +1,99 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import { extend } from '../../_modules/helpers';
// Add the options.
Mmenu.options.iconPanels = options;
export default function () {
var _this = this;
var options = extendShorthandOptions(this.opts.iconPanels);
this.opts.iconPanels = extend(options, Mmenu.options.iconPanels);
var keepFirst = false;
if (options.visible == 'first') {
keepFirst = true;
options.visible = 1;
}
options.visible = Math.min(3, Math.max(1, options.visible));
options.visible++;
// Add the iconpanels
if (options.add) {
this.bind('initMenu:after', function () {
var classnames = ['mm-menu_iconpanel'];
if (options.hideNavbar) {
classnames.push('mm-menu_hidenavbar');
}
if (options.hideDivider) {
classnames.push('mm-menu_hidedivider');
}
// IE11:
classnames.forEach(function (classname) {
_this.node.menu.classList.add(classname);
});
// Better browsers:
// this.node.menu.classList.add(...classnames);
});
var classnames_1 = [];
if (!keepFirst) {
for (var i = 0; i <= options.visible; i++) {
classnames_1.push('mm-panel_iconpanel-' + i);
}
}
this.bind('openPanel:start', function (panel) {
var panels = DOM.children(_this.node.pnls, '.mm-panel');
panel = panel || panels[0];
if (panel.parentElement.matches('.mm-listitem_vertical')) {
return;
}
if (keepFirst) {
panels.forEach(function (panel, p) {
panel.classList[p == 0 ? 'add' : 'remove']('mm-panel_iconpanel-first');
});
}
else {
// Remove the "iconpanel" classnames from all panels.
panels.forEach(function (panel) {
// IE11:
classnames_1.forEach(function (classname) {
panel.classList.remove(classname);
});
// Better browsers:
// panel.classList.remove(...classnames);
});
// Filter out panels that are not opened.
panels = panels.filter(function (panel) {
return panel.matches('.mm-panel_opened-parent');
});
// Add the current panel to the list.
var panelAdded_1 = false;
panels.forEach(function (elem) {
if (panel === elem) {
panelAdded_1 = true;
}
});
if (!panelAdded_1) {
panels.push(panel);
}
// Remove the "hidden" classname from all opened panels.
panels.forEach(function (panel) {
panel.classList.remove('mm-hidden');
});
// Slice the opened panels to the max visible amount.
panels = panels.slice(-options.visible);
// Add the "iconpanel" classnames.
panels.forEach(function (panel, p) {
panel.classList.add('mm-panel_iconpanel-' + p);
});
}
});
this.bind('initPanel:after', function (panel) {
if (options.blockPanel &&
!panel.parentElement.matches('.mm-listitem_vertical') &&
!DOM.children(panel, '.mm-panel__blocker')[0]) {
var blocker = DOM.create('a.mm-panel__blocker');
blocker.setAttribute('href', '#' + panel.closest('.mm-panel').id);
panel.prepend(blocker);
}
});
}
}

View File

@ -0,0 +1,23 @@
var options = {
enable: false,
enhance: false
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'boolean' || typeof options == 'string') {
options = {
enable: options
};
}
if (typeof options != 'object') {
options = {};
}
return options;
}
;

View File

@ -0,0 +1 @@
.mm-menu_keyboardfocus a:focus,.mm-menu_keyboardfocus.mm-menu_opened~.mm-wrapper__blocker a:focus{background:rgba(255,255,255,.4);background:var(--mm-color-background-emphasis);outline:0}.mm-wrapper__blocker .mm-tabstart{cursor:default;display:block;width:100%;height:100%}.mm-wrapper__blocker .mm-tabend{opacity:0;position:absolute;bottom:0}

View File

@ -0,0 +1,183 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import * as events from '../../_modules/eventlisteners';
import * as support from '../../_modules/support';
import { extend } from '../../_modules/helpers';
// Add the options.
Mmenu.options.keyboardNavigation = options;
export default function () {
var _this = this;
// Keyboard navigation on touchscreens opens the virtual keyboard :/
// Lets prevent that.
if (support.touch) {
return;
}
var options = extendShorthandOptions(this.opts.keyboardNavigation);
this.opts.keyboardNavigation = extend(options, Mmenu.options.keyboardNavigation);
// Enable keyboard navigation
if (options.enable) {
var menuStart_1 = DOM.create('button.mm-tabstart.mm-sronly'), menuEnd_1 = DOM.create('button.mm-tabend.mm-sronly'), blockerEnd_1 = DOM.create('button.mm-tabend.mm-sronly');
this.bind('initMenu:after', function () {
if (options.enhance) {
_this.node.menu.classList.add('mm-menu_keyboardfocus');
}
initWindow.call(_this, options.enhance);
});
this.bind('initOpened:before', function () {
_this.node.menu.prepend(menuStart_1);
_this.node.menu.append(menuEnd_1);
DOM.children(_this.node.menu, '.mm-navbars-top, .mm-navbars-bottom').forEach(function (navbars) {
navbars.querySelectorAll('.mm-navbar__title').forEach(function (title) {
title.setAttribute('tabindex', '-1');
});
});
});
this.bind('initBlocker:after', function () {
Mmenu.node.blck.append(blockerEnd_1);
DOM.children(Mmenu.node.blck, 'a')[0].classList.add('mm-tabstart');
});
var focusable_1 = 'input, select, textarea, button, label, a[href]';
var setFocus = function (panel) {
panel =
panel || DOM.children(_this.node.pnls, '.mm-panel_opened')[0];
var focus = null;
// Focus already is on an element in a navbar in this menu.
var navbar = document.activeElement.closest('.mm-navbar');
if (navbar) {
if (navbar.closest('.mm-menu') == _this.node.menu) {
return;
}
}
// Set the focus to the first focusable element by default.
if (options.enable == 'default') {
// First visible anchor in a listview in the current panel.
focus = DOM.find(panel, '.mm-listview a[href]:not(.mm-hidden)')[0];
// First focusable and visible element in the current panel.
if (!focus) {
focus = DOM.find(panel, focusable_1 + ':not(.mm-hidden)')[0];
}
// First focusable and visible element in a navbar.
if (!focus) {
var elements_1 = [];
DOM.children(_this.node.menu, '.mm-navbars_top, .mm-navbars_bottom').forEach(function (navbar) {
elements_1.push.apply(elements_1, DOM.find(navbar, focusable_1 + ':not(.mm-hidden)'));
});
focus = elements_1[0];
}
}
// Default.
if (!focus) {
focus = DOM.children(_this.node.menu, '.mm-tabstart')[0];
}
if (focus) {
focus.focus();
}
};
this.bind('open:finish', setFocus);
this.bind('openPanel:finish', setFocus);
// Add screenreader / aria support.
this.bind('initOpened:after:sr-aria', function () {
[_this.node.menu, Mmenu.node.blck].forEach(function (element) {
DOM.children(element, '.mm-tabstart, .mm-tabend').forEach(function (tabber) {
Mmenu.sr_aria(tabber, 'hidden', true);
Mmenu.sr_role(tabber, 'presentation');
});
});
});
}
}
/**
* Initialize the window for keyboard navigation.
* @param {boolean} enhance - Whether or not to also rich enhance the keyboard behavior.
**/
var initWindow = function (enhance) {
var _this = this;
// Re-enable tabbing in general
events.off(document.body, 'keydown.tabguard');
// Intersept the target when tabbing.
events.off(document.body, 'focusin.tabguard');
events.on(document.body, 'focusin.tabguard', function (evnt) {
if (_this.node.wrpr.matches('.mm-wrapper_opened')) {
var target = evnt.target;
if (target.matches('.mm-tabend')) {
var next = void 0;
// Jump from menu to blocker.
if (target.parentElement.matches('.mm-menu')) {
if (Mmenu.node.blck) {
next = Mmenu.node.blck;
}
}
// Jump to opened menu.
if (target.parentElement.matches('.mm-wrapper__blocker')) {
next = DOM.find(document.body, '.mm-menu_offcanvas.mm-menu_opened')[0];
}
// If no available element found, stay in current element.
if (!next) {
next = target.parentElement;
}
if (next) {
DOM.children(next, '.mm-tabstart')[0].focus();
}
}
}
});
// Add Additional keyboard behavior.
events.off(document.body, 'keydown.navigate');
events.on(document.body, 'keydown.navigate', function (evnt) {
var target = evnt.target;
var menu = target.closest('.mm-menu');
if (menu) {
var api = menu['mmApi'];
if (!target.matches('input, textarea')) {
switch (evnt.keyCode) {
// press enter to toggle and check
case 13:
if (target.matches('.mm-toggle') ||
target.matches('.mm-check')) {
target.dispatchEvent(new Event('click'));
}
break;
// prevent spacebar or arrows from scrolling the page
case 32: // space
case 37: // left
case 38: // top
case 39: // right
case 40: // bottom
evnt.preventDefault();
break;
}
}
if (enhance) {
// special case for input
if (target.matches('input')) {
switch (evnt.keyCode) {
// empty searchfield with esc
case 27:
target.value = '';
break;
}
}
else {
var api_1 = menu['mmApi'];
switch (evnt.keyCode) {
// close submenu with backspace
case 8:
var parent_1 = DOM.find(menu, '.mm-panel_opened')[0]['mmParent'];
if (parent_1) {
api_1.openPanel(parent_1.closest('.mm-panel'));
}
break;
// close menu with esc
case 27:
if (menu.matches('.mm-menu_offcanvas')) {
api_1.close();
}
break;
}
}
}
}
});
};

22
dist/addons/lazysubmenus/_options.js vendored Normal file
View File

@ -0,0 +1,22 @@
var options = {
load: false
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'boolean') {
options = {
load: options
};
}
if (typeof options != 'object') {
options = {};
}
return options;
}
;

View File

@ -0,0 +1,103 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import { extend } from '../../_modules/helpers';
// Add the options.
Mmenu.options.lazySubmenus = options;
export default function () {
var _this = this;
var options = extendShorthandOptions(this.opts.lazySubmenus);
this.opts.lazySubmenus = extend(options, Mmenu.options.lazySubmenus);
if (options.load) {
// Prevent all sub panels from being initialized.
this.bind('initMenu:after', function () {
var panels = [];
// Find all potential subpanels.
DOM.find(_this.node.pnls, 'li').forEach(function (listitem) {
panels.push.apply(panels, DOM.children(listitem, _this.conf.panelNodetype.join(', ')));
});
// Filter out all non-panels and add the lazyload classes
panels
.filter(function (panel) { return !panel.matches('.mm-listview_inset'); })
.filter(function (panel) { return !panel.matches('.mm-nolistview'); })
.filter(function (panel) { return !panel.matches('.mm-nopanel'); })
.forEach(function (panel) {
var classnames = [
'mm-panel_lazysubmenu',
'mm-nolistview',
'mm-nopanel'
];
// IE11:
classnames.forEach(function (classname) {
panel.classList.add(classname);
});
// Better browsers:
// panel.classList.add(...classnames);
});
});
// Prepare current and one level sub panels for initPanels
this.bind('initPanels:before', function () {
var panels = DOM.children(_this.node.pnls, _this.conf.panelNodetype.join(', '));
panels.forEach(function (panel) {
var filter = '.mm-panel_lazysubmenu', children = DOM.find(panel, filter);
if (panel.matches(filter)) {
children.unshift(panel);
}
children
.filter(function (child) {
return !child.matches('.mm-panel_lazysubmenu .mm-panel_lazysubmenu');
})
.forEach(function (child) {
var classnames = [
'mm-panel_lazysubmenu',
'mm-nolistview',
'mm-nopanel'
];
// IE11:
classnames.forEach(function (classname) {
child.classList.remove(classname);
});
// Better browsers:
// child.classList.remove(...classnames);
});
});
});
// initPanels for the default opened panel
this.bind('initOpened:before', function () {
var panels = [];
DOM.find(_this.node.pnls, '.' + _this.conf.classNames.selected).forEach(function (listitem) {
panels.push.apply(panels, DOM.parents(listitem, '.mm-panel_lazysubmenu'));
});
if (panels.length) {
panels.forEach(function (panel) {
var classnames = [
'mm-panel_lazysubmenu',
'mm-nolistview',
'mm-nopanel'
];
// IE11:
classnames.forEach(function (classname) {
panel.classList.remove(classname);
});
// Better browsers:
// panel.classList.remove(...classnames);
});
_this.initPanel(panels[panels.length - 1]);
}
});
// initPanels for current- and sub panels before openPanel
this.bind('openPanel:before', function (panel) {
var filter = '.mm-panel_lazysubmenu', panels = DOM.find(panel, filter);
if (panel.matches(filter)) {
panels.unshift(panel);
}
panels = panels.filter(function (panel) {
return !panel.matches('.mm-panel_lazysubmenu .mm-panel_lazysubmenu');
});
panels.forEach(function (panel) {
_this.initPanel(panel);
});
});
}
}

7
dist/addons/navbars/_configs.js vendored Normal file
View File

@ -0,0 +1,7 @@
var configs = {
breadcrumbs: {
separator: '/',
removeFirst: false
}
};
export default configs;

View File

@ -0,0 +1,53 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import * as DOM from '../../_modules/dom';
export default function (navbar) {
var _this = this;
// Add content
var breadcrumbs = DOM.create('div.mm-navbar__breadcrumbs');
navbar.append(breadcrumbs);
this.bind('initNavbar:after', function (panel) {
if (panel.querySelector('.mm-navbar__breadcrumbs')) {
return;
}
DOM.children(panel, '.mm-navbar')[0].classList.add('mm-hidden');
var crumbs = [], breadcrumbs = DOM.create('span.mm-navbar__breadcrumbs'), current = panel, first = true;
while (current) {
current = current.closest('.mm-panel');
if (!current.parentElement.matches('.mm-listitem_vertical')) {
var title = DOM.find(current, '.mm-navbar__title span')[0];
if (title) {
var text = title.textContent;
if (text.length) {
crumbs.unshift(first
? '<span>' + text + '</span>'
: '<a href="#' +
current.id +
'">' +
text +
'</a>');
}
}
first = false;
}
current = current['mmParent'];
}
if (_this.conf.navbars.breadcrumbs.removeFirst) {
crumbs.shift();
}
breadcrumbs.innerHTML = crumbs.join('<span class="mm-separator">' +
_this.conf.navbars.breadcrumbs.separator +
'</span>');
DOM.children(panel, '.mm-navbar')[0].append(breadcrumbs);
});
// Update for to opened panel
this.bind('openPanel:start', function (panel) {
var crumbs = panel.querySelector('.mm-navbar__breadcrumbs');
breadcrumbs.innerHTML = crumbs ? crumbs.innerHTML : '';
});
// Add screenreader / aria support
this.bind('initNavbar:after:sr-aria', function (panel) {
DOM.find(panel, '.mm-breadcrumbs a').forEach(function (anchor) {
Mmenu.sr_aria(anchor, 'owns', anchor.getAttribute('href').slice(1));
});
});
}

17
dist/addons/navbars/_navbar.close.js vendored Normal file
View File

@ -0,0 +1,17 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import * as DOM from '../../_modules/dom';
export default function (navbar) {
var _this = this;
// Add content
var close = DOM.create('a.mm-btn.mm-btn_close.mm-navbar__btn');
navbar.append(close);
// Update to page node
this.bind('setPage:after', function (page) {
close.setAttribute('href', '#' + page.id);
});
// Add screenreader / text support
this.bind('setPage:after:sr-text', function () {
close.innerHTML = Mmenu.sr_text(_this.i18n(_this.conf.screenReader.text.closeMenu));
Mmenu.sr_aria(close, 'owns', close.getAttribute('href').slice(1));
});
}

31
dist/addons/navbars/_navbar.next.js vendored Normal file
View File

@ -0,0 +1,31 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import * as DOM from '../../_modules/dom';
// DEPRECATED
// Will be removed in version 8.2
export default function (navbar) {
var _this = this;
// Add content
var next = DOM.create('a.mm-btn.mm-btn_next.mm-navbar__btn');
navbar.append(next);
// Update to opened panel
var org;
var _url, _txt;
this.bind('openPanel:start', function (panel) {
org = panel.querySelector('.' + _this.conf.classNames.navbars.panelNext);
_url = org ? org.getAttribute('href') : '';
_txt = org ? org.innerHTML : '';
if (_url) {
next.setAttribute('href', _url);
}
else {
next.removeAttribute('href');
}
next.classList[_url || _txt ? 'remove' : 'add']('mm-hidden');
next.innerHTML = _txt;
});
// Add screenreader / aria support
this.bind('openPanel:start:sr-aria', function (panel) {
Mmenu.sr_aria(next, 'hidden', next.matches('mm-hidden'));
Mmenu.sr_aria(next, 'owns', (next.getAttribute('href') || '').slice(1));
});
}

41
dist/addons/navbars/_navbar.prev.js vendored Normal file
View File

@ -0,0 +1,41 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import * as DOM from '../../_modules/dom';
export default function (navbar) {
var _this = this;
// Add content.
var prev = DOM.create('a.mm-btn.mm-btn_prev.mm-navbar__btn');
navbar.append(prev);
this.bind('initNavbar:after', function (panel) {
DOM.children(panel, '.mm-navbar')[0].classList.add('mm-hidden');
});
// Update to opened panel.
var org;
var _url, _txt;
this.bind('openPanel:start', function (panel) {
if (panel.parentElement.matches('.mm-listitem_vertical')) {
return;
}
org = panel.querySelector('.' + _this.conf.classNames.navbars.panelPrev);
if (!org) {
org = panel.querySelector('.mm-navbar__btn.mm-btn_prev');
}
_url = org ? org.getAttribute('href') : '';
_txt = org ? org.innerHTML : '';
if (_url) {
prev.setAttribute('href', _url);
}
else {
prev.removeAttribute('href');
}
prev.classList[_url || _txt ? 'remove' : 'add']('mm-hidden');
prev.innerHTML = _txt;
});
// Add screenreader / aria support
this.bind('initNavbar:after:sr-aria', function (panel) {
Mmenu.sr_aria(panel.querySelector('.mm-navbar'), 'hidden', true);
});
this.bind('openPanel:start:sr-aria', function (panel) {
Mmenu.sr_aria(prev, 'hidden', prev.matches('.mm-hidden'));
Mmenu.sr_aria(prev, 'owns', (prev.getAttribute('href') || '').slice(1));
});
}

View File

@ -0,0 +1,11 @@
import * as DOM from '../../_modules/dom';
import { type } from '../../_modules/helpers';
export default function (navbar) {
if (type(this.opts.searchfield) != 'object') {
this.opts.searchfield = {};
}
var searchfield = DOM.create('div.mm-navbar__searchfield');
navbar.append(searchfield);
this.opts.searchfield.add = true;
this.opts.searchfield.addTo = [searchfield];
}

40
dist/addons/navbars/_navbar.tabs.js vendored Normal file
View File

@ -0,0 +1,40 @@
import * as DOM from '../../_modules/dom';
export default function (navbar) {
var _this = this;
navbar.classList.add('mm-navbar_tabs');
navbar.parentElement.classList.add('mm-navbars_has-tabs');
var anchors = DOM.children(navbar, 'a');
navbar.addEventListener('click', function (evnt) {
var anchor = evnt.target;
if (!anchor.matches('a')) {
return;
}
if (anchor.matches('.mm-navbar__tab_selected')) {
evnt.stopImmediatePropagation();
return;
}
try {
_this.openPanel(_this.node.menu.querySelector(anchor.getAttribute('href')), false);
evnt.stopImmediatePropagation();
}
catch (err) { }
});
function selectTab(panel) {
anchors.forEach(function (anchor) {
anchor.classList.remove('mm-navbar__tab_selected');
});
var anchor = anchors.filter(function (anchor) {
return anchor.matches('[href="#' + panel.id + '"]');
})[0];
if (anchor) {
anchor.classList.add('mm-navbar__tab_selected');
}
else {
var parent = panel['mmParent'];
if (parent) {
selectTab.call(this, parent.closest('.mm-panel'));
}
}
}
this.bind('openPanel:start', selectTab);
}

60
dist/addons/navbars/_navbar.title.js vendored Normal file
View File

@ -0,0 +1,60 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import * as DOM from '../../_modules/dom';
export default function (navbar) {
var _this = this;
// Add content to the navbar.
var title = DOM.create('a.mm-navbar__title');
var titleText = DOM.create('span');
title.append(titleText);
navbar.append(title);
// Update the title to the opened panel.
var _url, _txt;
var original;
this.bind('openPanel:start', function (panel) {
// Do nothing in a vertically expanding panel.
if (panel.parentElement.matches('.mm-listitem_vertical')) {
return;
}
// Find the original title in the opened panel.
original = panel.querySelector('.' + _this.conf.classNames.navbars.panelTitle);
if (!original) {
original = panel.querySelector('.mm-navbar__title span');
}
// Get the URL for the title.
_url =
original && original.closest('a')
? original.closest('a').getAttribute('href')
: '';
if (_url) {
title.setAttribute('href', _url);
}
else {
title.removeAttribute('href');
}
// Get the text for the title.
_txt = original ? original.innerHTML : '';
titleText.innerHTML = _txt;
});
// Add screenreader / aria support
var prev;
this.bind('openPanel:start:sr-aria', function (panel) {
if (_this.opts.screenReader.text) {
if (!prev) {
var navbars = DOM.children(_this.node.menu, '.mm-navbars_top, .mm-navbars_bottom');
navbars.forEach(function (navbar) {
var btn = navbar.querySelector('.mm-btn_prev');
if (btn) {
prev = btn;
}
});
}
if (prev) {
var hidden = true;
if (_this.opts.navbar.titleLink == 'parent') {
hidden = !prev.matches('.mm-hidden');
}
Mmenu.sr_aria(title, 'hidden', hidden);
}
}
});
}

30
dist/addons/navbars/_options.js vendored Normal file
View File

@ -0,0 +1,30 @@
var options = [];
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'boolean' && options) {
options = {};
}
if (typeof options != 'object') {
options = {};
}
if (typeof options.content == 'undefined') {
options.content = ['prev', 'title'];
}
if (!(options.content instanceof Array)) {
options.content = [options.content];
}
if (typeof options.use == 'undefined') {
options.use = true;
}
if (typeof options.use == 'boolean' && options.use) {
options.use = true;
}
return options;
}
;

1
dist/addons/navbars/mmenu.navbars.css vendored Normal file
View File

@ -0,0 +1 @@
.mm-navbars_top{-ms-flex-negative:0;flex-shrink:0}.mm-navbars_top .mm-navbar:not(:last-child){border-bottom:none}.mm-navbars_bottom{-ms-flex-negative:0;flex-shrink:0}.mm-navbars_bottom .mm-navbar{border-bottom:none}.mm-navbars_bottom .mm-navbar:first-child{border-top:1px solid rgba(0,0,0,.1);border-top:1px solid var(--mm-color-border)}.mm-btn:not(.mm-hidden)+.mm-navbar__searchfield .mm-searchfield__input{padding-left:0}.mm-navbar__searchfield:not(:last-child) .mm-searchfield__input{padding-right:0}.mm-navbar__breadcrumbs{-o-text-overflow:ellipsis;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;-webkit-box-flex:1;-ms-flex:1 1 50%;flex:1 1 50%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;padding:0 20px;overflow-x:auto;-webkit-overflow-scrolling:touch}.mm-navbar__breadcrumbs>*{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding-right:6px}.mm-navbar__breadcrumbs>a{text-decoration:underline}.mm-navbar__breadcrumbs:not(:last-child){padding-right:0}.mm-btn:not(.mm-hidden)+.mm-navbar__breadcrumbs{padding-left:0}.mm-navbar_tabs>*{padding:0 10px;border:1px solid transparent}.mm-navbar__tab_selected{background:#f3f3f3;color:rgba(0,0,0,.75);background:var(--mm-color-background);color:var(--mm-color-text)}.mm-navbar__tab_selected:not(:first-child){border-left-color:rgba(0,0,0,.1)}.mm-navbar__tab_selected:not(:last-child){border-right-color:rgba(0,0,0,.1)}.mm-navbar__tab_selected:not(:first-child){border-left-color:var(--mm-color-border)}.mm-navbar__tab_selected:not(:last-child){border-right-color:var(--mm-color-border)}.mm-navbars_top .mm-navbar_tabs{border-bottom:none}.mm-navbars_top .mm-navbar_tabs>*{border-bottom-color:rgba(0,0,0,.1);border-bottom-color:var(--mm-color-border)}.mm-navbars_top .mm-navbar__tab_selected{border-top-color:rgba(0,0,0,.1);border-top-color:var(--mm-color-border);border-bottom-color:transparent}.mm-navbars_top.mm-navbars_has-tabs .mm-navbar{background:rgba(255,255,255,.4);background:var(--mm-color-background-emphasis)}.mm-navbars_top.mm-navbars_has-tabs .mm-navbar_tabs~.mm-navbar{background:#f3f3f3;background:var(--mm-color-background)}.mm-navbars_bottom .mm-navbar_tabs:first-child{border-top:none}.mm-navbars_bottom .mm-navbar_tabs>*{border-top-color:rgba(0,0,0,.1);border-top-color:var(--mm-color-border)}.mm-navbars_bottom .mm-navbar__tab_selected{border-bottom-color:rgba(0,0,0,.1);border-bottom-color:var(--mm-color-border);border-top-color:transparent}.mm-navbars_bottom.mm-navbars_has-tabs .mm-navbar{background:#f3f3f3;background:var(--mm-color-background)}.mm-navbars_bottom.mm-navbars_has-tabs .mm-navbar_tabs,.mm-navbars_bottom.mm-navbars_has-tabs .mm-navbar_tabs~.mm-navbar{background:rgba(255,255,255,.4);background:var(--mm-color-background-emphasis)}

120
dist/addons/navbars/mmenu.navbars.js vendored Normal file
View File

@ -0,0 +1,120 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import configs from './_configs';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import * as media from '../../_modules/matchmedia';
// Add the options and configs.
Mmenu.options.navbars = options;
Mmenu.configs.navbars = configs;
// Add the classnames.
Mmenu.configs.classNames.navbars = {
panelPrev: 'Prev',
panelTitle: 'Title'
};
import breadcrumbs from './_navbar.breadcrumbs';
import close from './_navbar.close';
import prev from './_navbar.prev';
import searchfield from './_navbar.searchfield';
import title from './_navbar.title';
Navbars.navbarContents = {
breadcrumbs: breadcrumbs,
close: close,
prev: prev,
searchfield: searchfield,
title: title
};
import tabs from './_navbar.tabs';
Navbars.navbarTypes = {
tabs: tabs
};
export default function Navbars() {
var _this = this;
var navs = this.opts.navbars;
if (typeof navs == 'undefined') {
return;
}
if (!(navs instanceof Array)) {
navs = [navs];
}
var navbars = {};
if (!navs.length) {
return;
}
navs.forEach(function (options) {
options = extendShorthandOptions(options);
if (!options.use) {
return false;
}
// Create the navbar element.
var navbar = DOM.create('div.mm-navbar');
// Get the position for the navbar.
var position = options.position;
// Restrict the position to either "bottom" or "top" (default).
if (position !== 'bottom') {
position = 'top';
}
// Create the wrapper for the navbar position.
if (!navbars[position]) {
navbars[position] = DOM.create('div.mm-navbars_' + position);
}
navbars[position].append(navbar);
// Add content to the navbar.
for (var c = 0, l = options.content.length; c < l; c++) {
var ctnt = options.content[c];
// The content is a string.
if (typeof ctnt == 'string') {
var func = Navbars.navbarContents[ctnt];
// The content refers to one of the navbar-presets ("prev", "title", etc).
if (typeof func == 'function') {
// Call the preset function.
func.call(_this, navbar);
// The content is just HTML.
}
else {
// Add the HTML.
// Wrap the HTML in a single node
var node = DOM.create('span');
node.innerHTML = ctnt;
// If there was only a single node, use that.
var children = DOM.children(node);
if (children.length == 1) {
node = children[0];
}
navbar.append(node);
}
// The content is not a string, it must be an element.
}
else {
navbar.append(ctnt);
}
}
// The type option is set.
if (typeof options.type == 'string') {
// The function refers to one of the navbar-presets ("tabs").
var func = Navbars.navbarTypes[options.type];
if (typeof func == 'function') {
// Call the preset function.
func.call(_this, navbar);
}
}
// En-/disable the navbar.
var enable = function () {
navbar.classList.remove('mm-hidden');
Mmenu.sr_aria(navbar, 'hidden', false);
};
var disable = function () {
navbar.classList.add('mm-hidden');
Mmenu.sr_aria(navbar, 'hidden', true);
};
if (typeof options.use != 'boolean') {
media.add(options.use, enable, disable);
}
});
// Add to menu.
this.bind('initMenu:after', function () {
for (var position in navbars) {
_this.node.menu[position == 'bottom' ? 'append' : 'prepend'](navbars[position]);
}
});
}

5
dist/addons/pagescroll/_configs.js vendored Normal file
View File

@ -0,0 +1,5 @@
var configs = {
scrollOffset: 0,
updateOffset: 50
};
export default configs;

23
dist/addons/pagescroll/_options.js vendored Normal file
View File

@ -0,0 +1,23 @@
var options = {
scroll: false,
update: false
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'boolean') {
options = {
scroll: options
};
}
if (typeof options != 'object') {
options = {};
}
return options;
}
;

View File

@ -0,0 +1,111 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import configs from './_configs';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import { extend } from '../../_modules/helpers';
// Add the options and configs.
Mmenu.options.pageScroll = options;
Mmenu.configs.pageScroll = configs;
export default function () {
var _this = this;
var options = extendShorthandOptions(this.opts.pageScroll);
this.opts.pageScroll = extend(options, Mmenu.options.pageScroll);
var configs = this.conf.pageScroll;
/** The currently "active" section */
var section;
function scrollTo() {
if (section) {
// section.scrollIntoView({ behavior: 'smooth' });
window.scrollTo({
top: section.getBoundingClientRect().top +
document.scrollingElement.scrollTop -
configs.scrollOffset,
behavior: 'smooth'
});
}
section = null;
}
function anchorInPage(href) {
try {
if (href != '#' && href.slice(0, 1) == '#') {
return Mmenu.node.page.querySelector(href);
}
return null;
}
catch (err) {
return null;
}
}
// Scroll to section after clicking menu item.
if (options.scroll) {
this.bind('close:finish', function () {
scrollTo();
});
}
// Add click behavior.
// Prevents default behavior when clicking an anchor.
if (this.opts.offCanvas && options.scroll) {
this.clck.push(function (anchor, args) {
section = null;
// Don't continue if the clicked anchor is not in the menu.
if (!args.inMenu) {
return;
}
// Don't continue if the targeted section is not on the page.
var href = anchor.getAttribute('href');
section = anchorInPage(href);
if (!section) {
return;
}
// If the sidebar add-on is "expanded"...
if (_this.node.menu.matches('.mm-menu_sidebar-expanded') &&
_this.node.wrpr.matches('.mm-wrapper_sidebar-expanded')) {
// ... scroll the page to the section.
scrollTo();
// ... otherwise...
}
else {
// ... close the menu.
return {
close: true
};
}
});
}
// Update selected menu item after scrolling.
if (options.update) {
var scts_1 = [];
this.bind('initListview:after', function (listview) {
var listitems = DOM.children(listview, '.mm-listitem');
DOM.filterLIA(listitems).forEach(function (anchor) {
var href = anchor.getAttribute('href');
var section = anchorInPage(href);
if (section) {
scts_1.unshift(section);
}
});
});
var _selected_1 = -1;
window.addEventListener('scroll', function (evnt) {
var scrollTop = window.scrollY;
for (var s = 0; s < scts_1.length; s++) {
if (scts_1[s].offsetTop < scrollTop + configs.updateOffset) {
if (_selected_1 !== s) {
_selected_1 = s;
var panel = DOM.children(_this.node.pnls, '.mm-panel_opened')[0];
var listitems = DOM.find(panel, '.mm-listitem');
var anchors = DOM.filterLIA(listitems);
anchors = anchors.filter(function (anchor) {
return anchor.matches('[href="#' + scts_1[s].id + '"]');
});
if (anchors.length) {
_this.setSelected(anchors[0].parentElement);
}
}
break;
}
}
});
}
}

7
dist/addons/searchfield/_configs.js vendored Normal file
View File

@ -0,0 +1,7 @@
var configs = {
clear: false,
form: false,
input: false,
submit: false
};
export default configs;

55
dist/addons/searchfield/_options.js vendored Normal file
View File

@ -0,0 +1,55 @@
var options = {
add: false,
addTo: 'panels',
cancel: false,
noResults: 'No results found.',
placeholder: 'Search',
panel: {
add: false,
dividers: true,
fx: 'none',
id: null,
splash: null,
title: 'Search'
},
search: true,
showTextItems: false,
showSubPanels: true
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'boolean') {
options = {
add: options
};
}
if (typeof options != 'object') {
options = {};
}
if (typeof options.panel == 'boolean') {
options.panel = {
add: options.panel
};
}
if (typeof options.panel != 'object') {
options.panel = {};
}
// Extend logical options.
if (options.addTo == 'panel') {
options.panel.add = true;
}
if (options.panel.add) {
options.showSubPanels = false;
if (options.panel.splash) {
options.cancel = true;
}
}
return options;
}
;

View File

@ -0,0 +1 @@
.mm-searchfield{height:44px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;height:var(--mm-navbar-size);padding:0;overflow:hidden}.mm-searchfield input{height:30.8px;line-height:30.8px}.mm-searchfield input,.mm-searchfield input:focus,.mm-searchfield input:hover{background:rgba(0,0,0,.05);color:rgba(0,0,0,.75)}.mm-searchfield input{display:block;width:100%;max-width:100%;height:calc(var(--mm-navbar-size) * .7);min-height:unset;max-height:unset;margin:0;padding:0 10px;-webkit-box-sizing:border-box;box-sizing:border-box;border:none!important;border-radius:4px;line-height:calc(var(--mm-navbar-size) * .7);-webkit-box-shadow:none!important;box-shadow:none!important;outline:0!important;font:inherit;font-size:inherit}.mm-searchfield input,.mm-searchfield input:focus,.mm-searchfield input:hover{background:var(--mm-color-background-highlight);color:var(--mm-color-text)}.mm-searchfield input::-ms-clear{display:none}.mm-searchfield__input{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1;flex:1;-webkit-box-align:center;-ms-flex-align:center;align-items:center;position:relative;width:100%;max-width:100%;padding:0 10px;-webkit-box-sizing:border-box;box-sizing:border-box}.mm-panel__noresultsmsg{color:rgba(0,0,0,.3);padding:50px 0;color:var(--mm-color-text-dimmed);text-align:center;font-size:150%}.mm-searchfield__btn{position:absolute;right:0;top:0;bottom:0}.mm-panel_search{left:0!important;right:0!important;width:100%!important;border-left:none!important}.mm-searchfield__cancel{line-height:44px;display:block;padding-right:10px;margin-right:-100px;line-height:var(--mm-navbar-size);text-decoration:none;-webkit-transition:margin .4s ease;-o-transition:margin .4s ease;transition:margin .4s ease}.mm-searchfield__cancel-active{margin-right:0}.mm-listitem_nosubitems>.mm-listitem__btn{display:none}.mm-listitem_nosubitems>.mm-listitem__text{padding-right:10px}.mm-listitem_onlysubitems>.mm-listitem__text:not(.mm-listitem__btn){z-index:-1;pointer-events:none}

View File

@ -0,0 +1,507 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import configs from './_configs';
import translate from './translations/translate';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import * as events from '../../_modules/eventlisteners';
import { type, extend } from '../../_modules/helpers';
// Add the translations.
translate();
// Add the options and configs.
Mmenu.options.searchfield = options;
Mmenu.configs.searchfield = configs;
export default function () {
var _this = this;
var options = extendShorthandOptions(this.opts.searchfield);
this.opts.searchfield = extend(options, Mmenu.options.searchfield);
var configs = this.conf.searchfield;
if (!options.add) {
return;
}
// Blur searchfield
this.bind('close:start', function () {
DOM.find(_this.node.menu, '.mm-searchfield').forEach(function (input) {
input.blur();
});
});
this.bind('initPanel:after', function (panel) {
var searchpanel = null;
// Add the search panel
if (options.panel.add) {
searchpanel = initSearchPanel.call(_this);
}
// Add the searchfield
var addTo = null;
switch (options.addTo) {
case 'panels':
addTo = [panel];
break;
case 'panel':
addTo = [searchpanel];
break;
default:
if (typeof options.addTo == 'string') {
addTo = DOM.find(_this.node.menu, options.addTo);
}
else if (type(options.addTo) == 'array') {
addTo = options.addTo;
}
break;
}
addTo.forEach(function (form) {
form = initSearchfield.call(_this, form);
if (options.search && form) {
initSearching.call(_this, form);
}
});
// Add the no-results message
if (options.noResults) {
initNoResultsMsg.call(_this, options.panel.add ? searchpanel : panel);
}
});
// Add click behavior.
// Prevents default behavior when clicking an anchor
this.clck.push(function (anchor, args) {
if (args.inMenu) {
if (anchor.matches('.mm-searchfield__btn')) {
// Clicking the clear button
if (anchor.matches('.mm-btn_close')) {
var form = anchor.closest('.mm-searchfield'), input = DOM.find(form, 'input')[0];
input.value = '';
_this.search(input);
return true;
}
// Clicking the submit button
if (anchor.matches('.mm-btn_next')) {
var form = anchor.closest('form');
if (form) {
form.submit();
}
return true;
}
}
}
});
}
var initSearchPanel = function () {
var options = this.opts.searchfield, configs = this.conf.searchfield;
var searchpanel = DOM.children(this.node.pnls, '.mm-panel_search')[0];
// Only once
if (searchpanel) {
return searchpanel;
}
searchpanel = DOM.create('div.mm-panel.mm-panel_search.mm-hidden');
if (options.panel.id) {
searchpanel.id = options.panel.id;
}
if (options.panel.title) {
searchpanel.setAttribute('data-mm-title', options.panel.title);
// searchpanel.dataset.mmTitle = options.panel.title; // IE10 has no dataset :(
}
var listview = DOM.create('ul');
searchpanel.append(listview);
this.node.pnls.append(searchpanel);
this.initListview(listview);
this._initNavbar(searchpanel);
switch (options.panel.fx) {
case false:
break;
case 'none':
searchpanel.classList.add('mm-panel_noanimation');
break;
default:
searchpanel.classList.add('mm-panel_fx-' + options.panel.fx);
break;
}
// Add splash content
if (options.panel.splash) {
var splash = DOM.create('div.mm-panel__content');
splash.innerHTML = options.panel.splash;
searchpanel.append(splash);
}
searchpanel.classList.add('mm-panel');
searchpanel.classList.add('mm-hidden');
this.node.pnls.append(searchpanel);
return searchpanel;
};
var initSearchfield = function (wrapper) {
var options = this.opts.searchfield, configs = this.conf.searchfield;
// No searchfield in vertical submenus
if (wrapper.parentElement.matches('.mm-listitem_vertical')) {
return null;
}
// Only one searchfield per panel
var form = DOM.find(wrapper, '.mm-searchfield')[0];
if (form) {
return form;
}
function addAttributes(element, attr) {
if (attr) {
for (var a in attr) {
element.setAttribute(a, attr[a]);
}
}
}
var form = DOM.create((configs.form ? 'form' : 'div') + '.mm-searchfield'), field = DOM.create('div.mm-searchfield__input'), input = DOM.create('input');
input.type = 'text';
input.autocomplete = 'off';
input.placeholder = this.i18n(options.placeholder);
field.append(input);
form.append(field);
wrapper.prepend(form);
// Add attributes to the input
addAttributes(input, configs.input);
// Add the clear button
if (configs.clear) {
var anchor = DOM.create('a.mm-btn.mm-btn_close.mm-searchfield__btn');
anchor.setAttribute('href', '#');
field.append(anchor);
}
// Add attributes and submit to the form
addAttributes(form, configs.form);
if (configs.form && configs.submit && !configs.clear) {
var anchor = DOM.create('a.mm-btn.mm-btn_next.mm-searchfield__btn');
anchor.setAttribute('href', '#');
field.append(anchor);
}
if (options.cancel) {
var anchor = DOM.create('a.mm-searchfield__cancel');
anchor.setAttribute('href', '#');
anchor.textContent = this.i18n('cancel');
form.append(anchor);
}
return form;
};
var initSearching = function (form) {
var _this = this;
var options = this.opts.searchfield, configs = this.conf.searchfield;
var data = {};
// In the searchpanel.
if (form.closest('.mm-panel_search')) {
data.panels = DOM.find(this.node.pnls, '.mm-panel');
data.noresults = [form.closest('.mm-panel')];
// In a panel
}
else if (form.closest('.mm-panel')) {
data.panels = [form.closest('.mm-panel')];
data.noresults = data.panels;
// Not in a panel, global
}
else {
data.panels = DOM.find(this.node.pnls, '.mm-panel');
data.noresults = [this.node.menu];
}
// Filter out search panel
data.panels = data.panels.filter(function (panel) { return !panel.matches('.mm-panel_search'); });
// Filter out vertical submenus
data.panels = data.panels.filter(function (panel) { return !panel.parentElement.matches('.mm-listitem_vertical'); });
// Find listitems and dividers.
data.listitems = [];
data.dividers = [];
data.panels.forEach(function (panel) {
var _a, _b;
(_a = data.listitems).push.apply(_a, DOM.find(panel, '.mm-listitem'));
(_b = data.dividers).push.apply(_b, DOM.find(panel, '.mm-divider'));
});
var searchpanel = DOM.children(this.node.pnls, '.mm-panel_search')[0], input = DOM.find(form, 'input')[0], cancel = DOM.find(form, '.mm-searchfield__cancel')[0];
input['mmSearchfield'] = data;
// Open the splash panel when focussing the input.
if (options.panel.add && options.panel.splash) {
events.off(input, 'focus.splash');
events.on(input, 'focus.splash', function (evnt) {
_this.openPanel(searchpanel);
});
}
if (options.cancel) {
// Show the cancel button when focussing the input.
events.off(input, 'focus.cancel');
events.on(input, 'focus.cancel', function (evnt) {
cancel.classList.add('mm-searchfield__cancel-active');
});
// Close the splash panel when clicking the cancel button.
events.off(cancel, 'click.splash');
events.on(cancel, 'click.splash', function (evnt) {
evnt.preventDefault();
cancel.classList.remove('mm-searchfield__cancel-active');
if (searchpanel.matches('.mm-panel_opened')) {
var parents = DOM.children(_this.node.pnls, '.mm-panel_opened-parent');
if (parents.length) {
_this.openPanel(parents[parents.length - 1]);
}
}
});
}
// Focus the input in the searchpanel when opening the searchpanel.
if (options.panel.add && options.addTo == 'panel') {
this.bind('openPanel:finish', function (panel) {
if (panel === searchpanel) {
input.focus();
}
});
}
// Search while typing.
events.off(input, 'input.search');
events.on(input, 'input.search', function (evnt) {
switch (evnt.keyCode) {
case 9: // tab
case 16: // shift
case 17: // control
case 18: // alt
case 37: // left
case 38: // top
case 39: // right
case 40: // bottom
break;
default:
_this.search(input);
break;
}
});
// Search initially.
this.search(input);
};
var initNoResultsMsg = function (wrapper) {
if (!wrapper) {
return;
}
var options = this.opts.searchfield, configs = this.conf.searchfield;
// Not in a panel
if (!wrapper.closest('.mm-panel')) {
wrapper = DOM.children(this.node.pnls, '.mm-panel')[0];
}
// Only once
if (DOM.children(wrapper, '.mm-panel__noresultsmsg').length) {
return;
}
// Add no-results message
var message = DOM.create('div.mm-panel__noresultsmsg.mm-hidden');
message.innerHTML = this.i18n(options.noResults);
wrapper.append(message);
};
Mmenu.prototype.search = function (input, query) {
var _this = this;
var _a;
var options = this.opts.searchfield, configs = this.conf.searchfield;
query = query || '' + input.value;
query = query.toLowerCase().trim();
var data = input['mmSearchfield'];
var form = input.closest('.mm-searchfield'), buttons = DOM.find(form, '.mm-btn'), searchpanel = DOM.children(this.node.pnls, '.mm-panel_search')[0];
/** The panels. */
var panels = data.panels;
/** The "no results" messages in a cloned array. */
var noresults = data.noresults;
/** The listitems in a cloned array. */
var listitems = data.listitems;
/** Tje dividers in a cloned array. */
var dividers = data.dividers;
// Reset previous results
listitems.forEach(function (listitem) {
listitem.classList.remove('mm-listitem_nosubitems');
listitem.classList.remove('mm-listitem_onlysubitems');
listitem.classList.remove('mm-hidden');
});
if (searchpanel) {
DOM.children(searchpanel, '.mm-listview')[0].innerHTML = '';
}
panels.forEach(function (panel) {
panel.scrollTop = 0;
});
// Search
if (query.length) {
// Initially hide all dividers.
dividers.forEach(function (divider) {
divider.classList.add('mm-hidden');
});
// Hide listitems that do not match.
listitems.forEach(function (listitem) {
var text = DOM.children(listitem, '.mm-listitem__text')[0];
var add = false;
// The listitem should be shown if:
// 1) The text matches the query and
// 2a) The text is a open-button and
// 2b) the option showSubPanels is set to true.
// or 3a) The text is not an anchor and
// 3b) the option showTextItems is set to true.
// or 4) The text is an anchor.
// 1
if (text &&
DOM.text(text)
.toLowerCase()
.indexOf(query) > -1) {
// 2a
if (text.matches('.mm-listitem__btn')) {
// 2b
if (options.showSubPanels) {
add = true;
}
}
// 3a
else if (!text.matches('a')) {
// 3b
if (options.showTextItems) {
add = true;
}
}
// 4
else {
add = true;
}
}
if (!add) {
listitem.classList.add('mm-hidden');
}
});
/** Whether or not the query yielded results. */
var hasResults = listitems.filter(function (listitem) { return !listitem.matches('.mm-hidden'); }).length;
// Show all mached listitems in the search panel
if (options.panel.add) {
// Clone all matched listitems into the search panel
var allitems_1 = [];
panels.forEach(function (panel) {
var listitems = DOM.filterLI(DOM.find(panel, '.mm-listitem'));
listitems = listitems.filter(function (listitem) { return !listitem.matches('.mm-hidden'); });
if (listitems.length) {
// Add a divider to indicate in what panel the listitems were.
if (options.panel.dividers) {
var divider = DOM.create('li.mm-divider');
var title = DOM.find(panel, '.mm-navbar__title')[0];
if (title) {
divider.innerHTML = title.innerHTML;
allitems_1.push(divider);
}
}
listitems.forEach(function (listitem) {
allitems_1.push(listitem.cloneNode(true));
});
}
});
// Remove toggles and checks.
allitems_1.forEach(function (listitem) {
listitem
.querySelectorAll('.mm-toggle, .mm-check')
.forEach(function (element) {
element.remove();
});
});
// Add to the search panel.
(_a = DOM.children(searchpanel, '.mm-listview')[0]).append.apply(_a, allitems_1);
// Open the search panel.
this.openPanel(searchpanel);
}
else {
// Also show listitems in sub-panels for matched listitems
if (options.showSubPanels) {
panels.forEach(function (panel) {
var listitems = DOM.find(panel, '.mm-listitem');
DOM.filterLI(listitems).forEach(function (listitem) {
var child = listitem['mmChild'];
if (child) {
DOM.find(child, '.mm-listitem').forEach(function (listitem) {
listitem.classList.remove('mm-hidden');
});
}
});
});
}
// Update parent for sub-panel
// .reverse() mutates the original array, therefor we "clone" it first using [...panels].
panels.slice().reverse().forEach(function (panel, p) {
var parent = panel['mmParent'];
if (parent) {
// The current panel has mached listitems
var listitems_1 = DOM.find(panel, '.mm-listitem');
if (DOM.filterLI(listitems_1).length) {
// Show parent
if (parent.matches('.mm-hidden')) {
parent.classList.remove('mm-hidden');
}
parent.classList.add('mm-listitem_onlysubitems');
}
else if (!input.closest('.mm-panel')) {
if (panel.matches('.mm-panel_opened') ||
panel.matches('.mm-panel_opened-parent')) {
// Compensate the timeout for the opening animation
setTimeout(function () {
_this.openPanel(parent.closest('.mm-panel'));
}, (p + 1) * (_this.conf.openingInterval * 1.5));
}
parent.classList.add('mm-listitem_nosubitems');
}
}
});
// Show parent panels of vertical submenus
panels.forEach(function (panel) {
var listitems = DOM.find(panel, '.mm-listitem');
DOM.filterLI(listitems).forEach(function (listitem) {
DOM.parents(listitem, '.mm-listitem_vertical').forEach(function (parent) {
if (parent.matches('.mm-hidden')) {
parent.classList.remove('mm-hidden');
parent.classList.add('mm-listitem_onlysubitems');
}
});
});
});
// Show first preceeding divider of parent
panels.forEach(function (panel) {
var listitems = DOM.find(panel, '.mm-listitem');
DOM.filterLI(listitems).forEach(function (listitem) {
var divider = DOM.prevAll(listitem, '.mm-divider')[0];
if (divider) {
divider.classList.remove('mm-hidden');
}
});
});
}
// Show submit / clear button
buttons.forEach(function (button) { return button.classList.remove('mm-hidden'); });
// Show/hide no results message
noresults.forEach(function (wrapper) {
DOM.find(wrapper, '.mm-panel__noresultsmsg').forEach(function (message) {
return message.classList[hasResults ? 'add' : 'remove']('mm-hidden');
});
});
if (options.panel.add) {
// Hide splash
if (options.panel.splash) {
DOM.find(searchpanel, '.mm-panel__content').forEach(function (splash) {
return splash.classList.add('mm-hidden');
});
}
// Re-show original listitems when in search panel
listitems.forEach(function (listitem) {
return listitem.classList.remove('mm-hidden');
});
dividers.forEach(function (divider) { return divider.classList.remove('mm-hidden'); });
}
// Don't search
}
else {
// Show all items
listitems.forEach(function (listitem) { return listitem.classList.remove('mm-hidden'); });
dividers.forEach(function (divider) { return divider.classList.remove('mm-hidden'); });
// Hide submit / clear button
buttons.forEach(function (button) { return button.classList.add('mm-hidden'); });
// Hide no results message
noresults.forEach(function (wrapper) {
DOM.find(wrapper, '.mm-panel__noresultsmsg').forEach(function (message) {
return message.classList.add('mm-hidden');
});
});
if (options.panel.add) {
// Show splash
if (options.panel.splash) {
DOM.find(searchpanel, '.mm-panel__content').forEach(function (splash) {
return splash.classList.remove('mm-hidden');
});
// Close panel
}
else if (!input.closest('.mm-panel_search')) {
var opened = DOM.children(this.node.pnls, '.mm-panel_opened-parent');
this.openPanel(opened.slice(-1)[0]);
}
}
}
// Update for other addons
this.trigger('updateListview');
};

View File

@ -0,0 +1,5 @@
export default {
Search: 'Suche',
'No results found.': 'Keine Ergebnisse gefunden.',
cancel: 'beenden'
};

View File

@ -0,0 +1,5 @@
export default {
Search: 'جستجو',
'No results found.': 'نتیجه‌ای یافت نشد.',
cancel: 'انصراف'
};

View File

@ -0,0 +1,5 @@
export default {
Search: 'Zoeken',
'No results found.': 'Geen resultaten gevonden.',
cancel: 'annuleren'
};

View File

@ -0,0 +1,5 @@
export default {
Search: 'Найти',
'No results found.': 'Ничего не найдено.',
cancel: 'отменить'
};

View File

@ -0,0 +1,11 @@
import { add } from '../../../_modules/i18n';
import nl from './nl';
import fa from './fa';
import de from './de';
import ru from './ru';
export default function () {
add(nl, 'nl');
add(fa, 'fa');
add(de, 'de');
add(ru, 'ru');
}

23
dist/addons/sectionindexer/_options.js vendored Normal file
View File

@ -0,0 +1,23 @@
var options = {
add: false,
addTo: 'panels'
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'boolean') {
options = {
add: options
};
}
if (typeof options != 'object') {
options = {};
}
return options;
}
;

View File

@ -0,0 +1 @@
.mm-sectionindexer{background:inherit;text-align:center;font-size:12px;-webkit-box-sizing:border-box;box-sizing:border-box;width:20px;position:absolute;top:0;bottom:0;right:-20px;z-index:5;-webkit-transition:right .4s ease;-o-transition:right .4s ease;transition:right .4s ease;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:space-evenly;-ms-flex-pack:space-evenly;justify-content:space-evenly}.mm-sectionindexer a{color:rgba(0,0,0,.3);color:var(--mm-color-text-dimmed);line-height:1;text-decoration:none;display:block}.mm-sectionindexer~.mm-panel{padding-right:0}.mm-sectionindexer_active{right:0}.mm-sectionindexer_active~.mm-panel{padding-right:20px}

View File

@ -0,0 +1,70 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import * as support from '../../_modules/support';
import { extend } from '../../_modules/helpers';
// Add the options.
Mmenu.options.sectionIndexer = options;
export default function () {
var _this = this;
var options = extendShorthandOptions(this.opts.sectionIndexer);
this.opts.sectionIndexer = extend(options, Mmenu.options.sectionIndexer);
if (!options.add) {
return;
}
this.bind('initPanels:after', function () {
// Add the indexer, only if it does not allready excists
if (!_this.node.indx) {
var buttons_1 = '';
'abcdefghijklmnopqrstuvwxyz'.split('').forEach(function (letter) {
buttons_1 += '<a href="#">' + letter + '</a>';
});
var indexer = DOM.create('div.mm-sectionindexer');
indexer.innerHTML = buttons_1;
_this.node.pnls.prepend(indexer);
_this.node.indx = indexer;
// Prevent default behavior when clicking an anchor
_this.node.indx.addEventListener('click', function (evnt) {
var anchor = evnt.target;
if (anchor.matches('a')) {
evnt.preventDefault();
}
});
// Scroll onMouseOver / onTouchStart
var mouseOverEvent = function (evnt) {
if (!evnt.target.matches('a')) {
return;
}
var letter = evnt.target.textContent, panel = DOM.children(_this.node.pnls, '.mm-panel_opened')[0];
var newTop = -1, oldTop = panel.scrollTop;
panel.scrollTop = 0;
DOM.find(panel, '.mm-divider')
.filter(function (divider) { return !divider.matches('.mm-hidden'); })
.forEach(function (divider) {
if (newTop < 0 &&
letter ==
divider.textContent
.trim()
.slice(0, 1)
.toLowerCase()) {
newTop = divider.offsetTop;
}
});
panel.scrollTop = newTop > -1 ? newTop : oldTop;
};
if (support.touch) {
_this.node.indx.addEventListener('touchstart', mouseOverEvent);
_this.node.indx.addEventListener('touchmove', mouseOverEvent);
}
else {
_this.node.indx.addEventListener('mouseover', mouseOverEvent);
}
}
// Show or hide the indexer
_this.bind('openPanel:start', function (panel) {
var active = DOM.find(panel, '.mm-divider').filter(function (divider) { return !divider.matches('.mm-hidden'); }).length;
_this.node.indx.classList[active ? 'add' : 'remove']('mm-sectionindexer_active');
});
});
}

25
dist/addons/setselected/_options.js vendored Normal file
View File

@ -0,0 +1,25 @@
var options = {
current: true,
hover: false,
parent: false
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'boolean') {
options = {
hover: options,
parent: options
};
}
if (typeof options != 'object') {
options = {};
}
return options;
}
;

View File

@ -0,0 +1 @@
.mm-menu_selected-hover .mm-listitem__btn,.mm-menu_selected-hover .mm-listitem__text,.mm-menu_selected-parent .mm-listitem__btn,.mm-menu_selected-parent .mm-listitem__text{-webkit-transition:background-color .4s ease;-o-transition:background-color .4s ease;transition:background-color .4s ease}.mm-menu_selected-hover .mm-listview:hover>.mm-listitem_selected>.mm-listitem__text{background:0 0}.mm-menu_selected-hover .mm-listitem__btn:hover,.mm-menu_selected-hover .mm-listitem__text:hover{background:rgba(255,255,255,.4);background:var(--mm-color-background-emphasis)}.mm-menu_selected-parent .mm-panel_opened-parent .mm-listitem:not(.mm-listitem_selected-parent)>.mm-listitem__text{background:0 0}.mm-menu_selected-parent .mm-listitem_selected-parent>.mm-listitem__btn,.mm-menu_selected-parent .mm-listitem_selected-parent>.mm-listitem__text{background:rgba(255,255,255,.4);background:var(--mm-color-background-emphasis)}

View File

@ -0,0 +1,66 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import { extend } from '../../_modules/helpers';
// Add the options.
Mmenu.options.setSelected = options;
export default function () {
var _this = this;
var options = extendShorthandOptions(this.opts.setSelected);
this.opts.setSelected = extend(options, Mmenu.options.setSelected);
// Find current by URL
if (options.current == 'detect') {
var findCurrent_1 = function (url) {
url = url.split('?')[0].split('#')[0];
var anchor = _this.node.menu.querySelector('a[href="' + url + '"], a[href="' + url + '/"]');
if (anchor) {
_this.setSelected(anchor.parentElement);
}
else {
var arr = url.split('/').slice(0, -1);
if (arr.length) {
findCurrent_1(arr.join('/'));
}
}
};
this.bind('initMenu:after', function () {
findCurrent_1.call(_this, window.location.href);
});
// Remove current selected item
}
else if (!options.current) {
this.bind('initListview:after', function (listview) {
DOM.children(listview, '.mm-listitem_selected').forEach(function (listitem) {
listitem.classList.remove('mm-listitem_selected');
});
});
}
// Add :hover effect on items
if (options.hover) {
this.bind('initMenu:after', function () {
_this.node.menu.classList.add('mm-menu_selected-hover');
});
}
// Set parent item selected for submenus
if (options.parent) {
this.bind('openPanel:finish', function (panel) {
// Remove all
DOM.find(_this.node.pnls, '.mm-listitem_selected-parent').forEach(function (listitem) {
listitem.classList.remove('mm-listitem_selected-parent');
});
// Move up the DOM tree
var parent = panel['mmParent'];
while (parent) {
if (!parent.matches('.mm-listitem_vertical')) {
parent.classList.add('mm-listitem_selected-parent');
}
parent = parent.closest('.mm-panel');
parent = parent['mmParent'];
}
});
this.bind('initMenu:after', function () {
_this.node.menu.classList.add('mm-menu_selected-parent');
});
}
}

62
dist/addons/sidebar/_options.js vendored Normal file
View File

@ -0,0 +1,62 @@
var options = {
collapsed: {
use: false,
blockMenu: true,
hideDivider: false,
hideNavbar: true
},
expanded: {
use: false,
initial: 'open'
}
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'string' ||
(typeof options == 'boolean' && options) ||
typeof options == 'number') {
options = {
expanded: options
};
}
if (typeof options != 'object') {
options = {};
}
// Extend collapsed shorthand options.
if (typeof options.collapsed == 'boolean' && options.collapsed) {
options.collapsed = {
use: true
};
}
if (typeof options.collapsed == 'string' ||
typeof options.collapsed == 'number') {
options.collapsed = {
use: options.collapsed
};
}
if (typeof options.collapsed != 'object') {
options.collapsed = {};
}
// Extend expanded shorthand options.
if (typeof options.expanded == 'boolean' && options.expanded) {
options.expanded = {
use: true
};
}
if (typeof options.expanded == 'string' ||
typeof options.expanded == 'number') {
options.expanded = {
use: options.expanded
};
}
if (typeof options.expanded != 'object') {
options.expanded = {};
}
return options;
}

1
dist/addons/sidebar/mmenu.sidebar.css vendored Normal file
View File

@ -0,0 +1 @@
:root{--mm-sidebar-collapsed-size:50px;--mm-sidebar-expanded-size:440px}.mm-wrapper_sidebar-collapsed body,.mm-wrapper_sidebar-expanded body{position:relative}.mm-wrapper_sidebar-collapsed .mm-slideout,.mm-wrapper_sidebar-expanded .mm-slideout{-webkit-transition-property:width,-webkit-transform;transition-property:width,-webkit-transform;-o-transition-property:width,transform;transition-property:width,transform;transition-property:width,transform,-webkit-transform}.mm-wrapper_sidebar-collapsed .mm-page,.mm-wrapper_sidebar-expanded .mm-page{background:inherit;-webkit-box-sizing:border-box;box-sizing:border-box;min-height:100vh}.mm-wrapper_sidebar-collapsed .mm-menu_sidebar-collapsed,.mm-wrapper_sidebar-expanded .mm-menu_sidebar-expanded{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;top:0!important;right:auto!important;bottom:0!important;left:0!important}.mm-wrapper_sidebar-collapsed .mm-slideout{width:calc(100% - 50px);-webkit-transform:translate3d(50px,0,0);transform:translate3d(50px,0,0);width:calc(100% - var(--mm-sidebar-collapsed-size));-webkit-transform:translate3d(var(--mm-sidebar-collapsed-size),0,0);transform:translate3d(var(--mm-sidebar-collapsed-size),0,0)}.mm-wrapper_sidebar-collapsed:not(.mm-wrapper_opening) .mm-menu_hidedivider .mm-divider,.mm-wrapper_sidebar-collapsed:not(.mm-wrapper_opening) .mm-menu_hidenavbar .mm-navbar{opacity:0}.mm-wrapper_sidebar-expanded .mm-menu_sidebar-expanded{width:440px;width:var(--mm-sidebar-expanded-size);min-width:0!important;max-width:100000px!important;border-right-width:1px;border-right-style:solid}.mm-wrapper_sidebar-expanded .mm-menu_sidebar-expanded.mm-menu_pageshadow:after{content:none;display:none}.mm-wrapper_sidebar-expanded.mm-wrapper_blocking,.mm-wrapper_sidebar-expanded.mm-wrapper_blocking body{overflow:visible}.mm-wrapper_sidebar-expanded .mm-wrapper__blocker{display:none!important}.mm-wrapper_sidebar-expanded:not(.mm-wrapper_sidebar-closed) .mm-menu_sidebar-expanded.mm-menu_opened~.mm-slideout{width:calc(100% - 440px);-webkit-transform:translate3d(440px,0,0);transform:translate3d(440px,0,0);width:calc(100% - var(--mm-sidebar-expanded-size));-webkit-transform:translate3d(var(--mm-sidebar-expanded-size),0,0);transform:translate3d(var(--mm-sidebar-expanded-size),0,0)}.mm-menu__blocker{background:rgba(3,2,1,0);display:block;position:absolute;top:0;right:0;bottom:0;left:0;z-index:3}.mm-menu_opened .mm-menu__blocker{display:none}[dir=rtl].mm-wrapper_sidebar-collapsed .mm-slideout{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}[dir=rtl].mm-wrapper_sidebar-expanded .mm-slideout{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}[dir=rtl].mm-wrapper_sidebar-expanded:not(.mm-wrapper_sidebar-closed) .mm-menu_sidebar-expanded.mm-menu_opened~.mm-slideout{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}

117
dist/addons/sidebar/mmenu.sidebar.js vendored Normal file
View File

@ -0,0 +1,117 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import * as media from '../../_modules/matchmedia';
import { extend } from '../../_modules/helpers';
// Add the options.
Mmenu.options.sidebar = options;
export default function () {
var _this = this;
if (!this.opts.offCanvas) {
return;
}
var options = extendShorthandOptions(this.opts.sidebar);
this.opts.sidebar = extend(options, Mmenu.options.sidebar);
// Collapsed
if (options.collapsed.use) {
// Make the menu collapsable.
this.bind('initMenu:after', function () {
_this.node.menu.classList.add('mm-menu_sidebar-collapsed');
if (options.collapsed.blockMenu &&
_this.opts.offCanvas &&
!DOM.children(_this.node.menu, '.mm-menu__blocker')[0]) {
var anchor = DOM.create('a.mm-menu__blocker');
anchor.setAttribute('href', '#' + _this.node.menu.id);
_this.node.menu.prepend(anchor);
}
if (options.collapsed.hideNavbar) {
_this.node.menu.classList.add('mm-menu_hidenavbar');
}
if (options.collapsed.hideDivider) {
_this.node.menu.classList.add('mm-menu_hidedivider');
}
});
// En-/disable the collapsed sidebar.
var enable = function () {
_this.node.wrpr.classList.add('mm-wrapper_sidebar-collapsed');
};
var disable = function () {
_this.node.wrpr.classList.remove('mm-wrapper_sidebar-collapsed');
};
if (typeof options.collapsed.use == 'boolean') {
this.bind('initMenu:after', enable);
}
else {
media.add(options.collapsed.use, enable, disable);
}
}
// Expanded
if (options.expanded.use) {
// Make the menu expandable
this.bind('initMenu:after', function () {
_this.node.menu.classList.add('mm-menu_sidebar-expanded');
});
// En-/disable the expanded sidebar.
var enable = function () {
_this.node.wrpr.classList.add('mm-wrapper_sidebar-expanded');
if (!_this.node.wrpr.matches('.mm-wrapper_sidebar-closed')) {
_this.open();
}
};
var disable = function () {
_this.node.wrpr.classList.remove('mm-wrapper_sidebar-expanded');
_this.close();
};
if (typeof options.expanded.use == 'boolean') {
this.bind('initMenu:after', enable);
}
else {
media.add(options.expanded.use, enable, disable);
}
// Manually en-/disable the expanded sidebar (open / close the menu)
this.bind('close:start', function () {
if (_this.node.wrpr.matches('.mm-wrapper_sidebar-expanded')) {
_this.node.wrpr.classList.add('mm-wrapper_sidebar-closed');
if (options.expanded.initial == 'remember') {
window.localStorage.setItem('mmenuExpandedState', 'closed');
}
}
});
this.bind('open:start', function () {
if (_this.node.wrpr.matches('.mm-wrapper_sidebar-expanded')) {
_this.node.wrpr.classList.remove('mm-wrapper_sidebar-closed');
if (options.expanded.initial == 'remember') {
window.localStorage.setItem('mmenuExpandedState', 'open');
}
}
});
// Set the initial state
var initialState = options.expanded.initial;
if (options.expanded.initial == 'remember') {
var state = window.localStorage.getItem('mmenuExpandedState');
switch (state) {
case 'open':
case 'closed':
initialState = state;
break;
}
}
if (initialState == 'closed') {
this.bind('initMenu:after', function () {
_this.node.wrpr.classList.add('mm-wrapper_sidebar-closed');
});
}
// Add click behavior.
// Prevents default behavior when clicking an anchor
this.clck.push(function (anchor, args) {
if (args.inMenu && args.inListview) {
if (_this.node.wrpr.matches('.mm-wrapper_sidebar-expanded')) {
return {
close: options.expanded.initial == 'closed'
};
}
}
});
}
}

1
dist/addons/toggles/mmenu.toggles.css vendored Normal file
View File

@ -0,0 +1 @@
input.mm-toggle{margin-top:5px;background:rgba(0,0,0,.1);display:inline-block;min-width:58px;width:58px;height:34px;margin:0 10px;margin-top:calc((var(--mm-listitem-size) - 34px)/ 2);border:none!important;background:var(--mm-color-border);border-radius:34px;-webkit-appearance:none!important;-moz-appearance:none!important;appearance:none!important;cursor:pointer;-webkit-transition:background-color .2s ease;-o-transition:background-color .2s ease;transition:background-color .2s ease}input.mm-toggle:before{background:#f3f3f3}input.mm-toggle:before{content:'';display:block;width:32px;height:32px;margin:1px;border-radius:34px;background:var(--mm-color-background);-webkit-transition:-webkit-transform .2s ease;transition:-webkit-transform .2s ease;-o-transition:transform .2s ease;transition:transform .2s ease;transition:transform .2s ease,-webkit-transform .2s ease}input.mm-toggle:checked{background:#4bd963}input.mm-toggle:checked:before{-webkit-transform:translateX(24px);-ms-transform:translateX(24px);transform:translateX(24px)}input.mm-check{margin-top:2px;-webkit-appearance:none!important;-moz-appearance:none!important;appearance:none!important;border:none!important;background:0 0!important;cursor:pointer;display:inline-block;width:40px;height:40px;margin:0 10px;margin-top:calc((var(--mm-listitem-size) - 40px)/ 2)}input.mm-check:before{content:'';display:block;width:40%;height:20%;margin:25% 0 0 20%;border-left:3px solid;border-bottom:3px solid;border-color:var(--mm-color-text);opacity:.3;-webkit-transform:rotate(-45deg);-ms-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-transition:opacity .2s ease;-o-transition:opacity .2s ease;transition:opacity .2s ease}input.mm-check:checked:before{opacity:1}[dir=rtl] input.mm-toggle:checked~label.mm-toggle:before{float:left}

17
dist/addons/toggles/mmenu.toggles.js vendored Normal file
View File

@ -0,0 +1,17 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import * as DOM from '../../_modules/dom';
// Add the classnames.
Mmenu.configs.classNames.toggles = {
toggle: 'Toggle',
check: 'Check'
};
export default function () {
var _this = this;
this.bind('initPanel:after', function (panel) {
// Refactor toggle classes
DOM.find(panel, 'input').forEach(function (input) {
DOM.reClass(input, _this.conf.classNames.toggles.toggle, 'mm-toggle');
DOM.reClass(input, _this.conf.classNames.toggles.check, 'mm-check');
});
});
}

13
dist/core/offcanvas/_configs.js vendored Normal file
View File

@ -0,0 +1,13 @@
var configs = {
clone: false,
menu: {
insertMethod: 'prepend',
insertSelector: 'body'
},
page: {
nodetype: 'div',
selector: null,
noSelector: []
}
};
export default configs;

18
dist/core/offcanvas/_options.js vendored Normal file
View File

@ -0,0 +1,18 @@
var options = {
blockUI: true,
moveBackground: true
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options != 'object') {
options = {};
}
return options;
}
;

View File

@ -0,0 +1 @@
.mm-page{-webkit-box-sizing:border-box;box-sizing:border-box;position:relative}.mm-slideout{-webkit-transition:-webkit-transform .4s ease;transition:-webkit-transform .4s ease;-o-transition:transform .4s ease;transition:transform .4s ease;transition:transform .4s ease,-webkit-transform .4s ease;z-index:1}.mm-wrapper_opened{overflow-x:hidden;position:relative}.mm-wrapper_opened .mm-page{min-height:100vh}.mm-wrapper_background .mm-page{background:inherit}.mm-menu_offcanvas{position:fixed;right:auto;z-index:0}.mm-menu_offcanvas:not(.mm-menu_opened){display:none}.mm-menu_offcanvas{width:80%;min-width:240px;max-width:440px}.mm-wrapper_opening .mm-menu_offcanvas~.mm-slideout{-webkit-transform:translate3d(80vw,0,0);transform:translate3d(80vw,0,0)}@media all and (max-width:300px){.mm-wrapper_opening .mm-menu_offcanvas~.mm-slideout{-webkit-transform:translate3d(240px,0,0);transform:translate3d(240px,0,0)}}@media all and (min-width:550px){.mm-wrapper_opening .mm-menu_offcanvas~.mm-slideout{-webkit-transform:translate3d(440px,0,0);transform:translate3d(440px,0,0)}}.mm-wrapper__blocker{background:rgba(3,2,1,0);overflow:hidden;display:none;position:fixed;top:0;right:0;bottom:0;left:0;z-index:2}.mm-wrapper_blocking{overflow:hidden}.mm-wrapper_blocking body{overflow:hidden}.mm-wrapper_blocking .mm-wrapper__blocker{display:block}

331
dist/core/offcanvas/mmenu.offcanvas.js vendored Normal file
View File

@ -0,0 +1,331 @@
import Mmenu from './../oncanvas/mmenu.oncanvas';
import options from './_options';
import configs from './_configs';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import * as events from '../../_modules/eventlisteners';
import { extend, transitionend, uniqueId, originalId } from '../../_modules/helpers';
// Add the options and configs.
Mmenu.options.offCanvas = options;
Mmenu.configs.offCanvas = configs;
export default function () {
var _this = this;
if (!this.opts.offCanvas) {
return;
}
var options = extendShorthandOptions(this.opts.offCanvas);
this.opts.offCanvas = extend(options, Mmenu.options.offCanvas);
var configs = this.conf.offCanvas;
// Add methods to the API.
this._api.push('open', 'close', 'setPage');
// Setup the menu.
this.vars.opened = false;
// Add off-canvas behavior.
this.bind('initMenu:before', function () {
// Clone if needed.
if (configs.clone) {
// Clone the original menu and store it.
_this.node.menu = _this.node.menu.cloneNode(true);
// Prefix all ID's in the cloned menu.
if (_this.node.menu.id) {
_this.node.menu.id = 'mm-' + _this.node.menu.id;
}
DOM.find(_this.node.menu, '[id]').forEach(function (elem) {
elem.id = 'mm-' + elem.id;
});
}
_this.node.wrpr = document.body;
// Prepend to the <body>
document
.querySelector(configs.menu.insertSelector)[configs.menu.insertMethod](_this.node.menu);
});
this.bind('initMenu:after', function () {
// Setup the UI blocker.
initBlocker.call(_this);
// Setup the page.
_this.setPage(Mmenu.node.page);
// Setup window events.
initWindow.call(_this);
// Setup the menu.
_this.node.menu.classList.add('mm-menu_offcanvas');
// Open if url hash equals menu id (usefull when user clicks the hamburger icon before the menu is created)
var hash = window.location.hash;
if (hash) {
var id = originalId(_this.node.menu.id);
if (id && id == hash.slice(1)) {
setTimeout(function () {
_this.open();
}, 1000);
}
}
});
// Sync the blocker to target the page.
this.bind('setPage:after', function (page) {
if (Mmenu.node.blck) {
DOM.children(Mmenu.node.blck, 'a').forEach(function (anchor) {
anchor.setAttribute('href', '#' + page.id);
});
}
});
// Add screenreader / aria support
this.bind('open:start:sr-aria', function () {
Mmenu.sr_aria(_this.node.menu, 'hidden', false);
});
this.bind('close:finish:sr-aria', function () {
Mmenu.sr_aria(_this.node.menu, 'hidden', true);
});
this.bind('initMenu:after:sr-aria', function () {
Mmenu.sr_aria(_this.node.menu, 'hidden', true);
});
// Add screenreader / text support
this.bind('initBlocker:after:sr-text', function () {
DOM.children(Mmenu.node.blck, 'a').forEach(function (anchor) {
anchor.innerHTML = Mmenu.sr_text(_this.i18n(_this.conf.screenReader.text.closeMenu));
});
});
// Add click behavior.
// Prevents default behavior when clicking an anchor
this.clck.push(function (anchor, args) {
// Open menu if the clicked anchor links to the menu
var id = originalId(_this.node.menu.id);
if (id) {
if (anchor.matches('[href="#' + id + '"]')) {
// Opening this menu from within this menu
// -> Open menu
if (args.inMenu) {
_this.open();
return true;
}
// Opening this menu from within a second menu
// -> Close the second menu before opening this menu
var menu = anchor.closest('.mm-menu');
if (menu) {
var api = menu['mmApi'];
if (api && api.close) {
api.close();
transitionend(menu, function () {
_this.open();
}, _this.conf.transitionDuration);
return true;
}
}
// Opening this menu
_this.open();
return true;
}
}
// Close menu
id = Mmenu.node.page.id;
if (id) {
if (anchor.matches('[href="#' + id + '"]')) {
_this.close();
return true;
}
}
return;
});
}
/**
* Open the menu.
*/
Mmenu.prototype.open = function () {
var _this = this;
// Invoke "before" hook.
this.trigger('open:before');
if (this.vars.opened) {
return;
}
this._openSetup();
// Without the timeout, the animation won't work because the menu had display: none;
setTimeout(function () {
_this._openStart();
}, this.conf.openingInterval);
// Invoke "after" hook.
this.trigger('open:after');
};
Mmenu.prototype._openSetup = function () {
var _this = this;
var options = this.opts.offCanvas;
// Close other menus
this.closeAllOthers();
// Store style and position
Mmenu.node.page['mmStyle'] = Mmenu.node.page.getAttribute('style') || '';
// Trigger window-resize to measure height
events.trigger(window, 'resize.page', { force: true });
var clsn = ['mm-wrapper_opened'];
// Add options
if (options.blockUI) {
clsn.push('mm-wrapper_blocking');
}
if (options.blockUI == 'modal') {
clsn.push('mm-wrapper_modal');
}
if (options.moveBackground) {
clsn.push('mm-wrapper_background');
}
// IE11:
clsn.forEach(function (classname) {
_this.node.wrpr.classList.add(classname);
});
// Better browsers:
// this.node.wrpr.classList.add(...clsn);
// Open
// Without the timeout, the animation won't work because the menu had display: none;
setTimeout(function () {
_this.vars.opened = true;
}, this.conf.openingInterval);
this.node.menu.classList.add('mm-menu_opened');
};
/**
* Finish opening the menu.
*/
Mmenu.prototype._openStart = function () {
var _this = this;
// Callback when the page finishes opening.
transitionend(Mmenu.node.page, function () {
_this.trigger('open:finish');
}, this.conf.transitionDuration);
// Opening
this.trigger('open:start');
this.node.wrpr.classList.add('mm-wrapper_opening');
};
Mmenu.prototype.close = function () {
var _this = this;
// Invoke "before" hook.
this.trigger('close:before');
if (!this.vars.opened) {
return;
}
// Callback when the page finishes closing.
transitionend(Mmenu.node.page, function () {
_this.node.menu.classList.remove('mm-menu_opened');
var classnames = [
'mm-wrapper_opened',
'mm-wrapper_blocking',
'mm-wrapper_modal',
'mm-wrapper_background'
];
// IE11:
classnames.forEach(function (classname) {
_this.node.wrpr.classList.remove(classname);
});
// Better browsers:
// this.node.wrpr.classList.remove(...classnames);
// Restore style and position
Mmenu.node.page.setAttribute('style', Mmenu.node.page['mmStyle']);
_this.vars.opened = false;
_this.trigger('close:finish');
}, this.conf.transitionDuration);
// Closing
this.trigger('close:start');
this.node.wrpr.classList.remove('mm-wrapper_opening');
// Invoke "after" hook.
this.trigger('close:after');
};
/**
* Close all other menus.
*/
Mmenu.prototype.closeAllOthers = function () {
var _this = this;
DOM.find(document.body, '.mm-menu_offcanvas').forEach(function (menu) {
if (menu !== _this.node.menu) {
var api = menu['mmApi'];
if (api && api.close) {
api.close();
}
}
});
};
/**
* Set the "page" node.
*
* @param {HTMLElement} page Element to set as the page.
*/
Mmenu.prototype.setPage = function (page) {
// Invoke "before" hook.
this.trigger('setPage:before', [page]);
var configs = this.conf.offCanvas;
// If no page was specified, find it.
if (!page) {
/** Array of elements that are / could be "the page". */
var pages = typeof configs.page.selector == 'string'
? DOM.find(document.body, configs.page.selector)
: DOM.children(document.body, configs.page.nodetype);
// Filter out elements that are absolutely not "the page".
pages = pages.filter(function (page) { return !page.matches('.mm-menu, .mm-wrapper__blocker'); });
// Filter out elements that are configured to not be "the page".
if (configs.page.noSelector.length) {
pages = pages.filter(function (page) { return !page.matches(configs.page.noSelector.join(', ')); });
}
// Wrap multiple pages in a single element.
if (pages.length > 1) {
var wrapper_1 = DOM.create('div');
pages[0].before(wrapper_1);
pages.forEach(function (page) {
wrapper_1.append(page);
});
pages = [wrapper_1];
}
page = pages[0];
}
page.classList.add('mm-page');
page.classList.add('mm-slideout');
page.id = page.id || uniqueId();
Mmenu.node.page = page;
// Invoke "after" hook.
this.trigger('setPage:after', [page]);
};
/**
* Initialize the window.
*/
var initWindow = function () {
var _this = this;
// Prevent tabbing
// Because when tabbing outside the menu, the element that gains focus will be centered on the screen.
// In other words: The menu would move out of view.
events.off(document.body, 'keydown.tabguard');
events.on(document.body, 'keydown.tabguard', function (evnt) {
if (evnt.keyCode == 9) {
if (_this.node.wrpr.matches('.mm-wrapper_opened')) {
evnt.preventDefault();
}
}
});
};
/**
* Initialize "blocker" node
*/
var initBlocker = function () {
var _this = this;
// Invoke "before" hook.
this.trigger('initBlocker:before');
var options = this.opts.offCanvas, configs = this.conf.offCanvas;
if (!options.blockUI) {
return;
}
// Create the blocker node.
if (!Mmenu.node.blck) {
var blck = DOM.create('div.mm-wrapper__blocker.mm-slideout');
blck.innerHTML = '<a></a>';
// Append the blocker node to the body.
document.querySelector(configs.menu.insertSelector).append(blck);
// Store the blocker node.
Mmenu.node.blck = blck;
}
// Close the menu when
// 1) clicking,
// 2) touching or
// 3) dragging the blocker node.
var closeMenu = function (evnt) {
evnt.preventDefault();
evnt.stopPropagation();
if (!_this.node.wrpr.matches('.mm-wrapper_modal')) {
_this.close();
}
};
Mmenu.node.blck.addEventListener('mousedown', closeMenu); // 1
Mmenu.node.blck.addEventListener('touchstart', closeMenu); // 2
Mmenu.node.blck.addEventListener('touchmove', closeMenu); // 3
// Invoke "after" hook.
this.trigger('initBlocker:after');
};

15
dist/core/oncanvas/_configs.js vendored Normal file
View File

@ -0,0 +1,15 @@
var configs = {
classNames: {
inset: 'Inset',
nolistview: 'NoListview',
nopanel: 'NoPanel',
panel: 'Panel',
selected: 'Selected',
vertical: 'Vertical'
},
language: null,
openingInterval: 25,
panelNodetype: ['ul', 'ol', 'div'],
transitionDuration: 400
};
export default configs;

18
dist/core/oncanvas/_options.js vendored Normal file
View File

@ -0,0 +1,18 @@
var options = {
hooks: {},
extensions: [],
wrappers: [],
navbar: {
add: true,
sticky: true,
title: 'Menu',
titleLink: 'parent'
},
onClick: {
close: null,
preventDefault: null,
setSelected: true
},
slidingSubmenus: true
};
export default options;

1
dist/core/oncanvas/mmenu.oncanvas.css vendored Normal file

File diff suppressed because one or more lines are too long

774
dist/core/oncanvas/mmenu.oncanvas.js vendored Normal file
View File

@ -0,0 +1,774 @@
import version from '../../_version';
import options from './_options';
import configs from './_configs';
import translate from './translations/translate';
import * as DOM from '../../_modules/dom';
import * as i18n from '../../_modules/i18n';
import * as media from '../../_modules/matchmedia';
import { type, extend, transitionend, uniqueId, valueOrFn } from '../../_modules/helpers';
// Add the translations.
translate();
/**
* Class for a mobile menu.
*/
var Mmenu = /** @class */ (function () {
/**
* Create a mobile menu.
* @param {HTMLElement|string} menu The menu node.
* @param {object} [options=Mmenu.options] Options for the menu.
* @param {object} [configs=Mmenu.configs] Configuration options for the menu.
*/
function Mmenu(menu, options, configs) {
// Extend options and configuration from defaults.
this.opts = extend(options, Mmenu.options);
this.conf = extend(configs, Mmenu.configs);
// Methods to expose in the API.
this._api = [
'bind',
'initPanel',
'initListview',
'openPanel',
'closePanel',
'closeAllPanels',
'setSelected'
];
// Storage objects for nodes, variables, hooks and click handlers.
this.node = {};
this.vars = {};
this.hook = {};
this.clck = [];
// Get menu node from string or element.
this.node.menu =
typeof menu == 'string' ? document.querySelector(menu) : menu;
if (typeof this._deprecatedWarnings == 'function') {
this._deprecatedWarnings();
}
this._initWrappers();
this._initAddons();
this._initExtensions();
this._initHooks();
this._initAPI();
this._initMenu();
this._initPanels();
this._initOpened();
this._initAnchors();
media.watch();
return this;
}
/**
* Open a panel.
* @param {HTMLElement} panel Panel to open.
* @param {boolean} [animation=true] Whether or not to open the panel with an animation.
*/
Mmenu.prototype.openPanel = function (panel, animation) {
var _this = this;
// Invoke "before" hook.
this.trigger('openPanel:before', [panel]);
// Find panel.
if (!panel) {
return;
}
if (!panel.matches('.mm-panel')) {
panel = panel.closest('.mm-panel');
}
if (!panel) {
return;
}
// /Find panel.
if (typeof animation != 'boolean') {
animation = true;
}
// Open a "vertical" panel.
if (panel.parentElement.matches('.mm-listitem_vertical')) {
// Open current and all vertical parent panels.
DOM.parents(panel, '.mm-listitem_vertical').forEach(function (listitem) {
listitem.classList.add('mm-listitem_opened');
DOM.children(listitem, '.mm-panel').forEach(function (panel) {
panel.classList.remove('mm-hidden');
});
});
// Open first non-vertical parent panel.
var parents = DOM.parents(panel, '.mm-panel').filter(function (panel) { return !panel.parentElement.matches('.mm-listitem_vertical'); });
this.trigger('openPanel:start', [panel]);
if (parents.length) {
this.openPanel(parents[0]);
}
this.trigger('openPanel:finish', [panel]);
// Open a "horizontal" panel.
}
else {
if (panel.matches('.mm-panel_opened')) {
return;
}
var panels = DOM.children(this.node.pnls, '.mm-panel'), current_1 = DOM.children(this.node.pnls, '.mm-panel_opened')[0];
// Close all child panels.
panels
.filter(function (parent) { return parent !== panel; })
.forEach(function (parent) {
parent.classList.remove('mm-panel_opened-parent');
});
// Open all parent panels.
var parent_1 = panel['mmParent'];
while (parent_1) {
parent_1 = parent_1.closest('.mm-panel');
if (parent_1) {
if (!parent_1.parentElement.matches('.mm-listitem_vertical')) {
parent_1.classList.add('mm-panel_opened-parent');
}
parent_1 = parent_1['mmParent'];
}
}
// Add classes for animation.
panels.forEach(function (panel) {
panel.classList.remove('mm-panel_highest');
});
panels
.filter(function (hidden) { return hidden !== current_1; })
.filter(function (hidden) { return hidden !== panel; })
.forEach(function (hidden) {
hidden.classList.add('mm-hidden');
});
panel.classList.remove('mm-hidden');
/** Start opening the panel. */
var openPanelStart_1 = function () {
if (current_1) {
current_1.classList.remove('mm-panel_opened');
}
panel.classList.add('mm-panel_opened');
if (panel.matches('.mm-panel_opened-parent')) {
if (current_1) {
current_1.classList.add('mm-panel_highest');
}
panel.classList.remove('mm-panel_opened-parent');
}
else {
if (current_1) {
current_1.classList.add('mm-panel_opened-parent');
}
panel.classList.add('mm-panel_highest');
}
// Invoke "start" hook.
_this.trigger('openPanel:start', [panel]);
};
/** Finish opening the panel. */
var openPanelFinish_1 = function () {
if (current_1) {
current_1.classList.remove('mm-panel_highest');
current_1.classList.add('mm-hidden');
}
panel.classList.remove('mm-panel_highest');
// Invoke "finish" hook.
_this.trigger('openPanel:finish', [panel]);
};
if (animation && !panel.matches('.mm-panel_noanimation')) {
// Without the timeout the animation will not work because the element had display: none;
setTimeout(function () {
// Callback
transitionend(panel, function () {
openPanelFinish_1();
}, _this.conf.transitionDuration);
openPanelStart_1();
}, this.conf.openingInterval);
}
else {
openPanelStart_1();
openPanelFinish_1();
}
}
// Invoke "after" hook.
this.trigger('openPanel:after', [panel]);
};
/**
* Close a panel.
* @param {HTMLElement} panel Panel to close.
*/
Mmenu.prototype.closePanel = function (panel) {
// Invoke "before" hook.
this.trigger('closePanel:before', [panel]);
var li = panel.parentElement;
// Only works for "vertical" panels.
if (li.matches('.mm-listitem_vertical')) {
li.classList.remove('mm-listitem_opened');
panel.classList.add('mm-hidden');
// Invoke main hook.
this.trigger('closePanel', [panel]);
}
// Invoke "after" hook.
this.trigger('closePanel:after', [panel]);
};
/**
* Close all opened panels.
* @param {HTMLElement} panel Panel to open after closing all other panels.
*/
Mmenu.prototype.closeAllPanels = function (panel) {
// Invoke "before" hook.
this.trigger('closeAllPanels:before');
// Close all "vertical" panels.
var listitems = this.node.pnls.querySelectorAll('.mm-listitem');
listitems.forEach(function (listitem) {
listitem.classList.remove('mm-listitem_selected');
listitem.classList.remove('mm-listitem_opened');
});
// Close all "horizontal" panels.
var panels = DOM.children(this.node.pnls, '.mm-panel'), opened = panel ? panel : panels[0];
DOM.children(this.node.pnls, '.mm-panel').forEach(function (panel) {
if (panel !== opened) {
panel.classList.remove('mm-panel_opened');
panel.classList.remove('mm-panel_opened-parent');
panel.classList.remove('mm-panel_highest');
panel.classList.add('mm-hidden');
}
});
// Open first panel.
this.openPanel(opened, false);
// Invoke "after" hook.
this.trigger('closeAllPanels:after');
};
/**
* Toggle a panel opened/closed.
* @param {HTMLElement} panel Panel to open or close.
*/
Mmenu.prototype.togglePanel = function (panel) {
var listitem = panel.parentElement;
// Only works for "vertical" panels.
if (listitem.matches('.mm-listitem_vertical')) {
this[listitem.matches('.mm-listitem_opened')
? 'closePanel'
: 'openPanel'](panel);
}
};
/**
* Display a listitem as being "selected".
* @param {HTMLElement} listitem Listitem to mark.
*/
Mmenu.prototype.setSelected = function (listitem) {
// Invoke "before" hook.
this.trigger('setSelected:before', [listitem]);
// First, remove the selected class from all listitems.
DOM.find(this.node.menu, '.mm-listitem_selected').forEach(function (li) {
li.classList.remove('mm-listitem_selected');
});
// Next, add the selected class to the provided listitem.
listitem.classList.add('mm-listitem_selected');
// Invoke "after" hook.
this.trigger('setSelected:after', [listitem]);
};
/**
* Bind functions to a hook (subscriber).
* @param {string} hook The hook.
* @param {function} func The function.
*/
Mmenu.prototype.bind = function (hook, func) {
// Create an array for the hook if it does not yet excist.
this.hook[hook] = this.hook[hook] || [];
// Push the function to the array.
this.hook[hook].push(func);
};
/**
* Invoke the functions bound to a hook (publisher).
* @param {string} hook The hook.
* @param {array} [args] Arguments for the function.
*/
Mmenu.prototype.trigger = function (hook, args) {
if (this.hook[hook]) {
for (var h = 0, l = this.hook[hook].length; h < l; h++) {
this.hook[hook][h].apply(this, args);
}
}
};
/**
* Create the API.
*/
Mmenu.prototype._initAPI = function () {
var _this = this;
// We need this=that because:
// 1) the "arguments" object can not be referenced in an arrow function in ES3 and ES5.
var that = this;
this.API = {};
this._api.forEach(function (fn) {
_this.API[fn] = function () {
var re = that[fn].apply(that, arguments); // 1)
return typeof re == 'undefined' ? that.API : re;
};
});
// Store the API in the HTML node for external usage.
this.node.menu['mmApi'] = this.API;
};
/**
* Bind the hooks specified in the options (publisher).
*/
Mmenu.prototype._initHooks = function () {
for (var hook in this.opts.hooks) {
this.bind(hook, this.opts.hooks[hook]);
}
};
/**
* Initialize the wrappers specified in the options.
*/
Mmenu.prototype._initWrappers = function () {
// Invoke "before" hook.
this.trigger('initWrappers:before');
for (var w = 0; w < this.opts.wrappers.length; w++) {
var wrpr = Mmenu.wrappers[this.opts.wrappers[w]];
if (typeof wrpr == 'function') {
wrpr.call(this);
}
}
// Invoke "after" hook.
this.trigger('initWrappers:after');
};
/**
* Initialize all available add-ons.
*/
Mmenu.prototype._initAddons = function () {
// Invoke "before" hook.
this.trigger('initAddons:before');
for (var addon in Mmenu.addons) {
Mmenu.addons[addon].call(this);
}
// Invoke "after" hook.
this.trigger('initAddons:after');
};
/**
* Initialize the extensions specified in the options.
*/
Mmenu.prototype._initExtensions = function () {
var _this = this;
// Invoke "before" hook.
this.trigger('initExtensions:before');
// Convert array to object with array.
if (type(this.opts.extensions) == 'array') {
this.opts.extensions = {
all: this.opts.extensions
};
}
// Loop over object.
Object.keys(this.opts.extensions).forEach(function (query) {
var classnames = _this.opts.extensions[query].map(function (extension) { return 'mm-menu_' + extension; });
if (classnames.length) {
media.add(query, function () {
// IE11:
classnames.forEach(function (classname) {
_this.node.menu.classList.add(classname);
});
// Better browsers:
// this.node.menu.classList.add(...classnames);
}, function () {
// IE11:
classnames.forEach(function (classname) {
_this.node.menu.classList.remove(classname);
});
// Better browsers:
// this.node.menu.classList.remove(...classnames);
});
}
});
// Invoke "after" hook.
this.trigger('initExtensions:after');
};
/**
* Initialize the menu.
*/
Mmenu.prototype._initMenu = function () {
var _this = this;
// Invoke "before" hook.
this.trigger('initMenu:before');
// Add class to the wrapper.
this.node.wrpr = this.node.wrpr || this.node.menu.parentElement;
this.node.wrpr.classList.add('mm-wrapper');
// Add an ID to the menu if it does not yet have one.
this.node.menu.id = this.node.menu.id || uniqueId();
// Wrap the panels in a node.
var panels = DOM.create('div.mm-panels');
DOM.children(this.node.menu).forEach(function (panel) {
if (_this.conf.panelNodetype.indexOf(panel.nodeName.toLowerCase()) >
-1) {
panels.append(panel);
}
});
this.node.menu.append(panels);
this.node.pnls = panels;
// Add class to the menu.
this.node.menu.classList.add('mm-menu');
// Invoke "after" hook.
this.trigger('initMenu:after');
};
/**
* Initialize panels.
*/
Mmenu.prototype._initPanels = function () {
var _this = this;
// Invoke "before" hook.
this.trigger('initPanels:before');
// Open / close panels.
this.clck.push(function (anchor, args) {
if (args.inMenu) {
var href = anchor.getAttribute('href');
if (href && href.length > 1 && href.slice(0, 1) == '#') {
try {
var panel = DOM.find(_this.node.menu, href)[0];
if (panel && panel.matches('.mm-panel')) {
if (anchor.parentElement.matches('.mm-listitem_vertical')) {
_this.togglePanel(panel);
}
else {
_this.openPanel(panel);
}
return true;
}
}
catch (err) { }
}
}
});
/** The panels to initiate */
var panels = DOM.children(this.node.pnls);
panels.forEach(function (panel) {
_this.initPanel(panel);
});
// Invoke "after" hook.
this.trigger('initPanels:after');
};
/**
* Initialize a single panel and its children.
* @param {HTMLElement} panel The panel to initialize.
*/
Mmenu.prototype.initPanel = function (panel) {
var _this = this;
/** Query selector for possible node-types for panels. */
var panelNodetype = this.conf.panelNodetype.join(', ');
if (panel.matches(panelNodetype)) {
// Only once
if (!panel.matches('.mm-panel')) {
panel = this._initPanel(panel);
}
if (panel) {
/** The sub panels. */
var children_1 = [];
// Find panel > panel
children_1.push.apply(children_1, DOM.children(panel, '.' + this.conf.classNames.panel));
// Find panel listitem > panel
DOM.children(panel, '.mm-listview').forEach(function (listview) {
DOM.children(listview, '.mm-listitem').forEach(function (listitem) {
children_1.push.apply(children_1, DOM.children(listitem, panelNodetype));
});
});
// Initiate subpanel(s).
children_1.forEach(function (child) {
_this.initPanel(child);
});
}
}
};
/**
* Initialize a single panel.
* @param {HTMLElement} panel Panel to initialize.
* @return {HTMLElement|null} Initialized panel.
*/
Mmenu.prototype._initPanel = function (panel) {
var _this = this;
// Invoke "before" hook.
this.trigger('initPanel:before', [panel]);
// Refactor panel classnames
DOM.reClass(panel, this.conf.classNames.panel, 'mm-panel');
DOM.reClass(panel, this.conf.classNames.nopanel, 'mm-nopanel');
DOM.reClass(panel, this.conf.classNames.inset, 'mm-listview_inset');
if (panel.matches('.mm-listview_inset')) {
panel.classList.add('mm-nopanel');
}
// Stop if not supposed to be a panel.
if (panel.matches('.mm-nopanel')) {
return null;
}
/** The original ID on the node. */
var id = panel.id || uniqueId();
// Vertical panel.
var vertical = panel.matches('.' + this.conf.classNames.vertical) ||
!this.opts.slidingSubmenus;
panel.classList.remove(this.conf.classNames.vertical);
// Wrap UL/OL in DIV
if (panel.matches('ul, ol')) {
panel.removeAttribute('id');
/** The panel. */
var wrapper = DOM.create('div');
// Wrap the listview in the panel.
panel.before(wrapper);
wrapper.append(panel);
panel = wrapper;
}
panel.id = id;
panel.classList.add('mm-panel');
panel.classList.add('mm-hidden');
/** The parent listitem. */
var parent = [panel.parentElement].filter(function (listitem) {
return listitem.matches('li');
})[0];
if (vertical) {
if (parent) {
parent.classList.add('mm-listitem_vertical');
}
}
else {
this.node.pnls.append(panel);
}
if (parent) {
// Store parent/child relation.
parent['mmChild'] = panel;
panel['mmParent'] = parent;
// Add open link to parent listitem
if (parent && parent.matches('.mm-listitem')) {
if (!DOM.children(parent, '.mm-btn').length) {
/** The text node. */
var item = DOM.children(parent, '.mm-listitem__text')[0];
if (item) {
/** The open link. */
var button = DOM.create('a.mm-btn.mm-btn_next.mm-listitem__btn');
button.setAttribute('href', '#' + panel.id);
// If the item has no link,
// Replace the item with the open link.
if (item.matches('span')) {
button.classList.add('mm-listitem__text');
button.innerHTML = item.innerHTML;
parent.insertBefore(button, item.nextElementSibling);
item.remove();
}
// Otherwise, insert the button after the text.
else {
parent.insertBefore(button, DOM.children(parent, '.mm-panel')[0]);
}
}
}
}
}
this._initNavbar(panel);
DOM.children(panel, 'ul, ol').forEach(function (listview) {
_this.initListview(listview);
});
// Invoke "after" hook.
this.trigger('initPanel:after', [panel]);
return panel;
};
/**
* Initialize a navbar.
* @param {HTMLElement} panel Panel for the navbar.
*/
Mmenu.prototype._initNavbar = function (panel) {
// Invoke "before" hook.
this.trigger('initNavbar:before', [panel]);
// Only one navbar per panel.
if (DOM.children(panel, '.mm-navbar').length) {
return;
}
/** The parent listitem. */
var parentListitem = null;
/** The parent panel. */
var parentPanel = null;
// The parent panel was specified in the data-mm-parent attribute.
if (panel.getAttribute('data-mm-parent')) {
parentPanel = DOM.find(this.node.pnls, panel.getAttribute('data-mm-parent'))[0];
}
// if (panel.dataset.mmParent) { // IE10 has no dataset
// parentPanel = DOM.find(this.node.pnls, panel.dataset.mmParent)[0];
// }
// The parent panel from a listitem.
else {
parentListitem = panel['mmParent'];
if (parentListitem) {
parentPanel = parentListitem.closest('.mm-panel');
}
}
// No navbar needed for vertical submenus.
if (parentListitem && parentListitem.matches('.mm-listitem_vertical')) {
return;
}
/** The navbar element. */
var navbar = DOM.create('div.mm-navbar');
// Hide navbar if specified in options.
if (!this.opts.navbar.add) {
navbar.classList.add('mm-hidden');
}
// Sticky navbars.
else if (this.opts.navbar.sticky) {
navbar.classList.add('mm-navbar_sticky');
}
// Add the back button.
if (parentPanel) {
/** The back button. */
var prev = DOM.create('a.mm-btn.mm-btn_prev.mm-navbar__btn');
prev.setAttribute('href', '#' + parentPanel.id);
navbar.append(prev);
}
/** The anchor that opens the panel. */
var opener = null;
// The anchor is in a listitem.
if (parentListitem) {
opener = DOM.children(parentListitem, '.mm-listitem__text')[0];
}
// The anchor is in a panel.
else if (parentPanel) {
opener = DOM.find(parentPanel, 'a[href="#' + panel.id + '"]')[0];
}
// Add the title.
var title = DOM.create('a.mm-navbar__title');
var titleText = DOM.create('span');
title.append(titleText);
titleText.innerHTML =
// panel.dataset.mmTitle || // IE10 has no dataset :(
panel.getAttribute('data-mm-title') ||
(opener ? opener.textContent : '') ||
this.i18n(this.opts.navbar.title) ||
this.i18n('Menu');
switch (this.opts.navbar.titleLink) {
case 'anchor':
if (opener) {
title.setAttribute('href', opener.getAttribute('href'));
}
break;
case 'parent':
if (parentPanel) {
title.setAttribute('href', '#' + parentPanel.id);
}
break;
}
navbar.append(title);
panel.prepend(navbar);
// Invoke "after" hook.
this.trigger('initNavbar:after', [panel]);
};
/**
* Initialize a listview.
* @param {HTMLElement} listview Listview to initialize.
*/
Mmenu.prototype.initListview = function (listview) {
var _this = this;
// Invoke "before" hook.
this.trigger('initListview:before', [listview]);
DOM.reClass(listview, this.conf.classNames.nolistview, 'mm-nolistview');
if (!listview.matches('.mm-nolistview')) {
listview.classList.add('mm-listview');
DOM.children(listview).forEach(function (listitem) {
listitem.classList.add('mm-listitem');
DOM.reClass(listitem, _this.conf.classNames.selected, 'mm-listitem_selected');
DOM.children(listitem, 'a, span').forEach(function (item) {
if (!item.matches('.mm-btn')) {
item.classList.add('mm-listitem__text');
}
});
});
}
// Invoke "after" hook.
this.trigger('initListview:after', [listview]);
};
/**
* Find and open the correct panel after creating the menu.
*/
Mmenu.prototype._initOpened = function () {
// Invoke "before" hook.
this.trigger('initOpened:before');
/** The selected listitem(s). */
var listitems = this.node.pnls.querySelectorAll('.mm-listitem_selected');
/** The last selected listitem. */
var lastitem = null;
// Deselect the listitems.
listitems.forEach(function (listitem) {
lastitem = listitem;
listitem.classList.remove('mm-listitem_selected');
});
// Re-select the last listitem.
if (lastitem) {
lastitem.classList.add('mm-listitem_selected');
}
/** The current opened panel. */
var current = lastitem
? lastitem.closest('.mm-panel')
: DOM.children(this.node.pnls, '.mm-panel')[0];
// Open the current opened panel.
this.openPanel(current, false);
// Invoke "after" hook.
this.trigger('initOpened:after');
};
/**
* Initialize anchors in / for the menu.
*/
Mmenu.prototype._initAnchors = function () {
var _this = this;
// Invoke "before" hook.
this.trigger('initAnchors:before');
document.addEventListener('click', function (evnt) {
/** The clicked element. */
var target = evnt.target.closest('a[href]');
if (!target) {
return;
}
/** Arguments passed to the bound methods. */
var args = {
inMenu: target.closest('.mm-menu') === _this.node.menu,
inListview: target.matches('.mm-listitem > a'),
toExternal: target.matches('[rel="external"]') ||
target.matches('[target="_blank"]')
};
var onClick = {
close: null,
setSelected: null,
preventDefault: target.getAttribute('href').slice(0, 1) == '#'
};
// Find hooked behavior.
for (var c = 0; c < _this.clck.length; c++) {
var click = _this.clck[c].call(_this, target, args);
if (click) {
if (typeof click == 'boolean') {
evnt.preventDefault();
return;
}
if (type(click) == 'object') {
onClick = extend(click, onClick);
}
}
}
// Default behavior for anchors in lists.
if (args.inMenu && args.inListview && !args.toExternal) {
// Set selected item, Default: true
if (valueOrFn(target, _this.opts.onClick.setSelected, onClick.setSelected)) {
_this.setSelected(target.parentElement);
}
// Prevent default / don't follow link. Default: false.
if (valueOrFn(target, _this.opts.onClick.preventDefault, onClick.preventDefault)) {
evnt.preventDefault();
}
// Close menu. Default: false
if (valueOrFn(target, _this.opts.onClick.close, onClick.close)) {
if (_this.opts.offCanvas &&
typeof _this.close == 'function') {
_this.close();
}
}
}
}, true);
// Invoke "after" hook.
this.trigger('initAnchors:after');
};
/**
* Get the translation for a text.
* @param {string} text Text to translate.
* @return {string} The translated text.
*/
Mmenu.prototype.i18n = function (text) {
return i18n.get(text, this.conf.language);
};
/** Plugin version. */
Mmenu.version = version;
/** Default options for menus. */
Mmenu.options = options;
/** Default configuration for menus. */
Mmenu.configs = configs;
/** Available add-ons for the plugin. */
Mmenu.addons = {};
/** Available wrappers for the plugin. */
Mmenu.wrappers = {};
/** Globally used HTML elements. */
Mmenu.node = {};
/** Globally used variables. */
Mmenu.vars = {};
return Mmenu;
}());
export default Mmenu;

3
dist/core/oncanvas/translations/de.js vendored Normal file
View File

@ -0,0 +1,3 @@
export default {
'Menu': 'Menü'
};

3
dist/core/oncanvas/translations/fa.js vendored Normal file
View File

@ -0,0 +1,3 @@
export default {
'Menu': 'منو'
};

3
dist/core/oncanvas/translations/nl.js vendored Normal file
View File

@ -0,0 +1,3 @@
export default {
'Menu': 'Menu'
};

3
dist/core/oncanvas/translations/ru.js vendored Normal file
View File

@ -0,0 +1,3 @@
export default {
'Menu': 'Меню'
};

View File

@ -0,0 +1,11 @@
import { add } from '../../../_modules/i18n';
import nl from './nl';
import fa from './fa';
import de from './de';
import ru from './ru';
export default function () {
add(nl, 'nl');
add(fa, 'fa');
add(de, 'de');
add(ru, 'ru');
}

9
dist/core/screenreader/_configs.js vendored Normal file
View File

@ -0,0 +1,9 @@
var configs = {
text: {
closeMenu: 'Close menu',
closeSubmenu: 'Close submenu',
openSubmenu: 'Open submenu',
toggleSubmenu: 'Toggle submenu'
}
};
export default configs;

24
dist/core/screenreader/_options.js vendored Normal file
View File

@ -0,0 +1,24 @@
var options = {
aria: true,
text: true
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'boolean') {
options = {
aria: options,
text: options
};
}
if (typeof options != 'object') {
options = {};
}
return options;
}
;

View File

@ -0,0 +1 @@
.mm-sronly{border:0!important;clip:rect(1px,1px,1px,1px)!important;-webkit-clip-path:inset(50%)!important;clip-path:inset(50%)!important;white-space:nowrap!important;width:1px!important;min-width:1px!important;height:1px!important;min-height:1px!important;padding:0!important;overflow:hidden!important;position:absolute!important}

View File

@ -0,0 +1,189 @@
import Mmenu from './../oncanvas/mmenu.oncanvas';
import options from './_options';
import configs from './_configs';
import translate from './translations/translate';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import { extend } from '../../_modules/helpers';
// Add the translations.
translate();
// Add the options and configs.
Mmenu.options.screenReader = options;
Mmenu.configs.screenReader = configs;
export default function () {
var _this = this;
// Extend options.
var options = extendShorthandOptions(this.opts.screenReader);
this.opts.screenReader = extend(options, Mmenu.options.screenReader);
// Extend configs.
var configs = this.conf.screenReader;
// Add Aria-* attributes
if (options.aria) {
// Add screenreader / aria hooks for add-ons
// In orde to keep this list short, only extend hooks that are actually used by other add-ons.
this.bind('initAddons:after', function () {
_this.bind('initMenu:after', function () {
this.trigger('initMenu:after:sr-aria', [].slice.call(arguments));
});
_this.bind('initNavbar:after', function () {
this.trigger('initNavbar:after:sr-aria', [].slice.call(arguments));
});
_this.bind('openPanel:start', function () {
this.trigger('openPanel:start:sr-aria', [].slice.call(arguments));
});
_this.bind('close:start', function () {
this.trigger('close:start:sr-aria', [].slice.call(arguments));
});
_this.bind('close:finish', function () {
this.trigger('close:finish:sr-aria', [].slice.call(arguments));
});
_this.bind('open:start', function () {
this.trigger('open:start:sr-aria', [].slice.call(arguments));
});
_this.bind('initOpened:after', function () {
this.trigger('initOpened:after:sr-aria', [].slice.call(arguments));
});
});
// Update aria-hidden for hidden / visible listitems
this.bind('updateListview', function () {
_this.node.pnls
.querySelectorAll('.mm-listitem')
.forEach(function (listitem) {
Mmenu.sr_aria(listitem, 'hidden', listitem.matches('.mm-hidden'));
});
});
// Update aria-hidden for the panels when opening and closing a panel.
this.bind('openPanel:start', function (panel) {
/** Panels that should be considered "hidden". */
var hidden = DOM.find(_this.node.pnls, '.mm-panel')
.filter(function (hide) { return hide !== panel; })
.filter(function (hide) { return !hide.parentElement.matches('.mm-panel'); });
/** Panels that should be considered "visible". */
var visible = [panel];
DOM.find(panel, '.mm-listitem_vertical .mm-listitem_opened').forEach(function (listitem) {
visible.push.apply(visible, DOM.children(listitem, '.mm-panel'));
});
// Set the panels to be considered "hidden" or "visible".
hidden.forEach(function (panel) {
Mmenu.sr_aria(panel, 'hidden', true);
});
visible.forEach(function (panel) {
Mmenu.sr_aria(panel, 'hidden', false);
});
});
this.bind('closePanel', function (panel) {
Mmenu.sr_aria(panel, 'hidden', true);
});
// Add aria-haspopup and aria-owns to prev- and next buttons.
this.bind('initPanel:after', function (panel) {
DOM.find(panel, '.mm-btn').forEach(function (button) {
Mmenu.sr_aria(button, 'haspopup', true);
var href = button.getAttribute('href');
if (href) {
Mmenu.sr_aria(button, 'owns', href.replace('#', ''));
}
});
});
// Add aria-hidden for navbars in panels.
this.bind('initNavbar:after', function (panel) {
/** The navbar in the panel. */
var navbar = DOM.children(panel, '.mm-navbar')[0];
/** Whether or not the navbar should be considered "hidden". */
var hidden = navbar.matches('.mm-hidden');
// Set the navbar to be considered "hidden" or "visible".
Mmenu.sr_aria(navbar, 'hidden', hidden);
});
// Text
if (options.text) {
// Add aria-hidden to titles in navbars
if (this.opts.navbar.titleLink == 'parent') {
this.bind('initNavbar:after', function (panel) {
/** The navbar in the panel. */
var navbar = DOM.children(panel, '.mm-navbar')[0];
/** Whether or not the navbar should be considered "hidden". */
var hidden = navbar.querySelector('.mm-btn_prev')
? true
: false;
// Set the navbar-title to be considered "hidden" or "visible".
Mmenu.sr_aria(DOM.find(navbar, '.mm-navbar__title')[0], 'hidden', hidden);
});
}
}
}
// Add screenreader text
if (options.text) {
// Add screenreader / text hooks for add-ons
// In orde to keep this list short, only extend hooks that are actually used by other add-ons.
this.bind('initAddons:after', function () {
_this.bind('setPage:after', function () {
this.trigger('setPage:after:sr-text', [].slice.call(arguments));
});
_this.bind('initBlocker:after', function () {
this.trigger('initBlocker:after:sr-text', [].slice.call(arguments));
});
});
// Add text to the prev-buttons.
this.bind('initNavbar:after', function (panel) {
var navbar = DOM.children(panel, '.mm-navbar')[0];
if (navbar) {
var button = DOM.children(navbar, '.mm-btn_prev')[0];
if (button) {
button.innerHTML = Mmenu.sr_text(_this.i18n(configs.text.closeSubmenu));
}
}
});
// Add text to the next-buttons.
this.bind('initListview:after', function (listview) {
var parent = listview.closest('.mm-panel')['mmParent'];
if (parent) {
var next = DOM.children(parent, '.mm-btn_next')[0];
if (next) {
var text = _this.i18n(configs.text[next.parentElement.matches('.mm-listitem_vertical')
? 'toggleSubmenu'
: 'openSubmenu']);
next.innerHTML += Mmenu.sr_text(text);
}
}
});
}
}
// Methods
(function () {
var attr = function (element, attr, value) {
element[attr] = value;
if (value) {
element.setAttribute(attr, value.toString());
}
else {
element.removeAttribute(attr);
}
};
/**
* Add aria (property and) attribute to a HTML element.
*
* @param {HTMLElement} element The node to add the attribute to.
* @param {string} name The (non-aria-prefixed) attribute name.
* @param {string|boolean} value The attribute value.
*/
Mmenu.sr_aria = function (element, name, value) {
attr(element, 'aria-' + name, value);
};
/**
* Add role attribute to a HTML element.
*
* @param {HTMLElement} element The node to add the attribute to.
* @param {string|boolean} value The attribute value.
*/
Mmenu.sr_role = function (element, value) {
attr(element, 'role', value);
};
/**
* Wrap a text in a screen-reader-only node.
*
* @param {string} text The text to wrap.
* @return {string} The wrapped text.
*/
Mmenu.sr_text = function (text) {
return '<span class="mm-sronly">' + text + '</span>';
};
})();

View File

@ -0,0 +1,6 @@
export default {
'Close menu': 'Menü schließen',
'Close submenu': 'Untermenü schließen',
'Open submenu': 'Untermenü öffnen',
'Toggle submenu': 'Untermenü wechseln'
};

View File

@ -0,0 +1,6 @@
export default {
'Close menu': 'بستن منو',
'Close submenu': 'بستن زیرمنو',
'Open submenu': 'بازکردن زیرمنو',
'Toggle submenu': 'سوییچ زیرمنو'
};

View File

@ -0,0 +1,6 @@
export default {
'Close menu': 'Menu sluiten',
'Close submenu': 'Submenu sluiten',
'Open submenu': 'Submenu openen',
'Toggle submenu': 'Submenu wisselen'
};

View File

@ -0,0 +1,6 @@
export default {
'Close menu': 'Закрыть меню',
'Close submenu': 'Закрыть подменю',
'Open submenu': 'Открыть подменю',
'Toggle submenu': 'Переключить подменю'
};

View File

@ -0,0 +1,11 @@
import { add } from '../../../_modules/i18n';
import nl from './nl';
import fa from './fa';
import de from './de';
import ru from './ru';
export default function () {
add(nl, 'nl');
add(fa, 'fa');
add(de, 'de');
add(ru, 'ru');
}

22
dist/core/scrollbugfix/_options.js vendored Normal file
View File

@ -0,0 +1,22 @@
var options = {
fix: true
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options) {
if (typeof options == 'boolean') {
options = {
fix: options
};
}
if (typeof options != 'object') {
options = {};
}
return options;
}
;

View File

@ -0,0 +1,91 @@
import Mmenu from './../oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import * as support from '../../_modules/support';
import { extend, touchDirection } from '../../_modules/helpers';
// Add the options.
Mmenu.options.scrollBugFix = options;
export default function () {
var _this = this;
// The scrollBugFix add-on fixes a scrolling bug
// 1) on touch devices
// 2) in an off-canvas menu
// 3) that -when opened- blocks the UI from interaction
if (!support.touch || // 1
!this.opts.offCanvas || // 2
!this.opts.offCanvas.blockUI // 3
) {
return;
}
// Extend options.
var options = extendShorthandOptions(this.opts.scrollBugFix);
this.opts.scrollBugFix = extend(options, Mmenu.options.scrollBugFix);
if (!options.fix) {
return;
}
var touchDir = touchDirection(this.node.menu);
/**
* Prevent an event from doing its default and stop its propagation.
* @param {ScrollBehavior} evnt The event to stop.
*/
function stop(evnt) {
evnt.preventDefault();
evnt.stopPropagation();
}
// Prevent the page from scrolling when scrolling in the menu.
this.node.menu.addEventListener('scroll', stop, {
// Make sure to tell the browser the event will be prevented.
passive: false
});
// Prevent the page from scrolling when dragging in the menu.
this.node.menu.addEventListener('touchmove', function (evnt) {
var panel = evnt.target.closest('.mm-panel');
if (panel) {
// When dragging a non-scrollable panel,
// we can simple preventDefault and stopPropagation.
if (panel.scrollHeight === panel.offsetHeight) {
stop(evnt);
}
// When dragging a scrollable panel,
// that is fully scrolled up (or down).
// It will not trigger the scroll event when dragging down (or up) (because you can't scroll up (or down)),
// so we need to match the dragging direction with the scroll position before preventDefault and stopPropagation,
// otherwise the panel would not scroll at all in any direction.
else if (
// When scrolled up and dragging down
(panel.scrollTop == 0 && touchDir.get() == 'down') ||
// When scrolled down and dragging up
(panel.scrollHeight ==
panel.scrollTop + panel.offsetHeight &&
touchDir.get() == 'up')) {
stop(evnt);
}
// When dragging anything other than a panel.
}
else {
stop(evnt);
}
}, {
// Make sure to tell the browser the event can be prevented.
passive: false
});
// Some small additional improvements
// Scroll the current opened panel to the top when opening the menu.
this.bind('open:start', function () {
var panel = DOM.children(_this.node.pnls, '.mm-panel_opened')[0];
if (panel) {
panel.scrollTop = 0;
}
});
// Fix issue after device rotation change.
window.addEventListener('orientationchange', function (evnt) {
var panel = DOM.children(_this.node.pnls, '.mm-panel_opened')[0];
if (panel) {
panel.scrollTop = 0;
// Apparently, changing the overflow-scrolling property triggers some event :)
panel.style['-webkit-overflow-scrolling'] = 'auto';
panel.style['-webkit-overflow-scrolling'] = 'touch';
}
});
}

View File

@ -0,0 +1 @@
.mm-menu_border-none .mm-listitem:after{content:none}.mm-menu_border-full .mm-listitem:after{left:0!important}

View File

@ -0,0 +1 @@
.mm-menu_fx-menu-slide{-webkit-transition:-webkit-transform .4s ease;transition:-webkit-transform .4s ease;-o-transition:transform .4s ease;transition:transform .4s ease;transition:transform .4s ease,-webkit-transform .4s ease}.mm-wrapper_opened .mm-menu_fx-menu-slide{-webkit-transform:translate3d(-30%,0,0);transform:translate3d(-30%,0,0)}.mm-wrapper_opening .mm-menu_fx-menu-slide{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.mm-wrapper_opened .mm-menu_fx-menu-slide.mm-menu_position-right{-webkit-transform:translate3d(30%,0,0);transform:translate3d(30%,0,0)}.mm-wrapper_opening .mm-menu_fx-menu-slide.mm-menu_position-right{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.mm-menu_fx-panels-none .mm-panel,.mm-panel_fx-none{-webkit-transition-property:none;-o-transition-property:none;transition-property:none}.mm-menu_fx-panels-none .mm-panel.mm-panel_opened-parent,.mm-panel_fx-none.mm-panel_opened-parent{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.mm-menu_fx-panels-slide-0 .mm-panel_opened-parent{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.mm-menu_fx-panels-slide-100 .mm-panel_opened-parent{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}

View File

@ -0,0 +1 @@
.mm-menu_fullscreen{width:100%;min-width:140px;max-width:10000px}.mm-wrapper_opening .mm-menu_fullscreen~.mm-slideout{-webkit-transform:translate3d(100vw,0,0);transform:translate3d(100vw,0,0)}@media all and (max-width:140px){.mm-wrapper_opening .mm-menu_fullscreen~.mm-slideout{-webkit-transform:translate3d(140px,0,0);transform:translate3d(140px,0,0)}}@media all and (min-width:10000px){.mm-wrapper_opening .mm-menu_fullscreen~.mm-slideout{-webkit-transform:translate3d(10000px,0,0);transform:translate3d(10000px,0,0)}}.mm-wrapper_opening .mm-menu_fullscreen.mm-menu_position-right.mm-menu_opened~.mm-slideout{-webkit-transform:translate3d(-100vw,0,0);transform:translate3d(-100vw,0,0)}@media all and (max-width:140px){.mm-wrapper_opening .mm-menu_fullscreen.mm-menu_position-right.mm-menu_opened~.mm-slideout{-webkit-transform:translate3d(-140px,0,0);transform:translate3d(-140px,0,0)}}@media all and (min-width:10000px){.mm-wrapper_opening .mm-menu_fullscreen.mm-menu_position-right.mm-menu_opened~.mm-slideout{-webkit-transform:translate3d(-10000px,0,0);transform:translate3d(-10000px,0,0)}}.mm-menu_fullscreen.mm-menu_position-top{height:100vh;min-height:140px;max-height:10000px}.mm-menu_fullscreen.mm-menu_position-bottom{height:100vh;min-height:140px;max-height:10000px}

View File

@ -0,0 +1 @@
.mm-menu_listview-justify .mm-panels>.mm-panel{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.mm-menu_listview-justify .mm-panels>.mm-panel:after{content:none;display:none}.mm-menu_listview-justify .mm-panels>.mm-panel .mm-listview{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;height:100%;margin-top:0;margin-bottom:0}.mm-menu_listview-justify .mm-panels>.mm-panel .mm-listitem{-webkit-box-flex:1;-ms-flex:1 0 auto;flex:1 0 auto;min-height:50px}.mm-menu_listview-justify .mm-panels>.mm-panel .mm-listitem__text{-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-box-flex:1;-ms-flex:1 0 auto;flex:1 0 auto;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.mm-listview_inset{list-style:inside disc;width:100%;padding:0 30px 15px 30px;margin:0}.mm-listview_inset .mm-listitem{padding:5px 0}

View File

@ -0,0 +1 @@
.mm-menu_multiline .mm-listitem__text{-o-text-overflow:clip;text-overflow:clip;white-space:normal}

View File

@ -0,0 +1 @@
[class*=mm-menu_pagedim].mm-menu_opened~.mm-wrapper__blocker{opacity:0}.mm-wrapper_opening [class*=mm-menu_pagedim].mm-menu_opened~.mm-wrapper__blocker{opacity:.3;-webkit-transition:opacity .4s ease .4s;-o-transition:opacity .4s ease .4s;transition:opacity .4s ease .4s}.mm-menu_opened.mm-menu_pagedim~.mm-wrapper__blocker{background:inherit}.mm-menu_opened.mm-menu_pagedim-black~.mm-wrapper__blocker{background:#000}.mm-menu_opened.mm-menu_pagedim-white~.mm-wrapper__blocker{background:#fff}

1
dist/extensions/popup/mmenu.popup.css vendored Normal file
View File

@ -0,0 +1 @@
.mm-menu_popup{-webkit-transition:opacity .4s ease;-o-transition:opacity .4s ease;transition:opacity .4s ease;opacity:0;-webkit-box-shadow:0 2px 10px rgba(0,0,0,.3);box-shadow:0 2px 10px rgba(0,0,0,.3);height:80%;min-height:140px;max-height:880px;top:50%;left:50%;bottom:auto;right:auto;z-index:2;-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0)}.mm-menu_popup.mm-menu_opened~.mm-slideout{-webkit-transform:none!important;-ms-transform:none!important;transform:none!important;z-index:0}.mm-menu_popup.mm-menu_opened~.mm-wrapper__blocker{-webkit-transition-delay:0s!important;-o-transition-delay:0s!important;transition-delay:0s!important;z-index:1}.mm-wrapper_opening .mm-menu_popup{opacity:1}

View File

@ -0,0 +1 @@
.mm-menu_position-right{left:auto;right:0}.mm-wrapper_opening .mm-menu_position-right.mm-menu_opened~.mm-slideout{-webkit-transform:translate3d(-80vw,0,0);transform:translate3d(-80vw,0,0)}@media all and (max-width:300px){.mm-wrapper_opening .mm-menu_position-right.mm-menu_opened~.mm-slideout{-webkit-transform:translate3d(-240px,0,0);transform:translate3d(-240px,0,0)}}@media all and (min-width:550px){.mm-wrapper_opening .mm-menu_position-right.mm-menu_opened~.mm-slideout{-webkit-transform:translate3d(-440px,0,0);transform:translate3d(-440px,0,0)}}.mm-menu_position-bottom,.mm-menu_position-front,.mm-menu_position-top{-webkit-transition:-webkit-transform .4s ease;transition:-webkit-transform .4s ease;-o-transition:transform .4s ease;transition:transform .4s ease;transition:transform .4s ease,-webkit-transform .4s ease}.mm-menu_position-bottom.mm-menu_opened,.mm-menu_position-front.mm-menu_opened,.mm-menu_position-top.mm-menu_opened{z-index:2}.mm-menu_position-bottom.mm-menu_opened~.mm-slideout,.mm-menu_position-front.mm-menu_opened~.mm-slideout,.mm-menu_position-top.mm-menu_opened~.mm-slideout{-webkit-transform:none!important;-ms-transform:none!important;transform:none!important;z-index:0}.mm-menu_position-bottom.mm-menu_opened~.mm-wrapper__blocker,.mm-menu_position-front.mm-menu_opened~.mm-wrapper__blocker,.mm-menu_position-top.mm-menu_opened~.mm-wrapper__blocker{z-index:1}.mm-menu_position-front{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.mm-menu_position-front.mm-menu_position-right{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.mm-menu_position-bottom,.mm-menu_position-top{width:100%;min-width:100%;max-width:100%}.mm-menu_position-top{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}.mm-menu_position-top{height:80vh;min-height:140px;max-height:880px}.mm-menu_position-bottom{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0);top:auto}.mm-menu_position-bottom{height:80vh;min-height:140px;max-height:880px}.mm-wrapper_opening .mm-menu_position-bottom,.mm-wrapper_opening .mm-menu_position-front,.mm-wrapper_opening .mm-menu_position-top{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}

View File

@ -0,0 +1 @@
.mm-menu_shadow-page:after{-webkit-box-shadow:0 0 10px rgba(0,0,0,.3);box-shadow:0 0 10px rgba(0,0,0,.3);content:'';display:block;width:20px;height:120%;position:absolute;left:100%;top:-10%;z-index:100;-webkit-clip-path:polygon(-20px 0,0 0,0 100%,-20px 100%);clip-path:polygon(-20px 0,0 0,0 100%,-20px 100%);-webkit-box-shadow:var(--mm-shadow);box-shadow:var(--mm-shadow)}.mm-menu_shadow-page.mm-menu_position-right:after{left:auto;right:100%;-webkit-clip-path:polygon(20px 0,40px 0,40px 100%,20px 100%);clip-path:polygon(20px 0,40px 0,40px 100%,20px 100%)}.mm-menu_shadow-page.mm-menu_position-front:after{content:none;display:none}.mm-menu_shadow-menu{-webkit-box-shadow:0 0 10px rgba(0,0,0,.3);box-shadow:0 0 10px rgba(0,0,0,.3);-webkit-box-shadow:var(--mm-shadow);box-shadow:var(--mm-shadow)}.mm-menu_shadow-panels .mm-panels>.mm-panel{-webkit-box-shadow:0 0 10px rgba(0,0,0,.3);box-shadow:0 0 10px rgba(0,0,0,.3);-webkit-box-shadow:var(--mm-shadow);box-shadow:var(--mm-shadow)}

View File

@ -0,0 +1 @@
.mm-menu_theme-white{--mm-color-border:rgba( 0,0,0, 0.1 );--mm-color-button:rgba( 0,0,0, 0.3 );--mm-color-text:rgba( 0,0,0, 0.7 );--mm-color-text-dimmed:rgba( 0,0,0, 0.3 );--mm-color-background:#fff;--mm-color-background-highlight:rgba( 0,0,0, 0.06 );--mm-color-background-emphasis:rgba( 0,0,0, 0.03 );--mm-shadow:0 0 10px rgba( 0,0,0, 0.2 )}.mm-menu_theme-dark{--mm-color-border:rgba( 0,0,0, 0.3 );--mm-color-button:rgba( 255,255,255, 0.4 );--mm-color-text:rgba( 255,255,255, 0.85 );--mm-color-text-dimmed:rgba( 255,255,255, 0.4 );--mm-color-background:#333;--mm-color-background-highlight:rgba( 255,255,255, 0.08 );--mm-color-background-emphasis:rgba( 0,0,0, 0.1 );--mm-shadow:0 0 20px rgba( 0,0,0, 0.5 )}.mm-menu_theme-black{--mm-color-border:rgba( 255,255,255, 0.25 );--mm-color-button:rgba( 255,255,255, 0.4 );--mm-color-text:rgba( 255,255,255, 0.75 );--mm-color-text-dimmed:rgba( 255,255,255, 0.4 );--mm-color-background:#000;--mm-color-background-highlight:rgba( 255,255,255, 0.2 );--mm-color-background-emphasis:rgba( 255,255,255, 0.15 );--mm-shadow:none}

10
dist/mmenu.css vendored Normal file

File diff suppressed because one or more lines are too long

12
dist/mmenu.js vendored Normal file

File diff suppressed because one or more lines are too long

150
dist/mmenu.polyfills.js vendored Normal file
View File

@ -0,0 +1,150 @@
// Source: https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach
if (window.NodeList && !NodeList.prototype.forEach) {
NodeList.prototype.forEach = function(callback, thisArg) {
thisArg = thisArg || window;
for (var i = 0; i < this.length; i++) {
callback.call(thisArg, this[i], i, this);
}
};
}
// Source: https://developer.mozilla.org/en-US/docs/Web/API/Element/matches
if (!Element.prototype.matches) {
Element.prototype.matches =
Element.prototype.matchesSelector ||
Element.prototype.mozMatchesSelector ||
Element.prototype.msMatchesSelector ||
Element.prototype.oMatchesSelector ||
Element.prototype.webkitMatchesSelector ||
function(s) {
var matches = (
this.document || this.ownerDocument
).querySelectorAll(s),
i = matches.length;
while (--i >= 0 && matches.item(i) !== this) {}
return i > -1;
};
}
// Source: https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
if (!Element.prototype.matches) {
Element.prototype.matches =
Element.prototype.msMatchesSelector ||
Element.prototype.webkitMatchesSelector;
}
if (!Element.prototype.closest) {
Element.prototype.closest = function(s) {
var el = this;
do {
if (el.matches(s)) return el;
el = el.parentElement || el.parentNode;
} while (el !== null && el.nodeType === 1);
return null;
};
}
// Source: https://github.com/jserz/js_piece/blob/master/DOM/ParentNode/prepend()/prepend().md
(function(arr) {
arr.forEach(function(item) {
if (item.hasOwnProperty('prepend')) {
return;
}
Object.defineProperty(item, 'prepend', {
configurable: true,
enumerable: true,
writable: true,
value: function prepend() {
var argArr = Array.prototype.slice.call(arguments),
docFrag = document.createDocumentFragment();
argArr.forEach(function(argItem) {
var isNode = argItem instanceof Node;
docFrag.appendChild(
isNode
? argItem
: document.createTextNode(String(argItem))
);
});
this.insertBefore(docFrag, this.firstChild);
}
});
});
})([Element.prototype, Document.prototype, DocumentFragment.prototype]);
// Source: https://github.com/jserz/js_piece/blob/master/DOM/ParentNode/append()/append().md
(function(arr) {
arr.forEach(function(item) {
if (item.hasOwnProperty('append')) {
return;
}
Object.defineProperty(item, 'append', {
configurable: true,
enumerable: true,
writable: true,
value: function append() {
var argArr = Array.prototype.slice.call(arguments),
docFrag = document.createDocumentFragment();
argArr.forEach(function(argItem) {
var isNode = argItem instanceof Node;
docFrag.appendChild(
isNode
? argItem
: document.createTextNode(String(argItem))
);
});
this.appendChild(docFrag);
}
});
});
})([Element.prototype, Document.prototype, DocumentFragment.prototype]);
// Source: https://github.com/jserz/js_piece/blob/master/DOM/ChildNode/before()/before().md
(function(arr) {
arr.forEach(function(item) {
if (item.hasOwnProperty('before')) {
return;
}
Object.defineProperty(item, 'before', {
configurable: true,
enumerable: true,
writable: true,
value: function before() {
var argArr = Array.prototype.slice.call(arguments),
docFrag = document.createDocumentFragment();
argArr.forEach(function(argItem) {
var isNode = argItem instanceof Node;
docFrag.appendChild(
isNode
? argItem
: document.createTextNode(String(argItem))
);
});
this.parentNode.insertBefore(docFrag, this);
}
});
});
})([Element.prototype, CharacterData.prototype, DocumentType.prototype]);
// Source: https://github.com/jserz/js_piece/blob/master/DOM/ChildNode/remove()/remove().md
(function(arr) {
arr.forEach(function(item) {
if (item.hasOwnProperty('remove')) {
return;
}
Object.defineProperty(item, 'remove', {
configurable: true,
enumerable: true,
writable: true,
value: function remove() {
if (this.parentNode !== null) this.parentNode.removeChild(this);
}
});
});
})([Element.prototype, CharacterData.prototype, DocumentType.prototype]);

View File

@ -0,0 +1,8 @@
export default function () {
this.opts.onClick = {
close: true,
preventDefault: false,
setSelected: true
};
}
;

View File

@ -0,0 +1 @@
body.modal-open .mm-slideout{z-index:unset}

View File

@ -0,0 +1,113 @@
import * as DOM from '../../_modules/dom';
export default function () {
var _this = this;
// Create the menu
if (this.node.menu.matches('.navbar-collapse')) {
// No need for cloning the menu...
if (this.conf.offCanvas) {
this.conf.offCanvas.clone = false;
}
// ... We'll create a new menu
var nav = DOM.create('nav'), panel = DOM.create('div');
nav.append(panel);
DOM.children(this.node.menu).forEach(function (child) {
switch (true) {
case child.matches('.navbar-nav'):
panel.append(cloneNav(child));
break;
case child.matches('.dropdown-menu'):
panel.append(cloneDropdown(child));
break;
case child.matches('.form-inline'):
_this.conf.searchfield.form = {
action: child.getAttribute('action') || null,
method: child.getAttribute('method') || null
};
_this.conf.searchfield.input = {
name: child.querySelector('input').getAttribute('name') ||
null
};
_this.conf.searchfield.clear = false;
_this.conf.searchfield.submit = true;
break;
default:
panel.append(child.cloneNode(true));
break;
}
});
// Set the menu
this.bind('initMenu:before', function () {
document.body.prepend(nav);
_this.node.menu = nav;
});
// Hijack the toggler.
var parent_1 = this.node.menu.parentElement;
if (parent_1) {
var toggler = parent_1.querySelector('.navbar-toggler');
if (toggler) {
toggler.removeAttribute('data-target');
// delete toggler.dataset.target; // IE10 has no dataset :(
toggler.removeAttribute('aria-controls');
// Remove all bound events.
toggler.outerHTML = toggler.outerHTML;
toggler = parent_1.querySelector('.navbar-toggler');
// Open the menu on-click.
toggler.addEventListener('click', function (evnt) {
evnt.preventDefault();
evnt.stopImmediatePropagation();
_this[_this.vars.opened ? 'close' : 'open']();
});
}
}
}
function cloneLink(anchor) {
var link = DOM.create(anchor.matches('a') ? 'a' : 'span');
// Copy attributes
var attr = ['href', 'title', 'target'];
for (var a = 0; a < attr.length; a++) {
if (typeof anchor.getAttribute(attr[a]) != 'undefined') {
link.setAttribute(attr[a], anchor.getAttribute(attr[a]));
}
}
// Copy contents
link.innerHTML = anchor.innerHTML;
// Remove Screen reader text.
DOM.find(link, '.sr-only').forEach(function (sro) {
sro.remove();
});
return link;
}
function cloneDropdown(dropdown) {
var list = DOM.create('ul');
DOM.children(dropdown).forEach(function (anchor) {
var item = DOM.create('li');
if (anchor.matches('.dropdown-divider')) {
item.classList.add('Divider');
}
else if (anchor.matches('.dropdown-item')) {
item.append(cloneLink(anchor));
}
list.append(item);
});
return list;
}
function cloneNav(nav) {
var list = DOM.create('ul');
DOM.find(nav, '.nav-item').forEach(function (anchor) {
var item = DOM.create('li');
if (anchor.matches('.active')) {
item.classList.add('Selected');
}
if (!anchor.matches('.nav-link')) {
var dropdown = DOM.children(anchor, '.dropdown-menu')[0];
if (dropdown) {
item.append(cloneDropdown(dropdown));
}
anchor = DOM.children(anchor, '.nav-link')[0];
}
item.prepend(cloneLink(anchor));
list.append(item);
});
return list;
}
}

View File

@ -0,0 +1,4 @@
export default function () {
this.conf.classNames.selected = 'active';
}
;

4
dist/wrappers/olark/mmenu.olark.js vendored Normal file
View File

@ -0,0 +1,4 @@
export default function () {
this.conf.offCanvas.page.noSelector.push('#olark');
}
;

View File

@ -0,0 +1,15 @@
export default function () {
var classnames;
document.addEventListener('turbolinks:before-visit', function (evnt) {
classnames = document
.querySelector('.mm-wrapper')
.className.split(' ')
.filter(function (name) { return /mm-/.test(name); });
});
document.addEventListener('turbolinks:load', function (evnt) {
if (typeof classnames === 'undefined') {
return;
}
document.querySelector('.mm-wrapper').className = classnames;
});
}

View File

@ -0,0 +1,8 @@
export default function () {
this.conf.classNames.selected = 'current-menu-item';
var wpadminbar = document.getElementById('wpadminbar');
if (wpadminbar) {
wpadminbar.style.position = 'fixed';
wpadminbar.classList.add('mm-slideout');
}
}

142
gulp/css.js Normal file
View File

@ -0,0 +1,142 @@
/*
CSS tasks.
*) The includes, variables and mixins are concatenated into the "input" dir.
**) For a custom build, the includes and variables are copied from the specified "custom input" dir (--i flag), into the "input" dir.
*/
const { src, dest, watch, series, parallel } = require('gulp');
const sass = require('gulp-sass');
const autoprefixer = require('gulp-autoprefixer');
const cleancss = require('gulp-clean-css');
const concat = require('gulp-concat');
const dirs = require('./dirs');
var dir = {};
/** Run all scripts. */
exports.all = CSSall = cb => {
dir = dirs(false);
series(
parallel(CSSconcatMixins, CSSconcatIncludes, CSSconcatVariables),
CSScompile
)(cb);
};
exports.custom = CSScustom = cb => {
dir = dirs(true);
const CSScompileCustom = cb => CSScompile(cb, dir.build + '/mmenu.scss');
series(parallel(CSScopyIncludes, CSScopyVariables), CSScompileCustom)(cb);
};
/** Put a watch on all files. */
exports.watch = CSSwatch = cb => {
dir = dirs(false);
watch([
dir.input + '/**/*.scss',
'!' + dir.input + '/_includes.scss',
'!' + dir.input + '/_variables.scss',
'!' + dir.input + '/_mixins.scss'
]).on('change', path => {
console.log('Change detected to .scss file "' + path + '"');
var cb = () => {
console.log('CSS compiled and concatenated.');
};
switch (true) {
// Changing an include, a variable or a mixin potentially affects all .scss files:
// - run all CSS tasks.
case path.indexOf('_includes.scss') > -1:
case path.indexOf('_variables.scss') > -1:
case path.indexOf('_mixins.scss') > -1:
CSSall(cb);
break;
// Changing any other file should only affect the files in the same directory:
// - compile only the directory to css;
// - concatenate all.
default:
var files = path.split('/');
files.pop();
files.shift();
files = files.join('/');
var CSScompileOne = cb =>
CSScompile(
cb,
dir.input + '/' + files + '/*.scss',
dir.output + '/' + files
);
series(CSScompileOne, CSScompile)(cb);
break;
}
});
cb();
};
// *) Concatenate includes into a single file.
const CSSconcatIncludes = cb => {
var files = [
dir.input + '/core/oncanvas/_includes.scss', // Include oncanvas includes first.
dir.input + '/core/**/_includes.scss', // Include core add-ons includes next.
dir.input + '/**/_includes.scss', // Include the rest of the includes.
'!' + dir.input + '/_includes.scss' // Exclude the includes destination file.
];
return src(files)
.pipe(concat('_includes.scss'))
.pipe(dest(dir.input));
};
// *) Concatenate variables into a single file.
const CSSconcatVariables = cb => {
var files = [
dir.input + '/core/oncanvas/_variables.scss', // Include oncanvas variables first.
dir.input + '/core/**/_variables.scss', // Include core add-ons variables next.
dir.input + '/**/_variables.scss', // Include the rest of the variables.
'!' + dir.input + '/_variables.scss' // Exclude the variables destination file.
];
return src(files)
.pipe(concat('_variables.scss'))
.pipe(dest(dir.input));
};
// Concatenate mixins into a single file.
const CSSconcatMixins = cb => {
var files = [
dir.input + '/core/oncanvas/_mixins.scss', // Include oncanvas mixins first.
dir.input + '/core/**/_mixins.scss', // Include core add-ons mixins next.
dir.input + '/**/_mixins.scss', // Include the rest of the mixins.
'!' + dir.input + '/_mixins.scss' // Exclude the mixins destination file.
];
return src(files)
.pipe(concat('_mixins.scss'))
.pipe(dest(dir.input));
};
// **) Copy includes from custom input into input.
const CSScopyIncludes = cb => {
return src(dir.build + '/**/_includes.scss').pipe(dest(dir.input));
};
// **) Copy variables from custom input into input.
const CSScopyVariables = cb => {
return src(dir.build + '/**/_variables.scss').pipe(dest(dir.input));
};
// Compile all (or some) SCSS files to CSS.
const CSScompile = (cb, input, output) => {
return src(input || dir.input + '/**/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(autoprefixer(['> 5%', 'last 5 versions']))
.pipe(cleancss())
.pipe(dest(output || dir.output));
};

35
gulp/dirs.js Normal file
View File

@ -0,0 +1,35 @@
const getOption = opt => {
var index = process.argv.indexOf('--' + opt);
if (index > -1) {
opt = process.argv[index + 1];
return opt && opt.slice(0, 2) !== '--' ? opt : false;
}
return false;
};
module.exports = findCustom => {
var dirs = {
input: 'src',
output: 'dist',
build: null
};
if (!findCustom) {
return dirs;
}
var i = getOption('i'),
o = getOption('o');
// Set custom input dir.
if (i) {
dirs.build = i;
}
// Set custom output dir.
if (o) {
dirs.output = o;
}
return dirs;
};

93
gulp/js.js Normal file
View File

@ -0,0 +1,93 @@
/*
JS tasks.
*) The "module" file is transpiled from the specified "custom input" dir.
*/
const { src, dest, watch, series } = require('gulp');
const typescript = require('gulp-typescript');
const webpack = require('webpack-stream');
const dirs = require('./dirs.js');
var dir = {};
/** Run all scripts. */
exports.all = JSall = cb => {
dir = dirs(false);
series(JStranspile, JSpack)(cb);
};
exports.custom = JScustom = cb => {
dir = dirs(true);
series(JSpack)(cb);
};
/** Put a watch on all files. */
exports.watch = JSwatch = cb => {
dir = dirs(false);
watch(dir.input + '/**/*.ts', {
ignored: [
dir.input + '/**/*.d.ts' // Exclude all typings.
]
}).on('change', path => {
console.log('Change detected to .ts file "' + path + '"');
var cb = () => {
console.log('JS transpiled and concatenated.');
};
// Changing any file only affects the files in the same directory:
// - transpile only the directory to js;
// - pack all.
var files = path.split('/');
files.pop();
files.shift();
files = files.join('/');
var input = dir.input + '/' + files + '/*.ts',
output = dir.output + '/' + files;
var JStranspileOne = cb => JStranspile(cb, input, output);
series(JStranspileOne, JSpack)(cb);
});
cb();
};
// *) Transpile all TS files to JS.
const JStranspile = (cb, input, output) => {
return src([
dir.input + '/**/*.d.ts', // Include all typings.
input || dir.input + '/**/*.ts' // Include the needed ts files.
])
.pipe(
typescript({
target: 'es5',
module: 'es6'
})
)
.pipe(dest(output || dir.output));
};
// Pack the files.
const JSpack = () => {
var input = dir.build || dir.input;
return src(input + '/mmenu.js')
.pipe(
webpack({
// mode: 'development',
mode: 'production',
output: {
filename: 'mmenu.js'
}
// optimization: {
// minimize: false
// }
})
)
.pipe(dest(dir.output));
};

27
gulp/polyfills.js Normal file
View File

@ -0,0 +1,27 @@
/*
Polyfill tasks.
*/
const { src, dest } = require('gulp');
const concat = require('gulp-concat');
const dirs = require('./dirs.js');
var dir = {};
module.exports = cb => {
dir = dirs(true);
// Some polyfills might rely on others,
// therefor we include 'em in a fixed order.
return src([
dir.input + '/_polyfills/api.foreach.js',
dir.input + '/_polyfills/api.matches.js',
dir.input + '/_polyfills/api.closest.js',
dir.input + '/_polyfills/dom.prepend.js',
dir.input + '/_polyfills/dom.append.js',
dir.input + '/_polyfills/dom.before.js',
dir.input + '/_polyfills/dom.remove.js'
])
.pipe(concat('mmenu.polyfills.js'))
.pipe(dest(dir.output));
};

69
gulpfile.js Executable file
View File

@ -0,0 +1,69 @@
/*
Tasks:
$ gulp : Runs the "js" and "css" tasks.
$ gulp js : Runs the "js" tasks.
$ gulp css : Runs the "css" tasks.
$ gulp watch : Starts a watch on the "js" and "css" tasks.
$ gulp polyfill : Creates the polyfill file.
Flags for the custom task:
--i ../path/to : Create a custom build using "mmenu.module.ts", "_includes.scss" and "_variables.scss" from the specified directory.
--o ../path/to : Sets the "output" directory to the specified directory.
Example:
$ gulp custom --i ../my-custom-input --o ../my-custom-output
$ gulp polyfill --i ../my-custom-input --o ../my-custom-output
*/
const { parallel, series } = require('gulp');
const js = require('./gulp/js');
const css = require('./gulp/css');
const polyfills = require('./gulp/polyfills');
/*
$ gulp
*/
exports.default = cb => {
parallel(js.all, css.all)(cb);
};
/*
$ gulp js
*/
exports.js = cb => {
js.all(cb);
};
/*
$ gulp css
*/
exports.css = cb => {
css.all(cb);
};
/*
$ gulp custom
*/
exports.custom = cb => {
parallel(js.custom, css.custom)(cb);
};
/*
$ gulp watch
*/
exports.watch = cb => {
parallel(series(js.all, js.watch), series(css.all, css.watch))(cb);
};
/*
$ gulp polyfill
*/
exports.polyfill = cb => {
parallel(polyfills)(cb);
};

28
index.html Normal file
View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="author" content="www.frebsite.nl" />
<meta content="width=600px user-scalable=yes" name="viewport" />
<meta name="robots" content="noindex, nofollow" />
<title>mmenu.js, app look-alike menus with sliding submenus.</title>
<link type="text/css" rel="stylesheet" href="http://fonts.googleapis.com/css?family=Pacifico" />
<link type="text/css" rel="stylesheet" href="demo/css/site.css" />
</head>
<body>
<div id="wrapper">
<div class="phone">
<iframe name="phone" src="demo/advanced.html" frameborder="0" width="320" height="480"></iframe>
</div>
<div id="page">
<h1>mmenu</h1>
<p>The best javascript plugin for app look-alike on- and off-canvas menus with sliding submenus for your website and web-app.</p>
<p>Check out the example on the left or <a href="https://mmenujs.com/examples.html" target="_blank">play around with the options</a>.</p>
<p>For the full documentation please visit: <a href="https://mmenujs.com" target="_blank">mmenujs.com</a></p>
<p>There also is a <a href="http://mmenujs.com/wordpress-plugin">WordPress plugin available</a>.</p>
</div>
</div>
</body>
</html>

6270
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

41
package.json Normal file
View File

@ -0,0 +1,41 @@
{
"name": "mmenu-js",
"version": "8.5.3",
"main": "dist/mmenu.js",
"module": "src/mmenu.js",
"author": "Fred Heusschen <info@frebsite.nl>",
"license": "CC-BY-NC-4.0",
"repository": {
"type": "git",
"url": "https://github.com/FrDH/mmenu-js.git"
},
"description": "The best javascript plugin for app look-alike on- and off-canvas menus with sliding submenus for your website and webapp.",
"keywords": [
"app",
"list",
"listview",
"megamenu",
"menu",
"mmenu",
"mobile",
"navigation",
"off-canvas",
"on-canvas",
"curtain",
"panels",
"submenu"
],
"scripts": {
"build": "gulp default"
},
"devDependencies": {
"gulp": "^4.0.2",
"gulp-autoprefixer": "^6.1.0",
"gulp-clean-css": "^4.2.0",
"gulp-concat": "^2.6.1",
"gulp-sass": "^4.0.2",
"gulp-typescript": "^5.0.1",
"typescript": "^3.4.5",
"webpack-stream": "^5.2.1"
}
}

93
src/_includes.scss Normal file
View File

@ -0,0 +1,93 @@
$mm_include_rtl: true !default;
$mm_include_vertical: true !default;
$mm_IE11Fallbacks: true !default;
$mm_include_offcanvas: true !default;
$mm_include_offcanvas_blocker: $mm_include_offcanvas !default;
$mm_include_screenreader: true !default;
$mm_include_autoheight: true !default;
$mm_include_columns: true !default;
$mm_include_counters: true !default;
$mm_include_dividers: true !default;
$mm_include_drag: true !default;
$mm_include_dropdown: true !default;
$mm_include_dropdown_tip: $mm_include_dropdown !default;
$mm_include_iconbar: true !default;
$mm_include_iconbar_tabs: $mm_include_iconbar !default;
$mm_include_iconpanels: true !default;
$mm_include_iconpanels_blocker: $mm_include_iconpanels !default;
$mm_include_keyboardnavigation: true !default;
$mm_include_navbars: true !default;
$mm_include_navbars_top: $mm_include_navbars !default;
$mm_include_navbars_bottom: $mm_include_navbars !default;
$mm_include_navbars_breadcrumbs: $mm_include_navbars !default;
$mm_include_navbars_searchfield: $mm_include_navbars !default;
$mm_include_navbars_tabs: $mm_include_navbars !default;
$mm_include_searchfield: true !default;
$mm_include_searchfield_btn: $mm_include_searchfield !default;
$mm_include_searchfield_searchpanel: $mm_include_searchfield !default;
$mm_include_sectionindexer: true !default;
$mm_include_setselected: true !default;
$mm_include_sidebar: true !default;
$mm_include_sidebar_collapsed: $mm_include_sidebar !default;
$mm_include_sidebar_expanded: $mm_include_sidebar !default;
$mm_include_sidebar_blocker: $mm_include_sidebar !default;
$mm_include_toggles: true !default;
$mm_include_checks: true !default;
$mm_include_borderstyle: true !default;
$mm_include_borderstyle_none: $mm_include_borderstyle !default;
$mm_include_borderstyle_full: $mm_include_borderstyle !default;
$mm_include_effects: true !default;
$mm_include_effects_menuslide: $mm_include_effects !default;
$mm_include_effects_panelsnone: $mm_include_effects !default;
$mm_include_effects_panelsslide: $mm_include_effects !default;
$mm_include_fullscreen: true !default;
$mm_include_listview: true !default;
$mm_include_listview_justify: $mm_include_listview !default;
$mm_include_listview_inset: $mm_include_listview !default;
$mm_include_multiline: true !default;
$mm_include_pagedim: true !default;
$mm_include_pagedim_default: $mm_include_pagedim !default;
$mm_include_pagedim_black: $mm_include_pagedim !default;
$mm_include_pagedim_white: $mm_include_pagedim !default;
$mm_include_popup: true !default;
$mm_include_positioning: true !default;
$mm_include_positioning_right: $mm_include_positioning !default;
$mm_include_positioning_top: $mm_include_positioning !default;
$mm_include_positioning_bottom: $mm_include_positioning !default;
$mm_include_positioning_front: $mm_include_positioning !default;
$mm_include_shadows: true !default;
$mm_include_shadows_page: $mm_include_shadows !default;
$mm_include_shadows_menu: $mm_include_shadows !default;
$mm_include_shadows_panels: $mm_include_shadows !default;
$mm_include_themes: true !default;
$mm_include_themes_white: $mm_include_themes !default;
$mm_include_themes_dark: $mm_include_themes !default;
$mm_include_themes_black: $mm_include_themes !default;

183
src/_mixins.scss Normal file
View File

@ -0,0 +1,183 @@
// Arrow buttons
@mixin mm_btn_arrow_prev {
transform: rotate( -45deg );
left: $mm_listitemIndent + 3;
right: auto;
}
@mixin mm_btn_arrow_next {
transform: rotate( 135deg );
right: $mm_listitemIndent + 3;
left: auto;
}
// Misc
@mixin mm_ellipsis() {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
@mixin mm_clearfix() {
&:after {
content: '';
display: block;
clear: both;
}
}
@mixin mm_offcanvas_size( $cls: ".mm-menu_offcanvas",
$width: $mm_menuWidth, $minWidth: $mm_menuMinWidth, $maxWidth: $mm_menuMaxWidth
) {
#{$cls} {
width: percentage( $width );
min-width: $minWidth;
max-width: $maxWidth;
}
.mm-wrapper_opening {
#{$cls} ~ .mm-slideout {
transform: translate3d( #{$width * 100}vw, 0, 0 );
}
}
// adjust for min- and max-width
@media all and ( max-width: $minWidth / $width ) {
.mm-wrapper_opening {
#{$cls} ~ .mm-slideout {
transform: translate3d( $minWidth, 0, 0 );
}
}
}
@media all and ( min-width: $maxWidth / $width ) {
.mm-wrapper_opening {
#{$cls} ~ .mm-slideout {
transform: translate3d( $maxWidth, 0, 0 );
}
}
}
}
@mixin mm_columns_size($nr) {
[class*='mm-menu_columns-'] .mm-panels > .mm-panel_columns-#{$nr} {
transform: translate3d($nr * 100%, 0, 0);
}
.mm-menu_columns-#{$nr} .mm-panels > .mm-panel {
z-index: $nr;
@if ($nr > 0) {
width: ceil(100% / $nr * 100) / 100;
}
else {
width: 100%;
}
&:not(.mm-panel_opened):not(.mm-panel_opened-parent) {
transform: translate3d(($nr + 1) * 100%, 0, 0);
}
}
@include mm_offcanvas_size(
'.mm-menu_columns-#{$nr}',
$mm_menuWidth,
$mm_menuMinWidth,
$mm_menuMaxWidth * $nr
);
@include mm_position_right(
'.mm-menu_columns-#{$nr}',
$mm_menuWidth,
$mm_menuMinWidth,
$mm_menuMaxWidth * $nr
);
}
@mixin mm_iconpanel_size($nr) {
.mm-panel_iconpanel-#{$nr} {
@if ($mm_IE11Fallbacks) {
width: calc(100% - #{$mm_iconpanelSize * $nr});
}
width: calc(100% - (var(--mm-iconpanel-size) * #{$nr}));
}
}
@mixin mm_navbar_tabs_borders( $top, $bottom, $last, $first ) {
.mm-navbars_#{$top} {
.mm-navbar_tabs:not( :#{$last}-child ) {
border-#{$bottom}-width: 1px;
border-#{$bottom}-style: solid;
}
.mm-navbar__tab_selected {
border-#{$bottom}: none;
margin-#{$bottom}: -1px;
}
.mm-navbar_tabs:#{$first}-child .mm-navbar__tab_selected {
border-#{$top}: none;
}
}
}
@mixin mm_effect_listitem_delay( $i ) {
&:nth-child( #{$i} ) {
transition-delay: ( $i * 50ms );
}
}
// Position right
@mixin mm_position_right(
$cls: '',
$width: $mm_menuWidth,
$minWidth: $mm_menuMinWidth,
$maxWidth: $mm_menuMaxWidth
) {
.mm-wrapper_opening {
#{$cls}.mm-menu_position-right.mm-menu_opened ~ .mm-slideout {
transform: translate3d(#{-$width * 100}vw, 0, 0);
}
}
// adjust for min- and max-width
@media all and (max-width: $minWidth / $width) {
.mm-wrapper_opening {
#{$cls}.mm-menu_position-right.mm-menu_opened ~ .mm-slideout {
transform: translate3d(-$minWidth, 0, 0);
}
}
}
@media all and (min-width: $maxWidth / $width) {
.mm-wrapper_opening {
#{$cls}.mm-menu_position-right.mm-menu_opened ~ .mm-slideout {
transform: translate3d(-$maxWidth, 0, 0);
}
}
}
}
// Position top
@mixin mm_position_top(
$cls: '',
$height: $mm_menuHeight,
$minHeight: $mm_menuMinHeight,
$maxHeight: $mm_menuMaxHeight
) {
#{$cls}.mm-menu_position-top {
height: #{$height * 100}vh;
min-height: $minHeight;
max-height: $maxHeight;
}
}
// Position bottom
@mixin mm_position_bottom(
$cls: '',
$height: $mm_menuHeight,
$minHeight: $mm_menuMinHeight,
$maxHeight: $mm_menuMaxHeight
) {
#{$cls}.mm-menu_position-bottom {
height: #{$height * 100}vh;
min-height: $minHeight;
max-height: $maxHeight;
}
}

158
src/_modules/dom.ts Normal file
View File

@ -0,0 +1,158 @@
/**
* Create an element with classname.
*
* @param {string} selector The nodeName and classnames for the element to create.
* @return {HTMLElement} The created element.
*/
export function create(selector: string): HTMLElement {
var args = selector.split('.');
var elem = document.createElement(args.shift());
// IE11:
args.forEach(classname => {
elem.classList.add(classname);
});
// Better browsers:
// elem.classList.add(...args);
return elem;
}
/**
* Find all elements matching the selector.
* Basically the same as element.querySelectorAll() but it returns an actuall array.
*
* @param {HTMLElement} element Element to search in.
* @param {string} filter The filter to match.
* @return {array} Array of elements that match the filter.
*/
export function find(
element: HTMLElement | Document,
filter: string
): HTMLElement[] {
return Array.prototype.slice.call(element.querySelectorAll(filter));
}
/**
* Find all child elements matching the (optional) selector.
*
* @param {HTMLElement} element Element to search in.
* @param {string} filter The filter to match.
* @return {array} Array of child elements that match the filter.
*/
export function children(element: HTMLElement, filter?: string): HTMLElement[] {
var children: HTMLElement[] = Array.prototype.slice.call(element.children);
return filter ? children.filter(child => child.matches(filter)) : children;
}
/**
* Find text excluding text from within child elements.
* @param {HTMLElement} element Element to search in.
* @return {string} The text.
*/
export function text(element: HTMLElement): string {
return Array.prototype.slice
.call(element.childNodes)
.filter(child => child.nodeType == 3)
.map(child => child.textContent)
.join(' ');
}
/**
* Find all preceding elements matching the selector.
*
* @param {HTMLElement} element Element to start searching from.
* @param {string} filter The filter to match.
* @return {array} Array of preceding elements that match the selector.
*/
export function parents(element: HTMLElement, filter?: string): HTMLElement[] {
/** Array of preceding elements that match the selector. */
var parents: HTMLElement[] = [];
/** Array of preceding elements that match the selector. */
var parent = element.parentElement;
while (parent) {
parents.push(parent);
parent = parent.parentElement;
}
return filter ? parents.filter(parent => parent.matches(filter)) : parents;
}
/**
* Find all previous siblings matching the selecotr.
*
* @param {HTMLElement} element Element to start searching from.
* @param {string} filter The filter to match.
* @return {array} Array of previous siblings that match the selector.
*/
export function prevAll(element: HTMLElement, filter?: string): HTMLElement[] {
/** Array of previous siblings that match the selector. */
var previous: HTMLElement[] = [];
/** Current element in the loop */
var current = element.previousElementSibling as HTMLElement;
while (current) {
if (!filter || current.matches(filter)) {
previous.push(current);
}
current = current.previousElementSibling as HTMLElement;
}
return previous;
}
/**
* Get an element offset relative to the document.
*
* @param {HTMLElement} element Element to start measuring from.
* @param {string} [direction=top] Offset top or left.
* @return {number} The element offset relative to the document.
*/
export function offset(element: HTMLElement, direction?: string): number {
return (
element.getBoundingClientRect()[direction] +
document.body[direction === 'left' ? 'scrollLeft' : 'scrollTop']
);
}
/**
* Filter out non-listitem listitems.
* @param {array} listitems Elements to filter.
* @return {array} The filtered set of listitems.
*/
export function filterLI(listitems: HTMLElement[]): HTMLElement[] {
return listitems.filter(listitem => !listitem.matches('.mm-hidden'));
}
/**
* Find anchors in listitems (excluding anchor that open a sub-panel).
* @param {array} listitems Elements to filter.
* @return {array} The found set of anchors.
*/
export function filterLIA(listitems: HTMLElement[]): HTMLElement[] {
var anchors = [];
filterLI(listitems).forEach(listitem => {
anchors.push(...children(listitem, 'a.mm-listitem__text'));
});
return anchors.filter(anchor => !anchor.matches('.mm-btn_next'));
}
/**
* Refactor a classname on multiple elements.
* @param {HTMLElement} element Element to refactor.
* @param {string} oldClass Classname to remove.
* @param {string} newClass Classname to add.
*/
export function reClass(
element: HTMLElement,
oldClass: string,
newClass: string
) {
if (element.matches('.' + oldClass)) {
element.classList.remove(oldClass);
element.classList.add(newClass);
}
}

View File

@ -0,0 +1,13 @@
/** How far from the sides the gesture can start. */
export const area: dragArea = {
top: 0,
right: 0,
bottom: 0,
left: 0
};
/** Tresholds for gestures. */
export const treshold: dragTreshold = {
start: 15,
swipe: 15
};

View File

@ -0,0 +1,15 @@
/**
* Calculate a distance from a percentage.
* @param {string|number} position The percentage (e.g. "75%").
* @param {number} size The available width or height in pixels.
* @return {number} The calculated distance.
*/
export const percentage2number = (position: string | number, size: number) => {
if (typeof position == 'string') {
if (position.slice(-1) == '%') {
position = parseInt(position.slice(0, -1), 10);
position = size * (position / 100);
}
}
return position;
};

View File

@ -0,0 +1,12 @@
/** Names of the possible directions. */
export const directionNames = {
x: ['Right', 'Left'],
y: ['Down', 'Up']
};
/** States for the gesture. */
export const state = {
inactive: 0,
watching: 1,
dragging: 2
};

View File

@ -0,0 +1,5 @@
/** Whether or not touch gestures are supported by the browser. */
export const touch =
'ontouchstart' in window ||
(navigator.msMaxTouchPoints ? true : false) ||
false;

24
src/_modules/dragevents/_typings.d.ts vendored Normal file
View File

@ -0,0 +1,24 @@
/** Options for the drag engine. */
interface dragOption {
area: dragArea;
}
/** How far from the sides the gesture can start. */
interface dragArea {
top?: number | string;
right?: number | string;
bottom?: number | string;
left?: number | string;
}
/** Tresholds for gestures. */
interface dragTreshold {
start?: number;
swipe?: number;
}
/** Set of x and y positions. */
interface dragCoordinates {
x: number;
y: number;
}

View File

@ -0,0 +1,298 @@
import * as support from './_support';
import * as options from './_defaults';
import * as settings from './_settings';
import { percentage2number } from './_helpers';
import { extend } from '../helpers';
export default class DragEvents {
/** The draggable area. */
surface: HTMLElement;
/** How far from the sides the gesture can start. */
area: dragArea;
/** Tresholds for gestures. */
treshold: dragTreshold;
/** Where the gesture started. */
startPosition: dragCoordinates;
/** The last measured x- and y- position. */
currentPosition: dragCoordinates;
/** The dragged x- and y-distances since the start. */
distance: dragCoordinates;
/** The dragged x- and y-distances since the last event. */
movement: dragCoordinates;
/** The axis of the gesture. */
axis: 'x' | 'y';
/** The state of the gesture. */
state: number;
/**
* Create the gestures.
* @param {HTMLElement} surface The surface for the gesture.
* @param {object} area Restriction where on the surface the gesture can be started.
* @param {object} treshold Treshold for the gestures.
*/
constructor(
surface: HTMLElement,
area?: dragArea,
treshold?: dragTreshold
) {
this.surface = surface;
this.area = extend(area, options.area);
this.treshold = extend(treshold, options.treshold);
// Set the mouse/touch events.
if (!this.surface['mmHasDragEvents']) {
this.surface.addEventListener(
support.touch ? 'touchstart' : 'mousedown',
this.start.bind(this)
);
this.surface.addEventListener(
support.touch ? 'touchend' : 'mouseup',
this.stop.bind(this)
);
this.surface.addEventListener(
support.touch ? 'touchleave' : 'mouseleave',
this.stop.bind(this)
);
this.surface.addEventListener(
support.touch ? 'touchmove' : 'mousemove',
this.move.bind(this)
);
}
this.surface['mmHasDragEvents'] = true;
}
/**
* Starting the touch gesture.
* @param {Event} event The touch event.
*/
start(event) {
this.currentPosition = {
x: event.touches ? event.touches[0].pageX : event.pageX || 0,
y: event.touches ? event.touches[0].pageY : event.pageY || 0
};
/** The widht of the surface. */
var width = this.surface.clientWidth;
/** The height of the surface. */
var height = this.surface.clientHeight;
// Check if the gesture started below the area.top.
var top = percentage2number(this.area.top, height);
if (typeof top == 'number') {
if (this.currentPosition.y < top) {
return;
}
}
// Check if the gesture started before the area.right.
var right = percentage2number(this.area.right, width);
if (typeof right == 'number') {
right = width - right;
if (this.currentPosition.x > right) {
return;
}
}
// Check if the gesture started above the area.bottom.
var bottom = percentage2number(this.area.bottom, height);
if (typeof bottom == 'number') {
bottom = height - bottom;
if (this.currentPosition.y > bottom) {
return;
}
}
// Check if the gesture started after the area.left.
var left = percentage2number(this.area.left, width);
if (typeof left == 'number') {
if (this.currentPosition.x < left) {
return;
}
}
// Store the start x- and y-position.
this.startPosition = {
x: this.currentPosition.x,
y: this.currentPosition.y
};
// Set the state of the gesture to "watching".
this.state = settings.state.watching;
}
/**
* Stopping the touch gesture.
* @param {Event} event The touch event.
*/
stop(event) {
// Dispatch the "dragEnd" events.
if (this.state == settings.state.dragging) {
/** The direction. */
const dragDirection = this._dragDirection();
/** The event information. */
const detail = this._eventDetail(dragDirection);
this._dispatchEvents('drag*End', detail);
// Dispatch the "swipe" events.
if (Math.abs(this.movement[this.axis]) > this.treshold.swipe) {
/** The direction. */
const swipeDirection = this._swipeDirection();
detail.direction = swipeDirection;
this._dispatchEvents('swipe*', detail);
}
}
// Set the state of the gesture to "inactive".
this.state = settings.state.inactive;
}
/**
* Doing the touch gesture.
* @param {Event} event The touch event.
*/
move(event) {
switch (this.state) {
case settings.state.watching:
case settings.state.dragging:
var position = {
x: event.changedTouches
? event.touches[0].pageX
: event.pageX || 0,
y: event.changedTouches
? event.touches[0].pageY
: event.pageY || 0
};
this.movement = {
x: position.x - this.currentPosition.x,
y: position.y - this.currentPosition.y
};
this.distance = {
x: position.x - this.startPosition.x,
y: position.y - this.startPosition.y
};
this.currentPosition = {
x: position.x,
y: position.y
};
this.axis =
Math.abs(this.distance.x) > Math.abs(this.distance.y)
? 'x'
: 'y';
/** The direction. */
const dragDirection = this._dragDirection();
/** The event information. */
const detail = this._eventDetail(dragDirection);
// Watching for the gesture to go past the treshold.
if (this.state == settings.state.watching) {
if (
Math.abs(this.distance[this.axis]) > this.treshold.start
) {
this._dispatchEvents('drag*Start', detail);
// Set the state of the gesture to "inactive".
this.state = settings.state.dragging;
}
}
// Dispatch the "drag" events.
if (this.state == settings.state.dragging) {
this._dispatchEvents('drag*Move', detail);
}
break;
}
}
/**
* Get the event details.
* @param {string} direction Direction for the event (up, right, down, left).
* @return {object} The event details.
*/
_eventDetail(direction: string) {
var distX = this.distance.x;
var distY = this.distance.y;
if (this.axis == 'x') {
distX -= distX > 0 ? this.treshold.start : 0 - this.treshold.start;
}
if (this.axis == 'y') {
distY -= distY > 0 ? this.treshold.start : 0 - this.treshold.start;
}
return {
axis: this.axis,
direction: direction,
movementX: this.movement.x,
movementY: this.movement.y,
distanceX: distX,
distanceY: distY
};
}
/**
* Dispatch the events
* @param {string} eventName The name for the events to dispatch.
* @param {object} detail The event details.
*/
_dispatchEvents(eventName: string, detail) {
/** General event, e.g. "drag" */
var event = new CustomEvent(eventName.replace('*', ''), { detail });
this.surface.dispatchEvent(event);
/** Axis event, e.g. "dragX" */
var axis = new CustomEvent(
eventName.replace('*', this.axis.toUpperCase()),
{ detail }
);
this.surface.dispatchEvent(axis);
/** Direction event, e.g. "dragLeft" */
var direction = new CustomEvent(
eventName.replace('*', detail.direction),
{
detail
}
);
this.surface.dispatchEvent(direction);
}
/**
* Get the dragging direction.
* @return {string} The direction in which the user is dragging.
*/
_dragDirection() {
return settings.directionNames[this.axis][
this.distance[this.axis] > 0 ? 0 : 1
];
}
/**
* Get the dragging direction.
* @return {string} The direction in which the user is dragging.
*/
_swipeDirection() {
return settings.directionNames[this.axis][
this.movement[this.axis] > 0 ? 0 : 1
];
}
}

View File

@ -0,0 +1,64 @@
/**
* Make the first letter in a word uppercase.
* @param {string} word The word.
*/
function ucFirst(word) {
if (!word) {
return '';
}
return word.charAt(0).toUpperCase() + word.slice(1);
}
/**
* Bind an event listener to an element.
* @param {HTMLElement} element The element to bind the event listener to.
* @param {string} evnt The event to listen to.
* @param {funcion} handler The function to invoke.
*/
export function on(
element: HTMLElement | Window,
evnt: string,
handler: EventListenerOrEventListenerObject
) {
// Extract the event name and space from the event (the event can include a namespace (click.foo)).
var evntParts = evnt.split('.');
evnt = 'mmEvent' + ucFirst(evntParts[0]) + ucFirst(evntParts[1]);
element[evnt] = element[evnt] || [];
element[evnt].push(handler);
element.addEventListener(evntParts[0], handler);
}
/**
* Remove an event listener from an element.
* @param {HTMLElement} element The element to remove the event listeners from.
* @param {string} evnt The event to remove.
*/
export function off(element: HTMLElement | Window, evnt: string) {
// Extract the event name and space from the event (the event can include a namespace (click.foo)).
var evntParts = evnt.split('.');
evnt = 'mmEvent' + ucFirst(evntParts[0]) + ucFirst(evntParts[1]);
(element[evnt] || []).forEach(handler => {
element.removeEventListener(evntParts[0], handler);
});
}
/**
* Trigger the bound event listeners on an element.
* @param {HTMLElement} element The element of which to trigger the event listeners from.
* @param {string} evnt The event to trigger.
* @param {object} [options] Options to pass to the handler.
*/
export function trigger(
element: HTMLElement | Window,
evnt: string,
options?: mmLooseObject
) {
var evntParts = evnt.split('.');
evnt = 'mmEvent' + ucFirst(evntParts[0]) + ucFirst(evntParts[1]);
(element[evnt] || []).forEach(handler => {
handler(options || {});
});
}

147
src/_modules/helpers.ts Normal file
View File

@ -0,0 +1,147 @@
/**
* Deep extend an object with the given defaults.
* Note that the extended object is not a clone, meaning the original object will also be updated.
*
* @param {object} orignl The object to extend to.
* @param {object} dfault The object to extend from.
* @return {object} The extended "orignl" object.
*/
export function extend(orignl: mmLooseObject, dfault: mmLooseObject) {
if (type(orignl) != 'object') {
orignl = {};
}
if (type(dfault) != 'object') {
dfault = {};
}
for (let k in dfault) {
if (!dfault.hasOwnProperty(k)) {
continue;
}
if (typeof orignl[k] == 'undefined') {
orignl[k] = dfault[k];
} else if (type(orignl[k]) == 'object') {
extend(orignl[k], dfault[k]);
}
}
return orignl;
}
/**
* Detect the touch / dragging direction on a touch device.
*
* @param {HTMLElement} surface The element to monitor for touch events.
* @return {object} Object with "get" function.
*/
export function touchDirection(surface) {
var direction = '';
surface.addEventListener('touchmove', evnt => {
direction = '';
if (evnt.movementY > 0) {
direction = 'down';
} else if (evnt.movementY < 0) {
direction = 'up';
}
});
return {
get: () => direction
};
}
/**
* Get the type of any given variable. Improvement of "typeof".
*
* @param {any} variable The variable.
* @return {string} The type of the variable in lowercase.
*/
export function type(variable: any): string {
return {}.toString
.call(variable)
.match(/\s([a-zA-Z]+)/)[1]
.toLowerCase();
}
/**
* Find the value from an option or function.
* @param {HTMLElement} element Scope for the function.
* @param {any} [option] Value or function.
* @param {any} [dfault] Default fallback value.
* @return {any} The given evaluation of the given option, or the default fallback value.
*/
export function valueOrFn(
element: HTMLElement,
option?: any,
dfault?: any
): any {
if (typeof option == 'function') {
var value = option.call(element);
if (typeof value != 'undefined') {
return value;
}
}
if (
(option === null ||
typeof option == 'function' ||
typeof option == 'undefined') &&
typeof dfault != 'undefined'
) {
return dfault;
}
return option;
}
/**
* Set and invoke a (single) transition-end function with fallback.
*
* @param {HTMLElement} element Scope for the function.
* @param {function} func Function to invoke.
* @param {number} duration The duration of the animation (for the fallback).
*/
export function transitionend(
element: HTMLElement,
func: Function,
duration: number
) {
var _ended = false,
_fn = function(evnt) {
if (typeof evnt !== 'undefined') {
if (evnt.target !== element) {
return;
}
}
if (!_ended) {
element.removeEventListener('transitionend', _fn);
element.removeEventListener('webkitTransitionEnd', _fn);
func.call(element);
}
_ended = true;
};
element.addEventListener('transitionend', _fn);
element.addEventListener('webkitTransitionEnd', _fn);
setTimeout(_fn, duration * 1.1);
}
/**
* Get a (page wide) unique ID.
*/
export function uniqueId() {
return 'mm-' + __id++;
}
var __id = 0;
/**
* Get the original ID from a possibly prefixed ID.
* @param id The possibly prefixed ID.
*/
export function originalId(id) {
if (id.slice(0, 3) == 'mm-') {
return id.slice(3);
}
return id;
}

39
src/_modules/i18n.ts Normal file
View File

@ -0,0 +1,39 @@
import { extend } from './helpers';
var translations = {};
/**
* Add translations to a language.
* @param {object} text Object of key/value translations.
* @param {string} language The translated language.
*/
export function add(text: object, language: string) {
if (typeof translations[language] == 'undefined') {
translations[language] = {};
}
extend(translations[language], text as object);
}
/**
* Find a translated text in a language.
* @param {string} text The text to find the translation for.
* @param {string} language The language to search in.
* @return {string} The translated text.
*/
export function get(text: string, language?: string): string {
if (
typeof language == 'string' &&
typeof translations[language] != 'undefined'
) {
return translations[language][text] || text;
}
return text;
}
/**
* Get all translated text in a language.
* @param {string} language The language to search for.
* @return {object} The translations.
*/
export function all(language: string): object {
return translations;
}

View File

@ -0,0 +1,44 @@
/** Collection of callback functions for media querys. */
var listeners = {};
/**
* Bind functions to a matchMedia listener (subscriber).
*
* @param {string|number} query Media query to match or number for min-width.
* @param {function} yes Function to invoke when the media query matches.
* @param {function} no Function to invoke when the media query doesn't match.
*/
export function add(query: string | number, yes: Function, no: Function) {
if (typeof query == 'number') {
query = '(min-width: ' + query + 'px)';
}
listeners[query] = listeners[query] || [];
listeners[query].push({ yes, no });
}
/**
* Initialize the matchMedia listener.
*/
export function watch() {
for (let query in listeners) {
let mqlist = window.matchMedia(query);
fire(query, mqlist);
mqlist.onchange = evnt => {
fire(query, mqlist);
};
}
}
/**
* Invoke the "yes" or "no" function for a matchMedia listener (publisher).
*
* @param {string} query Media query to check for.
* @param {MediaQueryList} mqlist Media query list to check with.
*/
export function fire(query: string, mqlist: MediaQueryList) {
var fn = mqlist.matches ? 'yes' : 'no';
for (let m = 0; m < listeners[query].length; m++) {
listeners[query][m][fn]();
}
}

5
src/_modules/support.ts Normal file
View File

@ -0,0 +1,5 @@
/** Whether or not touch gestures are supported by the browser. */
export const touch =
'ontouchstart' in window ||
(navigator.msMaxTouchPoints ? true : false) ||
false;

View File

@ -0,0 +1,18 @@
// Source: https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
if (!Element.prototype.matches) {
Element.prototype.matches =
Element.prototype.msMatchesSelector ||
Element.prototype.webkitMatchesSelector;
}
if (!Element.prototype.closest) {
Element.prototype.closest = function(s) {
var el = this;
do {
if (el.matches(s)) return el;
el = el.parentElement || el.parentNode;
} while (el !== null && el.nodeType === 1);
return null;
};
}

View File

@ -0,0 +1,9 @@
// Source: https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach
if (window.NodeList && !NodeList.prototype.forEach) {
NodeList.prototype.forEach = function(callback, thisArg) {
thisArg = thisArg || window;
for (var i = 0; i < this.length; i++) {
callback.call(thisArg, this[i], i, this);
}
};
}

View File

@ -0,0 +1,17 @@
// Source: https://developer.mozilla.org/en-US/docs/Web/API/Element/matches
if (!Element.prototype.matches) {
Element.prototype.matches =
Element.prototype.matchesSelector ||
Element.prototype.mozMatchesSelector ||
Element.prototype.msMatchesSelector ||
Element.prototype.oMatchesSelector ||
Element.prototype.webkitMatchesSelector ||
function(s) {
var matches = (
this.document || this.ownerDocument
).querySelectorAll(s),
i = matches.length;
while (--i >= 0 && matches.item(i) !== this) {}
return i > -1;
};
}

View File

@ -0,0 +1,28 @@
// Source: https://github.com/jserz/js_piece/blob/master/DOM/ParentNode/append()/append().md
(function(arr) {
arr.forEach(function(item) {
if (item.hasOwnProperty('append')) {
return;
}
Object.defineProperty(item, 'append', {
configurable: true,
enumerable: true,
writable: true,
value: function append() {
var argArr = Array.prototype.slice.call(arguments),
docFrag = document.createDocumentFragment();
argArr.forEach(function(argItem) {
var isNode = argItem instanceof Node;
docFrag.appendChild(
isNode
? argItem
: document.createTextNode(String(argItem))
);
});
this.appendChild(docFrag);
}
});
});
})([Element.prototype, Document.prototype, DocumentFragment.prototype]);

View File

@ -0,0 +1,28 @@
// Source: https://github.com/jserz/js_piece/blob/master/DOM/ChildNode/before()/before().md
(function(arr) {
arr.forEach(function(item) {
if (item.hasOwnProperty('before')) {
return;
}
Object.defineProperty(item, 'before', {
configurable: true,
enumerable: true,
writable: true,
value: function before() {
var argArr = Array.prototype.slice.call(arguments),
docFrag = document.createDocumentFragment();
argArr.forEach(function(argItem) {
var isNode = argItem instanceof Node;
docFrag.appendChild(
isNode
? argItem
: document.createTextNode(String(argItem))
);
});
this.parentNode.insertBefore(docFrag, this);
}
});
});
})([Element.prototype, CharacterData.prototype, DocumentType.prototype]);

View File

@ -0,0 +1,28 @@
// Source: https://github.com/jserz/js_piece/blob/master/DOM/ParentNode/prepend()/prepend().md
(function(arr) {
arr.forEach(function(item) {
if (item.hasOwnProperty('prepend')) {
return;
}
Object.defineProperty(item, 'prepend', {
configurable: true,
enumerable: true,
writable: true,
value: function prepend() {
var argArr = Array.prototype.slice.call(arguments),
docFrag = document.createDocumentFragment();
argArr.forEach(function(argItem) {
var isNode = argItem instanceof Node;
docFrag.appendChild(
isNode
? argItem
: document.createTextNode(String(argItem))
);
});
this.insertBefore(docFrag, this.firstChild);
}
});
});
})([Element.prototype, Document.prototype, DocumentFragment.prototype]);

View File

@ -0,0 +1,16 @@
// Source: https://github.com/jserz/js_piece/blob/master/DOM/ChildNode/remove()/remove().md
(function(arr) {
arr.forEach(function(item) {
if (item.hasOwnProperty('remove')) {
return;
}
Object.defineProperty(item, 'remove', {
configurable: true,
enumerable: true,
writable: true,
value: function remove() {
if (this.parentNode !== null) this.parentNode.removeChild(this);
}
});
});
})([Element.prototype, CharacterData.prototype, DocumentType.prototype]);

69
src/_variables.scss Normal file
View File

@ -0,0 +1,69 @@
// Animations
$mm_transitionDuration: 0.4s !default;
$mm_transitionDelay: 0.4s !default;
$mm_transitionFunction: ease !default;
// Colors
$mm_backgroundColor: #f3f3f3 !default;
$mm_borderColor: rgba(#000, 0.1) !default;
$mm_dimmedTextColor: rgba(#000, 0.3) !default;
$mm_emphasizedBackgroundColor: rgba(#fff, 0.4) !default;
$mm_highlightedBackgroundColor: rgba(#000, 0.05) !default;
$mm_navbarColor: rgba(#000, 0.3) !default;
$mm_textColor: rgba(#000, 0.75) !default;
// Positioning
$mm_offsetTop: 0 !default;
$mm_offsetRight: 0 !default;
$mm_offsetBottom: 0 !default;
$mm_offsetLeft: 0 !default;
// Sizes
$mm_listitemSize: 44px !default;
$mm_btnSize: 50px !default;
$mm_padding: 10px !default;
$mm_lineHeight: 20px !default;
$mm_listitemIndent: $mm_padding * 2 !default;
$mm_navbarSize: $mm_listitemSize !default;
$mm_panelPadding: $mm_padding * 2 !default;
$mm_subopenWidth: $mm_btnSize !default;
$mm_subpanelOffset: 30% !default;
$mm_menuWidth: 0.8 !default;
$mm_menuMinWidth: 240px !default;
$mm_menuMaxWidth: 440px !default;
$mm_menuHeight: 0.8 !default;
$mm_menuMinHeight: 140px !default;
$mm_menuMaxHeight: 880px !default;
$mm_opt_screenreader : true !default;
$mm_counterWidth: $mm_btnSize !default;
$mm_dropdownShadow: 0 2px 10px rgba( #000, 0.3 ) !default;
$mm_dropdownTipX: 20px !default;
$mm_dropdownTipY: 10px !default;
$mm_iconbarSize: $mm_btnSize !default;
$mm_iconpanelSize: $mm_btnSize !default;
$mm_sectionIndexerSize: 20px !default;
$mm_sidebarCollapsedSize: $mm_btnSize !default;
$mm_sidebarExpandedSize: $mm_menuMaxWidth !default;
$mm_toggleCheckedColor: #4bd963 !default;
$mm_toggleHeight: $mm_listitemSize - $mm_padding !default;
$mm_toggleWidth: ($mm_toggleHeight * 2) - $mm_padding !default;
$mm_checkHeight: $mm_btnSize - $mm_padding !default;
$mm_checkWidth: $mm_btnSize - $mm_padding !default;
$mm_fullscreen_full: 1 !default;
$mm_fullscreen_min: 140px !default;
$mm_fullscreen_max: 10000px !default;
$mm_pagedimOpacity: 0.3 !default;
$mm_pagedimDelay: 0.4s !default;
$mm_popupShadow: 0 2px 10px rgba( #000, 0.3 ) !default;

1
src/_version.ts Normal file
View File

@ -0,0 +1 @@
export default '8.5.3';

View File

@ -0,0 +1 @@
$mm_include_autoheight: true !default;

View File

@ -0,0 +1,33 @@
const opts : mmOptionsAutoheight = {
height: 'default'
};
export default opts;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(
options : mmOptionsAutoheight
) : mmOptionsAutoheight {
if ( typeof options == 'boolean' && options ) {
options = {
height: 'auto'
};
}
if ( typeof options == 'string' ) {
options = {
height: options
};
}
if ( typeof options != 'object' ) {
options = {};
}
return options;
};

6
src/addons/autoheight/_typings.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
/** Options for the autoHeight add-on. */
interface mmOptionsAutoheight {
/** What type of height to use. */
height ?: 'default' | 'auto' | 'highest'
}

View File

@ -0,0 +1,31 @@
@import '../../mixins', '../../includes', '../../variables';
.mm-menu_autoheight {
&:not(.mm-menu_offcanvas) {
position: relative;
}
&.mm-menu_position {
&-top,
&-bottom {
max-height: percentage($mm_menuHeight);
}
}
&-measuring {
.mm-panel {
display: block !important;
}
.mm-panels > .mm-panel {
bottom: auto !important;
height: auto !important;
}
@if ($mm_include_vertical) {
.mm-listitem_vertical:not(.mm-listitem_opened) .mm-panel {
display: none !important;
}
}
}
}

View File

@ -0,0 +1,105 @@
import Mmenu from './../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import { extend } from '../../_modules/helpers';
// Add the options.
Mmenu.options.autoHeight = options;
export default function(this: Mmenu) {
var options = extendShorthandOptions(this.opts.autoHeight);
this.opts.autoHeight = extend(options, Mmenu.options.autoHeight);
if (options.height != 'auto' && options.height != 'highest') {
return;
}
const setHeight = (() => {
const getCurrent = (): number => {
var panel = DOM.children(this.node.pnls, '.mm-panel_opened')[0];
if (panel) {
panel = measurablePanel(panel);
}
// Fallback, just to be sure we have a panel.
if (!panel) {
panel = DOM.children(this.node.pnls, '.mm-panel')[0];
}
return panel.scrollHeight;
};
const getHighest = (): number => {
var highest = 0;
DOM.children(this.node.pnls, '.mm-panel').forEach(panel => {
panel = measurablePanel(panel);
highest = Math.max(highest, panel.scrollHeight);
});
return highest;
};
const measurablePanel = (panel: HTMLElement): HTMLElement => {
// If it's a vertically expanding panel...
if (panel.parentElement.matches('.mm-listitem_vertical')) {
// ...find the first parent panel that isn't.
panel = DOM.parents(panel, '.mm-panel').filter(
panel =>
!panel.parentElement.matches('.mm-listitem_vertical')
)[0];
}
return panel;
};
return () => {
if (this.opts.offCanvas && !this.vars.opened) {
return;
}
var _hgh = 0;
var _dif =
this.node.menu.offsetHeight - this.node.pnls.offsetHeight;
// The "measuring" classname undoes some CSS to be able to measure the height.
this.node.menu.classList.add('mm-menu_autoheight-measuring');
// Measure the height.
if (options.height == 'auto') {
_hgh = getCurrent();
} else if (options.height == 'highest') {
_hgh = getHighest();
}
// Set the height.
this.node.menu.style.height = _hgh + _dif + 'px';
// Remove the "measuring" classname.
this.node.menu.classList.remove('mm-menu_autoheight-measuring');
};
})();
// Add the autoheight class to the menu.
this.bind('initMenu:after', () => {
this.node.menu.classList.add('mm-menu_autoheight');
});
if (this.opts.offCanvas) {
// Measure the height when opening the off-canvas menu.
this.bind('open:start', setHeight);
}
if (options.height == 'highest') {
// Measure the height when initiating panels.
this.bind('initPanels:after', setHeight);
}
if (options.height == 'auto') {
// Measure the height when updating listviews.
this.bind('updateListview', setHeight);
// Measure the height when opening a panel.
this.bind('openPanel:start', setHeight);
}
}

View File

@ -0,0 +1,28 @@
const options : mmOptionsBackbutton = {
close: false,
open: false
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(
options : mmOptionsBackbutton
) : mmOptionsBackbutton {
if ( typeof options == 'boolean' ) {
options = {
close: options
};
}
if ( typeof options != 'object' ) {
options = {};
}
return options;
};

9
src/addons/backbutton/_typings.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
/** Options for the backButton add-on. */
interface mmOptionsBackbutton {
/** Whether or not to close the menu with the back-( and forth-)button. */
close ?: boolean
/** Whether or not to open the menu with the back-( and forth-)button. */
open ?: boolean
}

View File

@ -0,0 +1,73 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import { extend } from '../../_modules/helpers';
// Add the options.
Mmenu.options.backButton = options;
export default function(this: Mmenu) {
if (!this.opts.offCanvas) {
return;
}
var options = extendShorthandOptions(this.opts.backButton);
this.opts.backButton = extend(options, Mmenu.options.backButton);
var _menu = '#' + this.node.menu.id;
// Close menu
if (options.close) {
var states = [];
const setStates = () => {
states = [_menu];
DOM.children(
this.node.pnls,
'.mm-panel_opened, .mm-panel_opened-parent'
).forEach(panel => {
states.push('#' + panel.id);
});
};
this.bind('open:finish', () => {
history.pushState(null, document.title, _menu);
});
this.bind('open:finish', setStates);
this.bind('openPanel:finish', setStates);
this.bind('close:finish', () => {
states = [];
history.back();
history.pushState(
null,
document.title,
location.pathname + location.search
);
});
window.addEventListener('popstate', evnt => {
if (this.vars.opened) {
if (states.length) {
states = states.slice(0, -1);
var hash = states[states.length - 1];
if (hash == _menu) {
this.close();
} else {
this.openPanel(this.node.menu.querySelector(hash));
history.pushState(null, document.title, _menu);
}
}
}
});
}
if (options.open) {
window.addEventListener('popstate', evnt => {
if (!this.vars.opened && location.hash == _menu) {
this.open();
}
});
}
}

View File

@ -0,0 +1 @@
$mm_include_columns: true !default;

View File

@ -0,0 +1,33 @@
@mixin mm_columns_size($nr) {
[class*='mm-menu_columns-'] .mm-panels > .mm-panel_columns-#{$nr} {
transform: translate3d($nr * 100%, 0, 0);
}
.mm-menu_columns-#{$nr} .mm-panels > .mm-panel {
z-index: $nr;
@if ($nr > 0) {
width: ceil(100% / $nr * 100) / 100;
}
else {
width: 100%;
}
&:not(.mm-panel_opened):not(.mm-panel_opened-parent) {
transform: translate3d(($nr + 1) * 100%, 0, 0);
}
}
@include mm_offcanvas_size(
'.mm-menu_columns-#{$nr}',
$mm_menuWidth,
$mm_menuMinWidth,
$mm_menuMaxWidth * $nr
);
@include mm_position_right(
'.mm-menu_columns-#{$nr}',
$mm_menuWidth,
$mm_menuMinWidth,
$mm_menuMaxWidth * $nr
);
}

View File

@ -0,0 +1,45 @@
const options : mmOptionsColumns = {
add: false,
visible: {
min: 1,
max: 3
}
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(
options : mmOptionsColumns
) : mmOptionsColumns {
if ( typeof options == 'boolean' ) {
options = {
add : options
};
}
if ( typeof options == 'number' ) {
options = {
add : true,
visible : options
};
}
if ( typeof options != 'object' ) {
options = {};
}
if ( typeof options.visible == 'number' ) {
options.visible = {
min : options.visible,
max : options.visible
};
}
return options;
};

19
src/addons/columns/_typings.d.ts vendored Normal file
View File

@ -0,0 +1,19 @@
/** Options for the columns add-on. */
interface mmOptionsColumns {
/** Whether or not a to split up the panels in multiple columns. */
add ?: boolean
/** Map for the visible columns. */
visible ?: mmOptionsColumnsVisible
}
/** Map for the visible columns. */
interface mmOptionsColumnsVisible {
/** The minimum number of visible columns. */
min ?: number
/** The maximum number of visible columns. */
max ?: number
}

View File

@ -0,0 +1,58 @@
@import '../../mixins', '../../includes', '../../variables';
[class*='mm-menu_columns-'] {
transition-property: width;
.mm-panels {
> .mm-panel {
right: auto;
transition-property: width, transform;
&_opened,
&_opened-parent {
display: block !important;
}
}
}
}
[class*='mm-panel_columns-'] {
border-right: 1px solid;
border-color: inherit;
}
.mm-menu_columns-1 .mm-panel_columns-0,
.mm-menu_columns-2 .mm-panel_columns-1,
.mm-menu_columns-3 .mm-panel_columns-2,
.mm-menu_columns-4 .mm-panel_columns-3 {
border-right: none;
}
@include mm_columns_size(0);
@include mm_columns_size(1);
@include mm_columns_size(2);
@include mm_columns_size(3);
@include mm_columns_size(4);
@if ($mm_include_positioning_top or $mm_include_positioning_bottom) {
[class*='mm-menu_columns-'] {
&.mm-menu_position {
&-bottom,
&-top {
width: 100%;
max-width: 100%;
min-width: 100%;
}
}
}
}
@if ($mm_include_positioning_front) {
.mm-wrapper_opening [class*='mm-menu_columns-'] {
&.mm-menu_position {
&-front {
transition-property: width, min-width, max-width, transform;
}
}
}
}

View File

@ -0,0 +1,137 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import { extend } from '../../_modules/helpers';
// Add the options.
Mmenu.options.columns = options;
export default function(this: Mmenu) {
var options = extendShorthandOptions(this.opts.columns);
this.opts.columns = extend(options, Mmenu.options.columns);
// Add the columns
if (options.add) {
options.visible.min = Math.max(1, Math.min(6, options.visible.min));
options.visible.max = Math.max(
options.visible.min,
Math.min(6, options.visible.max)
);
/** Columns related clasnames for the menu. */
var colm = [];
/** Columns related clasnames for the panels. */
var colp = [];
/** Classnames to remove from panels in favor of showing columns. */
var rmvc = [
'mm-panel_opened',
'mm-panel_opened-parent',
'mm-panel_highest'
];
for (var i = 0; i <= options.visible.max; i++) {
colm.push('mm-menu_columns-' + i);
colp.push('mm-panel_columns-' + i);
}
rmvc.push(...colp);
// Close all later opened panels
this.bind('openPanel:before', (panel: HTMLElement) => {
/** The parent panel. */
var parent: HTMLElement;
if (panel) {
parent = panel['mmParent'];
}
if (!parent) {
return;
}
parent = parent.closest('.mm-panel') as HTMLElement;
if (!parent) {
return;
}
var classname = parent.className;
if (!classname.length) {
return;
}
classname = classname.split('mm-panel_columns-')[1];
if (!classname) {
return;
}
var colnr = parseInt(classname.split(' ')[0], 10) + 1;
while (colnr > 0) {
panel = DOM.children(
this.node.pnls,
'.mm-panel_columns-' + colnr
)[0];
if (panel) {
colnr++;
panel.classList.add('mm-hidden');
// IE11:
rmvc.forEach(classname => {
panel.classList.remove(classname);
});
// Better browsers:
// panel.classList.remove(...rmvc);
} else {
colnr = -1;
break;
}
}
});
this.bind('openPanel:start', (panel: HTMLElement) => {
var columns = DOM.children(
this.node.pnls,
'.mm-panel_opened-parent'
).length;
if (!panel.matches('.mm-panel_opened-parent')) {
columns++;
}
columns = Math.min(
options.visible.max,
Math.max(options.visible.min, columns)
);
// IE11:
colm.forEach(classname => {
this.node.menu.classList.remove(classname);
});
// Better browsers:
// this.node.menu.classList.remove(...colm);
this.node.menu.classList.add('mm-menu_columns-' + columns);
var panels: HTMLElement[] = [];
DOM.children(this.node.pnls, '.mm-panel').forEach(panel => {
// IE11:
colp.forEach(classname => {
panel.classList.remove(classname);
});
// Better browsers:
// panel.classList.remove(...colp);
if (panel.matches('.mm-panel_opened-parent')) {
panels.push(panel);
}
});
panels.push(panel);
panels.slice(-options.visible.max).forEach((panel, p) => {
panel.classList.add('mm-panel_columns-' + p);
});
});
}
}

View File

@ -0,0 +1,8 @@
@if ($mm_include_rtl) {
[dir='rtl'] .mm-counter {
text-align: left;
float: left;
padding-left: 0;
padding-right: $mm_padding * 2;
}
}

View File

@ -0,0 +1 @@
$mm_include_counters: true !default;

View File

@ -0,0 +1,34 @@
const options: mmOptionsCounters = {
add: false,
addTo: 'panels',
count: false
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(
options: mmOptionsCounters
): mmOptionsCounters {
if (typeof options == 'boolean') {
options = {
add: options,
addTo: 'panels',
count: options
};
}
if (typeof options != 'object') {
options = {};
}
if (options.addTo == 'panels') {
options.addTo = '.mm-listview';
}
return options;
}

12
src/addons/counters/_typings.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
/** Options for the counters add-on. */
interface mmOptionsCounters {
/** Whether or not to automatically append a counter to each menu item that has a submenu. */
add ?: boolean
/** Where to add the counters. */
addTo ?: string
/** Whether or not to automatically count the number of items in the submenu. */
count ?: boolean
}

View File

@ -0,0 +1 @@
$mm_counterWidth: $mm_btnSize !default;

View File

@ -0,0 +1,23 @@
@import '../../mixins', '../../includes', '../../variables';
$mm_module: '.mm-counter';
#{$mm_module} {
@if ($mm_IE11Fallbacks) {
color: $mm_dimmedTextColor;
}
display: block;
padding-left: $mm_padding * 2;
float: right;
text-align: right;
color: var(--mm-color-text-dimmed);
}
@if ($mm_include_searchfield) {
.mm-listitem_nosubitems > #{$mm_module} {
display: none;
}
}
@import 'counters.rtl';

View File

@ -0,0 +1,79 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import { extend } from '../../_modules/helpers';
// Add the options.
Mmenu.options.counters = options;
// Add the classnames.
Mmenu.configs.classNames.counters = {
counter: 'Counter'
};
export default function(this: Mmenu) {
var options = extendShorthandOptions(this.opts.counters);
this.opts.counters = extend(options, Mmenu.options.counters);
// Refactor counter class
this.bind('initListview:after', (listview: HTMLElement) => {
var cntrclss = this.conf.classNames.counters.counter,
counters = DOM.find(listview, '.' + cntrclss);
counters.forEach(counter => {
DOM.reClass(counter as HTMLElement, cntrclss, 'mm-counter');
});
});
// Add the counters after a listview is initiated.
if (options.add) {
this.bind('initListview:after', (listview: HTMLElement) => {
if (!listview.matches(options.addTo)) {
return;
}
var parent: HTMLElement = listview.closest('.mm-panel')['mmParent'];
if (parent) {
// Check if no counter already excists.
if (!DOM.find(parent, '.mm-counter').length) {
let btn = DOM.children(parent, '.mm-btn')[0];
if (btn) {
btn.prepend(DOM.create('span.mm-counter'));
}
}
}
});
}
if (options.count) {
const count = (listview?: HTMLElement) => {
var panels: HTMLElement[] = listview
? [listview.closest('.mm-panel') as HTMLElement]
: DOM.children(this.node.pnls, '.mm-panel');
panels.forEach(panel => {
var parent: HTMLElement = panel['mmParent'];
if (!parent) {
return;
}
var counter = DOM.find(parent, '.mm-counter')[0];
if (!counter) {
return;
}
var listitems: HTMLElement[] = [];
DOM.children(panel, '.mm-listview').forEach(listview => {
listitems.push(...DOM.children(listview));
});
counter.innerHTML = DOM.filterLI(listitems).length.toString();
});
};
this.bind('initListview:after', count);
this.bind('updateListview', count);
}
}

View File

@ -0,0 +1 @@
$mm_include_dividers: true !default;

View File

@ -0,0 +1,31 @@
const options: mmOptionsDividers = {
add: false,
addTo: 'panels'
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(
options: mmOptionsDividers
): mmOptionsDividers {
if (typeof options == 'boolean') {
options = {
add: options
};
}
if (typeof options != 'object') {
options = {};
}
if (options.addTo == 'panels') {
options.addTo = '.mm-listview';
}
return options;
}

8
src/addons/dividers/_typings.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
/** Options for the dividers add-on. */
interface mmOptionsDividers {
/** Whether or not to automatically add dividers to the menu (dividing the listitems alphabetically). */
add?: boolean;
/** Where to add the dividers. */
addTo?: string;
}

View File

@ -0,0 +1,53 @@
@import '../../mixins', '../../includes', '../../variables';
$mm_module: '.mm-divider';
#{$mm_module} {
@if ($mm_IE11Fallbacks) {
position: relative;
min-height: $mm_lineHeight;
padding: (($mm_listitemSize * 0.65) - $mm_lineHeight) * 0.5;
background: $mm_backgroundColor;
&:before {
background: $mm_highlightedBackgroundColor;
}
}
@include mm_ellipsis;
@supports (position: sticky) {
position: sticky;
z-index: 2;
top: 0;
.mm-navbar_sticky:not(.mm-hidden) ~ .mm-listview & {
top: var(--mm-navbar-size);
}
}
min-height: var(--mm-line-height);
padding: calc(
((var(--mm-listitem-size) * 0.65) - var(--mm-line-height)) * 0.5
);
padding-right: $mm_padding;
padding-left: $mm_listitemIndent;
font-size: 75%;
text-transform: uppercase;
background: var(--mm-color-background);
opacity: 1;
transition: opacity $mm_transitionDuration $mm_transitionFunction;
&:before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
background: var(--mm-color-background-highlight);
}
}

View File

@ -0,0 +1,56 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import { extend } from '../../_modules/helpers';
// Add the options.
Mmenu.options.dividers = options;
// Add the classnames.
Mmenu.configs.classNames.divider = 'Divider';
export default function(this: Mmenu) {
var options = extendShorthandOptions(this.opts.dividers);
this.opts.dividers = extend(options, Mmenu.options.dividers);
// Refactor divider classname
this.bind('initListview:after', listview => {
DOM.children(listview).forEach(listitem => {
DOM.reClass(listitem, this.conf.classNames.divider, 'mm-divider');
if (listitem.matches('.mm-divider')) {
listitem.classList.remove('mm-listitem');
}
});
});
// Add dividers
if (options.add) {
this.bind('initListview:after', (listview: HTMLElement) => {
if (!listview.matches(options.addTo)) {
return;
}
DOM.find(listview, '.mm-divider').forEach(divider => {
divider.remove();
});
var lastletter = '',
listitems = DOM.children(listview);
DOM.filterLI(listitems).forEach(listitem => {
let letter = DOM.children(listitem, '.mm-listitem__text')[0]
.textContent.trim()
.toLowerCase()[0];
if (letter.length && letter != lastletter) {
lastletter = letter;
let divider = DOM.create('li.mm-divider');
divider.textContent = letter;
listview.insertBefore(divider, listitem);
}
});
});
}
}

View File

@ -0,0 +1,277 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import DragEvents from '../../_modules/dragevents/index';
import * as DOM from '../../_modules/dom';
import * as events from '../../_modules/eventlisteners';
import * as media from '../../_modules/matchmedia';
/** Instance of the DragEvents class. */
var dragInstance: DragEvents = null;
/** THe node that can be dragged. */
var dragNode: HTMLElement = null;
/** How far the page (or menu) can be dragged. */
var maxDistance: number = 0;
export default function(this: Mmenu, page) {
/** Variables that vary for each menu position (top, right, bottom, left. front, back). */
var vars: mmLooseObject = {};
/** Whether or not the page or menu is actually being moved. */
var moving: boolean = false;
/**
* Add the dragging events.
*/
const addEvents = () => {
if (dragNode) {
// Prepare the page or menu to be moved.
events.on(dragNode, 'dragStart', evnt => {
if (evnt['detail'].direction == vars.direction) {
moving = true;
// Class prevents interaction with the page.
this.node.wrpr.classList.add('mm-wrapper_dragging');
// Prepare the menu to be opened.
this._openSetup();
this.trigger('open:start');
// Get the maximum distance to move out the page or menu.
maxDistance = this.node.menu[
vars.axis == 'x' ? 'clientWidth' : 'clientHeight'
];
}
});
// Move the page or menu when dragging.
events.on(dragNode, 'dragMove', evnt => {
if (evnt['detail'].axis == vars.axis) {
if (moving) {
var distance =
evnt['detail'][
'distance' + vars.axis.toUpperCase()
];
switch (vars.position) {
case 'right':
case 'bottom':
distance = Math.min(
Math.max(distance, -maxDistance),
0
);
break;
default:
distance = Math.max(
Math.min(distance, maxDistance),
0
);
}
// Deviate for position front (the menu starts out of view).
if (vars.zposition == 'front') {
switch (vars.position) {
case 'right':
case 'bottom':
distance += maxDistance;
break;
default:
distance -= maxDistance;
break;
}
}
vars.slideOutNodes.forEach(node => {
node.style['transform'] =
'translate' +
vars.axis.toUpperCase() +
'(' +
distance +
'px)';
});
}
}
});
// Stop the page or menu from being moved.
events.on(dragNode, 'dragEnd', evnt => {
if (evnt['detail'].axis == vars.axis) {
if (moving) {
moving = false;
this.node.wrpr.classList.remove('mm-wrapper_dragging');
vars.slideOutNodes.forEach(node => {
node.style['transform'] = '';
});
// Determine if the menu should open or close.
let open =
Math.abs(
evnt['detail'][
'distance' + vars.axis.toUpperCase()
]
) >=
maxDistance * 0.75;
if (!open) {
let movement =
evnt['detail'][
'movement' + vars.axis.toUpperCase()
];
switch (vars.position) {
case 'right':
case 'bottom':
open = movement <= 0;
break;
default:
open = movement >= 0;
break;
}
}
if (open) {
this._openStart();
} else {
this.close();
}
}
}
});
}
};
/**
* Remove the dragging events.
*/
const removeEvents = () => {
if (dragNode) {
events.off(dragNode, 'dragStart');
events.off(dragNode, 'dragMove');
events.off(dragNode, 'dragEnd');
}
};
let addMatchMedia = () => {
var queries = Object.keys(this.opts.extensions);
if (queries.length) {
// A media query that'll match if any of the other media query matches:
// set the defaults if it doesn't match.
media.add(
queries.join(', '),
() => {},
() => {
vars = getPositionVars(vars, [], this.node.menu);
}
);
// The other media queries.
queries.forEach(query => {
media.add(
query,
() => {
vars = getPositionVars(
vars,
this.opts.extensions[query],
this.node.menu
);
},
() => {}
);
});
// No extensions, just use the defaults.
} else {
vars = getPositionVars(vars, [], this.node.menu);
}
};
// Remove events from previous "page"
removeEvents();
// Store new "page"
dragNode = page;
// Initialize the drag events.
dragInstance = new DragEvents(dragNode);
addMatchMedia();
addMatchMedia = () => {};
addEvents();
}
const getPositionVars = (
vars: mmLooseObject,
extensions: string[],
menu: HTMLElement
) => {
// Default position and z-position.
vars.position = 'left';
vars.zposition = 'back';
// Find position.
['right', 'top', 'bottom'].forEach(pos => {
if (extensions.indexOf('position-' + pos) > -1) {
vars.position = pos;
}
});
// Find z-position.
['front', 'top', 'bottom'].forEach(pos => {
if (extensions.indexOf('position-' + pos) > -1) {
vars.zposition = 'front';
}
});
// Set the area where the dragging can start.
dragInstance.area = {
top: vars.position == 'bottom' ? '75%' : 0,
right: vars.position == 'left' ? '75%' : 0,
bottom: vars.position == 'top' ? '75%' : 0,
left: vars.position == 'right' ? '75%' : 0
};
// What side of the menu to measure (width or height).
// What axis to drag the menu along (x or y).
switch (vars.position) {
case 'top':
case 'bottom':
vars.axis = 'y';
break;
default:
vars.axis = 'x';
}
// What direction to drag in.
switch (vars.position) {
case 'top':
vars.direction = 'Down';
break;
case 'right':
vars.direction = 'Left';
break;
case 'bottom':
vars.direction = 'Up';
break;
default:
vars.direction = 'Right';
}
// What nodes to slide out while dragging.
switch (vars.zposition) {
case 'front':
vars.slideOutNodes = [menu];
break;
default:
vars.slideOutNodes = DOM.find(document.body, '.mm-slideout');
}
return vars;
};

View File

@ -0,0 +1 @@
$mm_include_drag: true !default;

View File

@ -0,0 +1,25 @@
const options: mmOptionsDrag = {
open: false,
node: null
};
export default options;
/**
* Extend shorthand options.
*
* @param {object} options The options to extend.
* @return {object} The extended options.
*/
export function extendShorthandOptions(options: mmOptionsDrag): mmOptionsDrag {
if (typeof options == 'boolean') {
options = {
open: options
};
}
if (typeof options != 'object') {
options = {};
}
return options;
}

8
src/addons/drag/_typings.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
/** Options for the drag add-on. */
interface mmOptionsDrag {
/** Whether or not to open the menu when dragging the page. */
open?: boolean;
/** The element on which the user can drag to open the menu. */
node?: HTMLElement;
}

View File

@ -0,0 +1,17 @@
@import '../../mixins', '../../includes', '../../variables';
.mm-wrapper_dragging {
.mm-menu,
.mm-slideout {
transition-duration: 0s !important;
user-select: none !important;
}
.mm-menu {
pointer-events: none !important;
}
.mm-wrapper__blocker {
display: none !important;
}
}

View File

@ -0,0 +1,24 @@
import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import { extendShorthandOptions } from './_options';
import dragOpen from './_drag.open';
import { extend } from '../../_modules/helpers';
// Add the options and configs.
Mmenu.options.drag = options;
export default function(this: Mmenu) {
if (!this.opts.offCanvas) {
return;
}
var options = extendShorthandOptions(this.opts.drag);
this.opts.drag = extend(options, Mmenu.options.drag);
// Drag open the menu
if (options.open) {
this.bind('setPage:after', page => {
dragOpen.call(this, options.node || page);
});
}
}

View File

@ -0,0 +1,19 @@
const configs : mmConfigsDropdown = {
offset: {
button: {
x: -5,
y: 5
},
viewport: {
x: 20,
y: 20
}
},
height: {
max: 880
},
width: {
max: 440
}
};
export default configs;

Some files were not shown because too many files have changed in this diff Show More