From beb45b823c21c93a2d8a39c4fbf59eb5ceeea69a Mon Sep 17 00:00:00 2001 From: Adam Bichler Date: Mon, 1 Dec 2025 13:06:31 +0100 Subject: [PATCH] Fix modal event handling for ACF block version 3 Restructured the event listener attachment to fix compatibility issues: - Created new attachCropModalEvents() method that attaches event listeners directly to modal buttons after they're created in the DOM - Moved all button event logic from initialize() into this new method - Called attachCropModalEvents() in openModal() after modal DOM is rendered - Added proper cleanup in closeModal() to remove event listeners and prevent memory leaks - Added null check for cropper in closeModal() Benefits: - More reliable - listeners attached when needed, not during init - Better memory management with proper cleanup - Maintains backward compatibility - Uses namespaced event handlers (.aiarc) for clean unbinding --- assets/src/input.js | 282 ++++++++++++++++++++++++-------------------- 1 file changed, 157 insertions(+), 125 deletions(-) diff --git a/assets/src/input.js b/assets/src/input.js index cd4e9ca..73fffb4 100644 --- a/assets/src/input.js +++ b/assets/src/input.js @@ -193,130 +193,6 @@ import { sprintf } from 'sprintf-js'; } this.escapeHandlerBound = this.escapeHandler.bind(this); - - $(document).on('click', '.js-acf-image-aspect-ratio-crop-cancel', () => - this.closeModal(), - ); - - $(document) - .off('click', '.js-acf-image-aspect-ratio-crop-reset') - .on('click', '.js-acf-image-aspect-ratio-crop-reset', () => { - this.cropper.reset(); - }); - - $(document) - .off('click', '.js-acf-image-aspect-ratio-crop-crop') - .on('click', '.js-acf-image-aspect-ratio-crop-crop', function() { - var cropData = self.cropper.getData(true); - - $('.js-acf-image-aspect-ratio-crop-modal').css( - 'max-width', - self.cropper.containerData.width, - ); - - var cropType = $(field) - .find('.acf-image-uploader-aspect-ratio-crop') - .data('crop_type'); - - let acfKey = $(field) - .find('.acf-image-uploader-aspect-ratio-crop') - .data('key'); - - var data = { - id: $(this).data('id'), - aspectRatioHeight: $(this).data('aspect-ratio-height'), - aspectRatioWidth: $(this).data('aspect-ratio-width'), - cropType: $(this).data('crop-type'), - x: cropData.x, - y: cropData.y, - width: cropData.width, - height: cropData.height, - temp_post_id: aiarc.temp_post_id, - key: acfKey, - }; - - $('.js-acf-image-aspect-ratio-crop-crop').prop('disabled', true); - $('.js-acf-image-aspect-ratio-crop-reset').prop('disabled', true); - - // prettier-ignore - var loading = '
' + - '
' + - '' + - '' + - '' + - '' + - '
' + - '
' + - aiarc_translations.cropping_in_progress + - '
' + - '
'; - - // prettier-ignore - var error = '
' + - '
' + - '' + - '' + - '' + - '' + - '
' + - '
' + - aiarc_translations.cropping_failed + - '
' + - '
'; - - $('.js-acf-image-aspect-ratio-crop-modal-footer-status').empty(); - $('.js-acf-image-aspect-ratio-crop-modal-footer-status').html( - loading, - ); - self.cropper.disable(); - - let options = {}; - - let url = null; - - if (window.aiarc_settings.rest_api_compat === '') { - url = `${window.aiarc.api_root}/aiarc/v1/crop`; - options = { - headers: { - 'X-Aiarc-Nonce': window.aiarc.nonce, - 'X-WP-Nonce': window.aiarc.wp_rest_nonce, - }, - }; - } - - if (window.aiarc_settings.rest_api_compat === '1') { - url = ajaxurl; - data = qs.stringify({ - action: 'acf_image_aspect_ratio_crop_crop', - data: JSON.stringify(data), - }); - } - - axios - .post(url, data, options) - .then(response => { - self.cropComplete(response.data); - $('.js-acf-image-aspect-ratio-crop-crop').prop('disabled', false); - $('.js-acf-image-aspect-ratio-crop-reset').prop( - 'disabled', - false, - ); - $('.js-acf-image-aspect-ratio-crop-modal-footer-status').empty(); - }) - .catch(response => { - console.error(response); - self.cropper.enable(); - $('.js-acf-image-aspect-ratio-crop-crop').prop('disabled', false); - $('.js-acf-image-aspect-ratio-crop-reset').prop( - 'disabled', - false, - ); - $('.js-acf-image-aspect-ratio-crop-modal-footer-status').empty(); - $('.js-acf-image-aspect-ratio-crop-modal-footer-status').html( - error, - ); - }); - }); }, /* @@ -627,6 +503,149 @@ import { sprintf } from 'sprintf-js'; } }, + attachCropModalEvents: function() { + var self = this; + + // Remove any previously attached handlers to prevent duplicates + $('.js-acf-image-aspect-ratio-crop-cancel').off('click.aiarc'); + $('.js-acf-image-aspect-ratio-crop-reset').off('click.aiarc'); + $('.js-acf-image-aspect-ratio-crop-crop').off('click.aiarc'); + + // Attach cancel handler + $('.js-acf-image-aspect-ratio-crop-cancel').on('click.aiarc', function(e) { + e.preventDefault(); + self.closeModal(); + }); + + // Attach reset handler + $('.js-acf-image-aspect-ratio-crop-reset').on('click.aiarc', function(e) { + e.preventDefault(); + if (self.cropper) { + self.cropper.reset(); + } + }); + + // Attach crop handler + $('.js-acf-image-aspect-ratio-crop-crop').on('click.aiarc', function(e) { + e.preventDefault(); + + if (!self.cropper) { + console.error('Cropper not initialized'); + return; + } + + var cropData = self.cropper.getData(true); + + $('.js-acf-image-aspect-ratio-crop-modal').css( + 'max-width', + self.cropper.containerData.width, + ); + + var cropType = $(field) + .find('.acf-image-uploader-aspect-ratio-crop') + .data('crop_type'); + + let acfKey = $(field) + .find('.acf-image-uploader-aspect-ratio-crop') + .data('key'); + + var data = { + id: $(this).data('id'), + aspectRatioHeight: $(this).data('aspect-ratio-height'), + aspectRatioWidth: $(this).data('aspect-ratio-width'), + cropType: $(this).data('crop-type'), + x: cropData.x, + y: cropData.y, + width: cropData.width, + height: cropData.height, + temp_post_id: aiarc.temp_post_id, + key: acfKey, + }; + + $('.js-acf-image-aspect-ratio-crop-crop').prop('disabled', true); + $('.js-acf-image-aspect-ratio-crop-reset').prop('disabled', true); + + // prettier-ignore + var loading = '
' + + '
' + + '' + + '' + + '' + + '' + + '
' + + '
' + + aiarc_translations.cropping_in_progress + + '
' + + '
'; + + // prettier-ignore + var error = '
' + + '
' + + '' + + '' + + '' + + '' + + '
' + + '
' + + aiarc_translations.cropping_failed + + '
' + + '
'; + + $('.js-acf-image-aspect-ratio-crop-modal-footer-status').empty(); + $('.js-acf-image-aspect-ratio-crop-modal-footer-status').html( + loading, + ); + self.cropper.disable(); + + let options = {}; + + let url = null; + + if (window.aiarc_settings.rest_api_compat === '') { + url = `${window.aiarc.api_root}/aiarc/v1/crop`; + options = { + headers: { + 'X-Aiarc-Nonce': window.aiarc.nonce, + 'X-WP-Nonce': window.aiarc.wp_rest_nonce, + }, + }; + } + + if (window.aiarc_settings.rest_api_compat === '1') { + url = ajaxurl; + data = qs.stringify({ + action: 'acf_image_aspect_ratio_crop_crop', + data: JSON.stringify(data), + }); + } + + axios + .post(url, data, options) + .then(response => { + self.cropComplete(response.data); + $('.js-acf-image-aspect-ratio-crop-crop').prop('disabled', false); + $('.js-acf-image-aspect-ratio-crop-reset').prop( + 'disabled', + false, + ); + $('.js-acf-image-aspect-ratio-crop-modal-footer-status').empty(); + }) + .catch(response => { + console.error(response); + self.cropper.enable(); + $('.js-acf-image-aspect-ratio-crop-crop').prop('disabled', false); + $('.js-acf-image-aspect-ratio-crop-reset').prop( + 'disabled', + false, + ); + $('.js-acf-image-aspect-ratio-crop-modal-footer-status').empty(); + $('.js-acf-image-aspect-ratio-crop-modal-footer-status').html( + error, + ); + }); + }); + }, + openModal: function(data) { var url = data.attachment.attributes.url; var id = data.attachment.attributes.id; @@ -754,6 +773,10 @@ import { sprintf } from 'sprintf-js'; options, ); + // Attach event handlers to the modal buttons + // This needs to happen after the modal DOM is created + this.attachCropModalEvents(); + // Test helper window._acf_image_aspect_ratio_cropper = this.cropper; }, @@ -803,9 +826,18 @@ import { sprintf } from 'sprintf-js'; acf.val(this.$input, ''); this.render({}); } + + // Remove event listeners before removing DOM + $('.js-acf-image-aspect-ratio-crop-cancel').off('click.aiarc'); + $('.js-acf-image-aspect-ratio-crop-reset').off('click.aiarc'); + $('.js-acf-image-aspect-ratio-crop-crop').off('click.aiarc'); + $('.acf-image-aspect-ratio-crop-backdrop').remove(); document.removeEventListener('keydown', this.escapeHandlerBound); - this.cropper.destroy(); + + if (this.cropper) { + this.cropper.destroy(); + } }, });