@brandocms/jupiter 3.54.3 → 3.54.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brandocms/jupiter",
3
- "version": "3.54.3",
3
+ "version": "3.54.4",
4
4
  "description": "Frontend helpers.",
5
5
  "author": "Univers/Twined",
6
6
  "license": "UNLICENSED",
@@ -49,6 +49,7 @@ export default class Dropdown {
49
49
  this.open = false
50
50
  this.element = opts.el
51
51
  this.timeline = gsap.timeline({ paused: true, reversed: true })
52
+
52
53
  this.elements.trigger = Dom.find(this.element, this.opts.selectors.trigger)
53
54
  if (this.elements.trigger.hasAttribute('data-dropdown-target')) {
54
55
  const dropdownTarget = this.elements.trigger.getAttribute(
@@ -58,10 +59,15 @@ export default class Dropdown {
58
59
  } else {
59
60
  this.elements.menu = Dom.find(this.element, this.opts.selectors.menu)
60
61
  }
62
+
61
63
  this.elements.menuItems = Dom.all(
62
64
  this.elements.menu,
63
65
  this.opts.selectors.menuItems
64
66
  )
67
+
68
+ // Bind the document click handler to this instance
69
+ this.handleDocumentClick = this.handleDocumentClick.bind(this)
70
+
65
71
  this.initialize()
66
72
  this.checkForInitialOpen()
67
73
  }
@@ -76,7 +82,6 @@ export default class Dropdown {
76
82
  className: `${this.elements.menu.className} zero-height`,
77
83
  duration: 0.1,
78
84
  },
79
-
80
85
  'open'
81
86
  )
82
87
  .to(
@@ -88,26 +93,41 @@ export default class Dropdown {
88
93
  'open'
89
94
  )
90
95
  .call(() => {
91
- // check if we have space
92
- const subMenuBound = this.elements.menu.getBoundingClientRect()
93
- const windowHeight = window.innerHeight
94
-
95
- const subMenuY = subMenuBound.y
96
- const subMenuHeight = subMenuBound.height
97
-
96
+ // Get current bounds and viewport dimensions
97
+ const menuRect = this.elements.menu.getBoundingClientRect()
98
+ const viewportHeight = window.innerHeight
99
+ const viewportWidth = window.innerWidth
100
+ const menuHeight = menuRect.height
101
+ const menuTop = menuRect.top
102
+
103
+ // Update CSS variable for height (if used in your styles)
98
104
  Dom.setCSSVar(
99
105
  'dropdown-menu-height',
100
- `${subMenuHeight}px`,
106
+ `${menuHeight}px`,
101
107
  this.elements.menu
102
108
  )
103
109
 
104
- if (subMenuHeight + subMenuY > windowHeight) {
110
+ // Vertical placement: if the menu overflows the bottom, set placement to "top"
111
+ if (menuHeight + menuTop > viewportHeight) {
105
112
  this.elements.menu.setAttribute('data-dropdown-placement', 'top')
106
113
  } else {
107
114
  this.elements.menu.setAttribute('data-dropdown-placement', 'bottom')
108
115
  }
116
+
117
+ // Horizontal check: adjust left offset if the menu is offscreen
118
+ const computedStyle = window.getComputedStyle(this.elements.menu)
119
+ let currentLeft = parseFloat(computedStyle.left) || 0
120
+
121
+ if (menuRect.left < 0) {
122
+ // Shift right by the amount it’s off the left edge
123
+ this.elements.menu.style.left = `${currentLeft - menuRect.left}px`
124
+ } else if (menuRect.right > viewportWidth) {
125
+ // Shift left by the amount it’s off the right edge
126
+ this.elements.menu.style.left = `${currentLeft - (menuRect.right - viewportWidth)}px`
127
+ }
109
128
  })
110
129
  .to(this.elements.menu, { opacity: 1 })
130
+
111
131
  if (this.elements.menuItems.length) {
112
132
  this.timeline.from(
113
133
  this.elements.menuItems,
@@ -147,6 +167,9 @@ export default class Dropdown {
147
167
  this.open = true
148
168
  this.elements.trigger.dataset.dropdownActive = ''
149
169
 
170
+ // Add document click listener when menu is open.
171
+ document.addEventListener('click', this.handleDocumentClick)
172
+
150
173
  if (this.timeline.reversed()) {
151
174
  await this.timeline.play()
152
175
  } else {
@@ -159,6 +182,9 @@ export default class Dropdown {
159
182
  this.open = false
160
183
  delete this.elements.trigger.dataset.dropdownActive
161
184
 
185
+ // Remove the document click listener when menu closes.
186
+ document.removeEventListener('click', this.handleDocumentClick)
187
+
162
188
  if (this.timeline.reversed()) {
163
189
  await this.timeline.play()
164
190
  } else {
@@ -166,6 +192,15 @@ export default class Dropdown {
166
192
  }
167
193
  }
168
194
 
195
+ // Handler that checks if a click was outside the dropdown element.
196
+ handleDocumentClick(event) {
197
+ // If the click target is not inside the dropdown, close the menu.
198
+ if (!this.element.contains(event.target)) {
199
+ // this.closeMenu()
200
+ this.onClick(event)
201
+ }
202
+ }
203
+
169
204
  checkForInitialOpen() {
170
205
  if (this.elements.trigger.hasAttribute('data-dropdown-active')) {
171
206
  this.openMenu()
@@ -52,6 +52,11 @@ const DEFAULT_OPTIONS = {
52
52
  */
53
53
  clearMoonwalkOnAnchors: true,
54
54
 
55
+ /**
56
+ * If an element with moonwalk-run also has moonwalk-section, print a console warning
57
+ */
58
+ warnRunWithSection: true,
59
+
55
60
  /**
56
61
  * Determines how early the IntersectionObserver triggers
57
62
  */
@@ -120,6 +125,17 @@ export default class Moonwalk {
120
125
  .forEach((ms) => ms.removeAttribute('data-moonwalk'))
121
126
  }
122
127
 
128
+ if (this.opts.warnRunWithSection) {
129
+ container
130
+ .querySelectorAll('[data-moonwalk-run][data-moonwalk-section]')
131
+ .forEach((ms) =>
132
+ console.warn(
133
+ 'Element with moonwalk-run also has moonwalk-section. This may lead to rendering issues.',
134
+ ms
135
+ )
136
+ )
137
+ }
138
+
123
139
  if (this.opts.clearMoonwalkOnAnchors) {
124
140
  if (window.location.hash) {
125
141
  this.walkToThisPoint(window.location.hash)