-
Notifications
You must be signed in to change notification settings - Fork 23
/
IndexNavigation.js
128 lines (110 loc) · 4.1 KB
/
IndexNavigation.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import { Tweak } from '@squarespace/core';
import { getIndexSectionRect } from '../utils/getIndexSectionRect';
import { addScrollListener, removeScrollListener } from '../utils/rafScroll';
import resizeEnd from '../utils/resizeEnd';
import { addIndexGalleryChangeListener, removeIndexGalleryChangeListener } from './IndexGallery';
function IndexNavigation(element) {
if (element.classList.contains('Index--empty')) {
return;
}
let sectionRects;
let viewportHeight = window.innerHeight;
const sections = Array.from(element.querySelectorAll('.Index-page, .Index-gallery'));
const sectionMap = sections.reduce((acc, item) => {
acc[item.getAttribute('id')] = item;
return acc;
}, {});
const indexNav = element.querySelector('.Index-nav');
const indexNavItems = Array.from(indexNav.querySelectorAll('.Index-nav-item'));
const indexNavItemMap = indexNavItems.reduce((acc, item) => {
acc[item.getAttribute('href')] = item;
return acc;
}, {});
let currentIndexNavItem = indexNavItems.filter((item) => {
return item.classList.contains('active');
})[0];
let currentId = currentIndexNavItem.getAttribute('href').substring(1);
let currentIdForColor = null;
let isWithinBorder;
/**
* Get relevant section rects so we can know where the positions are and where
* to update the state of the index nav.
* @return {Array} Array of section rect positions
*/
const getSectionRects = () => {
return sections.reduce((acc, section) => {
const { top, bottom, left, right } = getIndexSectionRect(section);
acc[section.getAttribute('id')] = { top, bottom, left, right };
return acc;
}, {});
};
/**
* Convenience method for determining if a section is an overlay section.
* @param {HTMLElement} section
* @return {Boolean}
*/
const isOverlaySection = (section) => {
const isGallery = section.classList.contains('Index-gallery');
const isOverlayPage = section.classList.contains('Index-page--has-image');
return isGallery || isOverlayPage;
};
/**
* Scroll handler. Updates the color of the index nav depending on whether
* it's over an overlay section, and updates the index nav element that's
* currently active.
* @param {Number} scrollTop
*/
const handleScroll = (scrollTop) => {
const scrollMid = scrollTop + viewportHeight / 2;
Object.keys(sectionRects).forEach((id) => {
const { top, bottom } = sectionRects[id];
// Update active index nav element
if (currentId !== id && scrollTop >= top && scrollTop < bottom) {
const hashId = '#' + id;
currentIndexNavItem.classList.remove('active');
const indexNavItem = indexNavItemMap[hashId];
indexNavItem.classList.add('active');
currentId = id;
currentIndexNavItem = indexNavItem;
}
// Check if is within border before updating color
if (isWithinBorder) {
indexNav.classList.remove('overlay');
return;
}
// Update color
if (currentIdForColor !== id && scrollMid >= top && scrollMid < bottom) {
const currentSection = sectionMap[id];
indexNav.classList.toggle('overlay', isOverlaySection(currentSection));
currentIdForColor = id;
}
});
};
const sync = () => {
sectionRects = getSectionRects();
// Can get this from any of the section rects, as they are all the same
const borderWidth = sectionRects[Object.keys(sectionRects)[0]].left;
const position = Tweak.getValue('tweak-index-nav-position').toLowerCase();
const offset = parseFloat(window.getComputedStyle(indexNav)[position]);
isWithinBorder = borderWidth > offset;
handleScroll(window.pageYOffset);
};
const bindListeners = () => {
resizeEnd(() => {
viewportHeight = window.innerHeight;
sectionRects = getSectionRects();
});
addScrollListener('scroll', handleScroll);
addIndexGalleryChangeListener(sync);
};
const destroy = () => {
removeScrollListener('scroll', handleScroll);
removeIndexGalleryChangeListener(sync);
};
sync();
bindListeners();
return {
destroy: destroy
};
}
export default IndexNavigation;