@brandocms/jupiter 3.43.0 → 3.46.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ #### 3.46.0
2
+
3
+ - **BREAKING** Marquee: Drop `paddingLeft` -- add `startProgress`. This sets where in the
4
+ timeline we should start. Default is `0.5`
5
+ - Application: Add `hardScrollToTop()`
6
+ - Zoom: Try to improve zoom handling for chrome
7
+
8
+
9
+ #### 3.45.0
10
+
11
+ - Marquee: Reveal on `APPLICATION:REVEALED`
12
+ - Add Toggler module
13
+
14
+
15
+ #### 3.44.0
16
+
17
+ - Moonwalk: Add `data-moonwalked` to walked elements. Don't run again on
18
+ already walked elements.
19
+ - Dom: Add `hasAttribute`
20
+
21
+
1
22
  #### 3.43.0
2
23
 
3
24
  - **BREAKING** Marquee: More consistent `speed`. This means you should probably
package/README.md CHANGED
@@ -261,6 +261,57 @@ include images from this section. Otherwise, all lightboxed images will be inclu
261
261
  - `onPointerLeft/onPointerRight` - `function (lightbox)` - callback for when pointer direction changes
262
262
 
263
263
 
264
+ ## Toggler
265
+
266
+ #### Example
267
+
268
+ HTML
269
+ ```html
270
+ <div data-toggle>
271
+ <button data-toggle-trigger>Click to expand <span class="arrow icon">&darr;</span></button>
272
+ <div class="panel" data-toggle-content>
273
+ <div class="panel-content">
274
+ Content goes here
275
+ </div>
276
+ </div>
277
+ </div>
278
+ ```
279
+
280
+ CSS
281
+ ```css
282
+ [data-toggle-trigger] {
283
+ .arrow {
284
+ display: inline-block;
285
+ transform: scaleY(1);
286
+ transition: transform 250ms ease;
287
+ &.active {
288
+ transform: scaleY(-1);
289
+ }
290
+ }
291
+ }
292
+
293
+ [data-toggle-content] {
294
+ height: 0;
295
+ overflow: hidden;
296
+ opacity: 1;
297
+ position: relative;
298
+ }
299
+ ```
300
+
301
+ JS
302
+ ```es6
303
+ import { Toggler } from '@brandocms/jupiter'
304
+
305
+ app.togglers = []
306
+
307
+ const togglers = Dom.all('[data-toggle]')
308
+ togglers.forEach(toggleEl => {
309
+ app.togglers.push(new Toggler(app, toggleEl))
310
+ })
311
+ ```
312
+
313
+ #### Options
314
+
264
315
 
265
316
  ## Links
266
317
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brandocms/jupiter",
3
- "version": "3.43.0",
3
+ "version": "3.46.0",
4
4
  "description": "Frontend helpers.",
5
5
  "author": "Univers/Twined",
6
6
  "license": "UNLICENSED",
package/src/index.js CHANGED
@@ -31,6 +31,7 @@ import Popup from './modules/Popup'
31
31
  import ScrollSpy from './modules/ScrollSpy'
32
32
  import StackedBoxes from './modules/StackedBoxes'
33
33
  import StickyHeader from './modules/StickyHeader'
34
+ import Toggler from './modules/Toggler'
34
35
  import Typography from './modules/Typography'
35
36
 
36
37
  import imageIsLoaded from './utils/imageIsLoaded'
