dom-minimap
Version:
simply lovely dom minimap
220 lines (172 loc) • 6.95 kB
JavaScript
;
function _taggedTemplateLiteral(strings, raw) {
if (!raw) {
raw = strings.slice(0);
}
return Object.freeze(Object.defineProperties(strings, {
raw: {
value: Object.freeze(raw)
}
}));
}
function _templateObject5() {
var data = _taggedTemplateLiteral(["<div style='position:relative;", "'>", "</div>"]);
_templateObject5 = function _templateObject5() {
return data;
};
return data;
}
function _templateObject4() {
var data = _taggedTemplateLiteral(["<div class=\"dom-minimap-scroll dom-minimap-scroll-bottom\" style=\"top:", "\"></div>"]);
_templateObject4 = function _templateObject4() {
return data;
};
return data;
}
function _templateObject3() {
var data = _taggedTemplateLiteral(["<div class=\"dom-minimap-scroll dom-minimap-scroll-top\" style=\"bottom:", "\"></div>"]);
_templateObject3 = function _templateObject3() {
return data;
};
return data;
}
function _templateObject2() {
var data = _taggedTemplateLiteral(["\n <div class=\"dom-minimap-section unselectable\"\n title=", " onclick=", "\n style=\"top:", ";bottom:", ";", "\">\n ", "\n </div>\n "]);
_templateObject2 = function _templateObject2() {
return data;
};
return data;
}
function _templateObject() {
var data = _taggedTemplateLiteral(["<div style=\"margin-top:20px;text-align:center\">loading</div>"]);
_templateObject = function _templateObject() {
return data;
};
return data;
}
var yo = require('yo-yo');
var onload = require('on-load');
var nanoraf = require('nanoraf');
var insertCss = require('insert-css');
var debounce = require('lodash.debounce');
insertCss("\n .dom-minimap-section {\n position: absolute;\n background-color: lightgrey;\n overflow: hidden;\n color: grey;\n font-size: 11px;\n padding-left: 2px;\n border-radius: 2px;\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -khtml-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n cursor: pointer;\n left: 5px; right: 5px;\n }\n\n .dom-minimap-section:hover {\n background-color: #e6e6e6;\n }\n\n .dom-minimap-scroll {\n pointer-events: none;\n position: absolute;\n background-color: rgba(0,0,0,0.15);\n top: 0; left: 0; right: 0; bottom: 0;\n }\n");
module.exports = minimap;
function minimap(opts) {
opts = opts || {};
opts.sections = opts.sections || 'minimap-section';
if (typeof opts.sections !== 'function') {
var sectionName = opts.sections;
opts.sections = function (container) {
return Array.prototype.slice.call(container.getElementsByClassName(sectionName));
};
}
opts.title = opts.title || 'data-section-title';
if (typeof opts.title !== 'function') {
var titleName = opts.title;
opts.title = function (section) {
return section.getAttribute(titleName);
};
}
opts.tooltip = opts.tooltip || 'data-section-tooltip';
if (typeof opts.hover !== 'function') {
var tooltipName = opts.tooltip;
opts.tooltip = function (section) {
return section.getAttribute(tooltipName);
};
}
opts.content = opts.content || 'minimap-content';
opts.mapStyle = typeof opts.mapStyle !== 'undefined' ? opts.mapStyle : 'height: 100%;';
opts.sectionStyle = opts.sectionStyle || '';
opts.clickOffset = opts.clickOffset || 0;
var lastContainerHeight;
var container;
var element = document.createElement('div');
element.style.flex = '1';
var render = nanoraf(renderMap);
var state = {
opts: opts
};
onload(element, function load() {
container = typeof opts.content === 'string' ? document.getElementById(opts.content) : opts.content;
lastContainerHeight = container.scrollHeight; // update on scroll event
container.addEventListener('scroll', scrollUpdate); // update on window resize event
window.addEventListener('resize', debounce(update), 100); // update on element loaded
update();
}, null, minimap);
element.addEventListener('wheel', function (event) {
if (container) container.scrollTop = container.scrollTop + event.deltaY;
});
function scrollUpdate() {
state.scroll = getScroll(container);
var top = element.querySelector('.dom-minimap-scroll-top');
var bottom = element.querySelector('.dom-minimap-scroll-bottom');
if (!top || !bottom) return;
top.style.bottom = state.scroll.topFromBottom;
bottom.style.top = state.scroll.bottomFromTop;
}
function update() {
var sections = getSections(container, element, opts);
if (!sections) return;
var newState = Object.assign({}, state, {
sections: sections,
scroll: getScroll(container)
});
render(newState, state);
state = newState;
}
return function () {
if (container) {
setTimeout(function () {
if (lastContainerHeight !== container.scrollHeight) {
update();
}
}, 1);
}
return element;
};
function scrollTo() {
var top = this.style.top.slice(0, -1);
if (top) container.scrollTop = Math.round(container.scrollHeight * top / 100) + opts.clickOffset;
}
function renderMap(state) {
var content = yo(_templateObject());
if (state.sections) {
content = state.sections.map(function (section) {
return yo(_templateObject2(), section.tooltip, scrollTo, section.top, section.bottom, typeof opts.sectionStyle === 'function' ? opts.sectionStyle(section) : opts.sectionStyle, section.title);
}).concat([yo(_templateObject3(), state.scroll.topFromBottom), yo(_templateObject4(), state.scroll.bottomFromTop)]);
}
yo.update(element, yo(_templateObject5(), opts.mapStyle, content));
}
}
function getScroll(container) {
var top = container.scrollTop;
var cHeight = container.clientHeight;
var height = container.scrollHeight;
return {
topFromBottom: (height - top) / height * 100 + '%',
bottomFromTop: (1 - (height - top - cHeight) / height) * 100 + '%'
};
}
function getSections(content, map, opts) {
if (!map.parentElement) return false;
var cHeight = content.scrollHeight;
var mHeight = map.parentElement.clientHeight;
var cBounds = content.getBoundingClientRect();
var scrollTop = content.scrollTop;
return opts.sections(content).map(function (section) {
var bounds = section.getBoundingClientRect();
var top = (bounds.top - cBounds.top + scrollTop) / cHeight;
var bottom = (bounds.bottom - cBounds.top + scrollTop) / cHeight;
return {
top: top * 100 + '%',
bottom: applyPadding((1 - bottom) * 100 + '%', opts.paddingBottom),
title: opts.title(section, mHeight * bottom - mHeight * top),
tooltip: opts.tooltip(section),
element: section
};
});
}
function applyPadding(value, padding) {
if (!padding) return value;
return "calc(".concat(value, " + ").concat(padding, ")");
}