| @@ -183,11 +183,10 @@ | ||
| 183 | 183 | * @return {void} |
| 184 | 184 | */ |
| 185 | 185 | function initListPage() { |
| 186 | 186 | document.addEventListener( 'click', handleClickEventsForListPage ); |
| 187 | - // Add a timeout so Pro has a chance to add a filter first. | |
| 188 | - // 0 does not always work in Google Chrome, so use 1. | |
| 189 | - setTimeout( addHamburgerMenusToCards, 1 ); | |
| 187 | + // The dropdown options are added on the first open, so there is no longer a filter from Pro to wait for here. | |
| 188 | + addHamburgerMenusToCards(); | |
| 190 | 189 | initDatepickerSample(); |
| 191 | 190 | |
| 192 | 191 | const enableToggle = document.getElementById( 'frm_enable_styling' ); |
| 193 | 192 | const styleIdInput = getStyleIdInput(); |
| @@ -722,8 +721,72 @@ | ||
| 722 | 721 | hamburgerMenu.setAttribute( 'role', 'button' ); |
| 723 | 722 | hamburgerMenu.setAttribute( 'tabindex', 0 ); |
| 724 | 723 | |
| 725 | 724 | const isTemplate = data.templateKey !== undefined; |
| 725 | + | |
| 726 | + const dropdownMenu = div( { | |
| 727 | + // Use dropdown-menu-right to avoid an overlapping issue with the card to the right (where the # of forms would appear above the menu). | |
| 728 | + className: 'frm-dropdown-menu frm-style-options-menu frm-p-1' | |
| 729 | + } ); | |
| 730 | + | |
| 731 | + const isRtl = document.body.classList.contains( 'rtl' ); | |
| 732 | + dropdownMenu.classList.add( `dropdown-menu-${ isRtl ? 'left' : 'right' }` ); | |
| 733 | + | |
| 734 | + dropdownMenu.setAttribute( 'role', 'menu' ); | |
| 735 | + | |
| 736 | + fillDropdownMenuOnFirstOpen( hamburgerMenu, dropdownMenu, data, isTemplate ); | |
| 737 | + | |
| 738 | + return div( { | |
| 739 | + className: 'dropdown frm_wrap', // The .frm_wrap class prevents a blue outline on the active dropdown trigger. | |
| 740 | + children: [ hamburgerMenu, dropdownMenu ] | |
| 741 | + } ); | |
| 742 | + } | |
| 743 | + | |
| 744 | + /** | |
| 745 | + * Add the options to a style card dropdown the first time that dropdown is opened. | |
| 746 | + * | |
| 747 | + * The options are built through the frm_style_card_dropdown_options filter, and Pro registers its | |
| 748 | + * callback while its own script file runs. Adding the options while the page loads raced that file, | |
| 749 | + * so a request for it that was slow to come back left every card holding the Lite only options. | |
| 750 | + * Waiting for the first open takes the script order out of it, since Pro has always registered the | |
| 751 | + * filter by the time a card can be clicked. | |
| 752 | + * | |
| 753 | + * @since 6.35 | |
| 754 | + * | |
| 755 | + * @param {HTMLElement} hamburgerMenu The dropdown trigger. | |
| 756 | + * @param {HTMLElement} dropdownMenu The dropdown to add the options to. | |
| 757 | + * @param {DOMStringMap} data The dataset of the style card, or an object with the style ID on the edit page. | |
| 758 | + * @param {boolean} isTemplate Whether the card is for a style template. | |
| 759 | + * @return {void} | |
| 760 | + */ | |
| 761 | + function fillDropdownMenuOnFirstOpen( hamburgerMenu, dropdownMenu, data, isTemplate ) { | |
| 762 | + const fillDropdownMenu = () => { | |
| 763 | + if ( dropdownMenu.children.length ) { | |
| 764 | + return; | |
| 765 | + } | |
| 766 | + | |
| 767 | + const options = getDropdownMenuOptions( data, isTemplate ); | |
| 768 | + dropdownMenu.append( ...options.map( wrapDropdownItem ) ); | |
| 769 | + }; | |
| 770 | + | |
| 771 | + // Bootstrap fires this on the trigger before it measures and positions the dropdown. | |
| 772 | + hamburgerMenu.addEventListener( 'show.bs.dropdown', fillDropdownMenu ); | |
| 773 | + | |
| 774 | + // Fall back to the click in case the dropdown is ever opened without Bootstrap. Listen in the | |
| 775 | + // capture phase so the options are in place before Bootstrap's own delegated handler runs. | |
| 776 | + hamburgerMenu.addEventListener( 'click', fillDropdownMenu, true ); | |
| 777 | + } | |
| 778 | + | |
| 779 | + /** | |
| 780 | + * Get the options for a single style card dropdown. | |
| 781 | + * | |
| 782 | + * @since 6.35 | |
| 783 | + * | |
| 784 | + * @param {DOMStringMap} data The dataset of the style card, or an object with the style ID on the edit page. | |
| 785 | + * @param {boolean} isTemplate Whether the card is for a style template. | |
| 786 | + * @return {Array} The dropdown options, each an object with an anchor and a type. | |
| 787 | + */ | |
| 788 | + function getDropdownMenuOptions( data, isTemplate ) { | |
| 726 | 789 | let dropdownMenuOptions = []; |
| 727 | 790 | |
| 728 | 791 | if ( isListPage ) { |
| 729 | 792 | const applyOption = a( { |
| @@ -764,23 +827,9 @@ | ||
| 764 | 827 | if ( isListPage && ! isTemplate ) { |
| 765 | 828 | maybeAddDuplicateUpsell( dropdownMenuOptions ); |
| 766 | 829 | } |
| 767 | 830 | |
| 768 | - const dropdownMenu = div( { | |
| 769 | - // Use dropdown-menu-right to avoid an overlapping issue with the card to the right (where the # of forms would appear above the menu). | |
| 770 | - className: 'frm-dropdown-menu frm-style-options-menu frm-p-1', | |
| 771 | - children: dropdownMenuOptions.map( wrapDropdownItem ) | |
| 772 | - } ); | |
| 773 | - | |
| 774 | - const isRtl = document.body.classList.contains( 'rtl' ); | |
| 775 | - dropdownMenu.classList.add( `dropdown-menu-${ isRtl ? 'left' : 'right' }` ); | |
| 776 | - | |
| 777 | - dropdownMenu.setAttribute( 'role', 'menu' ); | |
| 778 | - | |
| 779 | - return div( { | |
| 780 | - className: 'dropdown frm_wrap', // The .frm_wrap class prevents a blue outline on the active dropdown trigger. | |
| 781 | - children: [ hamburgerMenu, dropdownMenu ] | |
| 782 | - } ); | |
| 831 | + return dropdownMenuOptions; | |
| 783 | 832 | } |
| 784 | 833 | |
| 785 | 834 | /** |
| 786 | 835 | * @param {Array} dropdownMenuOptions |