PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.16.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.16.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
← All changes | js/admin/style.js +152 -21 6.36.16.1 View file →
@@ -5,9 +5,13 @@
5 5 ( function() {
6 6 /* globals wp, frmDom, frmAdminBuild */
7 7 'use strict';
8 8
9 - const { __ } = wp.i18n;
9 + if ( ! document.getElementById( 'frm_active_style_form' ) ) {
10 + return;
11 + }
12 +
13 + const { __, sprintf } = wp.i18n;
10 14 const state = {
11 15 showingSampleForm: document.getElementById( 'frm_active_style_form' ).classList.contains( 'frm_hidden' ), // boolean
12 16 unsavedChanges: false, // boolean
13 17 autoId: 0, // Number
@@ -49,16 +53,34 @@
49 53 */
50 54 function initPreview() {
51 55 initFloatingLabels();
52 56 fillMissingSignatureValidationFunction();
57 + setSelectPlaceholderColor();
53 58
54 59 // Remove .wp-core-ui from the body so the preview can avoid it.
55 60 // Then add it back where we want to use admin styles (the sidebar, otherwise inputs appear short).
56 61 document.body.classList.remove( 'wp-core-ui' );
57 62 document.getElementById( 'frm_style_sidebar' ).classList.add( 'wp-core-ui' );
63 +
64 + jQuery( document ).on( 'input change', 'input[data-frmrange]', initSliderPreview );
65 +
58 66 }
59 67
60 68 /**
69 + * Initialize the slider functionality in the style preview.
70 + *
71 + * @param {HTMLElement} event
72 + * @returns {void}
73 + */
74 + function initSliderPreview( event ) {
75 + const wrapper = event.target.closest( '.frm_range_container' );
76 + if ( null === wrapper ) {
77 + return;
78 + }
79 + wrapper.querySelector( '.frm_range_value' ).innerHTML = parseInt( this.value, 10 );
80 + }
81 +
82 + /**
61 83 * Add the wp-core-ui class to the #wp-auth-check-wrap element.
62 84 * As this style isn't included on the body for the styler, the close button on the auth modal wasn't getting styled properly.
63 85 *
64 86 * @returns {void}
@@ -74,9 +96,11 @@
74 96 * @returns {void}
75 97 */
76 98 function initListPage() {
77 99 document.addEventListener( 'click', handleClickEventsForListPage );
78 - setTimeout( addHamburgerMenusToCards, 0 ); // Add a timeout so Pro has a chance to add a filter first.
100 + // Add a timeout so Pro has a chance to add a filter first.
101 + // 0 does not always work in Google Chrome, so use 1.
102 + setTimeout( addHamburgerMenusToCards, 1 );
79 103 initDatepickerSample();
80 104
81 105 const enableToggle = document.getElementById( 'frm_enable_styling' );
82 106 const styleIdInput = getStyleIdInput();
@@ -127,9 +151,10 @@
127 151 wrapper.querySelectorAll( '.frm-style-card:nth-child(3) ~ .frm-style-card' ).forEach(
128 152 card => card.classList.add( 'frm_hidden' )
129 153 );
130 154 const hiddenCount = wrapper.querySelectorAll( '.frm-style-card.frm_hidden' ).length;
131 - showAllAnchor.textContent = __( 'Show all (%d)', 'formidable' ).replace( '%d', hiddenCount );
155 + /* translators: %d: The number of hidden items to show. */
156 + showAllAnchor.textContent = sprintf( __( 'Show all (%d)', 'formidable' ), hiddenCount );
132 157 }
133 158 );
134 159 }
135 160 );
@@ -238,9 +263,10 @@
238 263 toggleSampleForm();
239 264 return;
240 265 }
241 266
242 - if ( 'frm_submit_side_top' === target.id || target.closest( '#frm_submit_side_top' ) ) {
267 + if ( 'frm_submit_side_top' === target.id || target.closest( '#frm_submit_side_top' ) || 'frm-style-advanced-settings-button' === target.id || target.closest( 'a#frm_style_back_to_quick_settings' ) ) {
268 + switchAdvancedSettingsFormAction( target );
243 269 handleUpdateClick();
244 270 return;
245 271 }
246 272
@@ -250,8 +276,27 @@
250 276 }
251 277 }
252 278
253 279 /**
280 + * This function is used to update the form action when switching from the advanced settings and quick-settings.
281 + * @param {Object} target The submit button event target
282 + * @return {void}
283 + */
284 + function switchAdvancedSettingsFormAction( target ) {
285 + const form = document.querySelector( '#frm_styling_form' );
286 + if ( null === form ) {
287 + return;
288 + }
289 + if ( target.closest( 'a#frm_style_back_to_quick_settings' ) ) {
290 + form.action = form.action.replace( '&section=advanced-settings', '' );
291 + return;
292 + }
293 + if ( 'frm-style-advanced-settings-button' === target.id ) {
294 + form.action += '&section=advanced-settings';
295 + }
296 + }
297 +
298 + /**
254 299 * @returns {void}
255 300 */
256 301 function disablePreviewSubmitButtons() {
257 302 const preview = document.getElementById( 'frm_style_preview' );
@@ -380,10 +425,9 @@
380 425 className: 'frm_warning_style',
381 426 children: [
382 427 span(
383 428 /* translators: %s: The required license type (ie. Plus, Business, or Elite) */
384 - __( 'Access to this style requires the %s plan.', 'formidable' )
385 - .replace( '%s', card.dataset.requires )
429 + sprintf( __( 'Access to this style requires the %s plan.', 'formidable' ), card.dataset.requires )
386 430 ),
387 431 a({
388 432 text: getUpgradeNowText(),
389 433 href: card.dataset.upgradeUrl,
@@ -700,9 +744,9 @@
700 744 * @param {String} styleId
701 745 * @returns {HTMLElement}
702 746 */
703 747 function getRenameOption( styleId ) {
704 - const renameOption = a( __( 'Rename', 'formidable-pro' ) );
748 + const renameOption = a( __( 'Rename', 'formidable' ) );
705 749 addIconToOption( renameOption, 'frm_signature_icon' );
706 750
707 751 let titleTarget;
708 752
@@ -719,9 +763,9 @@
719 763 const styleName = titleTarget.textContent;
720 764 stylerModal(
721 765 'frm_rename_style_modal',
722 766 {
723 - title: __( 'Rename style', 'formidable-pro' ),
767 + title: __( 'Rename style', 'formidable' ),
724 768 content: getStyleInputNameModalContent( 'rename', styleName ),
725 769 footer: getRenameStyleModalFooter( styleId )
726 770 }
727 771 );
@@ -757,9 +801,9 @@
757 801 // Create a form so we can listen to Enter key presses that trigger a form submit event.
758 802 const form = tag(
759 803 'form',
760 804 {
761 - child: labelledTextInput( 'frm_' + context + '_style_name_input', __( 'Style name', 'formidable-pro' ), 'style_name' )
805 + child: labelledTextInput( 'frm_' + context + '_style_name_input', __( 'Style name', 'formidable' ), 'style_name' )
762 806 }
763 807 );
764 808 form.addEventListener(
765 809 'submit',
@@ -807,12 +851,12 @@
807 851 * @param {String} styleId
808 852 * @returns {HTMLELement}
809 853 */
810 854 function getRenameStyleModalFooter( styleId ) {
811 - const cancelButton = footerButton({ text: __( 'Cancel', 'formidable-pro' ), buttonType: 'cancel' });
855 + const cancelButton = footerButton({ text: __( 'Cancel', 'formidable' ), buttonType: 'cancel' });
812 856 cancelButton.classList.add( 'dismiss' );
813 857
814 - const renameButton = footerButton({ text: __( 'Rename style', 'formidable-pro' ), buttonType: 'primary' });
858 + const renameButton = footerButton({ text: __( 'Rename style', 'formidable' ), buttonType: 'primary' });
815 859 onClickPreventDefault( renameButton, () => renameStyle( styleId ) );
816 860
817 861 return div({
818 862 children: [ cancelButton, renameButton ]
@@ -1104,18 +1148,34 @@
1104 1148 */
1105 1149 function initEditPage() {
1106 1150 const { debounce } = frmDom.util;
1107 1151 const debouncedPreviewUpdate = debounce( () => changeStyling(), 100 );
1152 + const debouncedColorChange = debounce(( event, value ) => {
1153 + /**
1154 + * Fires on style colorpicker change.
1155 + *
1156 + * @param {Event} data.event The color change event.
1157 + * @param {string} data.value New color value.
1158 + */
1159 + wp.hooks.doAction( 'frm_style_options_color_change', { event, value } );
1160 + }, 200 );
1108 1161
1162 + const debouncedTextSquishCheck = debounce( textSquishCheck, 300 );
1109 1163 initPosClass(); // It's important that this gets called before we add event listeners because it triggers change events.
1110 1164
1111 - document.getElementById( 'frm_field_height' ).addEventListener( 'change', textSquishCheck );
1112 - document.getElementById( 'frm_field_font_size' ).addEventListener( 'change', textSquishCheck );
1113 - document.getElementById( 'frm_field_pad' ).addEventListener( 'change', textSquishCheck );
1165 + ['frm_field_height', 'frm_field_font_size', 'frm_field_pad'].forEach( selector => {
1166 + document.getElementById( selector ).addEventListener( 'change', debouncedTextSquishCheck );
1167 + });
1114 1168
1115 1169 jQuery( 'input.hex' ).wpColorPicker({
1116 - change: function( event ) {
1170 + change: function( event, ui ) {
1171 + let color = jQuery( this ).wpColorPicker( 'color' );
1117 1172 trackUnsavedChange();
1173 + if ( ui.color._alpha < 1 ) {
1174 + // If there's transparency, use RGBA
1175 + color = ui.color.toCSS( 'rgba' );
1176 + }
1177 + debouncedColorChange( event, color );
1118 1178
1119 1179 if ( null !== event.target.getAttribute( 'data-alpha-color-type' ) ) {
1120 1180 debouncedPreviewUpdate();
1121 1181 return;
@@ -1120,16 +1180,19 @@
1120 1180 debouncedPreviewUpdate();
1121 1181 return;
1122 1182 }
1123 1183
1124 - const hexcolor = jQuery( this ).wpColorPicker( 'color' );
1125 - jQuery( event.target ).val( hexcolor ).trigger( 'change' );
1184 + jQuery( event.target ).val( color ).trigger( 'change' );
1126 1185 }
1127 1186 });
1128 1187 jQuery( '.wp-color-result-text' ).text( function( _, oldText ) {
1188 + const container = jQuery( this ).closest( '.wp-picker-container' );
1189 + if ( 'undefined' !== typeof container && container[0].parentElement.classList.contains( 'frm-colorpicker' ) ) {
1190 + return container[0].querySelector( '.wp-color-picker' ).value;
1191 + }
1129 1192 return oldText === 'Select Color' ? 'Select' : oldText;
1130 1193 });
1131 - jQuery( '#frm_styling_form .styling_settings' ).on( 'change', debouncedPreviewUpdate );
1194 + jQuery( '#frm_styling_form .styling_settings, #frm_styling_form .frm-field-shape, #frm_styling_form input[name="frm_style_setting[post_content][base_font_size]"]' ).on( 'change', debouncedPreviewUpdate );
1132 1195
1133 1196 // This is really only necessary for Pro. But if Pro is not up to date to initialize the datepicker in the sample form, it should still work because it's initialized here.
1134 1197 initDatepickerSample();
1135 1198
@@ -1158,9 +1221,12 @@
1158 1221 action: 'frm_change_styling',
1159 1222 nonce: frmGlobal.nonce,
1160 1223 frm_style_setting: locStr
1161 1224 },
1162 - success: handleChangeStylingSuccess
1225 + success: ( css ) => {
1226 + handleChangeStylingSuccess( css );
1227 + setSelectPlaceholderColor();
1228 + }
1163 1229 });
1164 1230 }
1165 1231
1166 1232 /**
@@ -1185,21 +1251,26 @@
1185 1251 *
1186 1252 * @returns {void}
1187 1253 */
1188 1254 function textSquishCheck() {
1189 - const size = document.getElementById( 'frm_field_font_size' ).value.replace( /\D/g, '' );
1255 + if ( null !== frmDom.util.getCookie( 'frm-style-text-squish-check' ) ) {
1256 + return;
1257 + }
1190 1258 const height = document.getElementById( 'frm_field_height' ).value.replace( /\D/g, '' );
1191 1259 const paddingEntered = document.getElementById( 'frm_field_pad' ).value.split( ' ' );
1192 1260 const paddingCount = paddingEntered.length;
1193 1261
1262 + frmDom.util.setCookie( 'frm-style-text-squish-check', 1, 30 );
1263 +
1194 1264 // If too many or too few padding entries, leave now
1195 1265 if ( paddingCount === 0 || paddingCount > 4 || height === '' ) {
1196 1266 return;
1197 1267 }
1198 1268
1269 + const size = document.getElementById( 'frm_field_font_size' ).value.replace( /\D/g, '' );
1199 1270 // Get the top and bottom padding from entered values
1200 1271 const paddingTop = paddingEntered[0].replace( /\D/g, '' );
1201 - const paddingBottom = paddingTop;
1272 + let paddingBottom = paddingTop;
1202 1273 if ( paddingCount >= 3 ) {
1203 1274 paddingBottom = paddingEntered[2].replace( /\D/g, '' );
1204 1275 }
1205 1276
@@ -1236,11 +1307,37 @@
1236 1307 }
1237 1308
1238 1309 radio.closest( '.dropdown-item' ).classList.add( 'active' );
1239 1310 });
1311 +
1312 + if ( frm_admin_js.requireAccordionTitleClickListener ) {
1313 + document.querySelectorAll( '.styling_settings h3.accordion-section-title' ).forEach( el => {
1314 + el.addEventListener( 'click', event => {
1315 + if ( ! event.target.closest( 'button' ) ) {
1316 + el.querySelector( 'button' ).click();
1317 + }
1318 + });
1319 + });
1320 + }
1240 1321 }
1241 1322
1242 1323 /**
1324 + * @param {Event} event
1325 + */
1326 + function maybeCollapseSettings( event ) {
1327 + let expanded;
1328 + const sectionParent = event.target.parentElement;
1329 + if ( event.type === 'keydown' ) {
1330 + expanded = sectionParent.classList.toggle( 'open' );
1331 + jQuery( sectionParent.querySelector( '.accordion-section-content' ) ).toggle( ! expanded ).slideToggle( 150 ); // Animate toggle as in click/enter.
1332 + } else {
1333 + expanded = sectionParent.classList.contains( 'open' );
1334 + }
1335 +
1336 + event.target.setAttribute( 'aria-expanded', expanded );
1337 + }
1338 +
1339 + /**
1243 1340 * @param {HTMLElement} input
1244 1341 * @param {HTMLElement} container
1245 1342 * @returns {void}
1246 1343 */
@@ -1352,8 +1449,42 @@
1352 1449 const $sample = jQuery( '#datepicker_sample' );
1353 1450 if ( $sample.length && 'function' === typeof $sample.datepicker ) {
1354 1451 $sample.datepicker({ changeMonth: true, changeYear: true });
1355 1452 }
1453 + }
1454 +
1455 + /**
1456 + * Set color for select placeholders.
1457 + *
1458 + * @since 6.5.1
1459 + */
1460 + function setSelectPlaceholderColor() {
1461 + const selects = document.querySelectorAll( '.form-field select' );
1462 + const styleElement = document.querySelector( '.with_frm_style' );
1463 + const textColorDisabled = styleElement ? getComputedStyle( styleElement ).getPropertyValue( '--text-color-disabled' ).trim() : '';
1464 +
1465 + // Exit if there are no select elements or the textColorDisabled property is missing
1466 + if ( ! selects.length || ! textColorDisabled ) {
1467 + return;
1468 + }
1469 +
1470 + // Function to change the color of a select element
1471 + const changeSelectColor = ( select ) => {
1472 + if ( select.options[select.selectedIndex] && select.options[select.selectedIndex].classList.contains( 'frm-select-placeholder' ) ) {
1473 + select.style.setProperty( 'color', textColorDisabled, 'important' );
1474 + } else {
1475 + select.style.color = '';
1476 + }
1477 + };
1478 +
1479 + // Use a loop to iterate through each select element
1480 + selects.forEach( ( select ) => {
1481 + // Apply the color change to each select element
1482 + changeSelectColor( select );
1483 +
1484 + // Add an event listener for future changes
1485 + select.addEventListener( 'change', () => changeSelectColor( select ) );
1486 + });
1356 1487 }
1357 1488
1358 1489 // Hook into the styleInit function in formidable_admin.js
1359 1490 wp.hooks.addAction( 'frm_style_editor_init', 'formidable', initEditPage );