Files
roi-theme/wp-content/plugins/thrive-product-manager/thrive-dashboard/js/modal.js
root a22573bf0b Commit inicial - WordPress Análisis de Precios Unitarios
- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 21:04:30 -06:00

405 lines
10 KiB
JavaScript
Executable File

window.TVE_Dash = window.TVE_Dash || {};
TVE_Dash.views = TVE_Dash.views || {};
( function ( $ ) {
require( './materialize/leanModal' );
window.Vel = require( './materialize/velocity.min' );
require( './materialize/global' );
require( './materialize/forms' );
require( './util/util' );
/**
* base class for modals
*
* backbone view wrapped over a modal div from materialize
*/
TVE_Dash.views.Modal = Backbone.View.extend( {
className: 'tvd-modal',
id: 'tvd-modal-base',
/**
* .tvd-modal-content div reference
*/
$_content: '',
/* this will be populated with data coming from params when instantiating the view / modal */
data: {},
/**
* Array of stored child views - to remove them when this view is destroyed
*/
$$childViews: [],
/**
* Get the main modal action button. Usually, this is the "Save" button.
* Overwrite this if custom behaviour is needed
*
* @return {JQuery}
*/
getModalActionButton() {
return this.$( '.tvd-modal-save' ).filter( ':visible' );
},
/**
* used to setup this.data field - it will set all constructor arguments into this.data so that they are available
*
* @param {Object} args
*/
initialize( args ) {
this.beforeInitialize( args );
this.data = args;
const self = this;
_.each( args, function ( v, k ) {
if ( typeof self[ k ] === 'undefined' ) {
self[ k ] = v;
}
} );
this.afterInitialize( args );
},
/**
* we should not override this, use afterRender() instead
* this does not open the modal, it just renders it and appends it to the body tag
*
* @return {TVE_Dash.views.Modal}
*/
render() {
this.$el.empty();
this.$el.appendTo( 'body' );
// make the whole data object available to the template
this.data = _.extend( {
model: this.model
}, this.data );
if ( typeof this.template === 'string' ) {
this.template = TVE_Dash.tpl( this.template );
}
this.$el.html( this.template( this.data ) );
if ( this.data[ 'max-width' ] ) {
/* max-width should always be sent in % */
this.$el.css( 'max-width', this.data[ 'max-width' ] );
} else {
this.$el.css( 'max-width', '' );
}
this.$el.css( 'max-height', this.data[ 'max-height' ] || '' );
if ( this.data.no_close ) {
this.$el.find( '.tvd-modal-close' ).remove();
} else if ( ! this.$el.children( '.tvd-modal-close' ).length ) {
this.$el.append( '<a href="javascript:void(0)" class="tvd-modal-action tvd-modal-close tvd-modal-close-x"><i class="tvd-icon-close2"></i></a>' );
}
if ( this.data.title ) {
let $title = this.$el.find( 'h3.tvd-modal-title' );
if ( ! $title.length ) {
$title = $( '<h3 class="tvd-modal-title"></h3>' );
this.$el.find( '.tvd-modal-content' ).prepend( $title );
}
$title.html( this.data.title );
}
if ( this.data.width ) {
this.$el.css( 'width', this.data.width );
}
if ( this.data.height ) {
this.$el.css( 'height', this.data.height + 'px' );
}
this.$_content = this.$el.find( '.tvd-modal-content' );
this.afterRender();
/**
* when the autocomplete opens, we need to scroll the .tvd-modal-content so that the results are visible
*/
this.$el.on( 'autocompleteopen', _.bind( this.autocomplete_scroll, this ) );
this.$el.on( 'tvdpickeropen', _.bind( this.pickadate_scroll, this ) );
return this;
},
/**
* called when a jquery autocomplete widget is opened inside a modal
* scrolls the modal frame to contain as much of the autocomplete element as possible
*
* @param event
* @param ui
*/
autocomplete_scroll( event ) {
const $input = $( event.target ),
$ul = $input.parent().find( 'ul.ui-autocomplete' ),
ui_height = $input.outerHeight() + $ul.outerHeight(),
ui_top = $input.offset().top,
c_top = this.$_content.offset().top,
delta_scroll = ui_top - c_top + this.$_content.scrollTop() - 25;
if ( ui_height + ui_top > this.$_content.outerHeight() + c_top ) {
this.$_content.animate( {
scrollTop: delta_scroll
} );
}
},
/**
* called when a datepicker (pickadate) is opened inside this modal
* it scroll the modal frame so that the date picker is fully visible
*
* @param {Object} event jQuery event instance
* @param {Object} P Picker instance
*/
pickadate_scroll( event, P ) {
const $input = $( event.target ),
$picker = P.$root,
picker_height = $picker.outerHeight( true ) + 40,
picker_top = $input.offset().top - picker_height / 2,
c_top = this.$_content.offset().top;
if ( picker_top + picker_height > this.$_content.outerHeight() + c_top ) {
this.$_content.animate( {
scrollTop: picker_height + picker_top - c_top - this.$_content.outerHeight() + this.$_content.scrollTop() - 25
} );
} else if ( picker_top < c_top ) {
this.$_content.animate( {
scrollTop: this.$_content.scrollTop() - ( c_top - picker_top )
} );
}
},
/**
* auto-focus the first input and bind the ENTER event
*
* @param {Object} [$root] optional, jquery wrapper over a DOM element
*/
input_focus( $root ) {
$root = $root || this.$el;
const $actionButton = this.getModalActionButton();
const $textInputs = $root.find( 'input:not(.tvd-no-focus,:checkbox,:radio,:hidden)' ).filter( ':visible' );
/**
* find the first focusable text input (or textarea) or action button
*/
const $autoFocus = $textInputs.add( this.$( 'textarea' ) ).add( $actionButton ).filter( ':visible' ).not( '.tvd-no-focus' ).first();
if ( $autoFocus.length ) {
requestAnimationFrame( () => $autoFocus.first().focus().select() );
}
/**
* Setup keyup listeners on the text inputs
*
* @type {*|number|bigint|T|T|undefined}
*/
$textInputs.not( '.tvd-skip-modal-save' ).off( 'keyup.tvd-save' ).on( 'keyup.tvd-save', function ( e ) {
if ( e.which === 13 ) {
/**
* `requestAnimationFrame` so that any other `change` events are processed first and model validation can be correctly performed
*/
requestAnimationFrame( () => {
if ( $actionButton.css( 'pointer-events' ) !== 'none' ) {
$actionButton.trigger( 'click' );
}
} );
return false;
}
return true;
} );
},
/**
* triggered immediately after the rendering is completed (after the template is added to DOM) but before showing the modal
* override this to populate extra stuff in the view
*/
afterRender() {
return this;
},
/**
* open the modal
* options for the modal can be sent when instantiating the view
*
* @return {TVE_Dash.Modal}
*/
open() {
const self = this;
const options = _.extend( {
in_duration: 200,
out_duration: 300,
dismissible: true,
beforeClose() {
self.beforeClose();
},
ready() {
self.onOpen.call( self );
/**
* allow also 'afterOpen' callback
*/
if ( typeof self.afterOpen === 'function' ) {
self.afterOpen.call( self );
}
if ( ! self.noMaterialize ) {
TVE_Dash.materialize( self.$el );
}
self.input_focus();
if ( typeof self.afterMaterialize === 'function' ) {
self.afterMaterialize.call( self );
}
},
complete() {
delete TVE_Dash.opened_modal_view;
self.$el.css( 'width', '' ).css( 'height', '' );
self.destroy().remove();
self.onClose.call( self );
/**
* allow also 'afterClose' callback
*/
if ( typeof self.afterClose === 'function' ) {
self.afterClose.call( self );
}
}
}, this.data );
this.modal_options = options;
this.$el.openModal( options );
TVE_Dash.opened_modal_view = this;
return this;
},
close() {
this.$el.closeModal( this.modal_options );
return this;
},
/**
* triggered after the modal has been opened
*/
onOpen() {
},
/**
* triggered after the modal has been closed and the html has been removed
*/
onClose() {
},
/**
* triggered before the modal has been closed and the html has been removed
*/
beforeClose() {
},
/**
* show a preloader over this modal
*/
showLoader() {
let _loader = this.$( '.tvd-modal-preloader-wrapper' );
if ( ! _loader.length ) {
_loader = $( TVE_Dash.tpl( 'modal-loader', {} ) );
this.$el.append( _loader );
}
_loader.find( '.tvd-modal-preloader' ).css( {
top: ( this.$el.outerHeight() / 2 ) + 'px'
} );
/* we do this to avoid calling the loader multiple times */
requestAnimationFrame( () => {
if ( ! this.isLoading ) {
_loader.fadeIn( 300 );
this.isLoading = true;
}
} );
this.$el.addClass( 'tvd-modal-disable' );
return this;
},
/**
* hide the preloader
*/
hideLoader() {
this.$el.removeClass( 'tvd-modal-disable' );
requestAnimationFrame( () => this.$( '.tvd-modal-preloader-wrapper' ).fadeOut( 300 ) );
this.isLoading = false;
return this;
},
/**
* set the loading state on a button
*
* @param $btn
* @return {*}
*/
btnLoading( $btn ) {
return jQuery( $btn ).addClass( 'tvd-disabled' ).prop( 'disabled', true );
},
beforeInitialize() {
return this;
},
afterInitialize() {
return this;
},
/**
* Add one or more child view reference. Can receive any number of arguments
*
* @param {Backbone.View|Backbone.View[]} views
*
* @return {Backbone.View} the caller view
*/
addChild( ...views ) {
if ( Array.isArray( views[ 0 ] ) ) {
views = views[ 0 ];
}
this.$$childViews = this.$$childViews.concat( views );
return this;
},
/**
* Completely destroy the view and un-delegate any events
*/
destroy() {
this.stopListening();
this.undelegateEvents();
this.$el.removeData().off();
this.$$childViews.filter( i => i ).forEach( view => {
if ( typeof view?.destroy === 'function' ) {
view.destroy();
}
} );
return this;
}
} );
$( document ).ready( function () {
if ( ! TVE_Dash.views.VideoModal ) {
TVE_Dash.views.VideoModal = TVE_Dash.views.Modal.extend( {
template: TVE_Dash.tpl( 'modal-video-modal' ),
className: 'tvd-modal video-container',
initialize( options ) {
this.source = options.source;
},
afterRender() {
this.$el.find( 'iframe' ).attr( 'src', `https://www.youtube.com/embed/${this.source}` );
},
} );
$( 'body' ).on( 'click', '.tvd-open-video', function ( e ) {
let $target = $( e.target );
if ( ! $target.hasClass( 'tvd-open-video' ) ) {
$target = $target.parents( '.tvd-open-video' );
}
TVE_Dash.modal( TVE_Dash.views.VideoModal, {
'max-width': '80%',
source: $target.attr( 'data-source' ),
} );
} );
}
} );
} )( jQuery );