@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
@@ -0,0 +1,238 @@
1
+ /**
2
+ * URL synchronization module for Dataloader
3
+ * Handles bidirectional sync between dataloader parameters and browser URL
4
+ */
5
+ export default class DataloaderUrlSync {
6
+ constructor(dataloader, config) {
7
+ this.dataloader = dataloader
8
+ this.config = config
9
+ this.language = document.documentElement.lang || 'en'
10
+
11
+ // Language handling options
12
+ this.languageInPath = config.languageInPath || false
13
+ this.hideDefaultLanguage = config.hideDefaultLanguage !== false // default true
14
+ this.defaultLanguage = config.defaultLanguage || 'en'
15
+
16
+ // Configure values that should be omitted from URLs (like "all" filters)
17
+ this.omitFromUrl = config.omitFromUrl || {
18
+ en: ['all'],
19
+ no: ['alle'],
20
+ // Add more languages as needed
21
+ }
22
+
23
+ this.initialize()
24
+ }
25
+
26
+ initialize() {
27
+ // Set initial state from URL
28
+ this.syncFromUrl()
29
+
30
+ // Listen for browser navigation
31
+ this.popstateHandler = () => {
32
+ this.syncFromUrl()
33
+ this.dataloader.fetch()
34
+ }
35
+ window.addEventListener('popstate', this.popstateHandler)
36
+ }
37
+
38
+ /**
39
+ * Build URL from parameters using template
40
+ */
41
+ buildUrl(params) {
42
+ // Use custom buildUrl function if provided
43
+ if (typeof this.config.buildUrl === 'function') {
44
+ return this.config.buildUrl(params, this.language, window.location.pathname)
45
+ }
46
+
47
+ const template = this.config.templates[this.language] ||
48
+ this.config.templates[this.defaultLanguage] ||
49
+ Object.values(this.config.templates)[0] // Fallback to first template
50
+
51
+ if (!template) {
52
+ console.error('No URL template found for dataloader:', this.dataloader.id)
53
+ return window.location.pathname
54
+ }
55
+
56
+ // Replace tokens in template
57
+ let url = template
58
+ Object.entries(params).forEach(([key, value]) => {
59
+ // Check if this value should be omitted from URL for the current language
60
+ const omitValues = this.omitFromUrl[this.language] || this.omitFromUrl[this.defaultLanguage] || []
61
+ const shouldOmit = !value || omitValues.includes(value)
62
+
63
+ url = url.replace(`:${key}`, shouldOmit ? '' : value)
64
+ })
65
+
66
+ // Clean up any remaining tokens and trailing slashes
67
+ url = url.replace(/\/:[^\/]+/g, '')
68
+ url = url.replace(/\/+$/, '') // Remove trailing slashes
69
+
70
+ // Ensure we don't end up with empty path
71
+ if (!url || url === '') {
72
+ url = template.split(':')[0].replace(/\/+$/, '') || '/'
73
+ }
74
+
75
+ // Add language prefix if configured
76
+ if (this.languageInPath) {
77
+ const shouldAddLang = this.language !== this.defaultLanguage || !this.hideDefaultLanguage
78
+ if (shouldAddLang && !url.startsWith(`/${this.language}/`)) {
79
+ url = `/${this.language}${url}`
80
+ }
81
+ }
82
+
83
+ return url
84
+ }
85
+
86
+ /**
87
+ * Parse current URL and extract parameters based on template
88
+ */
89
+ parseUrl() {
90
+ // Use custom parseUrl function if provided
91
+ if (typeof this.config.parseUrl === 'function') {
92
+ return this.config.parseUrl(window.location.pathname, this.language)
93
+ }
94
+
95
+ const template = this.config.templates[this.language] ||
96
+ this.config.templates[this.defaultLanguage] ||
97
+ Object.values(this.config.templates)[0]
98
+
99
+ if (!template) {
100
+ return {}
101
+ }
102
+
103
+ let path = window.location.pathname
104
+
105
+ // Strip language prefix if configured
106
+ if (this.languageInPath) {
107
+ const langPrefix = `/${this.language}/`
108
+ if (path.startsWith(langPrefix)) {
109
+ path = path.substring(langPrefix.length - 1)
110
+ } else if (this.hideDefaultLanguage && path.startsWith('/')) {
111
+ // Path might not have language prefix if it's the default language
112
+ // Continue with the path as-is
113
+ }
114
+ }
115
+
116
+ // Convert template to regex pattern
117
+ // First escape special regex characters (except for our tokens)
118
+ let pattern = template.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
119
+
120
+ // Extract parameter names from template
121
+ const paramNames = []
122
+ pattern = pattern.replace(/:(\w+)/g, (match, paramName) => {
123
+ paramNames.push(paramName)
124
+ return '([^/]+)'
125
+ })
126
+
127
+ // Make trailing parameters optional by replacing them with optional groups
128
+ const templateParts = template.split('/')
129
+ const pathParts = path.split('/')
130
+
131
+ // If path is shorter than template, try matching partial patterns
132
+ if (pathParts.length <= templateParts.length) {
133
+ const partialTemplate = templateParts.slice(0, pathParts.length).join('/')
134
+ let partialPattern = partialTemplate.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
135
+ const partialParamNames = []
136
+ partialPattern = partialPattern.replace(/:(\w+)/g, (match, paramName) => {
137
+ partialParamNames.push(paramName)
138
+ return '([^/]+)'
139
+ })
140
+
141
+ const partialRegex = new RegExp(`^${partialPattern}$`)
142
+ const partialMatch = path.match(partialRegex)
143
+
144
+ if (partialMatch) {
145
+ // Build params object from partial matches
146
+ const params = {}
147
+ partialParamNames.forEach((name, index) => {
148
+ if (partialMatch[index + 1]) {
149
+ params[name] = decodeURIComponent(partialMatch[index + 1])
150
+ }
151
+ })
152
+ return params
153
+ }
154
+ }
155
+
156
+ // Try full pattern match
157
+ const regex = new RegExp(`^${pattern}$`)
158
+ const match = path.match(regex)
159
+
160
+ if (!match) {
161
+ return {}
162
+ }
163
+
164
+ // Build params object from matches
165
+ const params = {}
166
+ paramNames.forEach((name, index) => {
167
+ if (match[index + 1]) {
168
+ params[name] = decodeURIComponent(match[index + 1])
169
+ }
170
+ })
171
+
172
+ return params
173
+ }
174
+
175
+ /**
176
+ * Update browser URL with current parameters
177
+ */
178
+ updateUrl(params) {
179
+ const url = this.buildUrl(params)
180
+
181
+ // Only update if URL actually changed
182
+ if (url !== window.location.pathname) {
183
+ history.pushState(
184
+ { dataloaderId: this.dataloader.id, params },
185
+ '',
186
+ url
187
+ )
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Sync dataloader parameters from current URL
193
+ */
194
+ syncFromUrl() {
195
+ const params = this.parseUrl()
196
+
197
+ // Clear all current selections first
198
+ this.dataloader.$paramEls.forEach($el => {
199
+ $el.removeAttribute('data-loader-param-selected')
200
+ })
201
+
202
+ // Clear internal state
203
+ this.dataloader.opts.loaderParam = {}
204
+
205
+ // Update dataloader parameters from URL
206
+ Object.entries(params).forEach(([key, value]) => {
207
+ // Find the corresponding data-loader-param element
208
+ const $paramEl = this.dataloader.$paramEls.find($el => {
209
+ const paramKey = $el.dataset.loaderParamKey || 'defaultParam'
210
+ return paramKey === key && $el.dataset.loaderParam === value
211
+ })
212
+
213
+
214
+ if ($paramEl) {
215
+ // Mark as selected
216
+ $paramEl.setAttribute('data-loader-param-selected', '')
217
+ }
218
+
219
+ // Always update internal state, even if no UI element found
220
+ // This ensures the API gets the parameter for filtering
221
+ if (key === 'defaultParam') {
222
+ this.dataloader.opts.loaderParam.defaultParam = value
223
+ } else {
224
+ this.dataloader.opts.loaderParam[key] = value
225
+ }
226
+ })
227
+
228
+ }
229
+
230
+ /**
231
+ * Clean up event listeners
232
+ */
233
+ destroy() {
234
+ if (this.popstateHandler) {
235
+ window.removeEventListener('popstate', this.popstateHandler)
236
+ }
237
+ }
238
+ }
@@ -1,4 +1,10 @@
1
+ /**
2
+ * DOM utility class for simplifying DOM operations
3
+ */
1
4
  class DOM {
5
+ /**
6
+ * Create a new DOM utility instance
7
+ */
2
8
  constructor() {
3
9
  this.body = document.body
4
10
  this.html = document.documentElement
@@ -157,6 +163,24 @@ class DOM {
157
163
 
158
164
  return vertInView && horInView
159
165
  }
166
+
167
+ /**
168
+ * Strict viewport check - element must be fully contained within viewport bounds
169
+ * Useful for popovers/tooltips that need to be completely visible
170
+ *
171
+ * @param {*} el
172
+ */
173
+ inViewportStrict(el) {
174
+ const rect = el.getBoundingClientRect()
175
+ const windowHeight = window.innerHeight || document.documentElement.clientHeight
176
+ const windowWidth = window.innerWidth || document.documentElement.clientWidth
177
+
178
+ // Element must be fully within viewport bounds
179
+ const vertInView = rect.top >= 0 && rect.bottom <= windowHeight
180
+ const horInView = rect.left >= 0 && rect.right <= windowWidth
181
+
182
+ return vertInView && horInView
183
+ }
160
184
  }
161
185
 
162
186
  export default new DOM()