187 lines
5.1 KiB
TypeScript
187 lines
5.1 KiB
TypeScript
/**
|
|
* Internal dependencies
|
|
*/
|
|
import { getAvailableCategories } from '../product-category/utils';
|
|
|
|
interface HTMLWPListElement extends HTMLElement {
|
|
wpList: {
|
|
settings: {
|
|
addAfter: (
|
|
returnedResponse: XMLDocument,
|
|
ajaxSettings: object,
|
|
wpListSettings: object
|
|
) => void;
|
|
};
|
|
};
|
|
}
|
|
|
|
declare global {
|
|
interface Window {
|
|
wpAjax: {
|
|
parseAjaxResponse: ( response: object ) => {
|
|
responses?: {
|
|
data?: string;
|
|
}[];
|
|
};
|
|
};
|
|
}
|
|
}
|
|
|
|
type NewCategory = {
|
|
name: string;
|
|
parent_id?: number;
|
|
};
|
|
|
|
/**
|
|
* Creates a category in the product category list. This function can only be used where the product category taxonomy list is available (e.g. on the product edit page).
|
|
*/
|
|
const createCategory = async ( category: NewCategory ) => {
|
|
const newCategoryInput = document.getElementById(
|
|
'newproduct_cat'
|
|
) as HTMLInputElement;
|
|
const newCategoryParentSelect = document.getElementById(
|
|
'newproduct_cat_parent'
|
|
) as HTMLSelectElement;
|
|
const newCategoryAddButton = document.getElementById(
|
|
'product_cat-add-submit'
|
|
) as HTMLButtonElement;
|
|
const addCategoryToggle = document.getElementById(
|
|
'product_cat-add-toggle'
|
|
) as HTMLButtonElement;
|
|
const categoryListElement = document.getElementById(
|
|
'product_catchecklist'
|
|
) as HTMLWPListElement;
|
|
|
|
if (
|
|
! [
|
|
newCategoryInput,
|
|
newCategoryParentSelect,
|
|
newCategoryAddButton,
|
|
addCategoryToggle,
|
|
categoryListElement,
|
|
].every( Boolean )
|
|
) {
|
|
throw new Error( 'Unable to find the category list elements' );
|
|
}
|
|
|
|
// show and hide the category inputs to make sure they are rendered at least once
|
|
addCategoryToggle.click();
|
|
addCategoryToggle.click();
|
|
|
|
// Preserve original addAfter function for restoration after use
|
|
const orgCatListAddAfter = categoryListElement.wpList.settings.addAfter;
|
|
|
|
const categoryCreatedPromise = new Promise< number >( ( resolve ) => {
|
|
categoryListElement.wpList.settings.addAfter = ( ...args ) => {
|
|
orgCatListAddAfter( ...args );
|
|
categoryListElement.wpList.settings.addAfter = orgCatListAddAfter;
|
|
|
|
const parsedResponse = window.wpAjax.parseAjaxResponse( args[ 0 ] );
|
|
if ( ! parsedResponse?.responses?.[ 0 ].data ) {
|
|
throw new Error( 'Unable to parse the ajax response' );
|
|
}
|
|
|
|
const parsedHtml = new DOMParser().parseFromString(
|
|
parsedResponse.responses[ 0 ].data,
|
|
'text/html'
|
|
);
|
|
const newlyAddedCategoryCheckbox = Array.from(
|
|
parsedHtml.querySelectorAll< HTMLInputElement >(
|
|
'input[name="tax_input[product_cat][]"]'
|
|
)
|
|
).find( ( input ) => {
|
|
return (
|
|
input.parentElement?.textContent?.trim() === category.name
|
|
);
|
|
} );
|
|
|
|
if ( ! newlyAddedCategoryCheckbox ) {
|
|
throw new Error( 'Unable to find the newly added category' );
|
|
}
|
|
|
|
resolve( Number( newlyAddedCategoryCheckbox.value ) );
|
|
};
|
|
} );
|
|
|
|
// Fill category name and select parent category if available
|
|
newCategoryInput.value = category.name;
|
|
if ( category.parent_id ) {
|
|
const parentEl = newCategoryParentSelect.querySelector(
|
|
'option[value="' + category.parent_id + '"]'
|
|
) as HTMLOptionElement;
|
|
if ( ! parentEl ) {
|
|
throw new Error( 'Unable to find the parent category in the list' );
|
|
}
|
|
newCategoryParentSelect.value = category.parent_id.toString();
|
|
parentEl.selected = true;
|
|
}
|
|
|
|
// click the add button to create the category
|
|
newCategoryAddButton.click();
|
|
|
|
return categoryCreatedPromise;
|
|
};
|
|
|
|
/**
|
|
* Gets the list of categories to create from a given path. The path is a string of categories separated by a > character. e.g. "Clothing > Shirts > T-Shirts"
|
|
*
|
|
* @param categoryPath
|
|
*/
|
|
const getCategoriesToCreate = async (
|
|
categoryPath: string
|
|
): Promise< NewCategory[] > => {
|
|
const categories: NewCategory[] = [];
|
|
const orderedList = categoryPath.split( ' > ' );
|
|
const availableCategories = await getAvailableCategories();
|
|
let parentCategoryId = 0;
|
|
orderedList.every( ( categoryName, index ) => {
|
|
const matchingCategory = availableCategories.find( ( category ) => {
|
|
return (
|
|
category.name === categoryName &&
|
|
category.parent === parentCategoryId
|
|
);
|
|
} );
|
|
if ( matchingCategory ) {
|
|
// This is the parent category ID for the next category in the path
|
|
parentCategoryId = matchingCategory.id;
|
|
} else {
|
|
categories.push( {
|
|
name: categoryName,
|
|
parent_id: parentCategoryId,
|
|
} );
|
|
|
|
for ( let i = index + 1; i < orderedList.length; i++ ) {
|
|
categories.push( {
|
|
name: orderedList[ i ],
|
|
} );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
} );
|
|
|
|
return categories;
|
|
};
|
|
|
|
/**
|
|
* Creates categories from a given path. The path is a string of categories separated by a > character. e.g. "Clothing > Shirts > T-Shirts"
|
|
*
|
|
* @param categoryPath
|
|
*/
|
|
export const createCategoriesFromPath = async ( categoryPath: string ) => {
|
|
const categoriesToCreate = await getCategoriesToCreate( categoryPath );
|
|
|
|
while ( categoriesToCreate.length ) {
|
|
const newCategoryId = await createCategory(
|
|
categoriesToCreate.shift() as NewCategory
|
|
);
|
|
|
|
if ( categoriesToCreate.length ) {
|
|
// Set the parent ID of the next category in the list to the ID of the newly created category so that it is created as a child of the newly created category
|
|
categoriesToCreate[ 0 ].parent_id = newCategoryId;
|
|
}
|
|
}
|
|
};
|