| @@ -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( '§ion=advanced-settings', '' ); | |
| 291 | + return; | |
| 292 | + } | |
| 293 | + if ( 'frm-style-advanced-settings-button' === target.id ) { | |
| 294 | + form.action += '§ion=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 ); |