Initial commit

This commit is contained in:
Rick Dullaart 2022-11-18 21:42:43 +01:00
commit f3781775ff
27 changed files with 944 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 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
View 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
View 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
View 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
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%);
}

75
demo/default.html Normal file
View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

40
dist/_helpers.js vendored Normal file
View 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
View File

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

9
dist/core/_options.js vendored Normal file
View 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
View 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
View File

@ -0,0 +1,8 @@
export default {
hooks: {},
scroll: {
hide: 0,
show: 0,
tolerance: 5
}
};

10
dist/mhead.css vendored Normal file
View 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
View 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
View 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
View 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
View 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
View File

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

9
src/core/_options.ts Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,8 @@
{
"include": [
"src/**/*"
],
"compilerOptions": {
"target": "es6"
}
}