@brandocms/jupiter 3.42.7 → 3.45.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,25 @@
1
+ #### 3.45.0
2
+
3
+ - Marquee: Reveal on `APPLICATION:REVEALED`
4
+ - Add Toggler module
5
+
6
+
7
+ #### 3.44.0
8
+
9
+ - Moonwalk: Add `data-moonwalked` to walked elements. Don't run again on
10
+ already walked elements.
11
+ - Dom: Add `hasAttribute`
12
+
13
+
14
+ #### 3.43.0
15
+
16
+ - **BREAKING** Marquee: More consistent `speed`. This means you should probably
17
+ verify that the speed you set looks ok with the new config
18
+ - Marquee: Add `paddingLeft` and `slowDownOnHover` options
19
+ - Application: send `widthChanged` and `heightChanged` booleans
20
+ in `APPLICATION:RESIZE` event detail
21
+
22
+
1
23
  #### 3.42.7
2
24
 
3
25
  - Links: Check for zero opacity body element on back/forward nav
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">&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.42.7",
3
+ "version": "3.45.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
@@ -507,12 +507,15 @@ export default class Application {
507
507
  * RAF'ed resize event
508
508
  */
509
509
  onResize (e) {
510
+ const widthChanged = (this.size.width !== window.innerWidth)
511
+ const heightChanged = (this.size.height !== window.innerHeight)
512
+
510
513
  this.size.width = window.innerWidth
511
514
  this.size.height = window.innerHeight
512
515
  this.setvh100()
513
516
  this.updateZoom()
514
517
 
515
- const evt = new CustomEvent(Events.APPLICATION_RESIZE, e)
518
+ const evt = new CustomEvent(Events.APPLICATION_RESIZE, { detail: { widthChanged, heightChanged } })
516
519
  window.dispatchEvent(evt)
517
520
  }
518
521
 
@@ -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 () {
@@ -3,8 +3,13 @@ import _defaultsDeep from 'lodash.defaultsdeep'
3
3
  import Dom from '../Dom'
4
4
 
5
5
  const DEFAULT_OPTIONS = {
6
- speed: 25,
7
- extraHeight: 0
6
+ speed: 100,
7
+ extraHeight: 0,
8
+ slowDownOnHover: true,
9
+ paddingLeft: 0,
10
+ onReveal: marqueeEl => {
11
+ gsap.to(marqueeEl, { opacity: 1 })
12
+ }
8
13
  }
9
14
 
10
15
  export default class Marquee {
@@ -23,21 +28,40 @@ export default class Marquee {
23
28
  }
24
29
 
25
30
  initialize () {
31
+ gsap.set(this.elements.$marquee, { opacity: 0 })
26
32
  window.addEventListener('APPLICATION:RESIZE', this.updateMarquee.bind(this))
33
+ window.addEventListener('APPLICATION:REVEALED', this.revealMarquee.bind(this))
27
34
  this.updateMarquee()
28
35
  this.setupObserver()
29
- this.elements.$el.addEventListener('mouseenter', this.slowDown.bind(this))
30
- this.elements.$el.addEventListener('mouseleave', this.speedUp.bind(this))
36
+ if (this.opts.slowDownOnHover) {
37
+ this.elements.$el.addEventListener('mouseenter', this.slowDown.bind(this))
38
+ this.elements.$el.addEventListener('mouseleave', this.speedUp.bind(this))
39
+ }
40
+ }
41
+
42
+ revealMarquee (e) {
43
+ this.updateMarquee()
44
+ this.opts.onReveal(this.elements.$marquee)
31
45
  }
32
46
 
33
- updateMarquee () {
47
+ updateMarquee (e) {
48
+ if (e) {
49
+ // updating cause of a resize. we only care about width change
50
+ if (!e.detail.widthChanged) {
51
+ return
52
+ }
53
+ }
54
+
34
55
  this.killTweens()
35
56
  this.clearHolders()
36
57
  this.setHeight()
37
58
  this.fillText()
59
+
38
60
  const holderWidth = this.elements.$holder.offsetWidth
39
61
  const $allHolders = Dom.all(this.elements.$el, '[data-marquee-holder]')
40
62
  const marqueeWidth = holderWidth * $allHolders.length
63
+
64
+ this.duration = holderWidth / this.opts.speed
41
65
 
42
66
  gsap.set(this.elements.$marquee, { width: marqueeWidth })
43
67
  this.initializeTween()
@@ -68,7 +92,7 @@ export default class Marquee {
68
92
 
69
93
  this.timeline = gsap.timeline({ paused: true })
70
94
  this.timeline
71
- .to($allHolders, this.opts.speed, { xPercent: -100, ease: 'none' })
95
+ .to($allHolders, { xPercent: -100, ease: 'none', duration: this.duration })
72
96
  .repeat(-1)
73
97
 
74
98
  window.timeline = this.timeline
@@ -147,14 +171,18 @@ export default class Marquee {
147
171
  this.elements.$holder.appendChild(Dom.new('<span>&nbsp;&mdash;&nbsp;</span>')[0])
148
172
 
149
173
  const textWidth = this.elements.$item.offsetWidth
150
- const count = Math.ceil(this.app.size.width / textWidth) - 1
151
-
152
- for (let i = 0; i < count; i += 1) {
153
- this.elements.$holder.append(this.elements.$item.cloneNode(true))
154
- this.elements.$holder.appendChild(Dom.new('<span>&nbsp;&mdash;&nbsp;</p>')[0])
174
+ if (textWidth) {
175
+ const count = Math.ceil(this.app.size.width / textWidth) - 1
176
+
177
+ for (let i = 0; i < count; i += 1) {
178
+ this.elements.$holder.append(this.elements.$item.cloneNode(true))
179
+ this.elements.$holder.appendChild(Dom.new('<span>&nbsp;&mdash;&nbsp;</p>')[0])
180
+ }
181
+
182
+ this.elements.$marquee.appendChild(this.elements.$holder.cloneNode(true))
183
+ } else {
184
+ console.error('no textWidth! probably image?', this.elements.$item)
155
185
  }
156
-
157
- this.elements.$marquee.appendChild(this.elements.$holder.cloneNode(true))
158
186
  }
159
187
 
160
188
  setHeight () {
@@ -655,6 +655,10 @@ export default class Moonwalk {
655
655
  let tweenPosition
656
656
  const startingPoint = tweenDuration - tweenOverlap
657
657
 
658
+ if (Dom.hasAttribute(target, 'data-moonwalked')) {
659
+ return
660
+ }
661
+
658
662
  if (section.timeline.isActive() && section.timeline.recent()) {
659
663
  if (section.timeline.recent().time() > startingPoint) {
660
664
  /* We're late for this tween if it was supposed to be sequential,
@@ -677,6 +681,7 @@ export default class Moonwalk {
677
681
  },
678
682
  tweenPosition()
679
683
  )
684
+ .call(() => { target.setAttribute('data-moonwalked', '') }, null, '<')
680
685
 
681
686
  if (alphaTween) {
682
687
  section.timeline.to(target, {
@@ -701,7 +706,7 @@ export default class Moonwalk {
701
706
  let tweenPosition
702
707
  const startingPoint = tweenDuration - tweenOverlap
703
708
 
704
- if (Dom.hasClass(target, 'moonwalked')) {
709
+ if (Dom.hasAttribute(target, 'data-moonwalked')) {
705
710
  return
706
711
  }
707
712
 
@@ -726,6 +731,6 @@ export default class Moonwalk {
726
731
  duration: tweenDuration
727
732
  },
728
733
  tweenPosition()
729
- )
734
+ ).call(() => { target.setAttribute('data-moonwalked', '') }, null, '<')
730
735
  }
731
736
  }
@@ -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
+ }