<tiny-one-page>
|
|
<div class="tiny-one-page">
|
|
<div style="{ getStyles() }" class={ getModalClasses() }>
|
|
<div class="tiny-one-page__inner"></div>
|
|
</div>
|
|
<button class="tiny-one-page__button button button--tiny-one-page" onclick={ handleToggle }>
|
|
<i class={ getIconClasses() } />
|
|
</button>
|
|
</div>
|
|
|
|
<script>
|
|
|
|
import SmoothScroll from 'smooth-scroll';
|
|
import { throttle, debounce } from 'throttle-debounce';
|
|
|
|
/**
|
|
* tiny-one-page.riot
|
|
*
|
|
* Tiny Component to one page navigation
|
|
*
|
|
* <tiny-one-page>
|
|
* <ul>
|
|
* <li><a href="#home">Home</a></li>
|
|
* <li><a href="#blog">Blog</a></li>
|
|
* <li><a href="#contact">Contact</a></li>
|
|
* </ul>
|
|
* </tiny-one-page>
|
|
*
|
|
* @author Björn Hase
|
|
* @license http://opensource.org/licenses/MIT The MIT License
|
|
* @link https://gitlab.tentakelfabrik.de/tentakelfabrik/tiny-components/tiny-one-page Gitlab Repository
|
|
*
|
|
*/
|
|
|
|
export default {
|
|
|
|
/**
|
|
*
|
|
*
|
|
*/
|
|
state:
|
|
{
|
|
isOpen: false,
|
|
animate: null,
|
|
namespace: 'tiny-one-page',
|
|
options: {
|
|
offset: 0
|
|
}
|
|
},
|
|
|
|
/**
|
|
* getting innerHTML and remove it
|
|
* setting up a animate object
|
|
*
|
|
*
|
|
*/
|
|
onBeforeMount()
|
|
{
|
|
// getting helper and add component
|
|
this.state.animate = this.props.animate;
|
|
this.state.animate.setComponent(this);
|
|
|
|
if (this.props.options) {
|
|
this.state.options = Object.assign(this.state.options, this.props.options);
|
|
}
|
|
|
|
this.content = this.root.innerHTML;
|
|
this.root.innerHTML = '';
|
|
},
|
|
|
|
/**
|
|
* getting innerHTML and remove
|
|
*
|
|
*
|
|
*/
|
|
onBeforeUnmount()
|
|
{
|
|
document.addEventListener('click', this.handleClick.bind(this));
|
|
this.$('.' + this.state.namespace + '__inner a').removeEventListener('click', this.handleClose.bind(this));
|
|
},
|
|
|
|
/**
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
onMounted()
|
|
{
|
|
// adding content to wrapper
|
|
const wrapper = this.$('.' + this.state.namespace + '__inner');
|
|
wrapper.innerHTML = this.content;
|
|
|
|
// adding scroll smooth, get all elements with hash
|
|
this.scroll = new SmoothScroll('.' + this.state.namespace + '__inner a[href*="#"]', this.state.options);
|
|
|
|
window.addEventListener('scroll', throttle(300, this.handleScroll.bind(this)), false);
|
|
|
|
// if hash is set
|
|
if (window.location.hash) {
|
|
this.$("." + this.state.namespace + "__inner a[href$='" + window.location.hash + "']").click();
|
|
} else {
|
|
this.addClass(this.$$('.' + this.state.namespace + '__inner a')[0]);
|
|
}
|
|
|
|
// adding for outer click and all a-tags click event to close
|
|
document.addEventListener('click', this.handleClick.bind(this))
|
|
this.$$('.tiny-one-page__inner a').forEach((a) => {
|
|
a.addEventListener('click', this.handleClose.bind(this));
|
|
});
|
|
},
|
|
|
|
/**
|
|
* add css class to parent element
|
|
*
|
|
* @param {Object} element
|
|
*/
|
|
addClass(element)
|
|
{
|
|
element
|
|
.parentElement
|
|
.classList
|
|
.add('current');
|
|
},
|
|
|
|
/**
|
|
* remove css class from parent element
|
|
*
|
|
* @param {Object} element
|
|
*/
|
|
removeClass(element)
|
|
{
|
|
element
|
|
.parentElement
|
|
.classList
|
|
.remove('current');
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @return {string}
|
|
*/
|
|
getStyles()
|
|
{
|
|
return this.state.animate.getStyles();
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @return {string}
|
|
*/
|
|
getIconClasses()
|
|
{
|
|
return this.state.isOpen ?
|
|
'icon icon-' + this.state.namespace + '-close' :
|
|
'icon icon-' + this.state.namespace + '-open';
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @return {string}
|
|
*/
|
|
getModalClasses()
|
|
{
|
|
return this.state.isOpen ?
|
|
this.state.namespace + '__modal ' + this.state.namespace + '__modal--open' :
|
|
this.state.namespace + '__modal ' + this.state.namespace + '__modal--closed';
|
|
},
|
|
|
|
/**
|
|
*
|
|
*
|
|
* @param {Object} event
|
|
*
|
|
*/
|
|
handleClick(event)
|
|
{
|
|
if (!this.root.contains(event.target)) {
|
|
this.handleClose();
|
|
}
|
|
},
|
|
|
|
/**
|
|
*
|
|
*
|
|
*/
|
|
handleOpen()
|
|
{
|
|
this.state.animate.prepareOpen();
|
|
this.state.isOpen = true;
|
|
|
|
this.update();
|
|
},
|
|
|
|
/**
|
|
*
|
|
*
|
|
*/
|
|
handleClose()
|
|
{
|
|
this.state.animate.prepareClose();
|
|
this.state.isOpen = false;
|
|
|
|
this.update();
|
|
},
|
|
|
|
/**
|
|
* toggle modal
|
|
*
|
|
*
|
|
*/
|
|
handleToggle()
|
|
{
|
|
if (this.state.isOpen === true) {
|
|
this.handleClose();
|
|
} else {
|
|
this.handleOpen();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* handle scrolling and check visibility of elements from navigation
|
|
*
|
|
* @param {Object} event
|
|
*/
|
|
handleScroll(event)
|
|
{
|
|
// get elements
|
|
const elements = this.$$('.' + this.state.namespace + '__inner a');
|
|
|
|
// @TODO check offset
|
|
const offset = this.state.options.offset - 25;
|
|
|
|
// if found
|
|
let hasFound = false;
|
|
|
|
// get result
|
|
let result = undefined;
|
|
|
|
elements.forEach(function(element, index) {
|
|
|
|
// getting target
|
|
let target = document.querySelector(element.hash);
|
|
|
|
let next = undefined;
|
|
let offsetTop = target.getBoundingClientRect().top + window.pageYOffset;
|
|
|
|
// check for next element
|
|
if (elements[index + 1]) {
|
|
next = document.querySelector(elements[index + 1].hash);
|
|
}
|
|
|
|
// check if element is visible
|
|
if (!result &&
|
|
((offsetTop - (window.innerHeight / 2)) <= (window.pageYOffset + offset)) &&
|
|
((offsetTop + target.offsetHeight) - (window.innerHeight / 2) > window.pageYOffset)) {
|
|
result = target;
|
|
}
|
|
|
|
// remove class from each element
|
|
this.removeClass(element);
|
|
|
|
}.bind(this));
|
|
|
|
if (result) {
|
|
this.addClass(this.$("." + this.state.namespace + "__inner a[href$='" + result.id + "']"));
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
</tiny-one-page>
|