@@ -63,6 +64,7 @@ export {
63
64
  ScrollSpy,
64
65
  StackedBoxes,
65
66
  StickyHeader,
67
+ Toggler,
66
68
  Typography,
67
69
 
68
70
  // Export utils
@@ -71,6 +71,7 @@ export default class Application {
71
71
  this.userAgent = navigator.userAgent
72
72
  this._lastWindowHeight = 0
73
73
  this.breakpoint = null
74
+ this.language = document.documentElement.lang
74
75
 
75
76
  this.size = {
76
77
  width: 0,
@@ -195,29 +196,41 @@ export default class Application {
195
196
  }
196
197
  }
197
198
 
198
- zoomCalculateChrome () {
199
- const currentDevicePixelRatio = Math.round(window.devicePixelRatio * 100)
200
- const delta = (currentDevicePixelRatio - this._lastDevicePixelRatio) / 100
201
- this.size.zoom += delta
202
- this._lastDevicePixelRatio = currentDevicePixelRatio
199
+ zoomCalculateChrome (dimsChanged) {
200
+ // only calc new zoom if width/height changed
201
+ if (dimsChanged) {
202
+ const currentDevicePixelRatio = Math.round(window.devicePixelRatio * 100)
203
+ const delta = (currentDevicePixelRatio - this._lastDevicePixelRatio) / 100
204
+ this.size.zoom += delta
205
+ if (this.size.zoom === 0) { this.size.zoom = 1 }
206
+ this._lastDevicePixelRatio = currentDevicePixelRatio
207
+ }
203
208
  }
204
209
 
205
210
  zoomCalculateSafari () {
211
+ // safari does not call resize when switching dpi/monitors
206
212
  this.size.zoom = this._zoomSVG.currentScale
207
213
  }
208
214
 
209
- updateZoom () {
215
+ updateZoom (dimsChanged = false, dprDelta = 0) {
210
216
  switch (this.browser) {
211
217
  case 'chrome':
212
- this.zoomCalculateChrome()
218
+ this.zoomCalculateChrome(dimsChanged)
213
219
  break
214
220
 
215
221
  case 'safari':
216
- this.zoomCalculateSafari()
222
+ this.zoomCalculateSafari(dimsChanged)
217
223
  break
218
224
 
219
225
  default:
220
- this.size.zoom = 1 + (zoom.calculate(this.browser) - this._initialZoom)
226
+ if ([1, -1].indexOf(dprDelta) === -1) {
227
+ if (dimsChanged) {
228
+ this.size.zoom = 1 + (zoom.calculate(this.browser) - this._initialZoom)
229
+ if (this.size.zoom === 0) { this.size.zoom = 1 }
230
+ }
231
+ } else {
232
+ this._initialZoom = Math.min(Math.max(this._initialZoom - dprDelta, 1), 2)
233
+ }
221
234
  }
222
235
 
223
236
  this.setZoom()
@@ -329,6 +342,10 @@ export default class Application {
329
342
  })
330
343
  }
331
344
 
345
+ hardScrollToTop () {
346
+ window.scrollTo(0, 0)
347
+ }
348
+
332
349
  hardScrollTo (target) {
333
350
  const element = Dom.find(target)
334
351
  if (element) {
@@ -450,6 +467,13 @@ export default class Application {
450
467
  }
451
468
  }
452
469
 
470
+ _getBaseVW () {
471
+ const fontBasePx = Dom.getCSSVar('--font-base-px')
472
+ const fontBase = parseFloat(fontBasePx, 10)
473
+ const innerWidth = window.innerWidth
474
+ return `${fontBase / innerWidth * 100}vw`
475
+ }
476
+
453
477
  setDims () {
454
478
  const root = document.querySelector(':root')
455
479
 
@@ -457,16 +481,18 @@ export default class Application {
457
481
  this.size.initialOuterHeight = window.outerHeight
458
482
  this.size.initialInnerWidth = window.innerWidth
459
483
  this.size.initialOuterWidth = window.outerWidth
460
-
484
+
461
485
  root.style.setProperty('--vp-initial-inner-h', `${this.size.initialInnerHeight}px`)
462
486
  root.style.setProperty('--vp-initial-outer-h', `${this.size.initialOuterHeight}px`)
463
487
  root.style.setProperty('--vp-initial-inner-w', `${this.size.initialInnerWidth}px`)
464
488
  root.style.setProperty('--vp-initial-outer-w', `${this.size.initialOuterWidth}px`)
465
489
  root.style.setProperty('--ec-zoom', `${this.size.zoom}`)
466
-
490
+
467
491
  this.setvh100Max()
468
492
  this.setvh100()
493
+ this.setFontBaseVw()
469
494
 
495
+ this.size.devicePixelRatio = window.devicePixelRatio
470
496
  this.size.container = Dom.getCSSVar('--container-padding')
471
497
  this.size.width = window.innerWidth
472
498
  this.size.height = window.innerHeight
@@ -474,6 +500,12 @@ export default class Application {
474
500
  this.position.left = window.pageXOffset
475
501
  }
476
502
 
503
+ setFontBaseVw () {
504
+ const root = document.querySelector(':root')
505
+ this.size.baseVW = this._getBaseVW()
506
+ root.style.setProperty('--font-base-vw', `${this.size.baseVW}`)
507
+ }
508
+
477
509
  setZoom () {
478
510
  const root = document.querySelector(':root')
479
511
  root.style.setProperty('--ec-zoom', `${this.size.zoom}`)
@@ -488,6 +520,12 @@ export default class Application {
488
520
  root.style.setProperty('--vp-1vh', `${window.innerHeight * 0.01}px`)
489
521
  }
490
522
 
523
+ setvw100 () {
524
+ const root = document.querySelector(':root')
525
+ root.style.setProperty('--vp-100vw', `${window.innerWidth}px`)
526
+ root.style.setProperty('--vp-1vw', `${window.innerWidth * 0.01}px`)
527
+ }
528
+
491
529
  /**
492
530
  * Get the max 100vh for iOS
493
531
  */
@@ -509,11 +547,16 @@ export default class Application {
509
547
  onResize (e) {
510
548
  const widthChanged = (this.size.width !== window.innerWidth)
511
549
  const heightChanged = (this.size.height !== window.innerHeight)
512
-
550
+ const dimsChanged = widthChanged || heightChanged
551
+ const dprDelta = this.size.devicePixelRatio - window.devicePixelRatio
552
+
513
553
  this.size.width = window.innerWidth
514
554
  this.size.height = window.innerHeight
555
+ this.size.devicePixelRatio = window.devicePixelRatio
556
+
557
+ this.updateZoom(dimsChanged, dprDelta)
515
558
  this.setvh100()
516
- this.updateZoom()
559
+ this.setFontBaseVw()
517
560
 
518
561
  const evt = new CustomEvent(Events.APPLICATION_RESIZE, { detail: { widthChanged, heightChanged } })
519
562
  window.dispatchEvent(evt)
@@ -0,0 +1,8 @@
1
+ /**
2
+ * <article
3
+ * data-loader>
4
+ * <div data-loader-canvas>
5
+ *
6
+ * </div>
7
+ *
8
+ */
@@ -73,6 +73,10 @@ class DOM {
73
73
  return classes.map(className => element.classList.toggle(className))
74
74
  }
75
75
 
76
+ hasAttribute (element, attributeName) {
77
+ return element.hasAttribute(attributeName)
78
+ }
79
+
76
80
  overlapsVertically ($div1, $div2) {
77
81
  // Div 1 data
78
82
  const d1Offset = $div1.getBoundingClientRect()
@@ -223,9 +223,11 @@ export default class FixedHeader {
223
223
  window.addEventListener(Events.APPLICATION_FORCED_SCROLL_END, this.pin.bind(this), false)
224
224
  }
225
225
 
226
- window.addEventListener(Events.APPLICATION_SCROLL, this.update.bind(this), {
227
- capture: false,
228
- passive: true
226
+ window.addEventListener(Events.APPLICATION_REVEALED, e => {
227
+ window.addEventListener(Events.APPLICATION_SCROLL, this.update.bind(this), {
228
+ capture: false,
229
+ passive: true
230
+ })
229
231
  })
230
232
 
231
233
  window.addEventListener(Events.APPLICATION_READY, this.unpinIfScrolled.bind(this))
@@ -249,9 +251,11 @@ export default class FixedHeader {
249
251
  this.checkBg(true)
250
252
  this.checkTop(true)
251
253
 
252
- setTimeout(() => {
253
- this.el.setAttribute('data-header-transitions', '')
254
- }, 350)
254
+ window.addEventListener(Events.APPLICATION_REVEALED, e => {
255
+ setTimeout(() => {
256
+ this.el.setAttribute('data-header-transitions', '')
257
+ }, 350)
258
+ })
255
259
  }
256
260
 
257
261
  lock () {
@@ -191,6 +191,14 @@ export default class Lazyload {
191
191
  img.removeAttribute('data-ll-blurred')
192
192
  img.removeAttribute('data-ll-loading')
193
193
  img.setAttribute('data-ll-loaded', '')
194
+
195
+ // set sizes attribute on load again, since firefox sometimes is a bit slow to
196
+ // get the actual image width
197
+ const width = this.getWidth(img)
198
+ img.setAttribute('sizes', `${width}px`)
199
+ if (img.parentNode) {
200
+ Array.from(Dom.all(img.parentNode, 'source')).forEach(source => source.setAttribute('sizes', `${width}px`))
201
+ }
194
202
  }
195
203
 
196
204
  img.addEventListener('load', onload, false)
@@ -15,7 +15,7 @@ const DEFAULT_OPTIONS = {
15
15
  links.app.scrollTo(target, links.opts.scrollDuration, links.opts.triggerEvents)
16
16
  },
17
17
 
18
- onTransition: href => {
18
+ onTransition: (href, app) => {
19
19
  const main = document.querySelector('main')
20
20
  const header = document.querySelector('header[data-nav]')
21
21
  const footer = document.querySelector('footer')
@@ -152,7 +152,7 @@ export default class Links {
152
152
 
153
153
  if (href.indexOf(document.location.hostname) > -1 || href.startsWith('/')) {
154
154
  e.preventDefault()
155
- this.opts.onTransition(href)
155
+ this.opts.onTransition(href, this.app)
156
156
  }
157
157
  })
158
158
  })
@@ -6,7 +6,12 @@ const DEFAULT_OPTIONS = {
6
6
  speed: 100,
7
7
  extraHeight: 0,
8
8
  slowDownOnHover: true,
9
- paddingLeft: 0
9
+ paddingLeft: 0, //DEPRECATED
10
+ startProgress: 0,
11
+
12
+ onReveal: marqueeEl => {
13
+ gsap.to(marqueeEl, { opacity: 1 })
14
+ }
10
15
  }
11
16
 
12
17
  export default class Marquee {
@@ -25,7 +30,9 @@ export default class Marquee {
25
30
  }
26
31
 
27
32
  initialize () {
33
+ gsap.set(this.elements.$marquee, { opacity: 0 })
28
34
  window.addEventListener('APPLICATION:RESIZE', this.updateMarquee.bind(this))
35
+ window.addEventListener('APPLICATION:REVEALED', this.revealMarquee.bind(this))
29
36
  this.updateMarquee()
30
37
  this.setupObserver()
31
38
  if (this.opts.slowDownOnHover) {
@@ -34,6 +41,11 @@ export default class Marquee {
34
41
  }
35
42
  }
36
43
 
44
+ revealMarquee (e) {
45
+ this.updateMarquee()
46
+ this.opts.onReveal(this.elements.$marquee)
47
+ }
48
+
37
49
  updateMarquee (e) {
38
50
  if (e) {
39
51
  // updating cause of a resize. we only care about width change
@@ -41,6 +53,7 @@ export default class Marquee {
41
53
  return
42
54
  }
43
55
  }
56
+
44
57
  this.killTweens()
45
58
  this.clearHolders()
46
59
  this.setHeight()
@@ -76,14 +89,16 @@ export default class Marquee {
76
89
  const $allHolders = Dom.all(this.elements.$el, '[data-marquee-holder]')
77
90
 
78
91
  Array.from($allHolders).forEach((h, idx) => {
79
- gsap.set(h, { position: 'absolute', left: h.offsetWidth * idx })
92
+ gsap.set(h, { position: 'absolute', left: (h.offsetWidth * idx) })
80
93
  })
81
94
 
82
95
  this.timeline = gsap.timeline({ paused: true })
83
96
  this.timeline
84
- .to($allHolders, { xPercent: -100, ease: 'none', duration: this.duration })
97
+ .to($allHolders, { xPercent: -100, ease: 'none', duration: this.duration }, 'standard')
85
98
  .repeat(-1)
86
99
 
100
+ this.timeline.totalProgress(this.opts.startProgress)
101
+
87
102
  window.timeline = this.timeline
88
103
  window.marquee = this
89
104
  }
@@ -160,14 +175,18 @@ export default class Marquee {
160
175
  this.elements.$holder.appendChild(Dom.new('<span>&nbsp;&mdash;&nbsp;</span>')[0])
161
176
 
162
177
  const textWidth = this.elements.$item.offsetWidth
163
- const count = Math.ceil(this.app.size.width / textWidth) - 1
164
-
165
- for (let i = 0; i < count; i += 1) {
166
- this.elements.$holder.append(this.elements.$item.cloneNode(true))
167
- this.elements.$holder.appendChild(Dom.new('<span>&nbsp;&mdash;&nbsp;</p>')[0])
178
+ if (textWidth) {
179
+ const count = Math.max(Math.ceil(this.app.size.width / textWidth) - 1, 2)
180
+
181
+ for (let i = 0; i < count; i += 1) {
182
+ this.elements.$holder.append(this.elements.$item.cloneNode(true))
183
+ this.elements.$holder.appendChild(Dom.new('<span>&nbsp;&mdash;&nbsp;</p>')[0])
184
+ }
185
+
186
+ this.elements.$marquee.appendChild(this.elements.$holder.cloneNode(true))
187
+ } else {
188
+ console.error('no textWidth! probably image?', this.elements.$item)
168
189
  }
169
-
170
- this.elements.$marquee.appendChild(this.elements.$holder.cloneNode(true))
171
190
  }
172
191
 
173
192
  setHeight () {
@@ -576,7 +576,7 @@ export default class Moonwalk {
576
576
  } = cfg
577
577
 
578
578
  let { alphaTween } = cfg
579
- let overlap = duration - interval
579
+ let overlap = (duration - interval) * -1 // flip it
580
580
 
581
581
  if (section.stage.firstTween) {
582
582
  overlap = 0
@@ -599,6 +599,7 @@ export default class Moonwalk {
599
599
  section,
600
600
  entry.target,
601
601
  duration,
602
+ interval,
602
603
  transition,
603
604
  overlap,
604
605
  alphaTween
@@ -651,10 +652,14 @@ export default class Moonwalk {
651
652
  * @param {*} tweenOverlap
652
653
  * @param {*} alphaTween
653
654
  */
654
- tweenJS (section, target, tweenDuration, tweenTransition, tweenOverlap, alphaTween) {
655
+ tweenJS (section, target, tweenDuration, tweenInterval, tweenTransition, tweenOverlap, alphaTween) {
655
656
  let tweenPosition
656
657
  const startingPoint = tweenDuration - tweenOverlap
657
658
 
659
+ if (Dom.hasAttribute(target, 'data-moonwalked')) {
660
+ return
661
+ }
662
+
658
663
  if (section.timeline.isActive() && section.timeline.recent()) {
659
664
  if (section.timeline.recent().time() > startingPoint) {
660
665
  /* We're late for this tween if it was supposed to be sequential,
@@ -662,10 +667,10 @@ export default class Moonwalk {
662
667
  tweenPosition = () => section.timeline.time()
663
668
  } else {
664
669
  /* Still time, add as normal overlap at the end */
665
- tweenPosition = () => `>-${tweenOverlap}`
670
+ tweenPosition = () => `>${tweenOverlap}`
666
671
  }
667
672
  } else {
668
- tweenPosition = () => '+=0'
673
+ tweenPosition = () => '>'
669
674
  }
670
675
 
671
676
  gsap.set(target, tweenTransition.from)
@@ -677,6 +682,7 @@ export default class Moonwalk {
677
682
  },
678
683
  tweenPosition()
679
684
  )
685
+ .call(() => { target.setAttribute('data-moonwalked', '') }, null, '<')
680
686
 
681
687
  if (alphaTween) {
682
688
  section.timeline.to(target, {
@@ -697,11 +703,11 @@ export default class Moonwalk {
697
703
  * @param {*} transition
698
704
  * @param {*} overlap
699
705
  */
700
- tweenCSS (section, target, tweenDuration, tweenTransition, tweenOverlap) {
706
+ tweenCSS (section, target, tweenDuration, tweenInterval, tweenTransition, tweenOverlap) {
701
707
  let tweenPosition
702
- const startingPoint = tweenDuration - tweenOverlap
703
-
704
- if (Dom.hasClass(target, 'moonwalked')) {
708
+ const startingPoint = tweenDuration - (tweenOverlap * -1)
709
+
710
+ if (Dom.hasAttribute(target, 'data-moonwalked')) {
705
711
  return
706
712
  }
707
713
 
@@ -712,10 +718,10 @@ export default class Moonwalk {
712
718
  tweenPosition = () => section.timeline.time()
713
719
  } else {
714
720
  /* Still time, add as normal overlap at the end */
715
- tweenPosition = () => `>-${tweenOverlap}`
721
+ tweenPosition = () => `>${tweenOverlap}`
716
722
  }
717
723
  } else {
718
- tweenPosition = () => '+=0'
724
+ tweenPosition = () => '>'
719
725
  }
720
726
 
721
727
  section.timeline.to(
@@ -726,6 +732,6 @@ export default class Moonwalk {
726
732
  duration: tweenDuration
727
733
  },
728
734
  tweenPosition()
729
- )
735
+ ).call(() => target.setAttribute('data-moonwalked', ''), null, '>')
730
736
  }
731
737
  }
@@ -0,0 +1,46 @@
1
+ import { gsap } from 'gsap'
2
+ import Dom from '../Dom'
3
+
4
+ const DEFAULT_OPTIONS = {}
5
+
6
+ export default class Toggler {
7
+ constructor(app, el) {
8
+ this.open = false
9
+ this.app = app
10
+ this.el = el
11
+ // this.opts = _defaultsDeep(opts, DEFAULT_OPTIONS)
12
+ this.trigger = Dom.find(this.el, '[data-toggle-trigger]')
13
+ this.triggerIcon = Dom.find(this.trigger, 'span.icon')
14
+ this.content = Dom.find(this.el, '[data-toggle-content]')
15
+ this.trigger.addEventListener('click', this.onClick.bind(this))
16
+ }
17
+
18
+ onClick() {
19
+ this.toggleState()
20
+
21
+ if (this.open) {
22
+ this.triggerIcon.classList.toggle('active')
23
+ gsap.set(this.content, { height: 'auto' })
24
+ this.el.classList.toggle('open')
25
+ gsap.from(this.content, { height: 0, ease: 'power1.inOut' })
26
+ } else {
27
+ this.triggerIcon.classList.toggle('active')
28
+ gsap.to(this.content, {
29
+ duration: 0.25,
30
+ onComplete: () => {
31
+ this.el.classList.toggle('open')
32
+ }
33
+ })
34
+
35
+ gsap.to(this.content, { height: 0, ease: 'power3.out' })
36
+ }
37
+ }
38
+
39
+ toggleState() {
40
+ if (this.open) {
41
+ this.open = false
42
+ } else {
43
+ this.open = true
44
+ }
45
+ }
46
+ }
@@ -18,6 +18,12 @@ export default class Typography {
18
18
  self.elems = [...parent.querySelectorAll(self.settings.selector)]
19
19
  }
20
20
 
21
+ // load children
22
+ const typoParents = document.querySelectorAll('[data-typo-children]')
23
+ typoParents.forEach(typoParent => {
24
+ self.elems = [...typoParent.children]
25
+ })
26
+
21
27
  this.apply()
22
28
  }
23
29
 
package/src/utils/zoom.js CHANGED
@@ -24,15 +24,10 @@ const _mediaQueryBinarySearch = (property, unit, a, b, maxIter, epsilon) => {
24
24
  return ratio
25
25
  }
26
26
 
27
- /**
28
- Firefox: OK
29
- Chrome: get the devicePixelRatio
30
-
31
- window.devicePixelRatio - initial
32
-
33
- */
27
+ const calculateFirefox = () => {
28
+ return Math.round(_mediaQueryBinarySearch('min--moz-device-pixel-ratio', '', 0, 10, 20, 0.0001) * 10) / 10
29
+ }
34
30
 
35
- const calculateFirefox = () => Math.round(_mediaQueryBinarySearch('min--moz-device-pixel-ratio', '', 0, 10, 20, 0.0001) * 10) / 10
36
31
  const calculateChrome = initial => Math.round(window.devicePixelRatio * 100) - initial
37
32
  const calculateDefault = () => Math.round(((window.outerWidth) / window.innerWidth) * 10) / 10
38
33