@bookbox/view-html
Version:
Bookbox view for html
167 lines (166 loc) • 6.14 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.replaceHistory = replaceHistory;
exports.pushHistory = pushHistory;
exports.getCurrentPage = getCurrentPage;
exports.getNavigation = getNavigation;
const contentsSelector = '.book-box_layout-settings-contents';
const currentHeaderClass = 'book-box_layout-settings-contents-item-current';
const currentHeaderSelector = `.${currentHeaderClass}`;
const getHeadersNavigationCallback = (current) => (entries) => {
const headersElems = document.querySelectorAll(contentsSelector);
headersElems.forEach(headersElem => {
const listHeaders = Array.from(headersElem.querySelectorAll('[data-name="header"]'));
const listParentHeaders = listHeaders.map(e => e.parentElement);
const listHeadersMap = new Map(listHeaders.map((elem, i) => [elem.dataset.key, i]));
const { targetIndex } = getCurrentItemIndex({
listMap: listHeadersMap,
current,
entries,
});
const targetElem = listParentHeaders[targetIndex];
// возможно заменить на прямой индекс
const currentElems = headersElem.querySelectorAll(currentHeaderSelector);
currentElems.forEach(currentElem => {
currentElem.classList.remove(currentHeaderClass);
});
targetElem.classList.add(currentHeaderClass);
targetElem.parentElement.scrollTop = targetElem.offsetTop - 50;
current.index = targetIndex;
});
};
const getPagesNavigationCallback = (listPagesMap, current) => (entries) => {
const { targetIndex } = getCurrentItemIndex({
listMap: listPagesMap,
current,
entries,
});
if (targetIndex === current.index) {
return;
}
replaceHistory({ hash: `page-${targetIndex}` });
current.index = targetIndex;
};
function getUrl(url, options) {
const targetUrl = typeof url === 'string' ? new URL(url) : url;
Object.assign(targetUrl, options !== null && options !== void 0 ? options : {});
return targetUrl;
}
function replaceHistory(options) {
window.history.replaceState(null, '', getUrl(window.location.toString(), options));
}
function pushHistory(options) {
window.history.pushState(null, '', getUrl(window.location.toString(), options));
}
function getCurrentItemIndex({ listMap, current, entries, }) {
var _a, _b;
let overTopMaximum = -Infinity;
let overBottomMinimum = Infinity;
for (const entry of entries) {
const { isIntersecting, target, boundingClientRect, intersectionRect } = entry;
const border = getBorder({ boundingClientRect, intersectionRect });
const key = (_a = target.dataset.key) !== null && _a !== void 0 ? _a : '';
const i = (_b = listMap.get(key)) !== null && _b !== void 0 ? _b : 0;
if (isIntersecting) {
current.visible.add(i);
}
else {
current.visible.delete(i);
if (border === 'top') {
// исчезание сверху
if (i > overTopMaximum) {
overTopMaximum = i;
}
}
else if (border === 'bottom') {
// исчезание снизу
if (i < overBottomMinimum) {
overBottomMinimum = i;
}
}
}
}
let targetIndex = current.index;
if (current.visible.size > 0) {
targetIndex = Math.min(...current.visible);
}
else {
const vars = [];
if (targetIndex !== null) {
vars.push(targetIndex);
}
if (overBottomMinimum !== Infinity) {
vars.push(overBottomMinimum - 1);
}
if (overTopMaximum !== -Infinity) {
vars.push(overTopMaximum);
}
// TODO: оптимизировать
targetIndex = Math.max(0, Math.min(...vars));
}
return { targetIndex };
}
function getBorder({ boundingClientRect, intersectionRect, }) {
const { top, bottom } = boundingClientRect;
const { top: itop, bottom: ibottom } = intersectionRect;
if ((top ^ 0) === (itop ^ 0)) {
return 'bottom';
}
return 'top';
}
const PAGE_PREFIX = 'page-'.length;
function getCurrentPage() {
const pageHash = window.location.hash;
const page = +pageHash.slice(PAGE_PREFIX + 1);
return Number.isNaN(page) ? null : page;
}
// headers navigation
function getNavigation(bookHtmlContainer = document.body) {
const contentHeaders = bookHtmlContainer.querySelectorAll('.book-box_content [data-name="header"]');
const headersCurrent = {
index: null,
visible: new Set(),
};
const headersNavigationCallback = getHeadersNavigationCallback(headersCurrent);
const headersObserver = new IntersectionObserver(headersNavigationCallback, {
threshold: 0.5,
});
const observeHeaders = () => {
for (const header of Array.from(contentHeaders)) {
headersObserver.observe(header);
}
};
// page navigation
const contentPages = Array.from(bookHtmlContainer.querySelectorAll('.book-box_content [data-name=".page"]'));
const listPageMap = new Map(contentPages
.map(elem => elem.dataset.key)
.filter((key) => Boolean(key))
.map(key => [key, +key.slice(PAGE_PREFIX)]));
const pagesCurrent = {
index: null,
visible: new Set(),
};
const pagesNavigationCallback = getPagesNavigationCallback(listPageMap, pagesCurrent);
const pageObserver = new IntersectionObserver(pagesNavigationCallback, {
threshold: 0.5,
});
const observePages = () => {
for (const page of contentPages) {
pageObserver.observe(page);
}
};
const observeNavigation = () => {
observeHeaders();
observePages();
};
const disconnectNavigation = () => {
headersObserver.disconnect();
pageObserver.disconnect();
};
return {
headersObserver,
pageObserver,
observeNavigation,
disconnectNavigation,
};
}