-
Notifications
You must be signed in to change notification settings - Fork 800
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Restaurant Menu CPT: Replace most jQuery with Javascript (#40645)
- Loading branch information
1 parent
64ab237
commit 829b8dc
Showing
4 changed files
with
173 additions
and
109 deletions.
There are no files selected for viewing
4 changes: 4 additions & 0 deletions
4
projects/plugins/jetpack/changelog/update-nova-cpt-jquery-to-js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Significance: patch | ||
Type: other | ||
|
||
Restaurant Menu CPT: Convert much of the jQuery usage to JavaScript |
207 changes: 120 additions & 87 deletions
207
projects/plugins/jetpack/modules/custom-post-types/js/many-items.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,111 +1,144 @@ | ||
( function ( $ ) { | ||
var menuSelector, nonceInput, methods; | ||
|
||
methods = { | ||
init: function ( /*options*/ ) { | ||
var $this = this, | ||
tbody, | ||
row; | ||
|
||
this.on( 'keypress.manyItemsTable', function ( event ) { | ||
if ( 13 !== event.which ) { | ||
return; | ||
} | ||
( function () { | ||
let menuSelector, nonceInput; | ||
const initializedTables = new Set(); | ||
|
||
const methods = { | ||
init: function ( table ) { | ||
let tbody = table.lastElementChild; | ||
while ( tbody && tbody.tagName !== 'TBODY' ) { | ||
tbody = tbody.previousElementSibling; | ||
} | ||
const row = tbody.querySelector( 'tr:first-child' ).cloneNode( true ); | ||
|
||
table.dataset.form = table.closest( 'form' ); | ||
table.dataset.tbody = tbody; | ||
table.dataset.row = row; | ||
table.dataset.currentRow = row; | ||
|
||
menuSelector = document.getElementById( 'nova-menu-tax' ); | ||
nonceInput = document.getElementById( '_wpnonce' ); | ||
|
||
table.addEventListener( 'keypress', function ( event ) { | ||
if ( event.which !== 13 ) return; | ||
|
||
event.preventDefault(); | ||
if ( 'function' === typeof FormData ) { | ||
methods.submitRow.apply( $this ); | ||
if ( typeof FormData === 'function' ) { | ||
methods.submitRow.call( table ); | ||
} | ||
methods.addRow.apply( $this ); | ||
} ).on( 'focus.manyItemsTable', ':input', function ( /*event*/ ) { | ||
$this.data( 'currentRow', $( this ).parents( 'tr:first' ) ); | ||
methods.addRow.call( table ); | ||
} ); | ||
|
||
tbody = this.find( 'tbody:last' ); | ||
row = tbody.find( 'tr:first' ).clone(); | ||
|
||
this.data( 'form', this.parents( 'form:first' ) ); | ||
this.data( 'tbody', tbody ); | ||
this.data( 'row', row ); | ||
this.data( 'currentRow', row ); | ||
|
||
menuSelector = $( '#nova-menu-tax' ); | ||
nonceInput = $( '#_wpnonce' ); | ||
table.addEventListener( 'focusin', function ( event ) { | ||
if ( event.target.tagName === 'INPUT' || event.target.tagName === 'TEXTAREA' ) { | ||
table.dataset.currentRow = event.target.closest( 'tr' ); | ||
} | ||
} ); | ||
|
||
return this; | ||
initializedTables.add( table ); | ||
return table; | ||
}, | ||
|
||
destroy: function () { | ||
this.off( '.manyItemsTable' ); | ||
|
||
return this; | ||
destroy: function ( table ) { | ||
if ( this.observer ) { | ||
this.observer.disconnect(); | ||
} | ||
table.removeEventListener( 'keypress', methods.keypressHandler ); | ||
table.removeEventListener( 'focusin', methods.focusinHandler ); | ||
initializedTables.delete( table ); | ||
return table; | ||
}, | ||
|
||
submitRow: function () { | ||
var submittedRow, currentInputs, allInputs, partialFormData; | ||
submitRow: function ( table ) { | ||
const submittedRow = table.dataset.currentRow; | ||
const currentInputs = submittedRow.querySelectorAll( 'input, textarea, select' ); | ||
const form = document.querySelector( table.dataset.form ); | ||
const allInputs = Array.from( form.querySelectorAll( 'input, textarea, select' ) ); | ||
|
||
submittedRow = this.data( 'currentRow' ); | ||
currentInputs = submittedRow.find( ':input' ); | ||
allInputs = this.data( 'form' ) | ||
.find( ':input' ) | ||
.not( currentInputs ) | ||
.attr( 'disabled', true ) | ||
.end(); | ||
currentInputs.forEach( input => ( input.disabled = true ) ); | ||
allInputs | ||
.filter( input => ! currentInputs.includes( input ) ) | ||
.forEach( input => ( input.disabled = true ) ); | ||
|
||
partialFormData = new FormData( this.data( 'form' ).get( 0 ) ); | ||
const partialFormData = new FormData( form ); | ||
partialFormData.append( 'ajax', '1' ); | ||
partialFormData.append( 'nova_menu_tax', menuSelector.val() ); | ||
partialFormData.append( '_wpnonce', nonceInput.val() ); | ||
|
||
allInputs.attr( 'disabled', false ); | ||
|
||
$.ajax( { | ||
url: '', | ||
type: 'POST', | ||
data: partialFormData, | ||
processData: false, | ||
contentType: false, | ||
} ).complete( function ( xhr ) { | ||
submittedRow.html( xhr.responseText ); | ||
} ); | ||
partialFormData.append( 'nova_menu_tax', menuSelector.value ); | ||
partialFormData.append( '_wpnonce', nonceInput.value ); | ||
|
||
currentInputs.attr( 'disabled', true ); | ||
fetch( '', { | ||
method: 'POST', | ||
body: partialFormData, | ||
} ) | ||
.then( response => response.text() ) | ||
.then( responseText => { | ||
submittedRow.innerHTML = responseText; | ||
} ); | ||
|
||
return this; | ||
allInputs.forEach( input => ( input.disabled = false ) ); | ||
|
||
return table; | ||
}, | ||
|
||
addRow: function () { | ||
var row = this.data( 'row' ).clone(); | ||
row.appendTo( this.data( 'tbody' ) ); | ||
row.find( ':input:first' ).focus(); | ||
addRow: function ( table ) { | ||
const row = table.dataset.row.cloneNode( true ); | ||
|
||
const tbody = table.dataset.tbody; | ||
tbody.appendChild( row ); | ||
|
||
const firstInput = row.querySelector( 'input, textarea, select' ); | ||
if ( firstInput ) firstInput.focus(); | ||
|
||
return this; | ||
return table; | ||
}, | ||
}; | ||
|
||
$.fn.manyItemsTable = function ( method ) { | ||
// Method calling logic | ||
if ( methods[ method ] ) { | ||
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ) ); | ||
} else if ( typeof method === 'object' || ! method ) { | ||
return methods.init.apply( this, arguments ); | ||
} | ||
$.error( 'Method ' + method + ' does not exist on jQuery.manyItemsTable' ); | ||
return this; | ||
clickAddRow: function ( table ) { | ||
let tbody = table.lastElementChild; | ||
|
||
while ( tbody && tbody.tagName !== 'TBODY' ) { | ||
tbody = tbody.previousElementSibling; | ||
} | ||
const row = tbody.querySelector( 'tr:first-child' ).cloneNode( true ); | ||
|
||
row.querySelectorAll( 'input, textarea' ).forEach( input => { | ||
input.value = ''; | ||
} ); | ||
|
||
tbody.appendChild( row ); | ||
}, | ||
}; | ||
|
||
$.fn.clickAddRow = function () { | ||
var tbody = this.find( 'tbody:last' ), | ||
row = tbody.find( 'tr:first' ).clone(); | ||
const observeTableRemoval = function ( list ) { | ||
const observer = new MutationObserver( mutations => { | ||
mutations.forEach( mutation => { | ||
mutation.removedNodes.forEach( node => { | ||
if ( node.matches && node.matches( '.many-items-table' ) ) { | ||
methods.destroy( node ); | ||
} | ||
} ); | ||
} ); | ||
} ); | ||
|
||
$( row ).find( 'input, textarea' ).val( '' ); | ||
$( row ).appendTo( tbody ); | ||
observer.observe( list, { childList: true, subtree: true } ); | ||
}; | ||
} )( jQuery ); | ||
|
||
jQuery( '.many-items-table' ).one( 'focus', ':input', function ( event ) { | ||
jQuery( event.delegateTarget ).manyItemsTable(); | ||
} ); | ||
jQuery( '.many-items-table' ).on( 'click', 'a.nova-new-row', function ( event ) { | ||
jQuery( event.delegateTarget ).clickAddRow(); | ||
} ); | ||
|
||
// Initialization for many-items-table | ||
document.addEventListener( 'focusin', event => { | ||
const table = event.target.closest( '.many-items-table' ); | ||
if ( table && ! initializedTables.has( table ) ) { | ||
methods.init( table ); | ||
} | ||
} ); | ||
|
||
document.addEventListener( 'click', event => { | ||
if ( event.target.matches( 'a.nova-new-row' ) ) { | ||
const table = event.target.closest( '.many-items-table' ); | ||
if ( table ) { | ||
event.preventDefault(); | ||
methods.clickAddRow( table ); | ||
} | ||
} | ||
} ); | ||
const list = document.querySelector( '#the-list' ); // Scope to the specific table | ||
if ( list ) { | ||
observeTableRemoval( list ); | ||
} | ||
} )(); |
67 changes: 47 additions & 20 deletions
67
projects/plugins/jetpack/modules/custom-post-types/js/menu-checkboxes.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,75 @@ | ||
( function ( $ ) { | ||
var NovaCheckBoxes = { | ||
( function () { | ||
const NovaCheckBoxes = { | ||
inputs: null, | ||
popInputs: null, | ||
|
||
initialize: function () { | ||
NovaCheckBoxes.popInputs = $( '#nova_menuchecklist-pop' ).find( ':checkbox' ); | ||
// Get all checkboxes in the "nova_menuchecklist-pop" | ||
NovaCheckBoxes.popInputs = document.querySelectorAll( | ||
'#nova_menuchecklist-pop input[type="checkbox"]' | ||
); | ||
|
||
NovaCheckBoxes.inputs = $( '#nova_menuchecklist' ) | ||
.find( ':checkbox' ) | ||
.change( NovaCheckBoxes.checkOne ) | ||
.change( NovaCheckBoxes.syncPop ); | ||
// Get all checkboxes in the "nova_menuchecklist" and add event listeners | ||
NovaCheckBoxes.inputs = document.querySelectorAll( | ||
'#nova_menuchecklist input[type="checkbox"]' | ||
); | ||
NovaCheckBoxes.inputs.forEach( input => { | ||
input.addEventListener( 'change', NovaCheckBoxes.checkOne ); | ||
input.addEventListener( 'change', NovaCheckBoxes.syncPop ); | ||
} ); | ||
|
||
// If no checkboxes are checked, check the first one | ||
if ( ! NovaCheckBoxes.isChecked() ) { | ||
NovaCheckBoxes.checkFirst(); | ||
} | ||
|
||
// Sync the state of the "pop" inputs | ||
NovaCheckBoxes.syncPop(); | ||
}, | ||
|
||
syncPop: function () { | ||
NovaCheckBoxes.popInputs.each( function () { | ||
var $this = $( this ); | ||
$this.prop( 'checked', $( '#in-nova_menu-' + $this.val() ).is( ':checked' ) ); | ||
NovaCheckBoxes.popInputs.forEach( popInput => { | ||
const linkedInput = document.querySelector( `#in-nova_menu-${ popInput.value }` ); | ||
popInput.checked = linkedInput ? linkedInput.checked : false; | ||
} ); | ||
}, | ||
|
||
isChecked: function () { | ||
return NovaCheckBoxes.inputs.is( ':checked' ); | ||
return Array.from( NovaCheckBoxes.inputs ).some( input => input.checked ); | ||
}, | ||
|
||
checkFirst: function () { | ||
NovaCheckBoxes.inputs.first().prop( 'checked', true ); | ||
const firstInput = NovaCheckBoxes.inputs[ 0 ]; | ||
if ( firstInput ) { | ||
firstInput.checked = true; | ||
} | ||
}, | ||
|
||
checkOne: function ( /*event*/ ) { | ||
if ( $( this ).is( ':checked' ) ) { | ||
return NovaCheckBoxes.inputs.not( this ).prop( 'checked', false ); | ||
checkOne: function () { | ||
const currentInput = this; | ||
|
||
// If the current checkbox is checked, uncheck all other checkboxes | ||
if ( currentInput.checked ) { | ||
NovaCheckBoxes.inputs.forEach( input => { | ||
if ( input !== currentInput ) { | ||
input.checked = false; | ||
} | ||
} ); | ||
return; | ||
} | ||
if ( $( this ).closest( '#nova_menuchecklist' ).find( ':checked' ).length > 0 ) { | ||
return $( this ).prop( 'checked', false ); | ||
const checklist = document.querySelector( '#nova_menuchecklist' ); | ||
|
||
// If at least one checkbox is still checked, uncheck the current one | ||
if ( checklist.querySelectorAll( 'input[type="checkbox"]:checked' ).length > 0 ) { | ||
currentInput.checked = false; | ||
return; | ||
} | ||
return NovaCheckBoxes.checkFirst(); | ||
|
||
// Otherwise, check the first checkbox | ||
NovaCheckBoxes.checkFirst(); | ||
}, | ||
}; | ||
|
||
$( NovaCheckBoxes.initialize ); | ||
} )( jQuery ); | ||
// Initialize when the DOM is fully loaded | ||
document.addEventListener( 'DOMContentLoaded', NovaCheckBoxes.initialize ); | ||
} )(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters