# learnpress/4.4.10/assets/src/js/admin/addons.js

LearnPress – WordPress LMS Plugin for Create and Sell Online Courses, version 4.4.10. 859 lines.

- Page: https://pluginprobe.com/plugins/learnpress/4.4.10/code/assets/src/js/admin/addons.js
- Raw: https://pluginprobe.com/plugins/learnpress/4.4.10/raw/assets/src/js/admin/addons.js
- Modified: 2026-10-09T07:39:42+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/learnpress/4.4.10/code/assets/src/js/admin/addons.js#L10-L20`.

```javascript
/**
 * Admin Add-ons page JS handler as a class.
 *
 * Add-ons list is rendered server-side via AdminAddonsPage::render_addons()
 * through TemplateAJAX::load_content_via_ajax(). This class only handles
 * UI interactions and addon actions via window.lpAJAXG.
 *
 * @since 4.4.7
 * @version 1.0.0
 */
import * as lpToastify from 'lpAssetsJsPath/lpToastify.js';
import * as lpUtils from 'lpAssetsJsPath/utils.js';

class AdminAddons {
	constructor() {
		this.elAddonsPage = null;
		this.urlParams = new URLSearchParams( window.location.search );
	}

	static selectors = {
		elAddonsPage: '.lp-addons-page',
		elToolbar: '.lp-addons-toolbar',
		elLPAddons: '#lp-addons',
		elAddonItem: '.lp-addon-item',
		elBtnAction: '.btn-addon-action',
		elNavTab: '.nav-tab',
		elNavTabActive: '.nav-tab.nav-tab-active',
		elFilter: '.lp-addons-filter',
		elFilterToggle: '.lp-addons-filter__toggle',
		elFilterMenu: '.lp-addons-filter__menu',
		elCategory: '.lp-addons-category',
		elCategoryActive: '.lp-addons-category.active',
		elSearch: '.lp-addons-search',
		elSearchBtn: '.lp-addons-search__btn',
		elSearchBtnClear: '.lp-addons-search__btn-clear',
		elSearchClose: '.lp-addons-search__close',
		elSearchInput: '#lp-search-addons__input',
		elItemPurchase: '.lp-addon-item__purchase',
		elPurchaseInstall: '.purchase-install',
		elPurchaseCode: '.enter-purchase-code',
		elLicense: '.lp-addon-license',
	};

	init() {
		// Content is injected via AJAX into .lp-target, wait for #lp-addons.
		lpUtils.lpOnElementReady( AdminAddons.selectors.elLPAddons, () => {
			this.elAddonsPage = document.querySelector(
				AdminAddons.selectors.elAddonsPage
			);
			if ( ! this.elAddonsPage ) {
				return;
			}

			const selectors = AdminAddons.selectors;
			const urlTab = this.urlParams.get( 'tab' );
			if ( urlTab ) {
				const elMatchingTab = this.elAddonsPage.querySelector(
					`${ selectors.elNavTab }[data-tab="${ urlTab }"]`
				);
				if ( elMatchingTab ) {
					this.elAddonsPage
						.querySelectorAll( selectors.elNavTab )
						.forEach( ( elTab ) => {
							elTab.classList.remove( 'nav-tab-active' );
							elTab.setAttribute( 'aria-pressed', 'false' );
						} );
					elMatchingTab.classList.add( 'nav-tab-active' );
					elMatchingTab.setAttribute( 'aria-pressed', 'true' );
				}
			}

			let elActiveTab = this.elAddonsPage.querySelector(
				selectors.elNavTabActive
			);
			if ( ! elActiveTab ) {
				elActiveTab =
					this.elAddonsPage.querySelector(
						`${ selectors.elNavTab }[data-tab="all"]`
					) || this.elAddonsPage.querySelector( selectors.elNavTab );
				if ( elActiveTab ) {
					elActiveTab.classList.add( 'nav-tab-active' );
					elActiveTab.setAttribute( 'aria-pressed', 'true' );
				}
			}

			let elActiveCategory = this.elAddonsPage.querySelector(
				selectors.elCategoryActive
			);
			if ( ! elActiveCategory ) {
				elActiveCategory =
					this.elAddonsPage.querySelector(
						`${ selectors.elCategory }[data-category="all"]`
					) || this.elAddonsPage.querySelector( selectors.elCategory );
				if ( elActiveCategory ) {
					elActiveCategory.classList.add( 'active' );
					elActiveCategory.setAttribute( 'aria-pressed', 'true' );
				}
			}

			this.filterAddons();
			this.updateSearchClearBtnState();
			this.updateFilterToggleState();
			this.events();
		} );
	}

	/**
	 * Register events.
	 *
	 * @return void
	 */
	events() {
		lpUtils.eventHandlers( 'click', [
			{
				selector: AdminAddons.selectors.elBtnAction,
				class: this,
				callBack: this.handleBtnAction.name,
			},
			{
				selector: AdminAddons.selectors.elNavTab,
				class: this,
				callBack: this.handleTabClick.name,
			},
			{
				selector: AdminAddons.selectors.elCategory,
				class: this,
				callBack: this.handleCategoryClick.name,
			},
			{
				selector: AdminAddons.selectors.elFilterToggle,
				class: this,
				callBack: this.handleFilterToggleClick.name,
			},
			{
				selector: AdminAddons.selectors.elSearchBtn,
				class: this,
				callBack: this.handleSearchBtnClick.name,
			},
			{
				selector: AdminAddons.selectors.elSearchBtnClear,
				class: this,
				callBack: this.handleSearchClearClick.name,
			},
			{
				selector: AdminAddons.selectors.elSearchClose,
				class: this,
				callBack: this.handleSearchCloseClick.name,
			},
		] );
		lpUtils.eventHandlers( 'input', [
			{
				selector: AdminAddons.selectors.elSearchInput,
				class: this,
				callBack: this.handleSearchInput.name,
			},
			{
				selector: AdminAddons.selectors.elPurchaseCode,
				class: this,
				callBack: this.handlePurchaseCodeInput.name,
			},
		] );
		lpUtils.eventHandlers( 'keydown', [
			{
				selector: AdminAddons.selectors.elSearchInput,
				class: this,
				callBack: this.handleSearchKeydown.name,
			},
		] );

		document.addEventListener( 'click', ( e ) => {
			if ( ! this.elAddonsPage ) {
				return;
			}
			const elToolbar = this.elAddonsPage.querySelector(
				AdminAddons.selectors.elToolbar
			);
			if ( elToolbar && elToolbar.classList.contains( 'is-filter-open' ) ) {
				if ( ! elToolbar.contains( e.target ) ) {
					this.toggleFilterMenu( false );
				}
			}
		} );

		document.addEventListener( 'keydown', ( e ) => {
			if ( 'Escape' === e.key ) {
				this.toggleFilterMenu( false );
			}
		} );
	}

	/**
	 * Get addon data embedded on the item element.
	 *
	 * @param {Element} elAddonItem Addon item element.
	 * @return {Object|null}
	 */
	getAddonData( elAddonItem ) {
		try {
			return JSON.parse( elAddonItem.dataset.addon );
		} catch ( e ) {
			return null;
		}
	}

	/**
	 * Send addon action to server via lpAJAXG.
	 *
	 * @param {Object} data { action, addon, purchase_code, el, elAddonItem }.
	 * @return void
	 */
	addonsAction( data ) {
		const { action, addon, el, elAddonItem } = data;
		const selectors = AdminAddons.selectors;

		const params = {
			action: 'addon_action',
			action_type: action,
			addon,
			purchase_code: data.purchase_code || '',
			id_url: 'addon-action',
		};

		const releaseHandling = () => {
			lpUtils.lpSetLoadingEl( el, 0 );
		};

		window.lpAJAXG.fetchAJAX( params, {
			success: ( response ) => {
				releaseHandling();

				const { status, message, data: resData } = response;

				lpToastify.show( message, status );

				if ( status === 'success' && resData?.html ) {
					elAddonItem.outerHTML = resData.html;
				} else if (
					'install' === action ||
					'update-purchase' === action
				) {
					const elPurchasePanel = el.closest(
						selectors.elPurchaseInstall
					);
					const elPurchaseCode = elPurchasePanel
						? elPurchasePanel.querySelector( selectors.elPurchaseCode )
						: null;
					if ( elPurchaseCode ) {
						elPurchaseCode.classList.add( 'is-error' );
						elPurchaseCode.setAttribute( 'aria-invalid', 'true' );
					}
				}

				//this.filterAddons();
			},
			error: ( error ) => {
				releaseHandling();
				lpToastify.show( `error js: ${ error }`, 'error' );
			},
		} );
	}

	/**
	 * Filter add-ons by status, category, and search query.
	 *
	 * @return void
	 */
	filterAddons() {
		const selectors = AdminAddons.selectors;
		const elAddonItems = this.elAddonsPage.querySelectorAll(
			selectors.elAddonItem
		);

		// Count items for each tab
		this.updateTabCounts( elAddonItems );
		const elActiveTab = this.elAddonsPage.querySelector(
			selectors.elNavTabActive
		);
		const elActiveCategory = this.elAddonsPage.querySelector(
			selectors.elCategoryActive
		);
		const elSearch = this.elAddonsPage.querySelector(
			selectors.elSearchInput
		);
		const tabName = elActiveTab ? elActiveTab.dataset.tab : 'all';
		const category = elActiveCategory
			? elActiveCategory.dataset.category
			: 'all';
		const keyword = elSearch ? elSearch.value.trim().toLowerCase() : '';
		const searchTerms = keyword.split( /\s+/ ).filter( Boolean );

		// Count items for category by tab
		this.updateCategoryCounts( elAddonItems, tabName );

		elAddonItems.forEach( ( elAddonItem ) => {
			const addonInfo = JSON.parse( elAddonItem.dataset.addon );

			const addonName = elAddonItem
				.querySelector( 'a' )
				.textContent.toLowerCase();
			const textCompare = addonName + addonInfo.description;

			const addonCategories = ( elAddonItem.dataset.category || '' )
				.split( /\s+/ )
				.filter( Boolean );
			const matchesTab =
				'all' === tabName || elAddonItem.classList.contains( tabName );
			const matchesCategory =
				'all' === category || addonCategories.includes( category );
			const matchesSearch = searchTerms.every( ( term ) => textCompare.includes( term ) );
			const isVisible = matchesTab && matchesCategory && matchesSearch;

			elAddonItem.classList.toggle( 'hide', ! matchesTab );
			elAddonItem.classList.toggle(
				'search-not-found',
				! isVisible
			);
		} );
	}

	updateCategoryCounts( elAddonItems, tabName ) {
		const categoryCounts = { all: 0 };

		elAddonItems.forEach( ( elAddonItem ) => {
			const matchesTab =
				'all' === tabName || elAddonItem.classList.contains( tabName );

			if ( ! matchesTab ) {
				return;
			}

			categoryCounts.all++;
			( elAddonItem.dataset.category || '' )
				.split( /\s+/ )
				.filter( Boolean )
				.forEach( ( addonCategory ) => {
					categoryCounts[ addonCategory ] =
						( categoryCounts[ addonCategory ] || 0 ) + 1;
				} );
		} );

		this.elAddonsPage
			.querySelectorAll( AdminAddons.selectors.elCategory )
			.forEach( ( elCategory ) => {
				const count =
					categoryCounts[ elCategory.dataset.category ] || 0;
				const elCount = elCategory.querySelector(
					'.lp-addons-category__count'
				);

				if ( elCount ) {
					elCount.textContent = `(${ count })`;
				}

				elCategory.hidden =
					'all' !== elCategory.dataset.category && 0 === count;
			} );
	}

	updateTabCounts( elAddonItems ) {
		this.elAddonsPage
			.querySelectorAll( AdminAddons.selectors.elNavTab )
			.forEach( ( elNavTab ) => {
				const tabName = elNavTab.dataset.tab;
				const count = Array.from( elAddonItems ).filter(
					( elAddonItem ) =>
						'all' === tabName ||
						elAddonItem.classList.contains( tabName )
				).length;
				const elCount = elNavTab.querySelector( 'span' );

				if ( elCount ) {
					elCount.textContent = count;
				}
			} );
	}

	/**
	 * Handle addon action button click.
	 *
	 * @param {Object} args Click event arguments.
	 * @return void
	 */
	handleBtnAction( args ) {
		const { e, target } = args;
		const selectors = AdminAddons.selectors;
		const el = target.closest( selectors.elBtnAction );

		// Ignore button type link
		if ( 'A' === el.tagName ) {
			return;
		}

		e.preventDefault();
		lpUtils.lpSetLoadingEl( el, 1 );

		let purchaseCode = '';
		const elAddonItem = el.closest( selectors.elAddonItem );
		const addon = this.getAddonData( elAddonItem );
		const action = el.dataset.action;
		const elItemPurchase = elAddonItem.querySelector(
			selectors.elItemPurchase
		);

		if ( ! addon ) {
			lpUtils.lpSetLoadingEl( el, 0 );
			return;
		}

		if ( action === 'purchase' || action === 'update-purchase-code' ) {
			const elPurchaseInstall = elItemPurchase.querySelector(
				selectors.elPurchaseInstall
			);
			const elPurchaseCode = elPurchaseInstall.querySelector(
				selectors.elPurchaseCode
			);
			const elPurchaseSubmit = elPurchaseInstall.querySelector(
				'.lp-addon-purchase__submit'
			);
			const elPurchaseClear = elPurchaseInstall.querySelector(
				'.lp-addon-purchase__clear'
			);
			const elPurchaseCancelClear = elPurchaseInstall.querySelector(
				'.lp-addon-purchase__cancel-clear'
			);
			let hasPurchaseCode = false;

			if ( action === 'update-purchase-code' ) {
				const elLicense = elAddonItem.querySelector( selectors.elLicense );
				elPurchaseCode.value = elLicense
					? elLicense.dataset.purchaseCodeMasked || ''
					: '';
				hasPurchaseCode = '' !== elPurchaseCode.value;
				elPurchaseSubmit.dataset.action = 'update-purchase';
			} else {
				elPurchaseCode.value = '';
				elPurchaseSubmit.dataset.action = 'install';
			}

			elPurchaseCode.disabled = hasPurchaseCode;
			elPurchaseSubmit.classList.toggle( 'lp-hidden', hasPurchaseCode );
			elPurchaseClear.classList.toggle( 'lp-hidden', ! hasPurchaseCode );
			elPurchaseCancelClear.classList.add( 'lp-hidden' );
			elPurchaseCode.classList.remove( 'is-error' );
			elPurchaseCode.removeAttribute( 'aria-invalid' );
			elItemPurchase.querySelector( 'input[name=purchase-code]' ).value =
				'';
			elPurchaseInstall.style.display = 'flex';
			elItemPurchase.style.display = 'block';
			if ( ! hasPurchaseCode ) {
				elPurchaseCode.focus();
			}
			lpUtils.lpSetLoadingEl( el, 0 );
			return;
		} else if ( action === 'clear-license' ) {
			const elPurchaseCode = elItemPurchase.querySelector(
				selectors.elPurchaseCode
			);
			const elPurchaseSubmit = elItemPurchase.querySelector(
				'.lp-addon-purchase__submit'
			);
			const elPurchaseClear = elItemPurchase.querySelector(
				'.lp-addon-purchase__clear'
			);
			const elPurchaseCancelClear = elItemPurchase.querySelector(
				'.lp-addon-purchase__cancel-clear'
			);

			elPurchaseCode.value = '';
			elPurchaseCode.disabled = false;
			elPurchaseCode.classList.remove( 'is-error' );
			elPurchaseCode.removeAttribute( 'aria-invalid' );
			elItemPurchase.querySelector( 'input[name=purchase-code]' ).value =
				'';
			elPurchaseSubmit.classList.remove( 'lp-hidden' );
			elPurchaseClear.classList.add( 'lp-hidden' );
			elPurchaseCancelClear.classList.remove( 'lp-hidden' );
			elPurchaseCode.focus();
			lpUtils.lpSetLoadingEl( el, 0 );
			return;
		} else if ( action === 'cancel-clear-license' ) {
			const elLicense = elAddonItem.querySelector( selectors.elLicense );
			const elPurchaseCode = elItemPurchase.querySelector(
				selectors.elPurchaseCode
			);
			const elPurchaseSubmit = elItemPurchase.querySelector(
				'.lp-addon-purchase__submit'
			);
			const elPurchaseClear = elItemPurchase.querySelector(
				'.lp-addon-purchase__clear'
			);

			elPurchaseCode.value = elLicense
				? elLicense.dataset.purchaseCodeMasked || ''
				: '';
			elPurchaseCode.disabled = true;
			elPurchaseCode.classList.remove( 'is-error' );
			elPurchaseCode.removeAttribute( 'aria-invalid' );
			elItemPurchase.querySelector( 'input[name=purchase-code]' ).value =
				'';
			elPurchaseSubmit.classList.add( 'lp-hidden' );
			elPurchaseClear.classList.remove( 'lp-hidden' );
			el.classList.add( 'lp-hidden' );
			lpUtils.lpSetLoadingEl( el, 0 );
			return;
		} else if ( action === 'cancel' ) {
			elItemPurchase.style.display = 'none';
			elItemPurchase.querySelector(
				selectors.elPurchaseInstall
			).style.display = 'none';
			lpUtils.lpSetLoadingEl( el, 0 );
			return;
		}

		// Send request to server.
		if ( elItemPurchase ) {
			purchaseCode = elItemPurchase.querySelector(
				'input[name=purchase-code]'
			).value;
		}

		this.addonsAction( {
			purchase_code: purchaseCode,
			action,
			addon,
			el,
			elAddonItem,
		} );
	}

	/**
	 * Handle tab click.
	 *
	 * @param {Object} args Click event arguments.
	 * @return void
	 */
	handleTabClick( args ) {
		const { e, target } = args;
		const selectors = AdminAddons.selectors;
		const el = target.closest( selectors.elNavTab );
		e.preventDefault();

		const elTabs = this.elAddonsPage.querySelectorAll( selectors.elNavTab );
		elTabs.forEach( ( elTab ) => {
			elTab.classList.remove( 'nav-tab-active' );
			elTab.setAttribute( 'aria-pressed', 'false' );
		} );
		el.classList.add( 'nav-tab-active' );
		el.setAttribute( 'aria-pressed', 'true' );
		if ( el.scrollIntoView ) {
			el.scrollIntoView( { inline: 'center', block: 'nearest', behavior: 'smooth' } );
		}

		const tabName = el.dataset.tab;
		const elSearch = this.elAddonsPage.querySelector(
			selectors.elSearchInput
		);
		if ( elSearch ) {
			elSearch.value = '';
		}
		const elSearchContainer = this.elAddonsPage.querySelector(
			selectors.elSearch
		);
		if ( elSearchContainer ) {
			elSearchContainer.classList.remove( 'is-open' );
			const btn = elSearchContainer.querySelector(
				selectors.elSearchBtn
			);
			if ( btn ) {
				btn.setAttribute( 'aria-expanded', 'false' );
			}
		}
		this.elAddonsPage
			.querySelectorAll( selectors.elCategory )
			.forEach( ( elCategory ) => {
				const isActive = 'all' === elCategory.dataset.category;
				elCategory.classList.toggle( 'active', isActive );
				elCategory.setAttribute(
					'aria-pressed',
					isActive ? 'true' : 'false'
				);
			} );

		this.urlParams.set( 'tab', tabName );
		window.history.pushState(
			{},
			'',
			`${ window.location.pathname }?${ this.urlParams.toString() }`
		);
		this.updateFilterToggleState();
		this.filterAddons();
	}

	/**
	 * Handle filter toggle button click.
	 *
	 * @param {Object} args Click event arguments.
	 * @return void
	 */
	handleFilterToggleClick( args ) {
		const { e } = args;
		e.preventDefault();
		e.stopPropagation();
		this.toggleFilterMenu();
	}

	/**
	 * Toggle or set filter menu dropdown open state.
	 *
	 * @param {boolean|undefined} open Optional boolean to force state.
	 * @return void
	 */
	toggleFilterMenu( open ) {
		const selectors = AdminAddons.selectors;
		const elToolbar = this.elAddonsPage.querySelector( selectors.elToolbar );
		const elFilter = this.elAddonsPage.querySelector( selectors.elFilter );
		if ( ! elToolbar && ! elFilter ) {
			return;
		}
		const toggleBtn = this.elAddonsPage.querySelector(
			selectors.elFilterToggle
		);
		const shouldOpen =
			undefined !== open
				? open
				: elToolbar
				? ! elToolbar.classList.contains( 'is-filter-open' )
				: ! elFilter.classList.contains( 'is-open' );

		if ( elToolbar ) {
			elToolbar.classList.toggle( 'is-filter-open', shouldOpen );
		}
		if ( elFilter ) {
			elFilter.classList.toggle( 'is-open', shouldOpen );
		}
		if ( toggleBtn ) {
			toggleBtn.setAttribute(
				'aria-expanded',
				shouldOpen ? 'true' : 'false'
			);
		}
	}

	/**
	 * Update filter toggle button has-filter state.
	 *
	 * @return void
	 */
	updateFilterToggleState() {
		const selectors = AdminAddons.selectors;
		const toggleBtn = this.elAddonsPage.querySelector(
			selectors.elFilterToggle
		);
		if ( ! toggleBtn ) {
			return;
		}
		const elActiveTab = this.elAddonsPage.querySelector(
			selectors.elNavTabActive
		);
		const elActiveCategory = this.elAddonsPage.querySelector(
			selectors.elCategoryActive
		);
		const tabName = elActiveTab ? elActiveTab.dataset.tab : 'all';
		const category = elActiveCategory
			? elActiveCategory.dataset.category
			: 'all';
		const hasFilter = 'all' !== tabName || 'all' !== category;
		toggleBtn.classList.toggle( 'has-filter', hasFilter );
	}

	/**
	 * Handle category click.
	 *
	 * @param {Object} args Click event arguments.
	 * @return void
	 */
	handleCategoryClick( args ) {
		const { e, target } = args;
		const elCategory = target.closest(
			AdminAddons.selectors.elCategory
		);
		e.preventDefault();

		this.elAddonsPage
			.querySelectorAll( AdminAddons.selectors.elCategory )
			.forEach( ( item ) => {
				const isActive = item === elCategory;
				item.classList.toggle( 'active', isActive );
				item.setAttribute(
					'aria-pressed',
					isActive ? 'true' : 'false'
				);
			} );
		this.updateFilterToggleState();
		this.filterAddons();
	}

	/**
	 * Handle search input.
	 *
	 * @return void
	 */
	handleSearchInput() {
		this.updateSearchClearBtnState();
		this.filterAddons();
	}

	/**
	 * Handle search button click to open search overlay.
	 *
	 * @param {Object} args Click event arguments.
	 * @return void
	 */
	handleSearchBtnClick( args ) {
		const { e } = args;
		e.preventDefault();
		this.toggleFilterMenu( false );
		const selectors = AdminAddons.selectors;
		const elSearch = this.elAddonsPage.querySelector(
			selectors.elSearch
		);
		const elSearchInput = this.elAddonsPage.querySelector(
			selectors.elSearchInput
		);
		if ( elSearch ) {
			elSearch.classList.add( 'is-open' );
			const btn = elSearch.querySelector( selectors.elSearchBtn );
			if ( btn ) {
				btn.setAttribute( 'aria-expanded', 'true' );
			}
		}
		if ( elSearchInput ) {
			this.updateSearchClearBtnState();
			elSearchInput.focus();
		}
	}

	/**
	 * Handle search clear button click.
	 *
	 * @param {Object} args Click event arguments.
	 * @return void
	 */
	handleSearchClearClick( args ) {
		const { e } = args;
		e.preventDefault();
		const selectors = AdminAddons.selectors;
		const elSearchInput = this.elAddonsPage.querySelector(
			selectors.elSearchInput
		);
		if ( elSearchInput ) {
			elSearchInput.value = '';
			this.updateSearchClearBtnState();
			this.filterAddons();
			elSearchInput.focus();
		}
	}

	/**
	 * Update disabled state of clear search button.
	 *
	 * @return void
	 */
	updateSearchClearBtnState() {
		const selectors = AdminAddons.selectors;
		const elSearchInput = this.elAddonsPage.querySelector(
			selectors.elSearchInput
		);
		const elClearBtn = this.elAddonsPage.querySelector(
			selectors.elSearchBtnClear
		);
		if ( elClearBtn && elSearchInput ) {
			elClearBtn.disabled = ! elSearchInput.value.trim();
		}
	}

	/**
	 * Handle search close button click.
	 *
	 * @param {Object} args Click event arguments.
	 * @return void
	 */
	handleSearchCloseClick( args ) {
		const { e } = args;
		e.preventDefault();
		this.closeSearch();
	}

	/**
	 * Handle search input keydown.
	 *
	 * @param {Object} args Keydown event arguments.
	 * @return void
	 */
	handleSearchKeydown( args ) {
		const { e } = args;
		if ( 'Escape' === e.key ) {
			e.preventDefault();
			this.closeSearch();
		}
	}

	/**
	 * Close search overlay and reset query if needed.
	 *
	 * @return void
	 */
	closeSearch() {
		const selectors = AdminAddons.selectors;
		const elSearch = this.elAddonsPage.querySelector(
			selectors.elSearch
		);
		const elSearchInput = this.elAddonsPage.querySelector(
			selectors.elSearchInput
		);
		if ( elSearch ) {
			elSearch.classList.remove( 'is-open' );
			const btn = elSearch.querySelector( selectors.elSearchBtn );
			if ( btn ) {
				btn.setAttribute( 'aria-expanded', 'false' );
				btn.focus();
			}
		}
		if ( elSearchInput && elSearchInput.value ) {
			elSearchInput.value = '';
			this.filterAddons();
		}
		this.updateSearchClearBtnState();
	}

	/**
	 * Handle purchase code input.
	 *
	 * @param {Object} args Input event arguments.
	 * @return void
	 */
	handlePurchaseCodeInput( args ) {
		const { e, target: el } = args;
		e.preventDefault();
		el.classList.remove( 'is-error' );
		el.removeAttribute( 'aria-invalid' );
		const purchaseCode = el.value;
		const elItemPurchase = el.closest(
			AdminAddons.selectors.elItemPurchase
		);

		if ( elItemPurchase ) {
			const input = elItemPurchase.querySelector(
				'input[name=purchase-code]'
			);
			input.value = purchaseCode;
		}
	}
}

const adminAddons = new AdminAddons();
adminAddons.init();

export default AdminAddons;

```
