class TaxonomySelectorPrev {
|
constructor (container, options = {}) {
|
|
this.a11y = window.jvbA11y;
|
this.cache = window.jvbCache;
|
this.error = window.jvbError;
|
|
this.taxonomy = container.dataset.taxonomy;
|
|
this.container = container;
|
this.initConfig(options);
|
this.initElements();
|
this.initListeners();
|
|
this.page = 1;
|
this.hasMore = true;
|
this.isLoading = false;
|
this.searchQuery = '';
|
|
this.terms = new Map();
|
|
this.navigationPath = [];
|
this.currentParent = 0;
|
this.currentParentName = '';
|
this.currentPath = '';
|
this.fetchSpecificTerms = false;
|
|
this.creator = false;
|
|
if ('jvbTaxCreator' in window && this.container.querySelector('.create-new-term-section')) {
|
this.creator = new window.jvbTaxCreator(this);
|
}
|
|
//Render the selected items
|
this.updateSelected();
|
this.modal.backButton.hidden = this.currentParent === 0;
|
}
|
|
initConfig(options) {
|
//Set up currently selected items
|
this.selectedTerms = {};
|
if (options.selected) {
|
if (typeof options.selected === 'object' && !window.isEmptyObject(options.selected)) {
|
this.options.selected.forEach(item => {
|
this.selectedTerms[item.id] = item.name;
|
});
|
} else {
|
this.selectedTerms = options.selected;
|
}
|
}
|
let embeddedOptions = JSON.parse(this.container.dataset.config??'{}');
|
this.maxSelections = options.maxSelections || embeddedOptions.maxSelections || 0;
|
this.isHierarchal = options.hierarchical || embeddedOptions.maxSelections || 0;
|
this.base = options.base || embeddedOptions.base || '';
|
this.onSuccess = options.onSuccess || embeddedOptions.onSuccess || null;
|
this.onClose = options.onClose || embeddedOptions.onClose || null;
|
|
this.isFeed = options.feed || embeddedOptions.feed || false;
|
if (this.isFeed) {
|
this.feedSelected = document.querySelector('.selected-items-section .selected-items')
|
}
|
}
|
|
initElements() {
|
//Separate items by container
|
this.elements = {
|
modal: this.container.querySelector('dialog'),
|
selectedTerms: this.container.querySelector('.selected-items'),
|
};
|
this.modal = {
|
searchInput: this.elements.modal.querySelector('input[type=search]'),
|
termsList: this.elements.modal.querySelector('.items-container'),
|
termsWrap: this.elements.modal.querySelector('.items-wrap'),
|
breadcrumbs: this.elements.modal.querySelector('nav.term-navigation'),
|
loading: this.elements.modal.querySelector('.loading'),
|
loadingText: this.elements.modal.querySelector('.loading span'),
|
clearSearch: this.elements.modal.querySelector('.clear-search'),
|
selectedTerms: this.elements.modal.querySelector('.selected-items'),
|
backButton: this.elements.modal.querySelector('.back-to-parent'),
|
sentinel: this.elements.modal.querySelector('.scroll-sentinel')
|
};
|
}
|
|
initListeners() {
|
this.container.addEventListener('click', this.handleClick.bind(this));
|
this.container.addEventListener('change', this.handleChange.bind(this));
|
|
let o = this.container.closest('.field')?.querySelector('.add-item-btn');
|
if (!o) {
|
o = '.filter-toggle';
|
// o = this.container.closest('.jvb-selector')?.querySelector('.filter-toggle');
|
} else {
|
o = '.add-item-btn';
|
}
|
|
this.observer = new IntersectionObserver(
|
entries => {
|
entries.forEach(entry => {
|
if (entry.isIntersecting && !this.isLoading && this.hasMore) {
|
this.fetchTerms();
|
}
|
});
|
}, {
|
root: this.modal.termsWrap,
|
threshold: 0.5
|
}
|
);
|
|
this.m = new window.jvbModal(
|
this.elements.modal,
|
{
|
save: null,
|
open: o,
|
openMessage: `Opened ${this.taxonomy} selection. Choose from checkboxes or search to filter results.`,
|
onOpen: () => this.openModal(),
|
onClose:() => this.closeModal()
|
}
|
);
|
}
|
handleClick(e) {
|
|
if (window.targetCheck(e, '.remove-item')) {
|
let item = window.targetCheck(e, '.selected-item');
|
if (item) {
|
this.removeSelectedTerm(item.dataset.id);
|
}
|
} else if (window.targetCheck(e, '.back-to-parent')) {
|
this.toParent();
|
} else if (window.targetCheck(e, '.toggle-children')) {
|
let item = e.target.closest('li');
|
this.toChild(parseInt(item.dataset.id), item.querySelector('.term-name').textContent);
|
} else if (window.targetCheck(e, '.path-level')) {
|
let btn = window.targetCheck(e, '.path-level');
|
if (btn.textContent !== this.currentParentName) {
|
|
window.removeChildren(this.modal.termsList);
|
let level = parseInt(btn.dataset.level);
|
|
this.navigatioPath = this.navigationPath.slice(0, level + 1);
|
this.currentParent = parseInt(btn.dataset.id);
|
this.currentParentName = btn.textContent;
|
this.page = 1;
|
this.hasMore = true;
|
this.fetchTerms();
|
}
|
}
|
}
|
handleChange(e) {
|
e.preventDefault();
|
e.stopPropagation();
|
let input = e.target;
|
if (e.target.checked) {
|
let label = e.target.closest('li').querySelector('label');
|
this.addSelectedTerm(input.id, label.title, label.dataset.path);
|
} else {
|
this.removeSelectedTerm(input.id);
|
}
|
}
|
|
handleSearch(e) {
|
let query = this.modal.searchInput.value.trim();
|
if (query === this.searchQuery) return;
|
|
this.searchQuery = query;
|
|
this.page = 1;
|
this.hasMore = true;
|
|
window.removeChildren(this.modal.termsList);
|
if (query.length >= 2 || query.length === 0) {
|
this.fetchTerms(false, true);
|
} else {
|
this.hideLoading();
|
this.showEmptyState('No Results. \nEnter at least 2 characters to search.')
|
}
|
}
|
|
openModal() {
|
this.observer.observe(this.modal.sentinel);
|
this.fetchTerms();
|
this.modal.searchInput.focus();
|
this.modal.searchInput.addEventListener('input',
|
window.debounce(() => this.handleSearch()));
|
}
|
closeModal() {
|
this.observer.unobserve(this.modal.sentinel);
|
window.removeChildren(this.modal.termsList);
|
this.updateSelected();
|
this.resetState();
|
if(this.isFeed && typeof this.onClose === 'function') {
|
this.onClose(this.taxonomy, this.selectedTerms);
|
}
|
this.modal.searchInput.removeEventListener('input', window.debounce(() => this.handleSearch()));
|
}
|
|
nextPage() {
|
if (this.hasMore) {
|
this.page++;
|
}
|
}
|
|
resetPage() {
|
this.page = 1;
|
this.hasMore = true;
|
}
|
|
resetState() {
|
this.resetPage();
|
this.searchQuery = '';
|
this.page = 1;
|
this.currentParent = 0;
|
this.currentParentName = '';
|
this.currentPath = '';
|
this.navigationPath = [];
|
this.hasMore = true;
|
this.isLoading = false;
|
this.retries = {
|
count: 0,
|
max: 3,
|
delay: 1000
|
};
|
}
|
|
toParent() {
|
this.navigationPath.pop();
|
|
let prv = this.navigationPath[this.navigationPath.length - 1];
|
this.currentParent = prv ? prv.id : 0;
|
this.currentParentName = prv ? prv.name : '';
|
|
window.removeChildren(this.modal.termsList);
|
this.page = 1;
|
this.hasMore = true;
|
this.fetchTerms();
|
}
|
|
toChild(termId, termName) {
|
this.navigationPath.push({
|
id: termId,
|
name: termName
|
});
|
this.currentParent = termId;
|
this.currentParentName = termName;
|
window.removeChildren(this.modal.termsList);
|
|
this.page = 1;
|
this.hasMore = true;
|
this.fetchTerms();
|
}
|
|
buildRequest() {
|
let params = new URLSearchParams({
|
taxonomy: this.taxonomy,
|
parent: this.currentParent,
|
search: this.searchQuery,
|
page: this.page
|
});
|
|
if (this.isFeed && this.fetchSpecificTerms) {
|
params.append('termIDs', this.fetchSpecificTerms);
|
}
|
|
if (this.isFeed && window.feedBlock?.config?.context) {
|
params.append('main_context', JSON.stringify({
|
context: window.feedBlock.config.context,
|
id: window.feedBlock.config.source
|
}));
|
params.append('content', window.feedBlock.filters.content);
|
}
|
|
return params.toString();
|
}
|
showLoading() {
|
this.isLoading = true;
|
this.modal.loading.hidden = false;
|
this.elements.modal.classList.add('loading');
|
|
let text = (this.searchQuery !== '') ? 'searching for "'+this.searchQuery+'" items' : ((this.currentParentName === '') ? 'loading items' : 'loading '+this.currentParentName+' items');
|
this.stopTyping = window.typeLoop(this.modal.loadingText, text);
|
|
}
|
hideLoading() {
|
this.isLoading = false;
|
this.modal.loading.hidden = true;
|
this.elements.modal.classList.remove('loading');
|
this.stopTyping();
|
}
|
async fetchTerms(forceRefresh = false, showPath = false) {
|
if (this.isLoading) {
|
return;
|
}
|
|
try {
|
this.showLoading();
|
|
const data = await this.cache.fetchWithCache(
|
`${jvbSettings.api}terms?`+this.buildRequest(),
|
{
|
method: 'GET',
|
headers: {
|
'Content-Type': 'application/json',
|
'X-WP-Nonce': jvbSettings.nonce,
|
}
|
},
|
{
|
content: this.taxonomy,
|
// forceRefresh: true
|
forceRefresh: forceRefresh
|
}
|
);
|
|
if (this.fetchSpecificTerms) {
|
this.fetchSpecificTerms = false;
|
return data.terms;
|
}
|
|
|
if (!data || !data.terms || data.terms.length === 0) {
|
if (this.page === 1) {
|
this.showEmptyState();
|
}
|
this.hasMore = false;
|
} else {
|
this.hasMore = data.pagination['has_more'];
|
this.renderTerms(data.terms, this.page > 1, showPath);
|
if (this.hasMore) {
|
this.nextPage();
|
}
|
}
|
} catch (e) {
|
this.handleError(e);
|
} finally {
|
this.hideLoading();
|
}
|
}
|
|
handleError(error){
|
return this.error.log(
|
error,
|
{
|
component: 'Taxonomy Selector',
|
action: 'fetchTerms'
|
},
|
() => this.fetchTerms()
|
);
|
}
|
|
addPlaceholders() {
|
|
}
|
|
renderTerms(terms, append = false, path = false) {
|
if (!append) {
|
window.removeChildren(this.modal.termsList);
|
this.addPlaceholders();
|
}
|
if (terms.length === 0) {
|
this.a11y.announceUpdate(0, append);
|
return;
|
}
|
|
this.updateBreadcrumbs();
|
|
|
|
this.disabled = this.isLimitReached();
|
for (let [id, term] of Object.entries(terms)) {
|
if (!term) return;
|
this.terms.set(term.id, term.name);
|
this.createTermElement({
|
id: parseInt(term.id),
|
name: term.name,
|
hasChildren: term.hasChildren,
|
path: term.path || null,
|
show: path
|
});
|
}
|
}
|
|
|
|
createTermElement(term) {
|
if (!term || !term.name) return;
|
|
let item = window.getTemplate('termListItem');
|
item.dataset.id = term.id;
|
const isSelected = (term.id in this.selectedTerms);
|
let input = item.querySelector('input');
|
let label = item.querySelector('label');
|
let name = item.querySelector('span');
|
|
[
|
input.id,
|
input.name,
|
input.value,
|
input.disabled,
|
input.checked,
|
label.htmlFor,
|
label.title,
|
label.dataset.path,
|
name.textContent
|
] = [
|
`${this.base}${term.id}`,
|
`${this.base}${this.taxonomy}-select`,
|
term.id,
|
isSelected ? false : this.disabled,
|
isSelected,
|
`${this.base}${term.id}`,
|
term.path || term.name,
|
term.path,
|
term.show ? term.path : term.name,
|
];
|
|
if (term.hasChildren) {
|
let button = window.getTemplate('termChildrenToggle');
|
button.ariaLabel = `View sub-terms of ${term.name}`;
|
item.append(button);
|
}
|
|
this.modal.termsList.append(item);
|
}
|
|
updateBreadcrumbs() {
|
window.removeChildren(this.modal.breadcrumbs);
|
this.modal.breadcrumbs.append(this.modal.backButton);
|
|
this.modal.backButton.hidden = this.currentParent === 0;
|
this.navigationPath.forEach((level, index) => {
|
let button = window.getTemplate('termBreadcrumb');
|
[
|
button.dataset.level,
|
button.dataset.id,
|
button.title,
|
button.textContent
|
] = [
|
index,
|
level.id,
|
level.path || level.name,
|
level.name
|
];
|
this.modal.breadcrumbs.append(button);
|
});
|
}
|
|
showEmptyState(message = ''){
|
let template = window.getTemplate('noResults');
|
if (message !== '') {
|
template.querySelector('span').textContent = message;
|
}
|
this.modal.termsList.append(template);
|
}
|
|
updateSelected() {
|
if (this.isFeed) {
|
return;
|
}
|
|
let checks = this.getSpacesToUpdate();
|
checks.forEach(check => {
|
window.removeChildren(check);
|
});
|
|
for (const [id, term] of Object.entries(this.selectedTerms)) {
|
this.addTermToBoxes(id, term.name, term.path);
|
}
|
|
this.disabled = this.isLimitReached();
|
this.setCheckboxes(this.disabled);
|
}
|
|
async addTermsFromURL(ids) {
|
this.fetchSpecificTerms = ids;
|
let terms = await this.fetchTerms();
|
terms.forEach(term => {
|
this.addSelectedTerm(term.id, term.name, term.path);
|
});
|
}
|
|
addSelectedTerm(id, name, path) {
|
this.selectedTerms[id] = {
|
name: name,
|
path: path
|
};
|
|
this.addTermToBoxes(id, name, path);
|
}
|
|
getSpacesToUpdate() {
|
let checks = [
|
this.modal.selectedTerms,
|
this.elements.selectedTerms
|
];
|
|
if (this.isFeed) {
|
checks.push(this.feedSelected);
|
}
|
return checks;
|
}
|
|
addTermToBoxes(id, name, path) {
|
let checks = this.getSpacesToUpdate();
|
|
checks.forEach(check => {
|
if (!check.querySelector(`[data-id="${id}"]`)) {
|
let item = window.getTemplate('selectedTerm');
|
[
|
item.dataset.id,
|
item.dataset.path,
|
item.dataset.name,
|
item.dataset.taxonomy,
|
item.querySelector('span').textContent,
|
item.querySelector('button').title
|
] = [
|
id,
|
path,
|
name,
|
this.taxonomy,
|
path,
|
`Remove ${name}`
|
];
|
if (check === this.feedSelected) {
|
item.prepend(window.getIcon(this.taxonomy));
|
}
|
check.append(item);
|
}
|
});
|
|
let input = this.modal.termsList.querySelector(`input[value="${id}"]`);
|
if (input) {
|
input.checked = true;
|
}
|
}
|
removeSelectedTerm(id) {
|
delete this.selectedTerms[id];
|
|
let checks = [
|
this.modal.selectedTerms,
|
this.elements.selectedTerms
|
];
|
if (this.isFeed) {
|
checks.push(this.feedSelected);
|
}
|
checks.forEach(check => {
|
check.querySelector(`[data-id="${id}"]`)?.remove();
|
});
|
|
|
let input = this.modal.termsList.querySelector(`input[value="${id}"]`);
|
if (input) {
|
input.checked = false;
|
}
|
}
|
|
setCheckboxes(disabled) {
|
this.modal.termsList.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
|
if (!checkbox.checked) {
|
checkbox.disabled = disabled;
|
}
|
});
|
}
|
|
isLimitReached() {
|
return this.maxSelections > 0 && this.getSelectedTerms().length > this.maxSelections;
|
}
|
|
getSelectedTerms() {
|
return this.modal.termsList.querySelectorAll('input:checked');
|
}
|
|
}
|
|
window.jvbSelector = TaxonomySelectorPrev;
|