@brandocms/jupiter 4.0.0-beta.1 → 5.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +191 -2
- package/package.json +20 -18
- package/src/index.js +10 -10
- package/src/modules/Application/index.js +203 -157
- package/src/modules/Cookies/index.js +34 -55
- package/src/modules/CoverOverlay/index.js +20 -13
- package/src/modules/Dataloader/index.js +71 -24
- package/src/modules/Dataloader/url-sync.js +238 -0
- package/src/modules/Dom/index.js +18 -0
- package/src/modules/DoubleHeader/index.js +571 -0
- package/src/modules/Dropdown/index.js +101 -75
- package/src/modules/EqualHeightElements/index.js +5 -7
- package/src/modules/EqualHeightImages/index.js +7 -2
- package/src/modules/FixedHeader/index.js +60 -30
- package/src/modules/FooterReveal/index.js +3 -3
- package/src/modules/HeroSlider/index.js +207 -91
- package/src/modules/HeroVideo/index.js +15 -27
- package/src/modules/Lazyload/index.js +101 -80
- package/src/modules/Lightbox/index.js +17 -55
- package/src/modules/Links/index.js +54 -49
- package/src/modules/Looper/index.js +1737 -0
- package/src/modules/Marquee/index.js +106 -37
- package/src/modules/MobileMenu/index.js +70 -124
- package/src/modules/Moonwalk/index.js +349 -150
- package/src/modules/Popover/index.js +186 -28
- package/src/modules/Popup/index.js +27 -34
- package/src/modules/StackedBoxes/index.js +3 -3
- package/src/modules/StickyHeader/index.js +364 -155
- package/src/modules/Toggler/index.js +184 -27
- package/src/utils/motion-helpers.js +330 -0
- package/types/index.d.ts +1 -30
- package/types/modules/Application/index.d.ts +6 -6
- package/types/modules/Breakpoints/index.d.ts +2 -0
- package/types/modules/Dataloader/index.d.ts +5 -2
- package/types/modules/Dataloader/url-sync.d.ts +36 -0
- package/types/modules/Dom/index.d.ts +7 -0
- package/types/modules/DoubleHeader/index.d.ts +63 -0
- package/types/modules/Dropdown/index.d.ts +7 -30
- package/types/modules/EqualHeightImages/index.d.ts +1 -1
- package/types/modules/FixedHeader/index.d.ts +1 -1
- package/types/modules/Lazyload/index.d.ts +9 -9
- package/types/modules/Lightbox/index.d.ts +0 -5
- package/types/modules/Looper/index.d.ts +127 -0
- package/types/modules/Moonwalk/index.d.ts +6 -15
- package/types/modules/Parallax/index.d.ts +10 -32
- package/types/modules/Popover/index.d.ts +12 -0
- package/types/modules/Popup/index.d.ts +6 -19
- package/types/modules/ScrollSpy/index.d.ts +1 -1
- package/types/modules/StickyHeader/index.d.ts +171 -14
- package/types/modules/Toggler/index.d.ts +24 -2
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A header that
|
|
2
|
+
* A header that uses position: sticky. Hides when scrolling down and is revealed on scrolling up.
|
|
3
|
+
* Unlike FixedHeader, the sticky header stays in document flow - when hidden via transform,
|
|
4
|
+
* its space is still reserved.
|
|
3
5
|
*
|
|
4
6
|
* You can pass different configs for different sections:
|
|
5
7
|
*
|
|
@@ -23,97 +25,188 @@
|
|
|
23
25
|
*
|
|
24
26
|
*/
|
|
25
27
|
|
|
26
|
-
import {
|
|
28
|
+
import { animate, stagger } from 'motion'
|
|
27
29
|
import _defaultsDeep from 'lodash.defaultsdeep'
|
|
28
30
|
import * as Events from '../../events'
|
|
31
|
+
import Dom from '../Dom'
|
|
32
|
+
import { set } from '../../utils/motion-helpers'
|
|
29
33
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {Object} StickyHeaderEvents
|
|
36
|
+
* @property {Function} [onPin] - Called when header is pinned
|
|
37
|
+
* @property {Function} [onUnpin] - Called when header is unpinned
|
|
38
|
+
* @property {Function} [onAltBg] - Called when alternate background is applied
|
|
39
|
+
* @property {Function} [onNotAltBg] - Called when regular background is applied
|
|
40
|
+
* @property {Function} [onSmall] - Called when header becomes small
|
|
41
|
+
* @property {Function} [onNotSmall] - Called when header becomes normal size
|
|
42
|
+
* @property {Function} [onTop] - Called when page is at the top
|
|
43
|
+
* @property {Function} [onNotTop] - Called when page is not at the top
|
|
44
|
+
* @property {Function} [onBottom] - Called when page is at the bottom
|
|
45
|
+
* @property {Function} [onNotBottom] - Called when page is not at the bottom
|
|
46
|
+
* @property {Function} [onMobileMenuOpen] - Called when mobile menu opens
|
|
47
|
+
* @property {Function} [onMobileMenuClose] - Called when mobile menu closes
|
|
48
|
+
* @property {Function} [onIntersect] - Called when header intersects with an element
|
|
49
|
+
* @property {Function} [onOutline] - Called when user tabs (outline mode)
|
|
50
|
+
*/
|
|
38
51
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
52
|
+
/**
|
|
53
|
+
* @typedef {Object} StickyHeaderSectionOptions
|
|
54
|
+
* @property {boolean} [unPinOnResize=true] - Whether to unpin header on window resize
|
|
55
|
+
* @property {Window|HTMLElement} [canvas=window] - Scrolling element
|
|
56
|
+
* @property {string|null} [intersects=null] - Selector for elements to check intersection with
|
|
57
|
+
* @property {Function} [beforeEnter] - Called before header enters
|
|
58
|
+
* @property {Function} [enter] - Called when header enters
|
|
59
|
+
* @property {number} [enterDelay=0] - Delay before enter animation
|
|
60
|
+
* @property {number} [tolerance=3] - Scroll tolerance before triggering hide/show
|
|
61
|
+
* @property {number|string|Function} [offset=0] - Offset from top before triggering hide
|
|
62
|
+
* @property {number|string|Function} [offsetSmall=50] - Offset from top before shrinking header
|
|
63
|
+
* @property {number|string|Function} [offsetBg=200] - Offset from top before changing background color
|
|
64
|
+
* @property {string|null} [regBgColor=null] - Regular background color
|
|
65
|
+
* @property {string|null} [altBgColor=null] - Alternate background color
|
|
66
|
+
*/
|
|
45
67
|
|
|
68
|
+
/**
|
|
69
|
+
* @typedef {Object} StickyHeaderOptions
|
|
70
|
+
* @property {string|HTMLElement} [el='header[data-nav]'] - Header element or selector
|
|
71
|
+
* @property {string} [on=Events.APPLICATION_REVEALED] - Event to initialize on
|
|
72
|
+
* @property {boolean} [unpinOnForcedScrollStart=true] - Whether to unpin on forced scroll start
|
|
73
|
+
* @property {boolean} [pinOnForcedScrollEnd=true] - Whether to pin on forced scroll end
|
|
74
|
+
* @property {boolean} [ignoreForcedScroll=false] - Whether to ignore forced scroll events
|
|
75
|
+
* @property {boolean} [rafScroll=true] - Whether to use requestAnimationFrame for scrolling
|
|
76
|
+
* @property {StickyHeaderSectionOptions} [default] - Default options for all sections
|
|
77
|
+
* @property {Object.<string, StickyHeaderSectionOptions>} [sections] - Section-specific options
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/** @type {StickyHeaderEvents} */
|
|
81
|
+
const DEFAULT_EVENTS = {
|
|
46
82
|
onPin: (h) => {
|
|
47
|
-
|
|
83
|
+
animate(h.el, {
|
|
84
|
+
yPercent: '0'
|
|
85
|
+
}, {
|
|
48
86
|
duration: 0.35,
|
|
49
|
-
|
|
50
|
-
ease: 'sine.out',
|
|
51
|
-
autoRound: true,
|
|
87
|
+
ease: 'easeOut'
|
|
52
88
|
})
|
|
53
89
|
},
|
|
54
90
|
|
|
55
91
|
onUnpin: (h) => {
|
|
56
92
|
h._hiding = true
|
|
57
|
-
|
|
93
|
+
animate(h.el, {
|
|
94
|
+
yPercent: '-100'
|
|
95
|
+
}, {
|
|
58
96
|
duration: 0.25,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
onComplete: () => {
|
|
63
|
-
h._hiding = false
|
|
64
|
-
},
|
|
97
|
+
ease: 'easeIn'
|
|
98
|
+
}).finished.then(() => {
|
|
99
|
+
h._hiding = false
|
|
65
100
|
})
|
|
66
101
|
},
|
|
67
|
-
|
|
102
|
+
|
|
103
|
+
onAltBg: (h) => {
|
|
104
|
+
if (h.opts.altBgColor) {
|
|
105
|
+
animate(h.el, {
|
|
106
|
+
backgroundColor: h.opts.altBgColor
|
|
107
|
+
}, {
|
|
108
|
+
duration: 0.2
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
onNotAltBg: (h) => {
|
|
114
|
+
if (h.opts.regBgColor) {
|
|
115
|
+
animate(h.el, {
|
|
116
|
+
backgroundColor: h.opts.regBgColor
|
|
117
|
+
}, {
|
|
118
|
+
duration: 0.4
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
// eslint-disable-next-line no-unused-vars
|
|
124
|
+
onSmall: (h) => {},
|
|
125
|
+
// eslint-disable-next-line no-unused-vars
|
|
126
|
+
onNotSmall: (h) => {},
|
|
127
|
+
// eslint-disable-next-line no-unused-vars
|
|
128
|
+
onTop: (h) => {},
|
|
129
|
+
// eslint-disable-next-line no-unused-vars
|
|
130
|
+
onNotTop: (h) => {},
|
|
131
|
+
// eslint-disable-next-line no-unused-vars
|
|
132
|
+
onBottom: (h) => {},
|
|
133
|
+
// eslint-disable-next-line no-unused-vars
|
|
134
|
+
onNotBottom: (h) => {},
|
|
135
|
+
// eslint-disable-next-line no-unused-vars
|
|
136
|
+
onMobileMenuOpen: (h) => {},
|
|
137
|
+
// eslint-disable-next-line no-unused-vars
|
|
138
|
+
onMobileMenuClose: (h) => {},
|
|
139
|
+
// eslint-disable-next-line no-unused-vars
|
|
140
|
+
onIntersect: (h) => {},
|
|
141
|
+
onOutline: (h) => {
|
|
142
|
+
h.preventUnpin = true
|
|
143
|
+
h.pin()
|
|
144
|
+
},
|
|
68
145
|
}
|
|
69
146
|
|
|
147
|
+
/** @type {StickyHeaderOptions} */
|
|
70
148
|
const DEFAULT_OPTIONS = {
|
|
71
149
|
el: 'header[data-nav]',
|
|
72
150
|
on: Events.APPLICATION_REVEALED,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
151
|
+
unpinOnForcedScrollStart: true,
|
|
152
|
+
pinOnForcedScrollEnd: true,
|
|
153
|
+
ignoreForcedScroll: false,
|
|
154
|
+
rafScroll: true,
|
|
76
155
|
|
|
77
156
|
default: {
|
|
78
|
-
|
|
157
|
+
unPinOnResize: true,
|
|
79
158
|
canvas: window,
|
|
159
|
+
intersects: null,
|
|
80
160
|
beforeEnter: (h) => {
|
|
81
|
-
|
|
161
|
+
set(h.el, { yPercent: -100 })
|
|
162
|
+
set(h.lis, { opacity: 0 })
|
|
82
163
|
},
|
|
164
|
+
|
|
83
165
|
enter: (h) => {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
166
|
+
// Header slides down
|
|
167
|
+
animate(h.el, {
|
|
168
|
+
yPercent: 0
|
|
169
|
+
}, {
|
|
170
|
+
duration: 1,
|
|
171
|
+
delay: h.opts.enterDelay,
|
|
172
|
+
ease: 'easeOut'
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
// Menu items fade in with stagger (starts at same time as header: '-=1' means 1s overlap)
|
|
176
|
+
animate(h.lis, {
|
|
177
|
+
opacity: 1
|
|
178
|
+
}, {
|
|
179
|
+
duration: 0.8,
|
|
180
|
+
delay: stagger(0.1, { startDelay: h.opts.enterDelay }),
|
|
181
|
+
ease: 'easeIn'
|
|
182
|
+
})
|
|
95
183
|
},
|
|
96
|
-
|
|
184
|
+
|
|
185
|
+
enterDelay: 0,
|
|
97
186
|
tolerance: 3,
|
|
98
187
|
offset: 0, // how far from the top before we trigger hide
|
|
99
188
|
offsetSmall: 50, // how far from the top before we trigger the shrinked padding,
|
|
100
189
|
offsetBg: 200, // how far down before changing backgroundcolor
|
|
190
|
+
regBgColor: null,
|
|
191
|
+
altBgColor: null,
|
|
101
192
|
...DEFAULT_EVENTS,
|
|
102
193
|
},
|
|
103
194
|
}
|
|
104
195
|
|
|
196
|
+
/**
|
|
197
|
+
* StickyHeader component for sticky navigation headers with scroll behaviors.
|
|
198
|
+
* Uses position: sticky instead of position: fixed, keeping the header in document flow.
|
|
199
|
+
*/
|
|
105
200
|
export default class StickyHeader {
|
|
201
|
+
/**
|
|
202
|
+
* Create a new StickyHeader instance
|
|
203
|
+
* @param {Object} app - Application instance
|
|
204
|
+
* @param {StickyHeaderOptions} [opts={}] - StickyHeader options
|
|
205
|
+
*/
|
|
106
206
|
constructor(app, opts = {}) {
|
|
107
207
|
this.app = app
|
|
108
208
|
this.mainOpts = _defaultsDeep(opts, DEFAULT_OPTIONS)
|
|
109
209
|
|
|
110
|
-
if (this.mainOpts.pinOnOutline) {
|
|
111
|
-
window.addEventListener(Events.APPLICATION_OUTLINE, () => {
|
|
112
|
-
this.preventUnpin = true
|
|
113
|
-
this.pin()
|
|
114
|
-
})
|
|
115
|
-
}
|
|
116
|
-
|
|
117
210
|
if (typeof this.mainOpts.el === 'string') {
|
|
118
211
|
this.el = document.querySelector(this.mainOpts.el)
|
|
119
212
|
} else {
|
|
@@ -125,34 +218,36 @@ export default class StickyHeader {
|
|
|
125
218
|
}
|
|
126
219
|
|
|
127
220
|
const section = document.body.getAttribute('data-script')
|
|
128
|
-
this.opts = this._getOptionsForSection(section, opts)
|
|
129
|
-
|
|
130
|
-
this.auxEl = this.opts.onClone(this)
|
|
131
|
-
this.auxEl.setAttribute('data-header-pinned', '')
|
|
132
|
-
this.auxEl.setAttribute('data-auxiliary-nav', '')
|
|
133
|
-
this.auxEl.removeAttribute('data-nav')
|
|
134
|
-
|
|
135
|
-
document.body.appendChild(this.auxEl)
|
|
136
|
-
|
|
137
|
-
this.small()
|
|
138
|
-
this.unpin()
|
|
139
221
|
|
|
222
|
+
this.opts = this._getOptionsForSection(section, opts)
|
|
140
223
|
this.lis = this.el.querySelectorAll('li')
|
|
224
|
+
|
|
141
225
|
this.preventPin = false
|
|
142
226
|
this.preventUnpin = false
|
|
143
|
-
this._isResizing = false
|
|
144
227
|
this._firstLoad = true
|
|
145
228
|
this._pinned = true
|
|
146
229
|
this._top = false
|
|
147
230
|
this._bottom = false
|
|
148
231
|
this._small = false
|
|
232
|
+
this._altBg = false
|
|
233
|
+
this._isResizing = false
|
|
149
234
|
this._hiding = false // if we're in the process of hiding the bar
|
|
150
235
|
this.lastKnownScrollY = 0
|
|
236
|
+
this.lastKnownScrollHeight = 0
|
|
237
|
+
this.currentScrollHeight = 0
|
|
151
238
|
this.currentScrollY = 0
|
|
152
239
|
this.mobileMenuOpen = false
|
|
153
240
|
this.timer = null
|
|
154
241
|
this.resetResizeTimer = null
|
|
155
|
-
this.
|
|
242
|
+
this.scrollSettleTimeout = null
|
|
243
|
+
|
|
244
|
+
if (this.opts.intersects) {
|
|
245
|
+
this.intersectingElements = Dom.all('[data-intersect]')
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
window.addEventListener(Events.APPLICATION_OUTLINE, () => {
|
|
249
|
+
this.opts.onOutline(this)
|
|
250
|
+
})
|
|
156
251
|
|
|
157
252
|
this.initialize()
|
|
158
253
|
}
|
|
@@ -160,20 +255,96 @@ export default class StickyHeader {
|
|
|
160
255
|
initialize() {
|
|
161
256
|
// bind to canvas scroll
|
|
162
257
|
this.lastKnownScrollY = this.getScrollY()
|
|
258
|
+
this.lastKnownScrollHeight = document.body.scrollHeight
|
|
163
259
|
this.currentScrollY = this.lastKnownScrollY
|
|
260
|
+
this.currentScrollHeight = this.lastKnownScrollHeight
|
|
261
|
+
this.pageIsScrolledOnReady = false
|
|
164
262
|
|
|
165
263
|
if (typeof this.opts.offsetBg === 'string') {
|
|
166
264
|
// get offset of element, with height of header subtracted
|
|
167
|
-
const
|
|
168
|
-
this.opts.offsetBg =
|
|
265
|
+
const offsetBgElm = document.querySelector(this.opts.offsetBg)
|
|
266
|
+
this.opts.offsetBg = offsetBgElm.offsetTop
|
|
267
|
+
} else if (typeof this.opts.offsetBg === 'function') {
|
|
268
|
+
this.opts.offsetBg = this.opts.offsetBg(this) - 1
|
|
169
269
|
}
|
|
170
270
|
|
|
171
|
-
this.
|
|
271
|
+
if (typeof this.opts.offset === 'string') {
|
|
272
|
+
// get offset of element, with height of header subtracted
|
|
273
|
+
const offsetElm = document.querySelector(this.opts.offset)
|
|
274
|
+
this.opts.offset = offsetElm.offsetTop - 1
|
|
275
|
+
} else if (typeof this.opts.offset === 'function') {
|
|
276
|
+
this.opts.offset = this.opts.offset(this) - 1
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (typeof this.opts.offsetSmall === 'string') {
|
|
280
|
+
// get offsetSmall of element, with height of header subtracted
|
|
281
|
+
const offsetSmallElm = document.querySelector(this.opts.offsetSmall)
|
|
282
|
+
this.opts.offsetSmall = offsetSmallElm.offsetTop - 1
|
|
283
|
+
} else if (typeof this.opts.offsetSmall === 'function') {
|
|
284
|
+
this.opts.offsetSmall = this.opts.offsetSmall(this) - 1
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (this.mainOpts.unpinOnForcedScrollStart) {
|
|
288
|
+
window.addEventListener(
|
|
289
|
+
Events.APPLICATION_FORCED_SCROLL_START,
|
|
290
|
+
this.unpin.bind(this),
|
|
291
|
+
false
|
|
292
|
+
)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (this.mainOpts.pinOnForcedScrollEnd) {
|
|
296
|
+
window.addEventListener(
|
|
297
|
+
Events.APPLICATION_FORCED_SCROLL_END,
|
|
298
|
+
this.pin.bind(this),
|
|
299
|
+
false
|
|
300
|
+
)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
this.app.registerCallback(Events.APPLICATION_REVEALED, () => {
|
|
304
|
+
let SCROLL_EVENT = Events.APPLICATION_SCROLL
|
|
305
|
+
if (!this.mainOpts.rafScroll) {
|
|
306
|
+
SCROLL_EVENT = 'scroll'
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
window.addEventListener(SCROLL_EVENT, this.redraw.bind(this), {
|
|
310
|
+
capture: false,
|
|
311
|
+
passive: true,
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
// Add debounced scroll listener for accurate top/bottom detection after scroll settles
|
|
315
|
+
// RAF-throttled events can lag behind actual scroll position during fast scrolls
|
|
316
|
+
window.addEventListener('scroll', () => {
|
|
317
|
+
clearTimeout(this.scrollSettleTimeout)
|
|
318
|
+
this.scrollSettleTimeout = setTimeout(() => {
|
|
319
|
+
// Get real-time scroll position after scroll has settled
|
|
320
|
+
const actualScrollY = this.opts.canvas === window || this.opts.canvas === document.body
|
|
321
|
+
? window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
|
|
322
|
+
: this.opts.canvas.scrollTop
|
|
323
|
+
|
|
324
|
+
// Update current scroll and force accurate boundary checks
|
|
325
|
+
this.currentScrollY = actualScrollY
|
|
326
|
+
this.checkTop(true)
|
|
327
|
+
this.checkBot(true)
|
|
328
|
+
}, 100)
|
|
329
|
+
}, {
|
|
330
|
+
capture: false,
|
|
331
|
+
passive: true,
|
|
332
|
+
})
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
this.app.registerCallback(
|
|
336
|
+
Events.APPLICATION_READY,
|
|
337
|
+
this.unpinIfScrolled.bind(this)
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
this.preflight()
|
|
341
|
+
|
|
342
|
+
window.addEventListener(this.mainOpts.on, this.enter.bind(this))
|
|
172
343
|
|
|
173
|
-
window.addEventListener(this.mainOpts.on, this.bindObserver.bind(this))
|
|
174
344
|
this._bindMobileMenuListeners()
|
|
175
345
|
|
|
176
|
-
|
|
346
|
+
// DON'T unpin on iOS since this will unpin when bottom menu bar appears on scrolling upwards!
|
|
347
|
+
if (this.opts.unPinOnResize && !this.app.featureTests.results.ios) {
|
|
177
348
|
window.addEventListener(
|
|
178
349
|
Events.APPLICATION_RESIZE,
|
|
179
350
|
this.setResizeTimer.bind(this),
|
|
@@ -184,58 +355,59 @@ export default class StickyHeader {
|
|
|
184
355
|
this.opts.beforeEnter(this)
|
|
185
356
|
}
|
|
186
357
|
|
|
187
|
-
|
|
188
|
-
this.
|
|
189
|
-
|
|
358
|
+
preflight() {
|
|
359
|
+
if (!this.opts.enter) {
|
|
360
|
+
this.checkSize(true)
|
|
361
|
+
this.checkBg(true)
|
|
362
|
+
this.checkTop(true)
|
|
363
|
+
}
|
|
190
364
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
this.firstReveal = false
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
this._navVisible = true
|
|
199
|
-
} else {
|
|
200
|
-
if (this._navVisible === true) {
|
|
201
|
-
this.opts.onMainInvisible(this)
|
|
202
|
-
}
|
|
203
|
-
this._navVisible = false
|
|
204
|
-
}
|
|
365
|
+
this.app.registerCallback(Events.APPLICATION_REVEALED, () => {
|
|
366
|
+
setTimeout(() => {
|
|
367
|
+
this.el.setAttribute('data-header-transitions', '')
|
|
368
|
+
}, 350)
|
|
205
369
|
})
|
|
370
|
+
}
|
|
206
371
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
372
|
+
lock() {
|
|
373
|
+
this.preventPin = true
|
|
374
|
+
this.preventUnpin = true
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
unlock() {
|
|
378
|
+
this.preventPin = false
|
|
379
|
+
this.preventUnpin = false
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
isScrolled() {
|
|
383
|
+
return (
|
|
384
|
+
(window.pageYOffset || document.documentElement.scrollTop) -
|
|
385
|
+
(document.documentElement.clientTop || 0) >
|
|
386
|
+
0
|
|
211
387
|
)
|
|
388
|
+
}
|
|
212
389
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
})
|
|
219
|
-
window.addEventListener(
|
|
220
|
-
Events.APPLICATION_FORCED_SCROLL_END,
|
|
221
|
-
() => {
|
|
222
|
-
this.preventPin = false
|
|
223
|
-
this.pin()
|
|
224
|
-
this.preventUnpin = false
|
|
225
|
-
},
|
|
226
|
-
false
|
|
227
|
-
)
|
|
390
|
+
unpinIfScrolled() {
|
|
391
|
+
if (this.isScrolled()) {
|
|
392
|
+
// page is scrolled on ready -- ensure we unpin
|
|
393
|
+
this.pageIsScrolledOnReady = true
|
|
394
|
+
this.unpin()
|
|
228
395
|
}
|
|
229
396
|
}
|
|
230
397
|
|
|
231
|
-
|
|
232
|
-
this.
|
|
398
|
+
enter() {
|
|
399
|
+
if (this.opts.enter) {
|
|
400
|
+
this.checkSize(true)
|
|
401
|
+
this.checkBg(true)
|
|
402
|
+
this.checkTop(true)
|
|
403
|
+
this.opts.enter(this)
|
|
404
|
+
}
|
|
233
405
|
}
|
|
234
406
|
|
|
235
407
|
setResizeTimer() {
|
|
236
408
|
this._isResizing = true
|
|
237
409
|
if (this._pinned) {
|
|
238
|
-
// unpin if resizing to prevent visual clutter
|
|
410
|
+
// unpin if resizing to prevent visual clutter.
|
|
239
411
|
this.unpin()
|
|
240
412
|
}
|
|
241
413
|
|
|
@@ -249,26 +421,8 @@ export default class StickyHeader {
|
|
|
249
421
|
}, 500)
|
|
250
422
|
}
|
|
251
423
|
|
|
252
|
-
_hideAlt() {
|
|
253
|
-
this.unpin()
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
_showAlt() {
|
|
257
|
-
this.pin()
|
|
258
|
-
}
|
|
259
|
-
|
|
260
424
|
update() {
|
|
261
|
-
this.redraw(
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
lock() {
|
|
265
|
-
this.preventPin = true
|
|
266
|
-
this.preventUnpin = true
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
unlock() {
|
|
270
|
-
this.preventPin = false
|
|
271
|
-
this.preventUnpin = false
|
|
425
|
+
this.redraw()
|
|
272
426
|
}
|
|
273
427
|
|
|
274
428
|
checkSize(force) {
|
|
@@ -285,6 +439,20 @@ export default class StickyHeader {
|
|
|
285
439
|
}
|
|
286
440
|
}
|
|
287
441
|
|
|
442
|
+
checkBg(force) {
|
|
443
|
+
if (this.currentScrollY > this.opts.offsetBg) {
|
|
444
|
+
if (force) {
|
|
445
|
+
this.altBg()
|
|
446
|
+
} else if (!this._altBg && !this._hiding) {
|
|
447
|
+
this.altBg()
|
|
448
|
+
}
|
|
449
|
+
} else if (force) {
|
|
450
|
+
this.notAltBg()
|
|
451
|
+
} else if (this._altBg) {
|
|
452
|
+
this.notAltBg()
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
288
456
|
checkTop(force) {
|
|
289
457
|
if (this.currentScrollY <= this.opts.offset) {
|
|
290
458
|
if (force) {
|
|
@@ -317,29 +485,27 @@ export default class StickyHeader {
|
|
|
317
485
|
}
|
|
318
486
|
|
|
319
487
|
checkPin(force, toleranceExceeded) {
|
|
320
|
-
if (this._navVisible) {
|
|
321
|
-
if (this._pinned) {
|
|
322
|
-
this.unpin()
|
|
323
|
-
return
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
488
|
if (this.shouldUnpin(toleranceExceeded)) {
|
|
328
489
|
if (this.mobileMenuOpen) {
|
|
329
490
|
return
|
|
330
491
|
}
|
|
331
|
-
if (
|
|
492
|
+
if (force) {
|
|
493
|
+
this.unpin()
|
|
494
|
+
} else if (this._pinned) {
|
|
332
495
|
this.unpin()
|
|
333
496
|
}
|
|
334
497
|
} else if (this.shouldPin(toleranceExceeded)) {
|
|
335
|
-
if (
|
|
498
|
+
if (force) {
|
|
499
|
+
this.pin()
|
|
500
|
+
} else if (!this._pinned) {
|
|
336
501
|
this.pin()
|
|
337
502
|
}
|
|
338
503
|
}
|
|
339
504
|
}
|
|
340
505
|
|
|
341
|
-
redraw(
|
|
506
|
+
redraw() {
|
|
342
507
|
this.currentScrollY = this.getScrollY()
|
|
508
|
+
this.currentScrollHeight = document.body.scrollHeight
|
|
343
509
|
const toleranceExceeded = this.toleranceExceeded()
|
|
344
510
|
|
|
345
511
|
if (this.isOutOfBounds()) {
|
|
@@ -347,8 +513,31 @@ export default class StickyHeader {
|
|
|
347
513
|
return
|
|
348
514
|
}
|
|
349
515
|
|
|
350
|
-
|
|
516
|
+
/* content-visibility: auto may CHANGE the scrollheight of the document
|
|
517
|
+
as we roll down/up. Try to avoid false positives here */
|
|
518
|
+
if (
|
|
519
|
+
this.currentScrollHeight !== this.lastKnownScrollHeight &&
|
|
520
|
+
!this._firstLoad
|
|
521
|
+
) {
|
|
522
|
+
this.lastKnownScrollY = this.currentScrollY
|
|
523
|
+
this.lastKnownScrollHeight = this.currentScrollHeight
|
|
524
|
+
return
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
this.checkSize(false)
|
|
528
|
+
this.checkBg(false)
|
|
529
|
+
this.checkTop(false)
|
|
530
|
+
this.checkBot(false)
|
|
531
|
+
|
|
532
|
+
if (this.mainOpts.ignoreForcedScroll && this.app.state.forcedScroll) {
|
|
533
|
+
// ignore forced scroll
|
|
534
|
+
} else {
|
|
535
|
+
this.checkPin(false, toleranceExceeded)
|
|
536
|
+
}
|
|
537
|
+
|
|
351
538
|
this.lastKnownScrollY = this.currentScrollY
|
|
539
|
+
this.lastKnownScrollHeight = this.currentScrollHeight
|
|
540
|
+
|
|
352
541
|
this._firstLoad = false
|
|
353
542
|
}
|
|
354
543
|
|
|
@@ -381,38 +570,54 @@ export default class StickyHeader {
|
|
|
381
570
|
}
|
|
382
571
|
|
|
383
572
|
unpin() {
|
|
384
|
-
if (
|
|
385
|
-
|
|
386
|
-
this.opts.onUnpin(this)
|
|
573
|
+
if (this.preventUnpin) {
|
|
574
|
+
return
|
|
387
575
|
}
|
|
576
|
+
this._pinned = false
|
|
577
|
+
this.el.setAttribute('data-header-unpinned', '')
|
|
578
|
+
this.el.removeAttribute('data-header-pinned')
|
|
579
|
+
this.opts.onUnpin(this)
|
|
388
580
|
}
|
|
389
581
|
|
|
390
582
|
pin() {
|
|
391
|
-
if (
|
|
392
|
-
|
|
393
|
-
this.opts.onSmall(this)
|
|
394
|
-
this.opts.onPin(this)
|
|
583
|
+
if (this.preventPin) {
|
|
584
|
+
return
|
|
395
585
|
}
|
|
586
|
+
this._pinned = true
|
|
587
|
+
this.el.setAttribute('data-header-pinned', '')
|
|
588
|
+
this.el.removeAttribute('data-header-unpinned')
|
|
589
|
+
this.opts.onPin(this)
|
|
396
590
|
}
|
|
397
591
|
|
|
398
592
|
notSmall() {
|
|
399
593
|
this._small = false
|
|
400
|
-
this.
|
|
401
|
-
this.
|
|
594
|
+
this.el.setAttribute('data-header-big', '')
|
|
595
|
+
this.el.removeAttribute('data-header-small')
|
|
402
596
|
this.opts.onNotSmall(this)
|
|
403
597
|
}
|
|
404
598
|
|
|
405
599
|
small() {
|
|
406
600
|
this._small = true
|
|
407
|
-
this.
|
|
408
|
-
this.
|
|
601
|
+
this.el.setAttribute('data-header-small', '')
|
|
602
|
+
this.el.removeAttribute('data-header-big')
|
|
409
603
|
this.opts.onSmall(this)
|
|
410
604
|
}
|
|
411
605
|
|
|
606
|
+
notAltBg() {
|
|
607
|
+
this._altBg = false
|
|
608
|
+
this.el.setAttribute('data-header-reg-bg', '')
|
|
609
|
+
this.el.removeAttribute('data-header-alt-bg')
|
|
610
|
+
this.opts.onNotAltBg(this)
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
altBg() {
|
|
614
|
+
this._altBg = true
|
|
615
|
+
this.el.setAttribute('data-header-alt-bg', '')
|
|
616
|
+
this.el.removeAttribute('data-header-reg-bg')
|
|
617
|
+
this.opts.onAltBg(this)
|
|
618
|
+
}
|
|
619
|
+
|
|
412
620
|
shouldUnpin(toleranceExceeded) {
|
|
413
|
-
if (this._navVisible) {
|
|
414
|
-
return true
|
|
415
|
-
}
|
|
416
621
|
const scrollingDown = this.currentScrollY > this.lastKnownScrollY
|
|
417
622
|
const pastOffset = this.currentScrollY >= this.opts.offset
|
|
418
623
|
|
|
@@ -423,8 +628,10 @@ export default class StickyHeader {
|
|
|
423
628
|
if (this._isResizing) {
|
|
424
629
|
return false
|
|
425
630
|
}
|
|
631
|
+
|
|
426
632
|
const scrollingUp = this.currentScrollY < this.lastKnownScrollY
|
|
427
633
|
const pastOffset = this.currentScrollY <= this.opts.offset
|
|
634
|
+
|
|
428
635
|
return (scrollingUp && toleranceExceeded) || pastOffset
|
|
429
636
|
}
|
|
430
637
|
|
|
@@ -517,20 +724,22 @@ export default class StickyHeader {
|
|
|
517
724
|
|
|
518
725
|
_bindMobileMenuListeners() {
|
|
519
726
|
window.addEventListener(
|
|
520
|
-
|
|
727
|
+
'APPLICATION:MOBILE_MENU:OPEN',
|
|
521
728
|
this._onMobileMenuOpen.bind(this)
|
|
522
729
|
)
|
|
523
730
|
window.addEventListener(
|
|
524
|
-
|
|
731
|
+
'APPLICATION:MOBILE_MENU:CLOSED',
|
|
525
732
|
this._onMobileMenuClose.bind(this)
|
|
526
733
|
)
|
|
527
734
|
}
|
|
528
735
|
|
|
529
736
|
_onMobileMenuOpen() {
|
|
737
|
+
this.opts.onMobileMenuOpen(this)
|
|
530
738
|
this.mobileMenuOpen = true
|
|
531
739
|
}
|
|
532
740
|
|
|
533
741
|
_onMobileMenuClose() {
|
|
742
|
+
this.opts.onMobileMenuClose(this)
|
|
534
743
|
this.mobileMenuOpen = false
|
|
535
744
|
}
|
|
536
745
|
}
|