From d7dbe7fee362d587dfc334135d9581b6216a4295 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sun, 23 Nov 2025 04:13:56 +0000
Subject: [PATCH] =Timeline block, and feed block updated. DataStore.js refactored to not block rendering
---
assets/js/dash/TaxonomyCreator.js | 242 +++++++++++++++++++-----------------------------
1 files changed, 97 insertions(+), 145 deletions(-)
diff --git a/assets/js/dash/TaxonomyCreator.js b/assets/js/dash/TaxonomyCreator.js
index 8f27b17..37b5960 100644
--- a/assets/js/dash/TaxonomyCreator.js
+++ b/assets/js/dash/TaxonomyCreator.js
@@ -8,19 +8,19 @@
constructor(selector) {
this.selector = selector;
- // Get taxonomy from current active field config
- this.taxonomy = selector.currentConfig?.taxonomy;
- if (!this.taxonomy) {
- console.error('TaxonomyCreator: No active field or taxonomy found');
- return;
+ // Only initialize modal elements if modal exists
+ if (selector.modal) {
+ this.createNew = selector.modal.querySelector('.create-new-term');
+ this.toggle = selector.modal.querySelector('.new-term-toggle');
+ this.form = this.createNew?.querySelector('.create-new-term-section');
}
- this.createNew = selector.modal.querySelector('.create-new-term');
- this.toggle = selector.modal.querySelector('.new-term-toggle');
- this.form = this.createNew.querySelector('.create-new-term-section');
-
this.initListeners();
- this.initTermCreation();
+
+ // Only init term creation UI if we have modal elements
+ if (this.form) {
+ this.initTermCreation();
+ }
}
initListeners() {
@@ -47,6 +47,9 @@
}
async handleTermCreation(e) {
+ const taxonomy = this.selector.currentConfig?.taxonomy;
+ if (!taxonomy) return;
+
const termName = this.form.querySelector('input[name="term_name"]').value.trim();
const parentId = parseInt(this.form.querySelector('input#select_parent')?.value) || 0;
@@ -54,98 +57,127 @@
try {
this.form.querySelector('button').disabled = true;
- const response = await this.createTerm(termName, parentId);
+ const response = await this.createTerm(termName, parentId, taxonomy);
if (response.success && response.term) {
let term = response.term;
- // Close the create new section
this.createNew.open = false;
+ await this.selector.store.clearCache();
- // Invalidate the cache for this taxonomy
- await this.selector.store.invalidate({ taxonomy: this.taxonomy });
+ // Add to store's data BEFORE display update
+ this.selector.store.data.set(term.id, {
+ id: term.id,
+ name: term.name,
+ path: termPath,
+ taxonomy: field.taxonomy,
+ parent: 0,
+ count: 0,
+ hasChildren: false,
+ slug: term.slug || termName.toLowerCase().replace(/\s+/g, '-')
+ });
- // Add to current modal selection
this.selector.addSelectedTermToModal(term.id, term.name, term.path || term.name);
- // If we're viewing the parent category where this was created, refresh the list
const currentParent = this.selector.store.filters.parent || 0;
if (currentParent === parentId) {
await this.selector.store.setFilters({
- taxonomy: this.taxonomy,
+ taxonomy,
parent: parentId,
page: 1,
search: ''
});
}
- // Clear the form
this.form.querySelector('input[name="term_name"]').value = '';
-
- // Clear suggestions
const suggestionContainer = this.createNew.querySelector('.term-suggestions');
if (suggestionContainer) {
suggestionContainer.hidden = true;
}
+ this.selector.store.cache.clear();
}
} catch (error) {
console.error('Error creating term:', error);
this.selector.error?.log(error, {
component: 'TaxonomyCreator',
action: 'handleTermCreation'
- }) || console.error('Failed to create term');
+ });
} finally {
this.form.querySelector('button').disabled = false;
}
}
async handleAutocompleteCreate(e) {
- const button = e.target;
+ const button = e.target.closest('.create-term');
const fieldId = this.selector.getFieldId(button);
const field = this.selector.fields.get(fieldId);
if (!field) return;
- // Get the current input value (not the debounced query)
const input = field.container.querySelector('input[data-autocomplete]');
const termName = input?.value.trim() || button.dataset.query;
if (!termName) return;
+ const originalHTML = button.innerHTML;
+
try {
button.disabled = true;
- const originalText = button.innerHTML;
button.textContent = 'Creating...';
- const response = await this.createTerm(termName, 0);
+ const response = await this.createTerm(termName, 0, field.taxonomy);
if (response.success && response.term) {
const term = response.term;
+ const termPath = term.path || term.name;
- // Add term to field
field.selectedTerms.add(parseInt(term.id));
- this.selector.addTermToDisplay(field.id, term.id, term.name, term.path || term.name);
+ // Add to store's data BEFORE display update
+ this.selector.store.data.set(term.id, {
+ id: term.id,
+ name: term.name,
+ path: termPath,
+ taxonomy: field.taxonomy,
+ parent: 0,
+ count: 0,
+ hasChildren: false,
+ slug: term.slug || termName.toLowerCase().replace(/\s+/g, '-')
+ });
+ this.selector.addTermToDisplay(field.id, term.id, term.name, termPath);
- // Update input
field.input.value = Array.from(field.selectedTerms).join(',');
field.input.dispatchEvent(new Event('change', { bubbles: true }));
- // Clear and hide dropdown
field.autocompleteDropdown.hidden = true;
if (input) input.value = '';
- // Refresh the store to include the new term
- await this.selector.store.setFilter('taxonomy', field.taxonomy);
- } else {
- button.innerHTML = originalText;
+ this.selector.store.clearCache();
+ await this.selector.store.setFilters({
+ taxonomy: field.taxonomy,
+ page: 1,
+ search: '',
+ parent: 0
+ });
+ } else if (response.reason === 'exists' && response.term) {
+ // Term already exists - just add it
+ const term = response.term;
+ field.selectedTerms.add(parseInt(term.id));
+ this.selector.addTermToDisplay(field.id, term.id, term.name, term.path || term.name);
+
+ field.input.value = Array.from(field.selectedTerms).join(',');
+ field.input.dispatchEvent(new Event('change', { bubbles: true }));
+
+ field.autocompleteDropdown.hidden = true;
+ if (input) input.value = '';
}
} catch (error) {
console.error('Error creating term:', error);
- button.textContent = 'Failed - Try again';
- setTimeout(() => {
- button.innerHTML = `<strong>Create:</strong> "${termName}"`;
- button.disabled = false;
- }, 2000);
+ button.innerHTML = originalHTML;
+ button.disabled = false;
+ this.selector.error?.log(error, {
+ component: 'TaxonomyCreator',
+ action: 'handleAutocompleteCreate'
+ });
}
}
@@ -161,6 +193,9 @@
}
resetParentOptions() {
+ const taxonomy = this.selector.currentConfig?.taxonomy;
+ if (!taxonomy) return;
+
let select = this.createNew.querySelector('#select_parent');
if (!select) return;
@@ -188,7 +223,7 @@
// Add all terms currently visible in the taxonomy (from store cache)
const visibleTerms = [];
this.selector.store.data.forEach(term => {
- if (term.taxonomy === this.taxonomy && term.parent === currentParent) {
+ if (term.taxonomy === taxonomy && term.parent === currentParent) {
visibleTerms.push(term);
}
});
@@ -206,45 +241,32 @@
});
}
- async createTerm(name, parent = 0) {
- let loadingMessage = this.createNew.querySelector('.loading-message.create-term');
- let text = loadingMessage?.querySelector('span');
-
+ async createTerm(name, parent = 0, taxonomy) {
try {
- if (loadingMessage) {
- loadingMessage.hidden = false;
- }
+ // Search for the exact term first
+ await this.selector.store.setFilters({
+ taxonomy: taxonomy,
+ search: name,
+ page: 1,
+ parent: 0
+ });
- if (text && window.typeText) {
- window.typeText(text, 'Checking term...');
- } else if (text) {
- text.textContent = 'Checking term...';
- }
+ // Check if exact match exists in results
+ const exactMatch = Array.from(this.selector.store.data.values())
+ .find(term =>
+ term.taxonomy === taxonomy &&
+ term.name.toLowerCase() === name.toLowerCase()
+ );
- // Search for existing terms with this name
- const searchResults = await this.searchExistingTerms(name);
-
- // Check for exact matches
- const exactMatches = searchResults.filter(term =>
- term.name.toLowerCase() === name.toLowerCase()
- );
-
- if (exactMatches.length > 0) {
- this.showTermSuggestions(exactMatches, true);
- return { success: false, reason: 'exists' };
- }
-
- // Show similar terms if found
- if (searchResults.length > 0) {
- this.showTermSuggestions(searchResults, false);
- return { success: false, reason: 'similar' };
+ if (exactMatch) {
+ // For modal context, show suggestions
+ if (this.createNew) {
+ this.showTermSuggestions([exactMatch], true);
+ }
+ return { success: false, reason: 'exists', term: exactMatch };
}
// Term doesn't exist, create it
- if (text) {
- text.textContent = 'Creating term...';
- }
-
const response = await fetch(`${jvbSettings.api}terms`, {
method: 'POST',
headers: {
@@ -252,7 +274,7 @@
'X-WP-Nonce': jvbSettings.nonce
},
body: JSON.stringify({
- taxonomy: this.taxonomy,
+ taxonomy: taxonomy,
name: name,
parent: parent
})
@@ -267,18 +289,13 @@
} catch (error) {
console.error('Error creating term:', error);
throw error;
- } finally {
- this.form.querySelector('button').disabled = false;
- if (loadingMessage) {
- loadingMessage.hidden = true;
- }
}
}
/**
* Search for existing terms using the store
*/
- async searchExistingTerms(searchQuery) {
+ async searchExistingTerms(searchQuery, taxonomy) {
return new Promise((resolve) => {
// Set up a one-time listener for the search results
const handleSearchResults = (event, data) => {
@@ -292,7 +309,7 @@
// Trigger search
this.selector.store.setFilters({
- taxonomy: this.taxonomy,
+ taxonomy: taxonomy,
search: searchQuery,
page: 1,
parent: 0
@@ -365,71 +382,6 @@
return container;
}
- /**
- * Create "Create new term" option for autocomplete dropdown
- */
- createAutocompleteOption(query, field) {
- const button = document.createElement('button');
- button.type = 'button';
- button.className = 'autocomplete-item create-term';
- button.innerHTML = `<span>Create "${query}"</span>`;
- button.dataset.query = query;
- button.dataset.fieldId = field.id;
-
- button.addEventListener('click', async () => {
- await this.handleAutocompleteCreate(button, query, field);
- });
-
- return button;
- }
-
-
- /**
- * Handle term creation from autocomplete
- */
- async handleAutocompleteCreate(button, termName, field) {
- if (!field) return;
-
- const originalHTML = button.innerHTML;
-
- try {
- button.disabled = true;
- button.innerHTML = '<span>Creating...</span>';
-
- const parentId = 0; // Autocomplete always creates at root level
- const result = await this.createTerm(termName, parentId);
-
- if (result.success && result.term) {
- const term = result.term;
-
- // Add to field
- field.selectedTerms.add(parseInt(term.id));
- this.selector.addTermToDisplay(field.id, term.id, term.name, term.path || term.name);
-
- // Update input
- field.input.value = Array.from(field.selectedTerms).join(',');
- field.input.dispatchEvent(new Event('change', { bubbles: true }));
-
- // Invalidate cache
- await this.selector.store.invalidate({ taxonomy: field.taxonomy });
-
- // Clear and hide dropdown
- field.autocompleteDropdown.hidden = true;
- const input = field.container.querySelector('input[data-autocomplete]');
- if (input) input.value = '';
- }
- // If result.success is false, suggestions are already shown
-
- } catch (error) {
- console.error('Error creating term:', error);
- button.innerHTML = originalHTML;
- button.disabled = false;
- this.selector.error?.log(error, {
- component: 'TaxonomyCreator',
- action: 'handleAutocompleteCreate'
- });
- }
- }
/**
* Clean up when modal closes
--
Gitblit v1.10.0