well-petal 0.0.22 → 0.0.23

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.
@@ -0,0 +1,124 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Petal Popup Demo</title>
7
+ <link rel="stylesheet" href="styles.css" />
8
+ </head>
9
+ <body>
10
+ <!-- Banner at top of page -->
11
+ <div petal-el="banner" petal="demo-banner" petal-session-ttl="5" class="banner">
12
+ <div class="banner-content">
13
+ <p>🎉 Welcome to Petal! This banner remembers you closed it for 5 minutes (set via petal-session-ttl).</p>
14
+ <button petal-el="banner-close" class="banner-close" aria-label="Close banner">×</button>
15
+ </div>
16
+ </div>
17
+
18
+ <div class="container">
19
+ <h1>Petal Popup Demo</h1>
20
+ <p>Click the buttons below to test different popup animations and configurations.</p>
21
+
22
+ <div class="buttons">
23
+ <!-- Scale Up Popup Trigger -->
24
+ <button petal-el="open" petal="scale-popup" class="btn">Open Scale Popup</button>
25
+
26
+ <!-- Slide Down Popup Trigger -->
27
+ <button petal-el="open" petal="slide-down-popup" class="btn">Open Slide Down Popup</button>
28
+
29
+ <!-- Slide Left Popup Trigger -->
30
+ <button petal-el="open" petal="slide-left-popup" class="btn">Open Slide Left Popup</button>
31
+
32
+ <!-- Mobile Responsive Popup Trigger -->
33
+ <button petal-el="open" petal="responsive-popup" class="btn">Open Responsive Popup</button>
34
+
35
+ <!-- Different Open/Close Animation Trigger -->
36
+ <button petal-el="open" petal="custom-mask-popup" class="btn">Open Different Animations Popup</button>
37
+ </div>
38
+ </div>
39
+
40
+ <!-- Scale Up Popup -->
41
+ <div petal-el="popup" petal="scale-popup" petal-anim-open="scale" petal-duration="0.5" petal-slot-opacity="0.5" class="popup">
42
+ <div petal-el="mask" petal-mask-opacity="0.15" class="mask"></div>
43
+ <div petal-el="slot" class="slot">
44
+ <div class="popup-content">
45
+ <h2>Scale Popup</h2>
46
+ <p>This popup uses a scale animation (auto-reverses on close).</p>
47
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
48
+ <button petal-el="close" petal="scale-popup" class="btn btn-close">Close</button>
49
+ </div>
50
+ </div>
51
+ </div>
52
+
53
+ <!-- Slide Down Popup -->
54
+ <div petal-el="popup" petal="slide-down-popup" petal-anim-open="slide-down" petal-duration="0.6" petal-mask-close="true" class="popup">
55
+ <div petal-el="mask" petal-mask-opacity="0.2" class="mask"></div>
56
+ <div petal-el="slot" class="slot">
57
+ <div class="popup-content">
58
+ <h2>Slide Down Popup</h2>
59
+ <p>This popup slides down from the top when opening, slides up to the top when closing.</p>
60
+ <p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
61
+ <button petal-el="close" petal="slide-down-popup" class="btn btn-close">Close</button>
62
+ </div>
63
+ </div>
64
+ </div>
65
+
66
+ <!-- Slide Left Popup -->
67
+ <div petal-el="popup" petal="slide-left-popup" petal-anim-open="slide-left" petal-duration="0.6" class="popup">
68
+ <div petal-el="mask" petal-mask-opacity="0.2" class="mask"></div>
69
+ <div petal-el="slot" class="slot">
70
+ <div class="popup-content">
71
+ <h2>Slide Left Popup</h2>
72
+ <p>This popup slides in from the left when opening, slides up to the top when closing.</p>
73
+ <p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
74
+ <button petal-el="close" petal="slide-left-popup" class="btn btn-close">Close</button>
75
+ </div>
76
+ </div>
77
+ </div>
78
+
79
+ <!-- Responsive Popup (Different animations for mobile/desktop) -->
80
+ <div petal-el="popup" petal="responsive-popup" petal-anim-open="scale" petal-anim-open-mobile="slide-up" petal-duration="0.5" class="popup">
81
+ <div petal-el="mask" petal-mask-opacity="0.3" class="mask"></div>
82
+ <div petal-el="slot" class="slot">
83
+ <div class="popup-content">
84
+ <h2>Responsive Popup</h2>
85
+ <p>This popup uses scale on desktop and slide-up (from bottom) on mobile.</p>
86
+ <p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>
87
+ <button petal-el="close" petal="responsive-popup" class="btn btn-close">Close</button>
88
+ </div>
89
+ </div>
90
+ </div>
91
+
92
+ <!-- Different Open/Close Animations -->
93
+ <div petal-el="popup" petal="custom-mask-popup" petal-anim-open="scale" petal-anim-close="slide-up" petal-duration="0.5" class="popup">
94
+ <div petal-el="mask" petal-mask-opacity="0.6" class="mask"></div>
95
+ <div petal-el="slot" class="slot">
96
+ <div class="popup-content">
97
+ <h2>Different Open/Close Animations</h2>
98
+ <p>This popup scales when opening and slides down (to the bottom) when closing!</p>
99
+ <p>Darker background mask (opacity: 0.6).</p>
100
+ <button petal-el="close" petal="custom-mask-popup" class="btn btn-close">Close</button>
101
+ </div>
102
+ </div>
103
+ </div>
104
+
105
+ <!-- Load GSAP -->
106
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
107
+
108
+ <!-- Load Petal -->
109
+ <script src="../dist/petal.js"></script>
110
+
111
+ <!-- Initialize Petal -->
112
+ <script>
113
+ // Initialize all popups and banners when page loads
114
+ if (window.petal) {
115
+ if (window.petal.initializeAllPopups) {
116
+ window.petal.initializeAllPopups();
117
+ }
118
+ if (window.petal.initializeBanner) {
119
+ window.petal.initializeBanner();
120
+ }
121
+ }
122
+ </script>
123
+ </body>
124
+ </html>
@@ -0,0 +1,244 @@
1
+ /* Reset and Base Styles */
2
+ * {
3
+ margin: 0;
4
+ padding: 0;
5
+ box-sizing: border-box;
6
+ }
7
+
8
+ body {
9
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
10
+ line-height: 1.6;
11
+ color: #333;
12
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
13
+ min-height: 100vh;
14
+ padding: 20px;
15
+ }
16
+
17
+ /* Banner Styles */
18
+ .banner {
19
+ position: fixed;
20
+ top: 0;
21
+ left: 0;
22
+ right: 0;
23
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
24
+ color: white;
25
+ z-index: 10000;
26
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
27
+ }
28
+
29
+ .banner-content {
30
+ max-width: 1200px;
31
+ margin: 0 auto;
32
+ padding: 12px 20px;
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: space-between;
36
+ gap: 20px;
37
+ }
38
+
39
+ .banner-content p {
40
+ margin: 0;
41
+ color: white;
42
+ font-size: 14px;
43
+ font-weight: 500;
44
+ }
45
+
46
+ .banner-close {
47
+ background: rgba(255, 255, 255, 0.2);
48
+ border: none;
49
+ color: white;
50
+ font-size: 24px;
51
+ width: 32px;
52
+ height: 32px;
53
+ border-radius: 50%;
54
+ cursor: pointer;
55
+ display: flex;
56
+ align-items: center;
57
+ justify-content: center;
58
+ transition: all 0.2s ease;
59
+ flex-shrink: 0;
60
+ line-height: 1;
61
+ padding: 0;
62
+ }
63
+
64
+ .banner-close:hover {
65
+ background: rgba(255, 255, 255, 0.3);
66
+ transform: scale(1.1);
67
+ }
68
+
69
+ .banner-close:active {
70
+ transform: scale(0.95);
71
+ }
72
+
73
+ /* Adjust body padding when banner is visible */
74
+ body {
75
+ padding-top: 70px;
76
+ }
77
+
78
+ /* Container */
79
+ .container {
80
+ max-width: 800px;
81
+ margin: 0 auto;
82
+ background: white;
83
+ padding: 40px;
84
+ border-radius: 12px;
85
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
86
+ }
87
+
88
+ h1 {
89
+ color: #667eea;
90
+ margin-bottom: 10px;
91
+ font-size: 2.5rem;
92
+ }
93
+
94
+ p {
95
+ color: #666;
96
+ margin-bottom: 20px;
97
+ }
98
+
99
+ /* Buttons */
100
+ .buttons {
101
+ display: flex;
102
+ flex-wrap: wrap;
103
+ gap: 15px;
104
+ margin-top: 30px;
105
+ }
106
+
107
+ .btn {
108
+ padding: 12px 24px;
109
+ background: #667eea;
110
+ color: white;
111
+ border: none;
112
+ border-radius: 8px;
113
+ font-size: 16px;
114
+ font-weight: 600;
115
+ cursor: pointer;
116
+ transition: all 0.3s ease;
117
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
118
+ }
119
+
120
+ .btn:hover {
121
+ background: #5568d3;
122
+ transform: translateY(-2px);
123
+ box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);
124
+ }
125
+
126
+ .btn:active {
127
+ transform: translateY(0);
128
+ }
129
+
130
+ /* Popup Structure */
131
+ .popup {
132
+ position: fixed;
133
+ top: 0;
134
+ left: 0;
135
+ width: 100%;
136
+ height: 100%;
137
+ display: none;
138
+ align-items: center;
139
+ justify-content: center;
140
+ z-index: 9999;
141
+ }
142
+
143
+ /* Mask (Background Overlay) */
144
+ .mask {
145
+ position: absolute;
146
+ top: 0;
147
+ left: 0;
148
+ width: 100%;
149
+ height: 100%;
150
+ background: black;
151
+ opacity: 0;
152
+ }
153
+
154
+ /* Slot (Popup Content Container) */
155
+ .slot {
156
+ position: relative;
157
+ z-index: 10000;
158
+ max-width: 90%;
159
+ max-height: 90vh;
160
+ overflow: auto;
161
+ opacity: 0;
162
+ }
163
+
164
+ /* Popup Content */
165
+ .popup-content {
166
+ background: white;
167
+ padding: 40px;
168
+ border-radius: 16px;
169
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
170
+ max-width: 500px;
171
+ width: 100%;
172
+ }
173
+
174
+ .popup-content h2 {
175
+ color: #667eea;
176
+ margin-bottom: 15px;
177
+ font-size: 2rem;
178
+ }
179
+
180
+ .popup-content p {
181
+ margin-bottom: 15px;
182
+ line-height: 1.8;
183
+ }
184
+
185
+ .popup-content p:last-of-type {
186
+ margin-bottom: 25px;
187
+ }
188
+
189
+ /* Close Button */
190
+ .btn-close {
191
+ background: #dc3545;
192
+ width: 100%;
193
+ }
194
+
195
+ .btn-close:hover {
196
+ background: #c82333;
197
+ }
198
+
199
+ /* Mobile Responsive */
200
+ @media (max-width: 768px) {
201
+ .container {
202
+ padding: 20px;
203
+ }
204
+
205
+ h1 {
206
+ font-size: 2rem;
207
+ }
208
+
209
+ .buttons {
210
+ flex-direction: column;
211
+ }
212
+
213
+ .btn {
214
+ width: 100%;
215
+ }
216
+
217
+ .popup-content {
218
+ padding: 30px 20px;
219
+ max-width: 95%;
220
+ }
221
+
222
+ .popup-content h2 {
223
+ font-size: 1.5rem;
224
+ }
225
+ }
226
+
227
+ /* Scrollbar Styling for Popup Slot */
228
+ .slot::-webkit-scrollbar {
229
+ width: 8px;
230
+ }
231
+
232
+ .slot::-webkit-scrollbar-track {
233
+ background: #f1f1f1;
234
+ border-radius: 10px;
235
+ }
236
+
237
+ .slot::-webkit-scrollbar-thumb {
238
+ background: #667eea;
239
+ border-radius: 10px;
240
+ }
241
+
242
+ .slot::-webkit-scrollbar-thumb:hover {
243
+ background: #5568d3;
244
+ }
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "well-petal",
3
- "version": "0.0.22",
3
+ "version": "0.0.23",
4
4
  "description": "Webflow Popups powered by attributes",
5
5
  "main": "petal.js",
6
6
  "scripts": {
7
7
  "test": "echo \"Error: no test specified\" && exit 1",
8
8
  "build": "webpack --config webpack.config.js",
9
+ "dev": "webpack serve --config webpack.config.js",
9
10
  "tsc": "tsc --noEmit"
10
11
  },
11
12
  "keywords": [],
@@ -24,7 +25,9 @@
24
25
  "prettier": "^3.5.3",
25
26
  "ts-loader": "^9.5.1",
26
27
  "typescript": "^5.7.3",
27
- "webpack-cli": "^5.1.4"
28
+ "webpack": "^5.96.1",
29
+ "webpack-cli": "^5.1.4",
30
+ "webpack-dev-server": "^5.2.0"
28
31
  },
29
32
  "peerDependencies": {
30
33
  "gsap": "^3.13.0"
package/src/animation.ts CHANGED
@@ -1,92 +1,99 @@
1
- // Popup animation types
2
- export type PopupAnimation = "scale-up" | "scale-down" | "slide-up" | "slide-down" | "slide-left" | "slide-right";
3
-
4
- const validAnimations: PopupAnimation[] = ["scale-up", "scale-down", "slide-up", "slide-down", "slide-left", "slide-right"];
1
+ import {
2
+ animateScaleUp,
3
+ animateOpenSlideUp,
4
+ animateOpenSlideDown,
5
+ animateOpenSlideLeft,
6
+ animateOpenSlideRight,
7
+ animateScaleDown,
8
+ animateCloseSlideUp,
9
+ animateCloseSlideDown,
10
+ animateCloseSlideLeft,
11
+ animateCloseSlideRight,
12
+ } from "./lib/animations";
13
+ import { ATTR_PETAL_ANIM_OPEN, ATTR_PETAL_ANIM_CLOSE, ATTR_PETAL_ANIM_OPEN_MOBILE, ATTR_PETAL_ANIM_CLOSE_MOBILE, ATTR_PETAL_DURATION } from "./lib/attributes";
14
+ import { isMobile } from "./lib/breakpoints";
5
15
 
6
- const animationMap: Record<PopupAnimation, [(show: boolean) => GSAPTweenVars, (show: boolean) => GSAPTweenVars]> = {
7
- "scale-up": [animateScaleUp, animateScaleDown],
8
- "scale-down": [animateScaleDown, animateScaleUp],
9
- "slide-up": [animateSlideUp, animateSlideDown],
10
- "slide-down": [animateSlideDown, animateSlideUp],
11
- "slide-left": [animateSlideLeft, animateSlideRight],
12
- "slide-right": [animateSlideRight, animateSlideLeft],
16
+ // Popup animation types
17
+ export type PopupAnimation = "scale" | "slide-up" | "slide-down" | "slide-left" | "slide-right";
18
+ const validAnimations: PopupAnimation[] = ["scale", "slide-up", "slide-down", "slide-left", "slide-right"];
19
+
20
+ const animationOpenMap: Record<PopupAnimation, () => GSAPTweenVars> = {
21
+ scale: animateScaleUp,
22
+ "slide-up": animateOpenSlideUp,
23
+ "slide-down": animateOpenSlideDown,
24
+ "slide-left": animateOpenSlideLeft,
25
+ "slide-right": animateOpenSlideRight,
13
26
  };
14
27
 
15
- export function getAnimationNew(popup: HTMLElement, open: boolean): GSAPTweenVars {
16
- // Popup Settings
17
- const animationDesktop = getPopupAnimation(popup.getAttribute("petal-anim"));
18
- const animationMobile = getPopupAnimation(popup.getAttribute("petal-anim-mobile")) || animationDesktop;
19
- const duration = parseFloat(popup.getAttribute("petal-duration") || "0.5");
20
-
21
- const animation = isMobile() ? animationMobile : animationDesktop;
22
-
23
- const [normal, reverse] = animationMap[animation];
24
- const gsapAnim = open ? reverse(false) : normal(true);
25
- gsapAnim.to.duration = duration;
26
- return gsapAnim;
27
- }
28
-
29
- // Individual animations
30
- function animateScaleUp(show: boolean): GSAPTweenVars {
31
- return {
32
- from: { scale: 0 },
33
- to: { scale: 1, opacity: show ? 1 : 0, ease: "power3.inOut" },
34
- };
35
- }
36
-
37
- function animateScaleDown(show: boolean): GSAPTweenVars {
38
- return {
39
- from: { scale: 1 },
40
- to: { scale: 0, opacity: show ? 1 : 0, ease: "power3.inOut" },
41
- };
42
- }
43
-
44
- function animateSlideUp(show: boolean): GSAPTweenVars {
45
- return {
46
- from: { y: "100%", x: "0%" },
47
- to: { y: "0%", opacity: show ? 1 : 0, ease: "power3.inOut" },
48
- };
49
- }
50
-
51
- function animateSlideDown(show: boolean): GSAPTweenVars {
52
- return {
53
- from: { y: "0%", x: "0%" },
54
- to: { y: "100%", opacity: show ? 1 : 0, ease: "power3.inOut" },
55
- };
56
- }
57
-
58
- function animateSlideRight(show: boolean): GSAPTweenVars {
59
- return {
60
- from: { x: "0%", y: "0%" },
61
- to: { x: "100%", opacity: show ? 1 : 0, ease: "power3.inOut" },
62
- };
63
- }
64
-
65
- function animateSlideLeft(show: boolean): GSAPTweenVars {
66
- return {
67
- from: { x: "100%", y: "0%" },
68
- to: { x: "0%", opacity: show ? 1 : 0, ease: "power3.inOut" },
69
- };
70
- }
28
+ const animationCloseMap: Record<PopupAnimation, () => GSAPTweenVars> = {
29
+ scale: animateScaleDown,
30
+ "slide-up": animateCloseSlideUp,
31
+ "slide-down": animateCloseSlideDown,
32
+ "slide-left": animateCloseSlideLeft,
33
+ "slide-right": animateCloseSlideRight,
34
+ };
71
35
 
72
- // Reusable mask animations
73
- export function animateMaskOpen(opacity: number): GSAPTweenVars {
74
- return {
75
- from: { opacity: 0 },
76
- to: { opacity: opacity, duration: 0.5 },
77
- };
78
- }
36
+ // Map open animations to their corresponding close animations
37
+ const reverseAnimationMap: Record<PopupAnimation, PopupAnimation> = {
38
+ scale: "scale",
39
+ "slide-up": "slide-down",
40
+ "slide-down": "slide-up",
41
+ "slide-left": "slide-right",
42
+ "slide-right": "slide-left",
43
+ };
79
44
 
80
- export function animateMaskClosed(opacity: number): GSAPTweenVars {
81
- return {
82
- from: { opacity: opacity },
83
- to: { opacity: 0, duration: 0.5 },
84
- };
45
+ /**
46
+ * Creates GSAP animation based on the popup's attributes
47
+ * @param popup The Popup element
48
+ * @param direction Open or Close
49
+ * @returns GSAP animation vars for the popup
50
+ */
51
+ export function getPopupGSAPAnimation(popup: HTMLElement, direction: "open" | "close"): GSAPTweenVars {
52
+ // Determine the name of the animation to perform
53
+ let animationName = getAnimationName(popup, direction);
54
+
55
+ // Get the animation function
56
+ let anim;
57
+ if (direction === "open") anim = animationOpenMap[animationName]();
58
+ else anim = animationCloseMap[animationName]();
59
+
60
+ // Set the animation duration
61
+ const duration = parseFloat(popup.getAttribute(ATTR_PETAL_DURATION) || "0.5");
62
+ anim.to.duration = duration;
63
+
64
+ return anim;
85
65
  }
86
66
 
87
- // Device check
88
- function isMobile(): boolean {
89
- return window.innerWidth <= 768;
67
+ /**
68
+ *
69
+ * @param popup The Popup element
70
+ * @param direction Open or Close
71
+ * @returns The name of the animation to perform
72
+ */
73
+ function getAnimationName(popup: HTMLElement, direction: "open" | "close"): PopupAnimation {
74
+ const openDesktop = getPopupAnimation(popup, ATTR_PETAL_ANIM_OPEN);
75
+ const openMobile = getPopupAnimation(popup, ATTR_PETAL_ANIM_OPEN_MOBILE);
76
+
77
+ const closeDesktop = getPopupAnimation(popup, ATTR_PETAL_ANIM_CLOSE);
78
+ const closeMobile = getPopupAnimation(popup, ATTR_PETAL_ANIM_CLOSE_MOBILE);
79
+
80
+ if (direction === "open") {
81
+ if (isMobile()) return openMobile;
82
+ else return openDesktop;
83
+ }
84
+ // If closing
85
+ else {
86
+ const hasCloseDesktop = popup.hasAttribute(ATTR_PETAL_ANIM_CLOSE);
87
+ const hasCloseMobile = popup.hasAttribute(ATTR_PETAL_ANIM_CLOSE_MOBILE);
88
+
89
+ if (isMobile()) {
90
+ // If no mobile close animation is set, use the reverse of the open animation
91
+ return hasCloseMobile ? closeMobile : reverseAnimationMap[openMobile];
92
+ } else {
93
+ // If no desktop close animation is set, use the reverse of the open animation
94
+ return hasCloseDesktop ? closeDesktop : reverseAnimationMap[openDesktop];
95
+ }
96
+ }
90
97
  }
91
98
 
92
99
  // Validate popup animation string
@@ -95,6 +102,7 @@ export function isPopupAnimation(value: string): value is PopupAnimation {
95
102
  }
96
103
 
97
104
  // Safe parser with fallback
98
- function getPopupAnimation(raw: string | null | undefined): PopupAnimation {
105
+ function getPopupAnimation(popup: HTMLElement, attr: string): PopupAnimation {
106
+ const raw = popup.getAttribute(attr);
99
107
  return isPopupAnimation(raw ?? "") ? (raw as PopupAnimation) : "slide-up";
100
108
  }
package/src/banner.ts ADDED
@@ -0,0 +1,41 @@
1
+ import { ATTR_PETAL_BANNER, ATTR_PETAL_BANNER_CLOSE, ATTR_PETAL_NAME, ATTR_PETAL_ELEMENT, ATTR_PETAL_SESSION_TTL, PetalElements } from "./lib/attributes";
2
+ import { getAllPetalElementsOfType } from "./lib/helpers";
3
+ import { storeClosedState, checkClosedState } from "./lib/memory";
4
+
5
+ export function initializeBanner() {
6
+ const banners = getAllPetalElementsOfType(ATTR_PETAL_BANNER);
7
+ banners.forEach((banner) => {
8
+ const name = banner.getAttribute(ATTR_PETAL_NAME);
9
+ if (!name) return;
10
+
11
+ const closeButtons = banner.querySelectorAll(`[${ATTR_PETAL_ELEMENT}="${ATTR_PETAL_BANNER_CLOSE}"]`);
12
+
13
+ // Get session TTL in minutes (default: 1440 minutes = 24 hours)
14
+ const sessionTTLMinutes = parseFloat(banner.getAttribute(ATTR_PETAL_SESSION_TTL) || "1440");
15
+
16
+ // Create PetalElements object for memory functions
17
+ const petal: PetalElements = {
18
+ name,
19
+ trigger: banner,
20
+ popup: banner as HTMLElement,
21
+ mask: banner,
22
+ slot: banner
23
+ };
24
+
25
+ // Check if banner was closed and session is still valid
26
+ if (checkClosedState("banner", petal, sessionTTLMinutes)) {
27
+ // If closed and session valid, hide the banner
28
+ (banner as HTMLElement).style.display = "none";
29
+ }
30
+
31
+ // Add click event listeners to close buttons
32
+ closeButtons.forEach((closeButton) => {
33
+ closeButton.addEventListener("click", () => {
34
+ // Hide the banner
35
+ (banner as HTMLElement).style.display = "none";
36
+ // Store closed state in sessionStorage with timestamp
37
+ storeClosedState("banner", petal);
38
+ });
39
+ });
40
+ });
41
+ }