Initial commit
This commit is contained in:
commit
f3781775ff
47
CONTRIBUTING.md
Normal file
47
CONTRIBUTING.md
Normal 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** — check if the issue has already been
|
||||||
|
reported.
|
||||||
|
|
||||||
|
2. **Check if the issue has been fixed** — try to reproduce it using the
|
||||||
|
latest branch in the repository.
|
||||||
|
|
||||||
|
3. **Isolate the problem** — 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
2
LICENSE.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
This work is licensed under the Creative Commons Attribution 4.0 International License.
|
||||||
|
To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
|
||||||
14
README.md
Normal file
14
README.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
mhead.js
|
||||||
|
================
|
||||||
|
|
||||||
|
A small framework for creating a beautiful mobile navigation header to accompany the mmenu.js navigation menu on your website and webapp.
|
||||||
|
|
||||||
|
Need help? Have a look at [the documentation](https://mmenujs.com/mhead-plugin/) for demos and documentation.
|
||||||
|
|
||||||
|
### Licence
|
||||||
|
The mhead javascript plugin is licensed under the [CC-BY-4.0 license](http://creativecommons.org/licenses/by/4.0/).
|
||||||
|
|
||||||
|
### Development
|
||||||
|
This project uses [Gulp](http://gulpjs.com/) to transpile, 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.
|
||||||
15
composer.json
Normal file
15
composer.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name" : "mhead-js",
|
||||||
|
"version" : "2.0.0",
|
||||||
|
"authors" : "Fred Heusschen <info@frebsite.nl>",
|
||||||
|
"description" : "A small framework for creating a beautiful mobile navigation header to accompany the mmenu.js menu on your website and webapp.",
|
||||||
|
"keywords" : [
|
||||||
|
"app",
|
||||||
|
"mobile",
|
||||||
|
"navigation",
|
||||||
|
"head",
|
||||||
|
"header",
|
||||||
|
"navbar"
|
||||||
|
],
|
||||||
|
"license": "CC-BY-4.0"
|
||||||
|
}
|
||||||
96
demo/css/demo.css
Normal file
96
demo/css/demo.css
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
body * {
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
line-height: 1.25;
|
||||||
|
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 {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
padding: 10px;
|
||||||
|
line-height: 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
background: #4bb5ef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header a {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 10px;
|
||||||
|
transform: translate( 0, -50% );
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.header span {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.header input {
|
||||||
|
display: block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 30px;
|
||||||
|
width: calc(100% - 30px);
|
||||||
|
padding: 0 10px;
|
||||||
|
margin: 0 0 0 30px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
.header button {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 20px;
|
||||||
|
display: block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
transform: translate( 0, -50% );
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
color: #999;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#page {
|
||||||
|
padding-bottom: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 30vh 50px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
75
demo/css/site.css
Normal file
75
demo/css/site.css
Normal 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%);
|
||||||
|
}
|
||||||
75
demo/default.html
Normal file
75
demo/default.html
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="author" content="mmenujs.com" />
|
||||||
|
<meta content="width=600px user-scalable=yes" name="viewport" />
|
||||||
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
|
|
||||||
|
<title>mhead.js demo</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/demo.css">
|
||||||
|
<link type="text/css" rel="stylesheet" href="http://fonts.googleapis.com/css?family=Pacifico" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="../dist/mhead.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="page">
|
||||||
|
|
||||||
|
<div class="header" style="padding-bottom: 0px;">
|
||||||
|
<span>demo</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="header sticky">
|
||||||
|
<a class="fa fa-bars" href="#/menu"></a>
|
||||||
|
<input placeholder="search" />
|
||||||
|
<button type="submit" class="fa fa-search"></button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<p><strong>This is a demo.</strong><br />
|
||||||
|
Scroll down to see the header in action.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<p><strong>Scroll a bit faster.</strong><br />
|
||||||
|
If the header did not yet disappear.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<p><strong>Now scroll back up.</strong><br />
|
||||||
|
To make the header appear again.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="../dist/mhead.js"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
// Create the sticky header.
|
||||||
|
new Mhead( '.header.sticky', {
|
||||||
|
scroll : {
|
||||||
|
hide: 200
|
||||||
|
},
|
||||||
|
// hooks: {
|
||||||
|
// 'scrolledIn': function () {
|
||||||
|
// console.log('scrolledIn');
|
||||||
|
// },
|
||||||
|
// 'scrolledOut': function () {
|
||||||
|
// console.log('scrolledOut');
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// For the demo.
|
||||||
|
document.addEventListener('click', ( evnt ) => {
|
||||||
|
if (evnt.target.closest('a[href^="#/"]')) {
|
||||||
|
evnt.preventDefault();
|
||||||
|
alert( 'Thank you for clicking, but that\'s a demo link.' );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
BIN
demo/img/iphonex-example-blue.png
Normal file
BIN
demo/img/iphonex-example-blue.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
BIN
demo/img/iphonex-example-camera.png
Normal file
BIN
demo/img/iphonex-example-camera.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
40
dist/_helpers.js
vendored
Normal file
40
dist/_helpers.js
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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 (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;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
1
dist/_version.js
vendored
Normal file
1
dist/_version.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default '2.0.0';
|
||||||
9
dist/core/_options.js
vendored
Normal file
9
dist/core/_options.js
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const options = {
|
||||||
|
hooks: {},
|
||||||
|
scroll: {
|
||||||
|
pin: 0,
|
||||||
|
unpin: 0,
|
||||||
|
tolerance: 5
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export default options;
|
||||||
138
dist/core/mhead.core.js
vendored
Normal file
138
dist/core/mhead.core.js
vendored
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
import options from './_options';
|
||||||
|
import version from '../_version';
|
||||||
|
/**
|
||||||
|
* Class for a sticky navigational header.
|
||||||
|
*/
|
||||||
|
export default class Mhead {
|
||||||
|
/**
|
||||||
|
* Create a sticky header.
|
||||||
|
* @param {HTMLElement|string} header The header node.
|
||||||
|
* @param {object} [options=Mhead.options] Options for the header.
|
||||||
|
*/
|
||||||
|
constructor(header, options = Mhead.options) {
|
||||||
|
// Get header node from string or element.
|
||||||
|
this.header =
|
||||||
|
typeof header == 'string' ? document.querySelector(header) : header;
|
||||||
|
// Stop if there is no header element found.
|
||||||
|
if (!header) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Extend options from defaults.
|
||||||
|
this.opts = Object.assign(options, Mhead.options);
|
||||||
|
this.initHooks();
|
||||||
|
this.initScroll();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Initiate the scroll functionality.
|
||||||
|
*/
|
||||||
|
initScroll() {
|
||||||
|
if (!this.opts.scroll || this.opts.scroll.unpin === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.header.classList.add('mh-sticky');
|
||||||
|
/** Minimum scroll position to unpin / hide the header. */
|
||||||
|
var _min = this.header.offsetHeight * 2;
|
||||||
|
this.opts.scroll.unpin = Math.max(_min, this.opts.scroll.unpin || 0);
|
||||||
|
this.opts.scroll.pin = Math.max(_min, this.opts.scroll.pin || 0);
|
||||||
|
this.state = null;
|
||||||
|
/** Previous scroll position. */
|
||||||
|
var lastYpos = 0;
|
||||||
|
const onscroll = (evnt = {}) => {
|
||||||
|
/** Current scroll position. */
|
||||||
|
var pos = document.documentElement.scrollTop || document.body.scrollTop;
|
||||||
|
/** Difference between current scroll position and previous scroll position. */
|
||||||
|
var dif = lastYpos - pos;
|
||||||
|
/** Direction of the scroll. */
|
||||||
|
var dir = dif < 0 ? 'down' : 'up';
|
||||||
|
dif = Math.abs(dif);
|
||||||
|
lastYpos = pos;
|
||||||
|
// If not pinned / scrolled out the viewport.
|
||||||
|
if (this.state == Mhead.UNPINNED) {
|
||||||
|
// If scrolling up
|
||||||
|
if (dir == 'up') {
|
||||||
|
// If scrolling fast enough or past minimum
|
||||||
|
if (pos < this.opts.scroll.pin ||
|
||||||
|
dif > this.opts.scroll.tolerance) {
|
||||||
|
this.pin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If pinned / not scrolled out the viewport.
|
||||||
|
else if (this.state == Mhead.PINNED) {
|
||||||
|
// If scrolling down.
|
||||||
|
if (dir == 'down') {
|
||||||
|
// If scrolling fast enough and past minimum.
|
||||||
|
if (pos > this.opts.scroll.unpin &&
|
||||||
|
dif > this.opts.scroll.tolerance) {
|
||||||
|
this.unpin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.pin();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener('scroll', onscroll, {
|
||||||
|
passive: true
|
||||||
|
});
|
||||||
|
onscroll();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Pin the header to the top of the viewport.
|
||||||
|
*/
|
||||||
|
pin() {
|
||||||
|
this.header.classList.add('mh-pinned');
|
||||||
|
this.header.classList.remove('mh-unpinned');
|
||||||
|
this.state = Mhead.PINNED;
|
||||||
|
this.trigger('pinned');
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Release the header from the top of the viewport.
|
||||||
|
*/
|
||||||
|
unpin() {
|
||||||
|
this.header.classList.remove('mh-pinned');
|
||||||
|
this.header.classList.add('mh-unpinned');
|
||||||
|
this.state = Mhead.UNPINNED;
|
||||||
|
this.trigger('unpinned');
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Bind the hooks specified in the options (publisher).
|
||||||
|
*/
|
||||||
|
initHooks() {
|
||||||
|
this.hooks = {};
|
||||||
|
for (let hook in this.opts.hooks) {
|
||||||
|
this.bind(hook, this.opts.hooks[hook]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Bind functions to a hook (subscriber).
|
||||||
|
* @param {string} hook The hook.
|
||||||
|
* @param {function} func The function.
|
||||||
|
*/
|
||||||
|
bind(hook, func) {
|
||||||
|
// Create an array for the hook if it does not yet excist.
|
||||||
|
this.hooks[hook] = this.hooks[hook] || [];
|
||||||
|
// Push the function to the array.
|
||||||
|
this.hooks[hook].push(func);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Invoke the functions bound to a hook (publisher).
|
||||||
|
* @param {string} hook The hook.
|
||||||
|
* @param {array} [args] Arguments for the function.
|
||||||
|
*/
|
||||||
|
trigger(hook, args) {
|
||||||
|
if (this.hooks[hook]) {
|
||||||
|
for (var h = 0, l = this.hooks[hook].length; h < l; h++) {
|
||||||
|
this.hooks[hook][h].apply(this, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** Plugin version. */
|
||||||
|
Mhead.version = version;
|
||||||
|
/** Default options for headers. */
|
||||||
|
Mhead.options = options;
|
||||||
|
/** State for a "pinned" header. */
|
||||||
|
Mhead.PINNED = 'pinned';
|
||||||
|
/** State for a "unpinned" header. */
|
||||||
|
Mhead.UNPINNED = 'unpinned';
|
||||||
8
dist/core/options.js
vendored
Normal file
8
dist/core/options.js
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export default {
|
||||||
|
hooks: {},
|
||||||
|
scroll: {
|
||||||
|
hide: 0,
|
||||||
|
show: 0,
|
||||||
|
tolerance: 5
|
||||||
|
}
|
||||||
|
};
|
||||||
10
dist/mhead.css
vendored
Normal file
10
dist/mhead.css
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/*!
|
||||||
|
* mhead.js
|
||||||
|
* mmenu.frebsite.nl/mhead-plugin
|
||||||
|
*
|
||||||
|
* Copyright (c) Fred Heusschen
|
||||||
|
* www.frebsite.nl
|
||||||
|
*
|
||||||
|
* License: CC-BY-4.0
|
||||||
|
* http://creativecommons.org/licenses/by/4.0/
|
||||||
|
*/.mh-sticky{position:-webkit-sticky;position:sticky;top:0;z-index:10;-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;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.mh-sticky.mh-unpinned{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}
|
||||||
12
dist/mhead.js
vendored
Normal file
12
dist/mhead.js
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
!function(t){var s={};function e(n){if(s[n])return s[n].exports;var i=s[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,e),i.l=!0,i.exports}e.m=t,e.c=s,e.d=function(t,s,n){e.o(t,s)||Object.defineProperty(t,s,{enumerable:!0,get:n})},e.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},e.t=function(t,s){if(1&s&&(t=e(t)),8&s)return t;if(4&s&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(e.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&s&&"string"!=typeof t)for(var i in t)e.d(n,i,function(s){return t[s]}.bind(null,i));return n},e.n=function(t){var s=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(s,"a",s),s},e.o=function(t,s){return Object.prototype.hasOwnProperty.call(t,s)},e.p="",e(e.s=0)}([function(t,s,e){"use strict";e.r(s);var n,i={hooks:{},scroll:{pin:0,unpin:0,tolerance:5}};class o{constructor(t,s=o.options){this.header="string"==typeof t?document.querySelector(t):t,t&&(this.opts=Object.assign(s,o.options),this.initHooks(),this.initScroll())}initScroll(){if(!this.opts.scroll||!1===this.opts.scroll.unpin)return;this.header.classList.add("mh-sticky");var t=2*this.header.offsetHeight;this.opts.scroll.unpin=Math.max(t,this.opts.scroll.unpin||0),this.opts.scroll.pin=Math.max(t,this.opts.scroll.pin||0),this.state=null;var s=0;const e=(t={})=>{var e=document.documentElement.scrollTop||document.body.scrollTop,n=s-e,i=n<0?"down":"up";n=Math.abs(n),s=e,this.state==o.UNPINNED?"up"==i&&(e<this.opts.scroll.pin||n>this.opts.scroll.tolerance)&&this.pin():this.state==o.PINNED?"down"==i&&e>this.opts.scroll.unpin&&n>this.opts.scroll.tolerance&&this.unpin():this.pin()};window.addEventListener("scroll",e,{passive:!0}),e()}pin(){this.header.classList.add("mh-pinned"),this.header.classList.remove("mh-unpinned"),this.state=o.PINNED,this.trigger("pinned")}unpin(){this.header.classList.remove("mh-pinned"),this.header.classList.add("mh-unpinned"),this.state=o.UNPINNED,this.trigger("unpinned")}initHooks(){this.hooks={};for(let t in this.opts.hooks)this.bind(t,this.opts.hooks[t])}bind(t,s){this.hooks[t]=this.hooks[t]||[],this.hooks[t].push(s)}trigger(t,s){if(this.hooks[t])for(var e=0,n=this.hooks[t].length;e<n;e++)this.hooks[t][e].apply(this,s)}}o.version="2.0.0",o.options=i,o.PINNED="pinned",o.UNPINNED="unpinned",
|
||||||
|
/*!
|
||||||
|
* mhead.js
|
||||||
|
* mmenu.frebsite.nl/mhead-plugin
|
||||||
|
*
|
||||||
|
* Copyright (c) Fred Heusschen
|
||||||
|
* www.frebsite.nl
|
||||||
|
*
|
||||||
|
* License: CC-BY-4.0
|
||||||
|
* http://creativecommons.org/licenses/by/4.0/
|
||||||
|
*/
|
||||||
|
window.Mhead=o,(n=window.jQuery||window.Zepto||null)&&(n.fn.mhead=function(t){return this.each((s,e)=>{new o(e,t)})})}]);
|
||||||
66
gulpfile.js
Normal file
66
gulpfile.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
Tasks:
|
||||||
|
|
||||||
|
$ gulp : Runs "css" and "js" tasks
|
||||||
|
$ gulp watch : Starts a watch on "css" and "js" tasks
|
||||||
|
*/
|
||||||
|
|
||||||
|
const gulp = require('gulp');
|
||||||
|
const sass = require('gulp-sass');
|
||||||
|
const autoprefixer = require('gulp-autoprefixer');
|
||||||
|
const cleancss = require('gulp-clean-css');
|
||||||
|
const typescript = require('gulp-typescript');
|
||||||
|
const webpack = require('webpack-stream');
|
||||||
|
|
||||||
|
const inputDir = 'src';
|
||||||
|
const outputDir = 'dist';
|
||||||
|
|
||||||
|
exports.css = css = () => {
|
||||||
|
return gulp
|
||||||
|
.src(inputDir + '/*.scss')
|
||||||
|
.pipe(sass().on('error', sass.logError))
|
||||||
|
.pipe(autoprefixer(['> 5%', 'last 5 versions']))
|
||||||
|
.pipe(cleancss())
|
||||||
|
.pipe(gulp.dest(outputDir));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Transpile all TS files to JS.
|
||||||
|
const JStranspile = cb => {
|
||||||
|
return gulp
|
||||||
|
.src(inputDir + '/**/*.ts')
|
||||||
|
.pipe(
|
||||||
|
typescript({
|
||||||
|
target: 'es6',
|
||||||
|
module: 'es6'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.pipe(gulp.dest(outputDir));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Pack the files.
|
||||||
|
const JSpack = () => {
|
||||||
|
return gulp
|
||||||
|
.src(inputDir + '/mhead.js')
|
||||||
|
.pipe(
|
||||||
|
webpack({
|
||||||
|
// mode: 'development',
|
||||||
|
mode: 'production',
|
||||||
|
output: {
|
||||||
|
filename: 'mhead.js'
|
||||||
|
}
|
||||||
|
// optimization: {
|
||||||
|
// minimize: false
|
||||||
|
// }
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.pipe(gulp.dest(outputDir));
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.js = js = gulp.series(JStranspile, JSpack);
|
||||||
|
exports.default = gulp.parallel(js, css);
|
||||||
|
|
||||||
|
exports.watch = cb => {
|
||||||
|
gulp.watch('src/**/*.ts', js);
|
||||||
|
gulp.watch('src/**/*.scss', css);
|
||||||
|
cb();
|
||||||
|
};
|
||||||
26
index.html
Normal file
26
index.html
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<!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>mhead.js - sticky mobile navigation headers.</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/default.html" frameborder="0" width="320" height="480"></iframe>
|
||||||
|
</div>
|
||||||
|
<div id="page">
|
||||||
|
<h1>mhead</h1>
|
||||||
|
<p>Keep your header in view when appropriate, while saving space the rest of the time.</p>
|
||||||
|
<p>Check out <a href="http://mmenujs.com/mhead-plugin" target="_blank">the documentation</a> for more information.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
30
package.json
Normal file
30
package.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "mhead-js",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"main": "dist/mhead.js",
|
||||||
|
"module": "src/mhead.js",
|
||||||
|
"author": "Fred Heusschen <info@frebsite.nl>",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/FrDH/jQuery.mhead.git"
|
||||||
|
},
|
||||||
|
"description": "A small framework for creating a beautiful mobile navigation header to accompany the mmenu.js menu on your website and webapp.",
|
||||||
|
"keywords": [
|
||||||
|
"app",
|
||||||
|
"mobile",
|
||||||
|
"navigation",
|
||||||
|
"head",
|
||||||
|
"header",
|
||||||
|
"navbar"
|
||||||
|
],
|
||||||
|
"license": "CC-BY-NC-4.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"gulp": "^4.0.0",
|
||||||
|
"gulp-autoprefixer": "^6.1.0",
|
||||||
|
"gulp-clean-css": "^4.0.0",
|
||||||
|
"gulp-sass": "^4.0.2",
|
||||||
|
"gulp-typescript": "^5.0.1",
|
||||||
|
"typescript": "^3.4.4",
|
||||||
|
"webpack-stream": "^5.2.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/_version.ts
Normal file
1
src/_version.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default '2.0.0';
|
||||||
9
src/core/_options.ts
Normal file
9
src/core/_options.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const options: mhOptions = {
|
||||||
|
hooks: {},
|
||||||
|
scroll: {
|
||||||
|
pin: 0,
|
||||||
|
unpin: 0,
|
||||||
|
tolerance: 5
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export default options;
|
||||||
30
src/core/_typings.d.ts
vendored
Normal file
30
src/core/_typings.d.ts
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/** An object with array of functions values. */
|
||||||
|
interface mhFunctionArrayObject {
|
||||||
|
[key: string]: Function[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** An object with function values. */
|
||||||
|
interface mhFunctionObject {
|
||||||
|
[key: string]: Function;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Options for the header. */
|
||||||
|
interface mhOptions {
|
||||||
|
/** Set of hooks for the header. */
|
||||||
|
hooks?: mhFunctionObject;
|
||||||
|
|
||||||
|
/** Scroll options for the header. */
|
||||||
|
scroll?: mhOptionsScroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Scroll options for the header. */
|
||||||
|
interface mhOptionsScroll {
|
||||||
|
/** Minimum scroll position to pin the header when scrolling up. */
|
||||||
|
pin?: number | false;
|
||||||
|
|
||||||
|
/** Minimum scroll position to unpin the header when scrolling down. */
|
||||||
|
unpin?: number | false;
|
||||||
|
|
||||||
|
/** Tolerance for scrolling speed. */
|
||||||
|
tolerance?: number;
|
||||||
|
}
|
||||||
11
src/core/mhead.core.scss
Normal file
11
src/core/mhead.core.scss
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
.mh-sticky {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 10;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
|
||||||
|
&.mh-unpinned {
|
||||||
|
transform: translate3d(0, -100%, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
183
src/core/mhead.core.ts
Normal file
183
src/core/mhead.core.ts
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
import options from './_options';
|
||||||
|
import version from '../_version';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for a sticky navigational header.
|
||||||
|
*/
|
||||||
|
export default class Mhead {
|
||||||
|
/** Plugin version. */
|
||||||
|
static version: string = version;
|
||||||
|
|
||||||
|
/** Default options for headers. */
|
||||||
|
static options: mhOptions = options;
|
||||||
|
|
||||||
|
/** State for a "pinned" header. */
|
||||||
|
static PINNED: string = 'pinned';
|
||||||
|
|
||||||
|
/** State for a "unpinned" header. */
|
||||||
|
static UNPINNED: string = 'unpinned';
|
||||||
|
|
||||||
|
/** The HTML element for the header. */
|
||||||
|
header: HTMLElement;
|
||||||
|
|
||||||
|
/** Options for the header. */
|
||||||
|
opts: mhOptions;
|
||||||
|
|
||||||
|
/** Callback hooks used for the header. */
|
||||||
|
hooks: mhFunctionArrayObject;
|
||||||
|
|
||||||
|
/** State of the header (pinned or unpinned). */
|
||||||
|
state: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a sticky header.
|
||||||
|
* @param {HTMLElement|string} header (Selector for) the header node.
|
||||||
|
* @param {object} [options=Mhead.options] Options for the header.
|
||||||
|
*/
|
||||||
|
constructor(header: HTMLElement | string, options: mhOptions = {}) {
|
||||||
|
// Get header node from string or element.
|
||||||
|
this.header =
|
||||||
|
typeof header == 'string' ? document.querySelector(header) : header;
|
||||||
|
|
||||||
|
// Stop if there is no header element found.
|
||||||
|
if (!header) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend options from defaults.
|
||||||
|
this.opts = Object.assign(options, Mhead.options);
|
||||||
|
|
||||||
|
this.initHooks();
|
||||||
|
this.initScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate the scroll functionality.
|
||||||
|
*/
|
||||||
|
initScroll() {
|
||||||
|
if (!this.opts.scroll || this.opts.scroll.unpin === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.header.classList.add('mh-sticky');
|
||||||
|
|
||||||
|
/** Minimum scroll position to unpin / hide the header. */
|
||||||
|
var _min = this.header.offsetHeight * 2;
|
||||||
|
this.opts.scroll.unpin = Math.max(_min, this.opts.scroll.unpin || 0);
|
||||||
|
this.opts.scroll.pin = Math.max(_min, this.opts.scroll.pin || 0);
|
||||||
|
|
||||||
|
this.state = null;
|
||||||
|
|
||||||
|
/** Previous scroll position. */
|
||||||
|
var lastYpos = 0;
|
||||||
|
|
||||||
|
const onscroll = (evnt = {}) => {
|
||||||
|
/** Current scroll position. */
|
||||||
|
var pos =
|
||||||
|
document.documentElement.scrollTop || document.body.scrollTop;
|
||||||
|
|
||||||
|
/** Difference between current scroll position and previous scroll position. */
|
||||||
|
var dif = lastYpos - pos;
|
||||||
|
|
||||||
|
/** Direction of the scroll. */
|
||||||
|
var dir = dif < 0 ? 'down' : 'up';
|
||||||
|
|
||||||
|
dif = Math.abs(dif);
|
||||||
|
lastYpos = pos;
|
||||||
|
|
||||||
|
// If not pinned / scrolled out the viewport.
|
||||||
|
if (this.state == Mhead.UNPINNED) {
|
||||||
|
// If scrolling up
|
||||||
|
if (dir == 'up') {
|
||||||
|
// If scrolling fast enough or past minimum
|
||||||
|
if (
|
||||||
|
pos < this.opts.scroll.pin ||
|
||||||
|
dif > this.opts.scroll.tolerance
|
||||||
|
) {
|
||||||
|
this.pin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If pinned / not scrolled out the viewport.
|
||||||
|
else if (this.state == Mhead.PINNED) {
|
||||||
|
// If scrolling down.
|
||||||
|
if (dir == 'down') {
|
||||||
|
// If scrolling fast enough and past minimum.
|
||||||
|
if (
|
||||||
|
pos > this.opts.scroll.unpin &&
|
||||||
|
dif > this.opts.scroll.tolerance
|
||||||
|
) {
|
||||||
|
this.unpin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.pin();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('scroll', onscroll, {
|
||||||
|
passive: true
|
||||||
|
});
|
||||||
|
|
||||||
|
onscroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pin the header to the top of the viewport.
|
||||||
|
*/
|
||||||
|
pin() {
|
||||||
|
this.header.classList.add('mh-pinned');
|
||||||
|
this.header.classList.remove('mh-unpinned');
|
||||||
|
this.state = Mhead.PINNED;
|
||||||
|
|
||||||
|
this.trigger('pinned');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release the header from the top of the viewport.
|
||||||
|
*/
|
||||||
|
unpin() {
|
||||||
|
this.header.classList.remove('mh-pinned');
|
||||||
|
this.header.classList.add('mh-unpinned');
|
||||||
|
this.state = Mhead.UNPINNED;
|
||||||
|
|
||||||
|
this.trigger('unpinned');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind the hooks specified in the options (publisher).
|
||||||
|
*/
|
||||||
|
initHooks() {
|
||||||
|
this.hooks = {};
|
||||||
|
for (let hook in this.opts.hooks) {
|
||||||
|
this.bind(hook, this.opts.hooks[hook]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind functions to a hook (subscriber).
|
||||||
|
* @param {string} hook The hook.
|
||||||
|
* @param {function} func The function.
|
||||||
|
*/
|
||||||
|
bind(hook: string, func: Function) {
|
||||||
|
// Create an array for the hook if it does not yet excist.
|
||||||
|
this.hooks[hook] = this.hooks[hook] || [];
|
||||||
|
|
||||||
|
// Push the function to the array.
|
||||||
|
this.hooks[hook].push(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoke the functions bound to a hook (publisher).
|
||||||
|
* @param {string} hook The hook.
|
||||||
|
* @param {array} [args] Arguments for the function.
|
||||||
|
*/
|
||||||
|
trigger(hook: string, args?: any[]) {
|
||||||
|
if (this.hooks[hook]) {
|
||||||
|
for (var h = 0, l = this.hooks[hook].length; h < l; h++) {
|
||||||
|
this.hooks[hook][h].apply(this, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/mhead.js
Normal file
26
src/mhead.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*!
|
||||||
|
* mhead.js
|
||||||
|
* mmenu.frebsite.nl/mhead
|
||||||
|
*
|
||||||
|
* Copyright (c) Fred Heusschen
|
||||||
|
* www.frebsite.nl
|
||||||
|
*
|
||||||
|
* License: CC-BY-4.0
|
||||||
|
* http://creativecommons.org/licenses/by/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Mhead from '../dist/core/mhead.core';
|
||||||
|
|
||||||
|
// Global namespace
|
||||||
|
window.Mhead = Mhead;
|
||||||
|
|
||||||
|
// jQuery plugin
|
||||||
|
(function($) {
|
||||||
|
if ($) {
|
||||||
|
$.fn.mhead = function(options) {
|
||||||
|
return this.each((e, element) => {
|
||||||
|
new Mhead(element, options);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})(window.jQuery || window.Zepto || null);
|
||||||
12
src/mhead.scss
Normal file
12
src/mhead.scss
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/*!
|
||||||
|
* mhead.js
|
||||||
|
* mmenu.frebsite.nl/mhead
|
||||||
|
*
|
||||||
|
* Copyright (c) Fred Heusschen
|
||||||
|
* www.frebsite.nl
|
||||||
|
*
|
||||||
|
* License: CC-BY-4.0
|
||||||
|
* http://creativecommons.org/licenses/by/4.0/
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import 'core/mhead.core';
|
||||||
8
tsconfig.json
Normal file
8
tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es6"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user