|
605 | 605 | }, |
606 | 606 | }; |
607 | 607 | // Check if layout has a custom label |
608 | | - const customLabel = layout.data( 'label' ); |
609 | | - if ( customLabel.length ) { |
| 608 | + const customLabel = $el.data( 'label' ); |
| 609 | + if ( customLabel && customLabel.length ) { |
610 | 610 | // Customize the popup title and text with the layout label |
611 | 611 | tooltipOptions.title = acf |
612 | 612 | .__( 'Delete %s' ) |
|
669 | 669 |
|
670 | 670 | onClickMoreActions: function ( e, $el ) { |
671 | 671 | const $layout = $el.closest( '.layout' ); |
672 | | - new TooltipConfirm( { |
| 672 | + new MoreLayoutActionsPopup( { |
673 | 673 | target: $el, |
674 | 674 | targetConfirm: false, |
675 | 675 | text: this.getMoreLayoutActionsHTML(), |
|
828 | 828 | }, |
829 | 829 | } ); |
830 | 830 |
|
| 831 | + /** |
| 832 | + * MoreLayoutActionsPopup |
| 833 | + * |
| 834 | + * Popup for showing more layout actions (remove, toggle, rename) |
| 835 | + */ |
| 836 | + const MoreLayoutActionsPopup = acf.models.TooltipConfirm.extend( { |
| 837 | + events: { |
| 838 | + 'click [data-action]': 'onConfirm', |
| 839 | + 'keydown [role="menu"]': 'onKeyDown', |
| 840 | + }, |
| 841 | + |
| 842 | + render: function () { |
| 843 | + const $layout = this.get( 'target' ).closest( '.layout' ); |
| 844 | + this.html( this.get( 'text' ) ); |
| 845 | + this.$el.addClass( 'acf-fc-popup acf-more-layout-actions' ); |
| 846 | + |
| 847 | + // Add enable-layout class if layout is disabled |
| 848 | + if ( $layout.attr( 'data-enabled' ) === '0' ) { |
| 849 | + this.$el.addClass( 'enable-layout' ); |
| 850 | + } else { |
| 851 | + this.$el.removeClass( 'enable-layout' ); |
| 852 | + } |
| 853 | + |
| 854 | + const self = this; |
| 855 | + setTimeout( function () { |
| 856 | + self.$el.find( 'a' ).first().trigger( 'focus' ); |
| 857 | + }, 1 ); |
| 858 | + }, |
| 859 | + |
| 860 | + show: function () { |
| 861 | + const $layout = this.get( 'target' ).closest( '.layout' ); |
| 862 | + $layout.append( this.$el ); |
| 863 | + }, |
| 864 | + |
| 865 | + position: function () { |
| 866 | + const $popup = this.$el; |
| 867 | + const $target = this.get( 'target' ); |
| 868 | + const $container = $target.closest( '.layout' ); |
| 869 | + positionPopup( $popup, $target, $container, 2 ); |
| 870 | + }, |
| 871 | + |
| 872 | + onKeyDown: function ( event, $el ) { |
| 873 | + if ( |
| 874 | + [ 'ArrowDown', 'ArrowUp', 'Escape', 'Tab' ].indexOf( |
| 875 | + event.key |
| 876 | + ) === -1 |
| 877 | + ) { |
| 878 | + return; |
| 879 | + } |
| 880 | + |
| 881 | + event.preventDefault(); |
| 882 | + |
| 883 | + if ( event.key === 'Escape' ) { |
| 884 | + return void this.onCancel( event, $el ); |
| 885 | + } |
| 886 | + |
| 887 | + const $menuItems = this.$el |
| 888 | + .find( '[role="menu"]' ) |
| 889 | + .find( '[role="menuitem"]:visible' ); |
| 890 | + const $activeElement = $( document.activeElement ); |
| 891 | + const menuItemsLength = $menuItems.length; |
| 892 | + |
| 893 | + let currentIndex = $menuItems.index( $activeElement ); |
| 894 | + let nextIndex; |
| 895 | + |
| 896 | + if ( |
| 897 | + event.key === 'ArrowDown' || |
| 898 | + ( event.key === 'Tab' && ! event.shiftKey ) |
| 899 | + ) { |
| 900 | + nextIndex = ( currentIndex + 1 ) % menuItemsLength; |
| 901 | + } else { |
| 902 | + nextIndex = |
| 903 | + ( currentIndex - 1 + menuItemsLength ) % menuItemsLength; |
| 904 | + } |
| 905 | + |
| 906 | + $menuItems.eq( nextIndex ).trigger( 'focus' ); |
| 907 | + }, |
| 908 | + } ); |
| 909 | + |
| 910 | + /** |
| 911 | + * RenameLayoutPopup |
| 912 | + * |
| 913 | + * Popup dialog for renaming layout labels |
| 914 | + */ |
| 915 | + const RenameLayoutPopup = acf.models.PopupConfirm.extend( { |
| 916 | + events: { |
| 917 | + 'click [data-event="close"]': 'onCancel', |
| 918 | + 'click .acf-close-popup': 'onClickClose', |
| 919 | + keydown: 'onPressEscapeClose', |
| 920 | + 'click [data-event="confirm"]': 'onConfirm', |
| 921 | + 'click .acf-reset-label': 'onReset', |
| 922 | + 'submit .acf-rename-layout-form': 'onConfirm', |
| 923 | + }, |
| 924 | + |
| 925 | + tmpl: function () { |
| 926 | + const resetButton = |
| 927 | + this.get( 'currentName' ) === '' |
| 928 | + ? '' |
| 929 | + : `<button type="button" data-event="reset-label" class="acf-btn acf-btn-secondary acf-reset-label">${ acf.strEscape( |
| 930 | + acf.__( 'Remove Custom Label' ) |
| 931 | + ) }</button>`; |
| 932 | + |
| 933 | + return ` |
| 934 | + <div id="acf-popup" role="dialog" aria-labelledby="acf-rename-layout-title" tabindex="-1"> |
| 935 | + <div class="acf-popup-box acf-box acf-confirm-popup acf-rename-layout-popup"> |
| 936 | + <div class="title"> |
| 937 | + <h3 id="acf-rename-layout-title">${ this.get( 'title' ) }</h3> |
| 938 | + <a href="#" data-event="close" aria-label="${ acf.strEscape( |
| 939 | + acf.__( 'Close modal' ) |
| 940 | + ) }"> |
| 941 | + <i class="acf-icon -close"></i> |
| 942 | + </a> |
| 943 | + </div> |
| 944 | + <form class="inner acf-rename-layout-form"> |
| 945 | + <div class="acf-field"> |
| 946 | + <div class="acf-label"> |
| 947 | + <label for="acf-new-layout-label">${ acf.strEscape( |
| 948 | + acf.__( 'New Label' ) |
| 949 | + ) }</label> |
| 950 | + </div> |
| 951 | + <div class="acf-input"> |
| 952 | + <input id="acf-new-layout-label" type="text" name="acf_new_layout_label" value="${ this.get( |
| 953 | + 'currentName' |
| 954 | + ) }"> |
| 955 | + </div> |
| 956 | + </div> |
| 957 | + <div class="acf-actions"> |
| 958 | + ${ resetButton } |
| 959 | + <button type="button" data-event="close" class="acf-btn acf-btn-secondary acf-close-popup">${ acf.strEscape( |
| 960 | + this.get( 'textCancel' ) |
| 961 | + ) }</button> |
| 962 | + <button type="submit" data-event="confirm" class="acf-btn acf-btn-primary acf-confirm">${ acf.strEscape( |
| 963 | + this.get( 'textConfirm' ) |
| 964 | + ) }</button> |
| 965 | + </div> |
| 966 | + </form> |
| 967 | + </div> |
| 968 | + <div class="bg" data-event="close"></div> |
| 969 | + </div>`; |
| 970 | + }, |
| 971 | + |
| 972 | + render: function () { |
| 973 | + acf.models.PopupConfirm.prototype.render.apply( this, arguments ); |
| 974 | + setTimeout( () => { |
| 975 | + const $input = this.$el.find( 'input#acf-new-layout-label' ); |
| 976 | + const textLength = $input.val().length; |
| 977 | + $input.trigger( 'focus' ); |
| 978 | + $input[ 0 ].setSelectionRange( textLength, textLength ); |
| 979 | + }, 1 ); |
| 980 | + }, |
| 981 | + |
| 982 | + onConfirm: function ( event, $el ) { |
| 983 | + event.preventDefault(); |
| 984 | + event.stopPropagation(); |
| 985 | + |
| 986 | + const newName = this.$el.find( 'input#acf-new-layout-label' ).val(); |
| 987 | + this.close(); |
| 988 | + |
| 989 | + const confirmCallback = this.get( 'confirm' ); |
| 990 | + const context = this.get( 'context' ) || this; |
| 991 | + confirmCallback.apply( context, [ event, $el, newName ] ); |
| 992 | + }, |
| 993 | + |
| 994 | + onReset: function ( event, $el ) { |
| 995 | + event.preventDefault(); |
| 996 | + event.stopPropagation(); |
| 997 | + this.$el.find( 'input#acf-new-layout-label' ).val( '' ); |
| 998 | + this.onConfirm( event, $el ); |
| 999 | + }, |
| 1000 | + } ); |
| 1001 | + |
| 1002 | + /** |
| 1003 | + * positionPopup |
| 1004 | + * |
| 1005 | + * Utility function to position popup relative to target within container |
| 1006 | + */ |
| 1007 | + const positionPopup = function ( $popup, $target, $container, offset ) { |
| 1008 | + if ( ! $target.length || ! $container.length ) return; |
| 1009 | + |
| 1010 | + const targetOffset = $target.offset(); |
| 1011 | + const containerOffset = $container.offset(); |
| 1012 | + const targetWidth = $target.outerWidth(); |
| 1013 | + const targetHeight = $target.outerHeight(); |
| 1014 | + const popupWidth = $popup.outerWidth(); |
| 1015 | + const popupHeight = $popup.outerHeight(); |
| 1016 | + const isRTL = $( 'body' ).hasClass( 'rtl' ); |
| 1017 | + const windowScrollTop = $( window ).scrollTop(); |
| 1018 | + const windowHeight = $( window ).height(); |
| 1019 | + |
| 1020 | + let left, positionClass; |
| 1021 | + let top = |
| 1022 | + targetOffset.top - containerOffset.top + targetHeight + offset; |
| 1023 | + let isAbove = false; |
| 1024 | + |
| 1025 | + // Check if popup would be cut off at bottom of viewport |
| 1026 | + if ( |
| 1027 | + targetOffset.top + targetHeight + popupHeight + offset > |
| 1028 | + windowScrollTop + windowHeight && |
| 1029 | + targetOffset.top - popupHeight - offset > windowScrollTop |
| 1030 | + ) { |
| 1031 | + top = targetOffset.top - containerOffset.top - popupHeight - offset; |
| 1032 | + isAbove = true; |
| 1033 | + } |
| 1034 | + |
| 1035 | + if ( isRTL ) { |
| 1036 | + left = targetOffset.left - containerOffset.left; |
| 1037 | + positionClass = isAbove ? 'bottom-left' : 'top-left'; |
| 1038 | + } else { |
| 1039 | + left = |
| 1040 | + targetOffset.left - |
| 1041 | + containerOffset.left + |
| 1042 | + targetWidth - |
| 1043 | + popupWidth; |
| 1044 | + positionClass = isAbove ? 'bottom-right' : 'top-right'; |
| 1045 | + } |
| 1046 | + |
| 1047 | + $popup |
| 1048 | + .removeClass( 'top-right bottom-right top-left bottom-left' ) |
| 1049 | + .css( { position: 'absolute', top: top, left: left } ) |
| 1050 | + .addClass( positionClass ); |
| 1051 | + }; |
| 1052 | + |
831 | 1053 | /** |
832 | 1054 | * conditions |
833 | 1055 | * |
|
0 commit comments