@brandocms/jupiter 3.55.0 → 4.0.0-beta.2

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.
Files changed (76) hide show
  1. package/README.md +509 -54
  2. package/package.json +30 -18
  3. package/src/index.js +15 -10
  4. package/src/modules/Application/index.js +236 -158
  5. package/src/modules/Breakpoints/index.js +116 -36
  6. package/src/modules/Cookies/index.js +95 -64
  7. package/src/modules/CoverOverlay/index.js +21 -14
  8. package/src/modules/Dataloader/index.js +71 -24
  9. package/src/modules/Dataloader/url-sync.js +238 -0
  10. package/src/modules/Dom/index.js +24 -0
  11. package/src/modules/DoubleHeader/index.js +571 -0
  12. package/src/modules/Dropdown/index.js +108 -73
  13. package/src/modules/EqualHeightElements/index.js +8 -8
  14. package/src/modules/EqualHeightImages/index.js +15 -7
  15. package/src/modules/FixedHeader/index.js +116 -30
  16. package/src/modules/FooterReveal/index.js +5 -5
  17. package/src/modules/HeroSlider/index.js +231 -106
  18. package/src/modules/HeroVideo/index.js +72 -44
  19. package/src/modules/Lazyload/index.js +128 -80
  20. package/src/modules/Lightbox/index.js +101 -80
  21. package/src/modules/Links/index.js +77 -51
  22. package/src/modules/Looper/index.js +1737 -0
  23. package/src/modules/Marquee/index.js +106 -37
  24. package/src/modules/MobileMenu/index.js +105 -130
  25. package/src/modules/Moonwalk/index.js +479 -153
  26. package/src/modules/Parallax/index.js +280 -57
  27. package/src/modules/Popover/index.js +187 -17
  28. package/src/modules/Popup/index.js +172 -53
  29. package/src/modules/ScrollSpy/index.js +21 -0
  30. package/src/modules/StackedBoxes/index.js +8 -6
  31. package/src/modules/StickyHeader/index.js +394 -164
  32. package/src/modules/Toggler/index.js +207 -11
  33. package/src/modules/Typography/index.js +33 -20
  34. package/src/utils/motion-helpers.js +330 -0
  35. package/types/README.md +159 -0
  36. package/types/events/index.d.ts +20 -0
  37. package/types/index.d.ts +6 -0
  38. package/types/modules/Application/index.d.ts +168 -0
  39. package/types/modules/Breakpoints/index.d.ts +40 -0
  40. package/types/modules/Cookies/index.d.ts +81 -0
  41. package/types/modules/CoverOverlay/index.d.ts +6 -0
  42. package/types/modules/Dataloader/index.d.ts +38 -0
  43. package/types/modules/Dataloader/url-sync.d.ts +36 -0
  44. package/types/modules/Dom/index.d.ts +47 -0
  45. package/types/modules/DoubleHeader/index.d.ts +63 -0
  46. package/types/modules/Dropdown/index.d.ts +15 -0
  47. package/types/modules/EqualHeightElements/index.d.ts +8 -0
  48. package/types/modules/EqualHeightImages/index.d.ts +11 -0
  49. package/types/modules/FeatureTests/index.d.ts +27 -0
  50. package/types/modules/FixedHeader/index.d.ts +219 -0
  51. package/types/modules/Fontloader/index.d.ts +5 -0
  52. package/types/modules/FooterReveal/index.d.ts +5 -0
  53. package/types/modules/HeroSlider/index.d.ts +28 -0
  54. package/types/modules/HeroVideo/index.d.ts +83 -0
  55. package/types/modules/Lazyload/index.d.ts +80 -0
  56. package/types/modules/Lightbox/index.d.ts +123 -0
  57. package/types/modules/Links/index.d.ts +55 -0
  58. package/types/modules/Looper/index.d.ts +127 -0
  59. package/types/modules/Marquee/index.d.ts +23 -0
  60. package/types/modules/MobileMenu/index.d.ts +63 -0
  61. package/types/modules/Moonwalk/index.d.ts +322 -0
  62. package/types/modules/Parallax/index.d.ts +71 -0
  63. package/types/modules/Popover/index.d.ts +29 -0
  64. package/types/modules/Popup/index.d.ts +76 -0
  65. package/types/modules/ScrollSpy/index.d.ts +29 -0
  66. package/types/modules/StackedBoxes/index.d.ts +9 -0
  67. package/types/modules/StickyHeader/index.d.ts +220 -0
  68. package/types/modules/Toggler/index.d.ts +48 -0
  69. package/types/modules/Typography/index.d.ts +77 -0
  70. package/types/utils/dispatchElementEvent.d.ts +1 -0
  71. package/types/utils/imageIsLoaded.d.ts +1 -0
  72. package/types/utils/imagesAreLoaded.d.ts +1 -0
  73. package/types/utils/loadScript.d.ts +2 -0
  74. package/types/utils/prefersReducedMotion.d.ts +4 -0
  75. package/types/utils/rafCallback.d.ts +2 -0
  76. package/types/utils/zoom.d.ts +4 -0
