PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.32.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.32.1
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / js / admin / forms-list.js

forms-list.js in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.32.1, at js/admin/forms-list.js

290 lines 8.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 'use strict';
2
3 ( function() {
4 const { documentOn } = frmDom.util;
5 const { __ } = wp.i18n;
6
7 function closeSettingsPanel( panel ) {
8 panel.classList.remove( 'frm-forms-list-settings--visible' );
9 panel.addEventListener( 'transitionend', () => {
10 panel.classList.add( 'frm_hidden' );
11 }, { once: true } );
12 }
13
14 function handleClickFormsListSettings( event ) {
15 event.preventDefault();
16 const btn = event.target.closest( 'a' );
17 if ( ! btn ) {
18 return;
19 }
20
21 // If dropdown is already moved here, toggle it.
22 if ( btn.nextElementSibling && 'frm-forms-list-settings' === btn.nextElementSibling.id && ! btn.nextElementSibling.classList.contains( 'frm_hidden' ) ) {
23 closeSettingsPanel( btn.nextElementSibling );
24 return;
25 }
26
27 // Move the dropdown to after the button (HTML structure) and use CSS for positioning.
28 const dropdownWrapper = document.getElementById( 'frm-forms-list-settings' );
29 if ( ! dropdownWrapper ) {
30 return;
31 }
32
33 // Get the button's position relative to the viewport.
34 const btnRect = btn.getBoundingClientRect();
35 const viewportHeight = window.innerHeight;
36 const spaceAbove = btnRect.top;
37 const spaceBelow = viewportHeight - btnRect.bottom;
38
39 // Always insert after the button, but use CSS classes for positioning.
40 btn.after( dropdownWrapper );
41
42 // Remove existing position classes.
43 dropdownWrapper.classList.remove( 'frm-dropdown-above', 'frm-dropdown-below' );
44
45 // Add position class based on available space.
46 if ( spaceAbove > spaceBelow ) {
47 // Position above the button (more space above).
48 dropdownWrapper.classList.add( 'frm-dropdown-above' );
49 } else {
50 // Position below the button (more space below).
51 dropdownWrapper.classList.add( 'frm-dropdown-below' );
52 }
53
54 // Hide dropdown when clicking outside.
55 const handleOutsideClick = event => {
56 const dropdown = document.getElementById( 'frm-forms-list-settings' );
57 if ( dropdown && ! dropdown.contains( event.target ) && ! btn.contains( event.target ) ) {
58 closeSettingsPanel( dropdown );
59 document.removeEventListener( 'click', handleOutsideClick );
60 }
61 };
62
63 // Add outside click listener.
64 document.addEventListener( 'click', handleOutsideClick );
65
66 dropdownWrapper.classList.remove( 'frm_hidden' );
67 // Force reflow so the browser registers the element before adding the transition class.
68 dropdownWrapper.getBoundingClientRect();
69 dropdownWrapper.classList.add( 'frm-forms-list-settings--visible' );
70 }
71
72 function handleChangeColumns( event ) {
73 if ( ! event.target.dataset.wpColumnInputId ) {
74 return;
75 }
76
77 const wpInput = document.getElementById( event.target.dataset.wpColumnInputId );
78 if ( ! wpInput ) {
79 return;
80 }
81
82 wpInput.checked = event.target.checked;
83 wpInput.dispatchEvent( new Event( 'click' ) );
84 }
85
86 /**
87 * Update the 'mode' query param in the current URL based on the show description toggle.
88 * The screen-options redirect uses HTTP_REFERER, so the updated URL is preserved after submit.
89 *
90 * @since 6.32
91 *
92 * @return {void}
93 */
94 function syncModeToUrl() {
95 const showDescCheckbox = document.getElementById( 'frm-forms-list-show-desc' );
96 if ( ! showDescCheckbox ) {
97 return;
98 }
99
100 const url = new URL( window.location.href );
101 if ( showDescCheckbox.checked ) {
102 url.searchParams.set( 'mode', 'excerpt' );
103 } else {
104 url.searchParams.delete( 'mode' );
105 }
106 history.replaceState( null, '', url.toString() );
107 }
108
109 function handleClickApplyBtn() {
110 syncModeToUrl();
111
112 // Update the screen options form inputs.
113 const screenOptionsForm = document.querySelector( 'form#adv-settings' );
114 if ( ! screenOptionsForm ) {
115 // This page may not support screen options.
116 applySettingsWithoutScreenOptions();
117 return;
118 }
119
120 document.querySelectorAll( '#frm-forms-list-settings [data-wp-screen-option-id]' ).forEach( input => {
121 const screenOptionInput = document.getElementById( input.dataset.wpScreenOptionId );
122 if ( ! screenOptionInput ) {
123 return;
124 }
125
126 if ( 'INPUT' === input.tagName && 'checkbox' === input.type ) {
127 screenOptionInput.checked = input.checked;
128 } else {
129 screenOptionInput.value = input.value;
130 }
131 } );
132
133 screenOptionsForm.submit();
134 }
135
136 function applySettingsWithoutScreenOptions() {
137 const perPageInput = frmDom.tag( 'input' );
138 perPageInput.type = 'hidden';
139 perPageInput.name = 'wp_screen_options[value]';
140 perPageInput.value = document.getElementById( 'frm-forms-list-per-page' ).value;
141
142 const perPageOptionNameInput = frmDom.tag( 'input' );
143 perPageOptionNameInput.type = 'hidden';
144 perPageOptionNameInput.name = 'wp_screen_options[option]';
145 perPageOptionNameInput.value = 'formidable_page_formidable_per_page';
146
147 const nonceInput = frmDom.tag( 'input' );
148 nonceInput.type = 'hidden';
149 nonceInput.name = 'screenoptionnonce';
150 nonceInput.value = document.getElementById( 'screenoptionnonce' ).value;
151
152 const form = frmDom.tag( 'form', {
153 className: 'frm_hidden',
154 children: [ perPageInput, perPageOptionNameInput, nonceInput ],
155 } );
156 form.method = 'post';
157 document.body.append( form );
158 form.submit();
159 }
160
161 function handleClickCollapsibleBtn( event ) {
162 event.preventDefault();
163 const container = event.target.closest( '.frm-collapsible-box' );
164 if ( ! container ) {
165 return;
166 }
167 const content = container.querySelector( '.frm-collapsible-box__content' );
168 if ( content ) {
169 content.classList.toggle( 'frm-collapsible-box__content--collapsed' );
170 }
171 container.classList.toggle( 'frm-collapsible-box--collapsed' );
172 }
173
174 // Click the gear icon.
175 documentOn( 'click', '.frm-forms-list-settings-btn', handleClickFormsListSettings );
176
177 documentOn( 'change', 'input[data-wp-column-input-id]', handleChangeColumns );
178 documentOn( 'click', '#frm-save-forms-list-settings-btn', handleClickApplyBtn );
179
180 documentOn( 'click', '.frm-collapsible-box__btn', handleClickCollapsibleBtn );
181
182 // Embeds toggle functionality
183 const documentFragment = document.createDocumentFragment();
184
185 documentOn( 'click', '.frm-forms-list-embeds-btn', event => {
186 event.preventDefault();
187
188 let btn = event.target;
189 if ( ! event.target.classList.contains( 'frm-forms-list-embeds-btn' ) ) {
190 btn = event.target.closest( '.frm-forms-list-embeds-btn' );
191 }
192
193 if ( ! btn.dataset.posts ) {
194 return;
195 }
196
197 const posts = JSON.parse( btn.dataset.posts );
198 if ( ! posts.length ) {
199 return;
200 }
201
202 const btnOpenedClass = 'frm-forms-list-embeds-btn--opened';
203
204 const rowEl = btn.closest( 'tr' );
205 if ( rowEl.nextElementSibling?.id.startsWith( 'frm-forms-list-embeds-row-' ) ) {
206 // Remove the extra row if it exists. Move it to fragment to reuse later.
207 btn.classList.remove( btnOpenedClass );
208 documentFragment.append( rowEl.nextElementSibling );
209 return;
210 }
211
212 const id = rowEl.id.replace( 'item-action-', '' );
213 const trInFragment = documentFragment.querySelector( `#frm-forms-list-embeds-row-${ id }` );
214 if ( trInFragment ) {
215 // Use the existing fragment row if it exists.
216 btn.classList.add( btnOpenedClass );
217 rowEl.after( trInFragment );
218 return;
219 }
220
221 const columnsCount = rowEl.querySelectorAll( 'td:not(.hidden), th:not(.hidden)' ).length;
222 const extraTdEl = frmDom.tag( 'td', {
223 className: 'colspanchange',
224 children: [
225 frmDom.tag( 'h4', {
226 text: __( 'Embed Locations', 'formidable' )
227 } ),
228 frmDom.div( {
229 className: 'frm-forms-list-embeds-posts',
230 children: posts.map( post => {
231 const postLink = frmDom.a( {
232 href: post.edit_link,
233 target: '_blank',
234 text: post.post_title || ( post.post_name ? `/${ post.post_name }` : __( '(no title)', 'formidable' ) ),
235 } );
236 if ( post.title_contains_html ) {
237 postLink.innerHTML = frmAdminBuild.purifyHtml( post.post_title || ( post.post_name ? `/${ post.post_name }` : __( '(no title)', 'formidable' ) ) );
238 }
239
240 const leftChildren = [
241 postLink,
242 post.post_title && post.post_name && post.post_name !== '' ? frmDom.span( {
243 text: `/${ post.post_name }`
244 } ) : undefined
245 ].filter( Boolean );
246
247 return frmDom.div( {
248 className: 'frm-forms-list-embeds-post',
249 children: [
250 frmDom.div( {
251 className: 'frm-forms-list-embeds-post__left',
252 children: leftChildren,
253 } ),
254 frmDom.div( {
255 className: 'frm-forms-list-embeds-post__right',
256 child: frmDom.a( {
257 href: post.permalink,
258 target: '_blank',
259 children: [
260 __( 'View in new tab', 'formidable' ),
261 frmDom.svg( {
262 href: '#frm_arrowup8_icon',
263 classList: [ 'frm-rotate-45' ],
264 } )
265 ]
266 } )
267 } )
268 ]
269 } );
270 } )
271 } )
272 ]
273 } );
274
275 extraTdEl.colSpan = columnsCount - 1;
276 const extraRowEl = frmDom.tag( 'tr', {
277 children: [
278 frmDom.tag( 'td' ),
279 extraTdEl,
280 ],
281 id: `frm-forms-list-embeds-row-${ rowEl.id.replace( 'item-action-', '' ) }`,
282 className: 'frm-forms-list-embeds-row',
283 } );
284
285 btn.classList.add( btnOpenedClass );
286 documentFragment.append( extraRowEl );
287 rowEl.after( extraRowEl );
288 } );
289 }() );
290