From 552d48a1424417da160c4952650ea6f4a3d7bafa Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sun, 04 Jan 2026 20:18:23 +0000
Subject: [PATCH] =Taxonomy Selector and Creator refactor
---
assets/js/concise/TaxonomyCreator.js | 300 +++++++++++++++++++++++++++++++++++------------------------
1 files changed, 179 insertions(+), 121 deletions(-)
diff --git a/assets/js/concise/TaxonomyCreator.js b/assets/js/concise/TaxonomyCreator.js
index 7307202..2ea387b 100644
--- a/assets/js/concise/TaxonomyCreator.js
+++ b/assets/js/concise/TaxonomyCreator.js
@@ -1,8 +1,7 @@
/**
- * This separates out all create logic from the base TaxonomySelector.js
- * Updated to work with centralized DataStore architecture
+ * TaxonomyCreator - Handles term creation for TaxonomySelector
+ * Simplified to focus only on creation logic
*/
-
class TaxonomyCreator {
constructor(selector) {
@@ -23,12 +22,19 @@
}
}
+ /**
+ * Initialize event listeners
+ */
initListeners() {
this.clickHandler = this.handleClick.bind(this);
document.addEventListener('click', this.clickHandler);
}
+ /**
+ * Handle click events
+ */
handleClick(e) {
+ // Handle opening create term form
if (window.targetCheck(e, '.create-new-term summary')) {
if (this.createNew.open) {
this.createNew.querySelector('input[name="term_name"]').focus();
@@ -36,16 +42,20 @@
this.resetParentOptions();
}
+ // Handle term creation submission
if (window.targetCheck(e, '.submit-term')) {
- this.handleTermCreation(e).then(()=>{});
+ this.handleTermCreation(e).then(() => {});
}
// Handle autocomplete create button
if (window.targetCheck(e, '.create-term')) {
- this.handleAutocompleteCreate(e).then(()=>{});
+ this.handleAutocompleteCreate(e).then(() => {});
}
}
+ /**
+ * Handle term creation from modal form
+ */
async handleTermCreation(e) {
const taxonomy = this.selector.currentConfig?.taxonomy;
if (!taxonomy) return;
@@ -55,61 +65,71 @@
if (!termName) return;
+ const submitButton = this.form.querySelector('button');
+
try {
- const submitButton = this.form.querySelector('button');
if (submitButton) {
submitButton.disabled = true;
}
+
const response = await this.createTerm(termName, parentId, taxonomy);
if (response.success && response.term) {
- let term = response.term;
- const termPath = term.path || term.name;
-
- this.createNew.open = false;
- await this.selector.store.clearCache();
-
- this.selector.store.data.set(term.id, {
- id: term.id,
- name: term.name,
- path: termPath,
- taxonomy: taxonomy,
- parent: parentId,
- count: 0,
- hasChildren: false,
- slug: term.slug || termName.toLowerCase().replace(/\s+/g, '-')
- });
-
- this.selector.addSelectedTermToModal(term.id, term.name, termPath);
-
- const currentParent = this.selector.store.filters.parent || 0;
- if (currentParent === parentId) {
- await this.selector.store.setFilters({
- taxonomy,
- parent: parentId,
- page: 1,
- search: ''
- });
- }
-
- this.form.querySelector('input[name="term_name"]').value = '';
- const suggestionContainer = this.createNew.querySelector('.term-suggestions');
- if (suggestionContainer) {
- suggestionContainer.hidden = true;
- }
-
+ await this.handleSuccessfulCreation(response.term, taxonomy, parentId);
+ this.clearForm();
}
} catch (error) {
console.error('Error creating term:', error);
- this.selector.error?.log(error, {
- component: 'TaxonomyCreator',
- action: 'handleTermCreation'
- });
+ this.selector.handleError(error, 'handleTermCreation');
} finally {
- this.form.querySelector('button').disabled = false;
+ if (submitButton) {
+ submitButton.disabled = false;
+ }
}
}
+ /**
+ * Handle successful term creation
+ */
+ async handleSuccessfulCreation(term, taxonomy, parentId) {
+ const termPath = term.path || term.name;
+
+ // Close create form
+ this.createNew.open = false;
+
+ // Clear cache to ensure fresh data
+ await this.selector.store.clearCache();
+
+ // Add to DataStore
+ this.selector.store.data.set(term.id, {
+ id: term.id,
+ name: term.name,
+ path: termPath,
+ taxonomy: taxonomy,
+ parent: parentId,
+ count: 0,
+ hasChildren: false,
+ slug: term.slug || termName.toLowerCase().replace(/\s+/g, '-')
+ });
+
+ // Add to modal selection
+ this.selector.addSelectedTermToModal(term.id, term.name, termPath);
+
+ // Refresh current view if we're viewing the same parent
+ const currentParent = this.selector.store.filters.parent || 0;
+ if (currentParent === parentId) {
+ await this.selector.store.setFilters({
+ taxonomy,
+ parent: parentId,
+ page: 1,
+ search: ''
+ });
+ }
+ }
+
+ /**
+ * Handle autocomplete create button
+ */
async handleAutocompleteCreate(e) {
const button = e.target.closest('.create-term');
const fieldId = this.selector.getFieldId(button);
@@ -131,65 +151,74 @@
const response = await this.createTerm(termName, 0, field.taxonomy);
if (response.success && response.term) {
- const term = response.term;
- const termPath = term.path || term.name;
-
- field.selectedTerms.add(parseInt(term.id));
- 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);
-
- field.input.value = Array.from(field.selectedTerms).join(',');
- field.input.dispatchEvent(new Event('change', { bubbles: true }));
-
- field.autocompleteDropdown.hidden = true;
- if (input) input.value = '';
-
- // Clear cache AND refresh this taxonomy's data
- this.selector.store.clearCache();
-
- // Trigger a background refresh for this taxonomy
- await this.selector.store.setFilters({
- taxonomy: field.taxonomy,
- page: 1,
- search: '',
- parent: 0
- });
+ await this.handleAutocompleteSuccess(response.term, field, input);
} else if (response.reason === 'exists' && response.term) {
- 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 = '';
+ this.handleExistingTerm(response.term, field, input);
}
} catch (error) {
console.error('Error creating term:', error);
- this.selector.error?.log(error, {
- component: 'TaxonomyCreator',
- action: 'handleAutocompleteCreate'
- });
+ this.selector.handleError(error, 'handleAutocompleteCreate');
} finally {
button.innerHTML = originalHTML;
button.disabled = false;
}
}
+ /**
+ * Handle successful autocomplete creation
+ */
+ async handleAutocompleteSuccess(term, field, input) {
+ const termPath = term.path || term.name;
+
+ // Add to field
+ field.selectedTerms.add(parseInt(term.id));
+
+ // Add to DataStore
+ 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 || term.name.toLowerCase().replace(/\s+/g, '-')
+ });
+
+ // Update display
+ this.selector.addTermDisplay(term.id, term.name, termPath, 'field', field.id);
+
+ // Update input and trigger change
+ 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 = '';
+
+ // Clear cache for this taxonomy
+ await this.selector.store.clearCache();
+ }
+
+ /**
+ * Handle selecting existing term from autocomplete
+ */
+ handleExistingTerm(term, field, input) {
+ field.selectedTerms.add(parseInt(term.id));
+ this.selector.addTermDisplay(term.id, term.name, term.path || term.name, 'field', field.id);
+
+ field.input.value = Array.from(field.selectedTerms).join(',');
+ field.input.dispatchEvent(new Event('change', { bubbles: true }));
+
+ field.autocompleteDropdown.hidden = true;
+ if (input) input.value = '';
+ }
+
+ /**
+ * Initialize term creation form
+ */
initTermCreation() {
- if (!this.form) {
- return;
- }
+ if (!this.form) return;
this.form.addEventListener('change', (e) => {
e.preventDefault();
@@ -197,6 +226,9 @@
});
}
+ /**
+ * Reset parent options in create form
+ */
resetParentOptions() {
const taxonomy = this.selector.currentConfig?.taxonomy;
if (!taxonomy) return;
@@ -225,7 +257,7 @@
}
}
- // Add all terms currently visible in the taxonomy (from store cache)
+ // Add all terms currently visible in the taxonomy
const visibleTerms = [];
this.selector.store.data.forEach(term => {
if (term.taxonomy === taxonomy && term.parent === currentParent) {
@@ -246,9 +278,12 @@
});
}
+ /**
+ * Create a new term
+ */
async createTerm(name, parent = 0, taxonomy) {
try {
- // Search to ensure we have latest data for duplicate check
+ // Search to check for duplicates
await this.selector.store.setFilters({
taxonomy: taxonomy,
search: name,
@@ -256,10 +291,10 @@
parent: 0
});
- // Wait a bit for the data to load
+ // Wait for data to load
await new Promise(resolve => setTimeout(resolve, 100));
- // Check if exact match exists in results
+ // Check if exact match exists
const exactMatch = Array.from(this.selector.store.data.values())
.find(term =>
term.taxonomy === taxonomy &&
@@ -323,27 +358,7 @@
suggestions.forEach(term => {
const item = document.createElement('li');
-
- const button = document.createElement('button');
- button.type = 'button';
- button.className = 'use-existing-term';
- button.setAttribute('data-id', term.id);
- button.textContent = term.path || term.name;
-
- button.addEventListener('click', () => {
- // Add this term to modal selection
- this.selector.addSelectedTermToModal(term.id, term.name, term.path || term.name);
-
- // Close the create new section
- this.createNew.open = false;
-
- // Clear suggestions
- suggestionContainer.hidden = true;
-
- // Clear the form
- this.form.querySelector('input[name="term_name"]').value = '';
- });
-
+ const button = this.createSuggestionButton(term);
item.appendChild(button);
list.appendChild(item);
});
@@ -353,7 +368,36 @@
}
/**
- * Create container for term suggestions if it doesn't exist
+ * Create suggestion button
+ */
+ createSuggestionButton(term) {
+ const button = document.createElement('button');
+ button.type = 'button';
+ button.className = 'use-existing-term';
+ button.dataset.id = term.id;
+ button.textContent = term.path || term.name;
+
+ button.addEventListener('click', () => {
+ // Add this term to modal selection
+ this.selector.addSelectedTermToModal(term.id, term.name, term.path || term.name);
+
+ // Close the create new section
+ this.createNew.open = false;
+
+ // Clear suggestions and form
+ const suggestionContainer = this.createNew.querySelector('.term-suggestions');
+ if (suggestionContainer) {
+ suggestionContainer.hidden = true;
+ }
+
+ this.clearForm();
+ });
+
+ return button;
+ }
+
+ /**
+ * Create container for term suggestions
*/
createSuggestionContainer() {
const container = document.createElement('div');
@@ -365,9 +409,23 @@
return container;
}
+ /**
+ * Clear the creation form
+ */
+ clearForm() {
+ const nameInput = this.form.querySelector('input[name="term_name"]');
+ if (nameInput) {
+ nameInput.value = '';
+ }
+
+ const suggestionContainer = this.createNew.querySelector('.term-suggestions');
+ if (suggestionContainer) {
+ suggestionContainer.hidden = true;
+ }
+ }
/**
- * Clean up when modal closes
+ * Clean up when destroyed
*/
destroy() {
// Remove event listeners
--
Gitblit v1.10.0