woocommerce-paypal-payments/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js

253 lines
5.3 KiB
JavaScript
Raw Normal View History

2024-04-08 11:31:12 +01:00
class FormFieldGroup {
#stored;
#data = {};
#active = false;
#baseSelector;
#contentSelector;
#fields = {};
#template;
2024-07-12 12:58:34 +02:00
constructor( config ) {
this.#baseSelector = config.baseSelector;
this.#contentSelector = config.contentSelector;
this.#fields = config.fields || {};
this.#template = config.template;
this.#stored = new Map();
2024-07-12 12:58:34 +02:00
}
setData( data ) {
this.#data = data;
2024-07-12 12:58:34 +02:00
this.refresh();
}
dataValue( fieldKey ) {
if ( ! fieldKey || ! this.#fields[ fieldKey ] ) {
2024-07-12 12:58:34 +02:00
return '';
}
if ( typeof this.#fields[ fieldKey ].valueCallback === 'function' ) {
return this.#fields[ fieldKey ].valueCallback( this.#data );
2024-07-12 12:58:34 +02:00
}
const path = this.#fields[ fieldKey ].valuePath;
2024-07-12 12:58:34 +02:00
if ( ! path ) {
return '';
}
const value = path
.split( '.' )
.reduce(
( acc, key ) =>
acc && acc[ key ] !== undefined ? acc[ key ] : undefined,
this.#data
2024-07-12 12:58:34 +02:00
);
return value ? value : '';
}
/**
* Activate form group: Render a custom Fastlane UI to replace the WooCommerce form.
*
* Indicates: Ryan flow.
*/
2024-07-12 12:58:34 +02:00
activate() {
this.#active = true;
this.storeFormData();
2024-07-12 12:58:34 +02:00
this.refresh();
}
/**
* Deactivate form group: Remove the custom Fastlane UI - either display the default
* WooCommerce checkout form or no form at all (when no email was provided yet).
*
* Indicates: Gary flow / no email provided / not using Fastlane.
*/
2024-07-12 12:58:34 +02:00
deactivate() {
this.#active = false;
this.restoreFormData();
2024-07-12 12:58:34 +02:00
this.refresh();
}
toggle() {
if ( this.#active ) {
this.deactivate();
} else {
this.activate();
}
2024-07-12 12:58:34 +02:00
}
refresh() {
const content = document.querySelector( this.#contentSelector );
2024-07-12 12:58:34 +02:00
if ( ! content ) {
return;
}
content.innerHTML = '';
if ( ! this.#active ) {
this.hideField( this.#contentSelector );
2024-07-12 12:58:34 +02:00
} else {
this.showField( this.#contentSelector );
2024-07-12 12:58:34 +02:00
}
this.loopFields( ( { selector } ) => {
if ( this.#active /* && ! field.showInput */ ) {
this.hideField( selector );
2024-07-12 12:58:34 +02:00
} else {
this.showField( selector );
2024-07-12 12:58:34 +02:00
}
} );
if ( typeof this.#template === 'function' ) {
content.innerHTML = this.#template( {
2024-07-12 12:58:34 +02:00
value: ( fieldKey ) => {
return this.dataValue( fieldKey );
},
isEmpty: () => {
let isEmpty = true;
this.loopFields( ( field, fieldKey ) => {
2024-07-12 12:58:34 +02:00
if ( this.dataValue( fieldKey ) ) {
isEmpty = false;
return false;
}
} );
2024-07-12 12:58:34 +02:00
return isEmpty;
},
} );
}
}
/**
* Invoke a callback on every field in the current group.
*
* @param {(field: object, key: string) => void} callback
*/
loopFields( callback ) {
Object.keys( this.#fields ).forEach( ( key ) => {
const field = this.#fields[ key ];
const fieldSelector = `${ this.#baseSelector } ${ field.selector }`;
callback(
{
inputSelector: field.inputName
? `${ fieldSelector } [name="${ field.inputName }"]`
: '',
...field,
},
key
);
} );
}
/**
* Stores the current form data in an internal storage.
* This allows the original form to be restored later.
*/
storeFormData() {
const storeValue = ( field, name ) => {
if ( 'checkbox' === field.type || 'radio' === field.type ) {
this.#stored.set( name, field.checked );
} else {
this.#stored.set( name, field.value );
}
};
this.loopFields( ( { inputSelector }, fieldKey ) => {
if ( inputSelector && ! this.#stored.has( fieldKey ) ) {
const elInput = document.querySelector( inputSelector );
if ( elInput ) {
storeValue( elInput, fieldKey );
}
}
} );
}
/**
* Restores the form data to its initial state before the form group was activated.
* This function iterates through the stored form fields and resets their values or states.
*/
restoreFormData() {
const restoreValue = ( field, name ) => {
if ( 'checkbox' === field.type || 'radio' === field.type ) {
field.checked = this.#stored.get( name );
} else {
field.value = this.#stored.get( name );
}
};
this.loopFields( ( { inputSelector }, fieldKey ) => {
if ( inputSelector && this.#stored.has( fieldKey ) ) {
const elInput = document.querySelector( inputSelector );
if ( elInput ) {
restoreValue( elInput, fieldKey );
}
this.#stored.delete( fieldKey );
}
} );
}
2024-07-12 12:58:34 +02:00
showField( selector ) {
const field = document.querySelector(
this.#baseSelector + ' ' + selector
2024-07-12 12:58:34 +02:00
);
if ( field ) {
field.classList.remove( 'ppcp-axo-field-hidden' );
}
}
hideField( selector ) {
const field = document.querySelector(
this.#baseSelector + ' ' + selector
2024-07-12 12:58:34 +02:00
);
if ( field ) {
field.classList.add( 'ppcp-axo-field-hidden' );
}
}
inputElement( name ) {
const baseSelector = this.#fields[ name ].selector;
2024-07-12 12:58:34 +02:00
const select = document.querySelector( baseSelector + ' select' );
if ( select ) {
return select;
}
const input = document.querySelector( baseSelector + ' input' );
if ( input ) {
return input;
}
return null;
}
inputValue( name ) {
const el = this.inputElement( name );
return el ? el.value : '';
}
toSubmitData( data ) {
this.loopFields( ( field, fieldKey ) => {
2024-07-12 12:58:34 +02:00
if ( ! field.valuePath || ! field.selector ) {
return true;
}
const inputElement = this.inputElement( fieldKey );
if ( ! inputElement ) {
return true;
}
data[ inputElement.name ] = this.dataValue( fieldKey );
} );
}
2024-03-08 14:39:50 +00:00
}
2024-04-08 11:31:12 +01:00
export default FormFieldGroup;