11// feedback.js
2- document . addEventListener ( "DOMContentLoaded" , ( ) => {
2+
3+ function feedbackModalInit ( ) {
4+ // Remove any previous event listeners by replacing the button and modal with clones
5+ const oldBtn = document . querySelector ( "#feedbackButton" ) ;
6+ const oldModal = document . querySelector ( "#feedbackModal" ) ;
7+ if ( oldBtn ) {
8+ const newBtn = oldBtn . cloneNode ( true ) ;
9+ oldBtn . parentNode . replaceChild ( newBtn , oldBtn ) ;
10+ }
11+ if ( oldModal ) {
12+ const newModal = oldModal . cloneNode ( true ) ;
13+ oldModal . parentNode . replaceChild ( newModal , oldModal ) ;
14+ }
15+
16+ // Now re-select after replacement
317 const feedbackButton = document . querySelector ( "#feedbackButton" ) ;
418 const modal = document . querySelector ( "#feedbackModal" ) ;
519
@@ -30,11 +44,9 @@ document.addEventListener("DOMContentLoaded", () => {
3044 }
3145
3246 function openModal ( ) {
33- // store previous active element so we can restore focus when modal closes
3447 lastActiveElement = document . activeElement ;
3548 modal . classList . remove ( "tw-hidden" ) ;
3649 calculatePosition ( ) ;
37- // focus the textarea for immediate typing if present
3850 const ta = modal . querySelector ( "textarea" ) ;
3951 if ( ta ) ta . focus ( ) ;
4052 }
@@ -44,35 +56,25 @@ document.addEventListener("DOMContentLoaded", () => {
4456 form . reset ( ) ;
4557 errorView . classList . add ( "tw-hidden" ) ;
4658 successView . classList . add ( "tw-hidden" ) ;
47- // remove layout class when hidden to avoid display conflicts
4859 successView . classList . remove ( "tw-flex" ) ;
49- // clear any inline positioning set during calculatePosition
5060 try {
5161 modal . style . top = "" ;
5262 modal . style . bottom = "" ;
53- } catch ( e ) {
54- /* ignore */
55- }
63+ } catch ( e ) { }
5664 formView . classList . remove ( "tw-hidden" ) ;
57- // restore focus to previously active element
5865 try {
5966 if ( lastActiveElement && typeof lastActiveElement . focus === "function" ) {
6067 lastActiveElement . focus ( ) ;
6168 }
62- } catch ( e ) {
63- /* ignore */
64- }
69+ } catch ( e ) { }
6570 }
6671
6772 function calculatePosition ( ) {
68- // class-based positioning like the Vue component: toggle top-full / bottom-full
6973 try {
7074 const btnRect = feedbackButton . getBoundingClientRect ( ) ;
7175 const screenHeight = window . innerHeight ;
7276 const buttonCenter = btnRect . top + btnRect . height / 2 ;
7377 const placeAbove = buttonCenter > screenHeight / 2 ;
74-
75- // rely on CSS classes and the parent .tw-relative for positioning
7678 modal . classList . remove (
7779 "tw-top-full" ,
7880 "tw-bottom-full" ,
@@ -81,22 +83,18 @@ document.addEventListener("DOMContentLoaded", () => {
8183 ) ;
8284 if ( placeAbove ) {
8385 modal . classList . add ( "tw-bottom-full" , "tw-mb-4" ) ;
84- // explicitly position above using inline style to avoid CSS specificity issues
8586 modal . style . bottom = "100%" ;
8687 modal . style . top = "" ;
8788 } else {
8889 modal . classList . add ( "tw-top-full" , "tw-mt-4" ) ;
89- // explicitly position below
9090 modal . style . top = "100%" ;
9191 modal . style . bottom = "" ;
9292 }
93- // ensure right alignment like Vue: right-0 on the modal container
9493 if ( ! modal . classList . contains ( "tw-right-0" ) )
9594 modal . classList . add ( "tw-right-0" ) ;
9695 } catch ( err ) { }
9796 }
9897
99- // wire tab clicks with keyboard navigation and ARIA handling
10098 if ( tabs && tabs . length ) {
10199 const setActiveTab = ( index ) => {
102100 tabs . forEach ( ( tb , i ) => {
@@ -137,7 +135,6 @@ document.addEventListener("DOMContentLoaded", () => {
137135 } ) ;
138136 } ) ;
139137
140- // init
141138 setActiveTab ( 0 ) ;
142139 }
143140
@@ -165,56 +162,34 @@ document.addEventListener("DOMContentLoaded", () => {
165162
166163 form . addEventListener ( "submit" , ( e ) => {
167164 e . preventDefault ( ) ;
168-
169- // First, let the browser run HTML5 validation UI (native popup) if any
170- // required fields are missing. reportValidity() will show the native
171- // validation message and return false if invalid.
172165 if ( typeof form . reportValidity === "function" ) {
173166 const ok = form . reportValidity ( ) ;
174167 if ( ! ok ) {
175- // browser showed a native message; stop submission
176168 return ;
177169 }
178170 }
179-
180- // hide any previous custom error
181171 try {
182172 errorView . classList . add ( "tw-hidden" ) ;
183- } catch ( err ) {
184- /* ignore */
185- }
186-
187- // grab textarea and read trimmed value (we already know it's non-empty)
173+ } catch ( err ) { }
188174 const ta =
189175 form . querySelector ( "textarea" ) || modal . querySelector ( "textarea" ) ;
190176 const message = ( ta && ta . value && ta . value . trim ( ) ) || "" ;
191-
192177 const data = {
193- // use the prepared hidden input value (always present)
194178 type : ( typeInput && typeInput . value ) || "Issue" ,
195179 message : message ,
196180 currentUrl : window . location . href ,
197181 userAgent : navigator . userAgent ,
198182 source : "feedback_form" ,
199183 } ;
200-
201- // Track feedback in Segment (if segment.js is loaded)
202184 if ( typeof window . trackFeedback === "function" ) {
203185 try {
204186 window . trackFeedback ( data ) ;
205- } catch ( e ) {
206- // Segment tracking error should not block submission
207- }
187+ } catch ( e ) { }
208188 }
209-
210- // show immediate success view (keeps original UX), then submit in background
211189 formView . classList . add ( "tw-hidden" ) ;
212- // ensure success view displays as flex column when visible
213190 successView . classList . add ( "tw-flex" ) ;
214191 successView . classList . remove ( "tw-hidden" ) ;
215-
216192 setTimeout ( closeModal , 1500 ) ;
217-
218193 fetch (
219194 "https://script.google.com/macros/s/AKfycby5A7NSQCmG4KIBdM0HkRP-5zpRPy8aTrQHiQoe9uG_c_rv1VCiAnnZE8co7-kofgw-hg/exec" ,
220195 {
@@ -224,7 +199,6 @@ document.addEventListener("DOMContentLoaded", () => {
224199 headers : { "Content-Type" : "application/json" } ,
225200 }
226201 ) . catch ( ( ) => {
227- // network failure: hide success and show error
228202 try {
229203 successView . classList . add ( "tw-hidden" ) ;
230204 successView . classList . remove ( "tw-flex" ) ;
@@ -235,9 +209,18 @@ document.addEventListener("DOMContentLoaded", () => {
235209 errorView . classList . remove ( "tw-hidden" ) ;
236210 }
237211 if ( ta && typeof ta . focus === "function" ) ta . focus ( ) ;
238- } catch ( err ) {
239- /* ignore */
240- }
212+ } catch ( err ) { }
241213 } ) ;
242214 } ) ;
215+ }
216+
217+ // Run on DOMContentLoaded and MkDocs instant navigation
218+ if ( typeof window . document$ !== "undefined" ) {
219+ window . document$ . subscribe ( ( ) => {
220+ setTimeout ( feedbackModalInit , 0 ) ;
221+ } ) ;
222+ }
223+ // Always run on DOMContentLoaded (for initial load)
224+ document . addEventListener ( "DOMContentLoaded" , ( ) => {
225+ setTimeout ( feedbackModalInit , 0 ) ;
243226} ) ;
0 commit comments