@@ -1,7 +1,27 @@
1
- import { gsap } from 'gsap'
1
+ import { animate } from 'motion'
2
+ import { set } from '../../utils/motion-helpers'
2
3
  import _defaultsDeep from 'lodash.defaultsdeep'
3
4
 
5
+ /**
6
+ * @typedef {Object} PopupOptions
7
+ * @property {string} [selector] - CSS selector to find popup elements
8
+ * @property {Function} [responsive] - Function that determines if popup should be shown on current breakpoint
9
+ * @property {Function} [onOpen] - Called when popup opens
10
+ * @property {Function} [onClose] - Called when popup closes
11
+ * @property {Function} [tweenIn] - Animation function for opening popup
12
+ * @property {Function} [tweenOut] - Animation function for closing popup
13
+ */
14
+
15
+ /** @type {PopupOptions} */
4
16
  const DEFAULT_OPTIONS = {
17
+ /**
18
+ * selector
19
+ *
20
+ * CSS selector to find popup elements
21
+ * Default: '[data-popup]'
22
+ */
23
+ selector: '[data-popup]',
24
+
5
25
  /**
6
26
  * responsive
7
27
  *
@@ -17,117 +37,216 @@ const DEFAULT_OPTIONS = {
17
37
  onClose: () => {},
18
38
 
19
39
  tweenIn: (trigger, target, popup) => {
20
- gsap.set(popup.backdrop, { display: 'block' })
21
- gsap.to(popup.backdrop, {
22
- duration: 0.3,
23
- opacity: 1,
24
- onComplete: () => {
25
- gsap.fromTo(
40
+ popup.backdrop.style.display = 'block'
41
+ animate(popup.backdrop, { opacity: 1 }, { duration: 0.3 })
42
+ .finished
43
+ .then(() => {
44
+ target.style.display = 'block'
45
+ animate(
26
46
  target,
27
47
  {
28
- duration: 0.3,
29
- yPercent: -50,
30
- x: -5,
31
- xPercent: -50,
32
- opacity: 0,
33
- display: 'block'
48
+ transform: [
49
+ 'translate(calc(-50% - 5px), -50%)',
50
+ 'translate(-50%, -50%)'
51
+ ],
52
+ opacity: [0, 1]
34
53
  },
35
- {
36
- duration: 0.3,
37
- yPercent: -50,
38
- xPercent: -50,
39
- x: 0,
40
- opacity: 1
41
- }
54
+ { duration: 0.3, ease: [0.4, 0, 0.2, 1] }
42
55
  )
43
- }
44
- })
56
+ })
45
57
  },
46
58
 
47
- tweenOut: popup => {
48
- const popups = document.querySelectorAll('[data-popup]')
49
- gsap.to(popups, {
50
- duration: 0.3,
51
- opacity: 0,
52
- display: 'none'
53
- })
54
- gsap.to(popup.backdrop, {
55
- duration: 0.3,
56
- opacity: 0,
57
- onComplete: () => {
58
- gsap.set(popup.backdrop, { display: 'none' })
59
- }
60
- })
61
- }
59
+ tweenOut: (popup) => {
60
+ console.log('default tweenOut')
61
+ const popupElement = popup.currentPopup
62
+ if (popupElement) {
63
+ animate(popupElement, { opacity: 0 }, { duration: 0.3 })
64
+ .finished
65
+ .then(() => {
66
+ popupElement.style.display = 'none'
67
+ })
68
+ }
69
+ animate(popup.backdrop, { opacity: 0 }, { duration: 0.3 })
70
+ .finished
71
+ .then(() => {
72
+ // Remove the backdrop completely instead of just hiding it
73
+ popup.backdrop.remove()
74
+ })
75
+ },
62
76
  }
63
77
 
78
+ /**
79
+ * Popup component for modal dialogs and popups
80
+ */
64
81
  export default class Popup {
65
- constructor(app, opts = {}) {
82
+ /**
83
+ * Create a new Popup instance
84
+ * @param {Object} app - Application instance
85
+ * @param {string} [selector] - CSS selector to find popup elements
86
+ * @param {PopupOptions} [opts={}] - Popup options
87
+ */
88
+ constructor(app, selector = '[data-popup]', opts = {}) {
66
89
  this.app = app
67
90
  this.opts = _defaultsDeep(opts, DEFAULT_OPTIONS)
68
- this.createBackdrop()
91
+ this.opts.selector = selector
92
+ this.backdrop = null
93
+ this.currentPopup = null
94
+ this.popupKey = null
69
95
  this.bindTriggers()
70
96
  }
71
97
 
98
+ /**
99
+ * Bind click handlers to popup triggers and close buttons
100
+ */
72
101
  bindTriggers() {
73
- const triggers = document.querySelectorAll('[data-popup-trigger]')
74
- const closers = document.querySelectorAll('[data-popup-close]')
102
+ // Find all triggers that match this popup's selector
103
+ const allTriggers = document.querySelectorAll('[data-popup-trigger]')
104
+ const matchingTriggers = Array.from(allTriggers).filter(trigger => {
105
+ const target = trigger.getAttribute('data-popup-trigger')
106
+ if (typeof target === 'string') {
107
+ const element = document.querySelector(target)
108
+ return element && element.matches(this.opts.selector)
109
+ }
110
+ return false
111
+ })
112
+
113
+ // Get all popups that match this instance's selector
114
+ const popups = document.querySelectorAll(this.opts.selector)
115
+
116
+ // Find all close buttons inside matching popups
117
+ const closers = []
118
+ popups.forEach(popup => {
119
+ const popupClosers = popup.querySelectorAll('[data-popup-close]')
120
+ closers.push(...popupClosers)
121
+ })
75
122
 
76
- Array.from(triggers).forEach(trigger => {
123
+ // Bind click handlers to matching triggers
124
+ matchingTriggers.forEach((trigger) => {
77
125
  const triggerTarget = trigger.getAttribute('data-popup-trigger')
78
- trigger.addEventListener('click', event => {
126
+ // Get the popup key if present
127
+ const popupKey = trigger.getAttribute('data-popup-key') || this.getKeyFromTarget(triggerTarget)
128
+
129
+ trigger.addEventListener('click', (event) => {
79
130
  if (this.opts.responsive(this.app)) {
80
131
  event.stopImmediatePropagation()
81
132
  event.preventDefault()
82
- this.open(trigger, triggerTarget)
133
+ this.open(trigger, triggerTarget, popupKey)
83
134
  }
84
135
  })
85
136
  })
86
137
 
87
- Array.from(closers).forEach(closer => {
88
- closer.addEventListener('click', event => {
138
+ // Bind click handlers to close buttons
139
+ closers.forEach((closer) => {
140
+ // Get the popup key from the closest parent popup element
141
+ const popupElement = closer.closest(this.opts.selector)
142
+ const popupKey = popupElement ? popupElement.getAttribute('data-popup-key') : null
143
+
144
+ closer.addEventListener('click', (event) => {
89
145
  event.stopImmediatePropagation()
90
146
  event.preventDefault()
91
- this.opts.onClose(this)
92
- this.close()
147
+
148
+ // Only close if keys match or no key is set
149
+ if (!this.popupKey || !popupKey || this.popupKey === popupKey) {
150
+ this.opts.onClose(this)
151
+ this.close()
152
+ }
93
153
  })
94
154
  })
95
155
  }
96
156
 
97
- createBackdrop() {
157
+ /**
158
+ * Extract key from target selector or element
159
+ * @param {HTMLElement|string} target - Target element or selector
160
+ * @returns {string|null} - The popup key or null
161
+ */
162
+ getKeyFromTarget(target) {
163
+ if (typeof target === 'string') {
164
+ const element = document.querySelector(target)
165
+ return element ? element.getAttribute('data-popup-key') : null
166
+ } else if (target instanceof HTMLElement) {
167
+ return target.getAttribute('data-popup-key')
168
+ }
169
+ return null
170
+ }
171
+
172
+ /**
173
+ * Create backdrop element for popup
174
+ * @param {string|null} key - Optional popup key to associate with backdrop
175
+ * @returns {HTMLElement} The created backdrop element
176
+ */
177
+ createBackdrop(key) {
98
178
  const backdrop = document.createElement('div')
99
179
  backdrop.setAttribute('data-popup-backdrop', '')
100
- gsap.set(backdrop, { opacity: 0, display: 'none', zIndex: 4999 })
180
+ if (key) {
181
+ backdrop.setAttribute('data-popup-key', key)
182
+ }
183
+ backdrop.style.display = 'none'
184
+ backdrop.style.zIndex = '4999'
185
+ set(backdrop, { opacity: 0 })
101
186
 
102
- backdrop.addEventListener('click', e => {
187
+ backdrop.addEventListener('click', (e) => {
103
188
  e.stopPropagation()
104
189
  this.close()
105
190
  })
106
191
 
107
192
  document.body.append(backdrop)
108
- this.backdrop = backdrop
193
+ return backdrop
109
194
  }
110
195
 
111
- open(trigger, target) {
196
+ /**
197
+ * Open a popup
198
+ * @param {HTMLElement} trigger - Element that triggered the popup
199
+ * @param {HTMLElement|string} target - Popup element or selector
200
+ * @param {string|null} key - Optional popup key
201
+ */
202
+ open(trigger, target, key = null) {
112
203
  this.keyUpListener = this.onKeyup.bind(this)
113
204
  document.addEventListener('keyup', this.keyUpListener)
205
+
206
+ // Store the popup key
207
+ this.popupKey = key || this.getKeyFromTarget(target)
208
+
209
+ // Create a new backdrop for this popup
210
+ this.backdrop = this.createBackdrop(this.popupKey)
211
+
114
212
  if (typeof target === 'string') {
115
213
  target = document.querySelector(target)
116
214
  }
117
215
 
118
216
  if (!target) {
119
217
  console.error(`JUPITER/POPUP >>> Element ${target} not found`)
218
+ return
219
+ }
220
+
221
+ // Store the current popup element for reference
222
+ this.currentPopup = target
223
+
224
+ // If key isn't already set on the popup element, set it now
225
+ if (this.popupKey && !target.hasAttribute('data-popup-key')) {
226
+ target.setAttribute('data-popup-key', this.popupKey)
120
227
  }
228
+
121
229
  this.opts.onOpen(trigger, target, this)
122
230
  this.opts.tweenIn(trigger, target, this)
123
231
  }
124
232
 
233
+ /**
234
+ * Close the popup
235
+ */
125
236
  close() {
126
237
  document.removeEventListener('keyup', this.keyUpListener)
127
238
  this.opts.onClose(this)
128
239
  this.opts.tweenOut(this)
240
+
241
+ // Reset popup state
242
+ this.popupKey = null
243
+ this.currentPopup = null
129
244
  }
130
245
 
246
+ /**
247
+ * Handle keyup event for Escape key to close popup
248
+ * @param {KeyboardEvent} e - Keyboard event
249
+ */
131
250
  onKeyup(e) {
132
251
  const key = e.keyCode || e.which
133
252
 
@@ -1,17 +1,34 @@
1
1
  import _defaultsDeep from 'lodash.defaultsdeep'
2
2
  import Dom from '../Dom'
3
3
 
4
+ /**
5
+ * @typedef {Object} ScrollSpyOptions
6
+ * @property {Function} [onIntersect] - Called when a target intersects with the viewport
7
+ */
8
+
9
+ /** @type {ScrollSpyOptions} */
4
10
  const DEFAULT_OPTIONS = {
5
11
  onIntersect: (target, trigger) => {}
6
12
  }
7
13
 
14
+ /**
15
+ * ScrollSpy component for highlighting active sections during scrolling
16
+ */
8
17
  export default class ScrollSpy {
18
+ /**
19
+ * Create a new ScrollSpy instance
20
+ * @param {Object} app - Application instance
21
+ * @param {ScrollSpyOptions} [opts={}] - ScrollSpy options
22
+ */
9
23
  constructor(app, opts = {}) {
10
24
  this.app = app
11
25
  this.opts = _defaultsDeep(opts, DEFAULT_OPTIONS)
12
26
  this.initialize()
13
27
  }
14
28
 
29
+ /**
30
+ * Initialize ScrollSpy
31
+ */
15
32
  initialize() {
16
33
  this.triggers = Dom.all('[data-scrollspy-trigger]')
17
34
  const config = {
@@ -29,6 +46,10 @@ export default class ScrollSpy {
29
46
  this.triggers.forEach(section => observer.observe(section))
30
47
  }
31
48
 
49
+ /**
50
+ * Handle intersection with viewport
51
+ * @param {IntersectionObserverEntry} entry - Intersection observer entry
52
+ */
32
53
  intersectionHandler(entry) {
33
54
  const id = entry.target.dataset.scrollspyTrigger
34
55
  const currentlyActive = document.querySelector('[data-scrollspy-active]')
@@ -1,4 +1,4 @@
1
- import { gsap } from 'gsap'
1
+ import { set } from '../../utils/motion-helpers'
2
2
  import _defaultsDeep from 'lodash.defaultsdeep'
3
3
 
4
4
  const DEFAULT_OPTIONS = {}
@@ -13,14 +13,14 @@ export default class StackedBoxes {
13
13
  initialize() {
14
14
  const boxes = document.querySelectorAll('[data-boxes-stacked]')
15
15
 
16
- const observer = new IntersectionObserver(entries => {
16
+ const observer = new IntersectionObserver((entries) => {
17
17
  const [{ isIntersecting, target }] = entries
18
18
  if (isIntersecting) {
19
19
  this.adjustBox(target)
20
20
  }
21
21
  })
22
22
 
23
- Array.from(boxes).forEach(box => {
23
+ Array.from(boxes).forEach((box) => {
24
24
  observer.observe(box)
25
25
  })
26
26
  }
@@ -53,17 +53,19 @@ export default class StackedBoxes {
53
53
  break
54
54
 
55
55
  default:
56
- console.error('==> JUPITER/STACKEDBOXES: `data-boxes-stacked-pull` has wrong value')
56
+ console.error(
57
+ '==> JUPITER/STACKEDBOXES: `data-boxes-stacked-pull` has wrong value'
58
+ )
57
59
  }
58
60
  this.pull(pull, pullPx)
59
61
  }
60
62
  }
61
63
 
62
64
  pull(box, amnt) {
63
- gsap.set(box, { y: amnt * -1, marginBottom: amnt * -1 })
65
+ set(box, { y: amnt * -1, marginBottom: amnt * -1 })
64
66
  }
65
67
 
66
68
  size(target, src) {
67
- gsap.set(target, { height: src.clientHeight })
69
+ set(target, { height: src.clientHeight })
68
70
  }
69
71
  }