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  |  297 +++++---
 assets/js/min/selector.min.js         |    2 
 assets/js/concise/TaxonomySelector.js | 1526 +++++++++++++++++--------------------------------
 assets/js/min/creator.min.js          |    2 
 4 files changed, 709 insertions(+), 1,118 deletions(-)

diff --git a/assets/js/concise/TaxonomyCreator.js b/assets/js/concise/TaxonomyCreator.js
index 3c5f24d..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,62 +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));
-
-				// Add to store's data map
-				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 ALL cache for this taxonomy
-				// This forces next search to hit the server
-				await this.selector.store.clearCache();
-
+				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();
@@ -194,6 +226,9 @@
 		});
 	}
 
+	/**
+	 * Reset parent options in create form
+	 */
 	resetParentOptions() {
 		const taxonomy = this.selector.currentConfig?.taxonomy;
 		if (!taxonomy) return;
@@ -222,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) {
@@ -243,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,
@@ -253,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 &&
@@ -320,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);
 		});
@@ -350,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');
@@ -362,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
diff --git a/assets/js/concise/TaxonomySelector.js b/assets/js/concise/TaxonomySelector.js
index 8ae44bb..b411d11 100644
--- a/assets/js/concise/TaxonomySelector.js
+++ b/assets/js/concise/TaxonomySelector.js
@@ -1,6 +1,6 @@
 /**
- * Centralized Taxonomy Selector with DataStore Integration
- * Handles all taxonomy selection fields using DataStore for state management
+ * TaxonomySelector - Streamlined version
+ * Manages taxonomy selection fields with DataStore integration
  */
 class TaxonomySelector {
 	constructor() {
@@ -8,138 +8,133 @@
 		this.error = window.jvbError;
 		this.index = -1;
 
-		this.hasAutocomplete = false;
 		this.isInitializing = true;
 		this.taxonomiesToFetch = new Set();
-
-		this.triggers = new Set(['.taxonomy-toggle']);
-
 		this.subscribers = new Set();
 
-		const store = window.jvbStore.register(
-			'taxonomies',
-			{
-				storeName: `terms`,
-				keyPath: 'id',
-				showLoading: false,
-				indexes: [
-					{name: 'taxonomy', keyPath: 'taxonomy'},
-					{name: 'parent', keyPath: 'parent'},
-					{name: 'slug', keyPath: 'slug', unique: true},
-					{name: 'count', keyPath: 'count'},
-				],
-				endpoint: 'terms',
-				TTL: 2 * 60 * 1000, //2 hours
-				filters: {
-					taxonomy: '',
-					page: 1,
-					search: '',
-					parent: 0
-				},
-				required: 'taxonomy',
-				delayFetch: true,
-			});
+		// Register DataStore
+		const store = window.jvbStore.register('taxonomies', {
+			storeName: 'terms',
+			keyPath: 'id',
+			showLoading: false,
+			indexes: [
+				{name: 'taxonomy', keyPath: 'taxonomy'},
+				{name: 'parent', keyPath: 'parent'},
+				{name: 'slug', keyPath: 'slug', unique: true},
+				{name: 'count', keyPath: 'count'},
+			],
+			endpoint: 'terms',
+			TTL: 2 * 60 * 1000,
+			filters: {
+				taxonomy: '',
+				page: 1,
+				search: '',
+				parent: 0
+			},
+			required: 'taxonomy',
+			delayFetch: true,
+		});
 		this.store = store.terms;
 
-		// Central field management
+		// Field management
 		this.fields = new Map();
-		this.selectedTerms = new Map();    // Current modal selection
+		this.selectedTerms = new Map(); // Current modal selection
 
-		// Current modal context
+		// Modal context
 		this.activeField = null;
 		this.currentConfig = null;
-		this.currentSingular = null;
-		this.currentPlural = null;
-
-		// Modal state
 		this.disabled = false;
 
-		// Search debouncing
-		this.searchHandler = null;
-		this.autocompleteHandler = null;
-		this.isAutocompleteActive = false;
+		// Search contexts
+		this.searchContexts = new Map();
 
 		this.init();
 	}
 
-	/**
-	 * Initialize the selector
-	 */
 	init() {
 		this.initModal();
 		this.scanExistingFields();
 		this.initGlobalListeners();
 
-		if (this.hasAutocomplete && window.jvbTaxCreator) {
+		// Initialize creator if needed
+		if (this.needsCreator() && window.jvbTaxCreator) {
 			this.creator = new window.jvbTaxCreator(this);
 		}
+
 		this.store.subscribe(this.handleStoreEvent.bind(this));
-		// Complete initialization
+
 		this.isInitializing = false;
 		this.batchFetchTaxonomies();
 	}
 
-	/**
-	 * Handle DataStore events
-	 */
+	needsCreator() {
+		return Array.from(this.fields.values()).some(field =>
+			field.canCreate || field.hasAutocomplete
+		);
+	}
+
+	/***********************************************************************
+	 * DATASTORE EVENT HANDLING
+	 ***********************************************************************/
+
 	handleStoreEvent(event, data) {
-		switch (event) {
-			case 'data-loaded':
-				const taxonomy = this.store.filters.taxonomy;
-				// Handle batch taxonomy loading (comma-separated)
-				if (taxonomy?.includes(',')) {
-					this.handleBatchDataLoaded(taxonomy, data);
-				}
-				// Update button states for this taxonomy (or taxonomies)
-				if (taxonomy) {
-					// Handle comma-separated taxonomies from batch fetch
-					const taxonomies = taxonomy.includes(',')
-						? taxonomy.split(',').map(t => t.trim())
-						: [taxonomy];
+		const handlers = {
+			'data-loaded': () => this.handleDataLoaded(data),
+			'filters-changed': () => this.handleFiltersChanged(data),
+			'fetch-error': () => this.handleFetchError(data.error),
+		};
 
-					taxonomies.forEach(tax => {
-						this.updateFieldsForTaxonomy(tax);
-					});
-				}
+		handlers[event]?.();
+	}
 
-				// Only render if modal is open OR autocomplete active
-				if (this.modal?.open) {
-					this.handleTermsLoaded(data);
-				}
+	handleDataLoaded(data) {
+		const taxonomy = this.store.filters.taxonomy;
 
-				if (this.isAutocompleteActive && this.activeField) {
-					const field = this.fields.get(this.activeField);
-					const terms = data.data?.items || [];
-					const query = data.filters?.search || '';
-					this.showAutocompleteResults(field, terms, query);
-					this.isAutocompleteActive = false;
-				}
-				break;
+		// Update field states for affected taxonomies
+		if (taxonomy) {
+			const taxonomies = taxonomy.includes(',')
+				? taxonomy.split(',').map(t => t.trim())
+				: [taxonomy];
 
-			case 'filters-changed':
-				if (this.modal?.open) {
-					this.showLoading();
-				}
-				break;
+			taxonomies.forEach(tax => this.updateFieldsForTaxonomy(tax));
+		}
 
-			case 'fetch-error':
-				if (this.isAutocompleteActive && this.activeField) {
-					this.showAutocompleteError(this.activeField);
-					this.isAutocompleteActive = false;
+		// Initialize displays on first load
+		if (this.isInitializing) {
+			this.fields.forEach((config, fieldId) => {
+				if (config.selectedTerms.size > 0) {
+					this.initFieldDisplay(fieldId);
 				}
-				this.handleFetchError(data.error);
-				break;
+			});
+		}
+
+		// Render based on context
+		this.renderSearchResults(data);
+	}
+
+	renderSearchResults(data) {
+		const context = this.getActiveSearchContext();
+
+		if (context === 'modal') {
+			this.renderModalResults(data);
+		} else if (context === 'autocomplete') {
+			this.renderAutocompleteResults(data);
 		}
 	}
 
-	/**
-	 * Handle loaded terms from DataStore
-	 */
-	handleTermsLoaded(data) {
+	getActiveSearchContext() {
+		if (this.modal?.open) return 'modal';
+		if (this.activeField && this.searchContexts.has(this.activeField)) {
+			return this.searchContexts.get(this.activeField);
+		}
+		return null;
+	}
+
+	renderModalResults(data) {
 		this.hideLoading();
-		const terms = this.store.getFiltered();  // Use getFiltered() instead of getFilteredItems()
+		const terms = this.store.getFiltered();
 		const response = this.store.lastResponse?.page || {};
-		const isSearch = data.filters?.search && data.filters.search.length > 0;
+		const isSearch = data.filters?.search?.length > 0;
 		const append = response.page > 1;
 
 		this.notify('terms-loaded', { terms, filters: data.filters });
@@ -152,7 +147,6 @@
 		} else {
 			this.renderTerms(terms, append, isSearch);
 
-			// Handle pagination
 			if (response.has_more) {
 				this.observer.observe(this.ui.sentinel);
 			} else {
@@ -160,107 +154,88 @@
 			}
 		}
 
-		// Announce to screen readers
 		this.a11y?.announce(terms.length, append);
 	}
 
-	/**
-	 * Handle fetch errors
-	 */
+	renderAutocompleteResults(data) {
+		const field = this.fields.get(this.activeField);
+		if (!field?.autocompleteDropdown) return;
+
+		const terms = this.store.getFiltered();
+		const query = data.filters?.search || '';
+
+		this.showAutocompleteResults(field, terms, query);
+		this.searchContexts.delete(this.activeField);
+	}
+
+	handleFiltersChanged(data) {
+		if (this.modal?.open) {
+			this.showLoading();
+		}
+	}
+
 	handleFetchError(error) {
-		console.error('Taxonomy fetch error:', error);
 		this.hideLoading();
 
-		if (this.error?.log) {
-			this.error.log(error, {
-				component: 'TaxonomySelector',
-				action: 'fetchTerms'
-			}, () => this.fetchCurrentTerms());
+		const context = this.getActiveSearchContext();
+
+		if (context === 'autocomplete') {
+			this.showAutocompleteError(this.activeField);
+			this.searchContexts.delete(this.activeField);
 		} else {
-			this.showEmptyState('Error loading terms. Please try again.');
+			this.handleError(error, 'fetch');
 		}
 	}
 
+	/***********************************************************************
+	 * FIELD MANAGEMENT
+	 ***********************************************************************/
 
-	/**
-	 * Check if taxonomy has terms and update button states
-	 */
-	updateFieldButtonState(fieldId) {
-		const field = this.fields.get(fieldId);
-		if (!field) return;
-
-		// Check store for items of this specific taxonomy
-		const hasTerms = Array.from(this.store.data.values())
-			.some(term => term.taxonomy === field.taxonomy);
-
-		if (field.toggle) {
-			field.toggle.disabled = !hasTerms && !field.canCreate;
-			field.toggle.title = !hasTerms
-				? `No ${this.getSingular(field.taxonomy)} available`
-				: `Select ${this.getPlural(field.taxonomy)}`;
-		}
-	}
-	/**
-	 * Update fields when taxonomy items are updated
-	 */
 	updateFieldsForTaxonomy(taxonomy) {
 		this.getFieldsForTaxonomy(taxonomy).forEach(field => {
 			this.updateFieldButtonState(field.id);
 		});
 	}
 
-	/**
-	 * Get fields for a specific taxonomy
-	 */
+	updateFieldButtonState(fieldId) {
+		const field = this.fields.get(fieldId);
+		if (!field) return;
+
+		const hasTerms = Array.from(this.store.data.values())
+			.some(term => term.taxonomy === field.taxonomy);
+
+		if (field.toggle) {
+			field.toggle.disabled = !hasTerms && !field.canCreate;
+			field.toggle.title = !hasTerms
+				? `No ${this.getLabel(field.taxonomy, 'single')} available`
+				: `Select ${this.getLabel(field.taxonomy, 'plural')}`;
+		}
+	}
+
 	getFieldsForTaxonomy(taxonomy) {
 		return Array.from(this.fields.values())
 			.filter(field => field.taxonomy === taxonomy);
 	}
 
-
-
-	/**
-	 * Scan page for existing taxonomy fields and register them
-	 */
-	scanExistingFields(container = null) {
-		if (!container) {
-			container = document.body;
-		}
-		const selectors = container.querySelectorAll('.field.taxonomy, .field.post');
-
-		selectors.forEach(selector => {
+	scanExistingFields(container = document.body) {
+		container.querySelectorAll('.field.taxonomy, .field.post').forEach(selector => {
 			try {
 				this.registerField(selector);
 			} catch (error) {
-				this.error.log(error, {
-					component: 'TaxonomySelector',
-					action: 'scanExistingFields',
-					container: selector.dataset.name
-				});
+				this.handleError(error, 'scanExistingFields', selector.dataset.name);
 			}
 		});
 	}
 
-	/**
-	 * Register a taxonomy field
-	 */
-	registerField(field, options = {}) {
-		let input = field.querySelector('input[type=hidden]');
-		if (!input) {
-			return false;
-		}
-		if (!('fieldId' in field.dataset)) {
-			field.dataset.fieldId = this.createFieldId(field);
-		}
-		let fieldId = field.dataset.fieldId;
+	registerField(field) {
+		const input = field.querySelector('input[type=hidden]');
+		if (!input) return false;
 
-		let button = (Object.hasOwn(options, 'button')) ? options.button : field.querySelector('button.taxonomy-toggle');
+		const fieldId = this.createFieldId(field);
+		field.dataset.fieldId = fieldId;
 
-		if (Object.hasOwn(options, 'buttonSelector')) {
-			this.triggers.add(options.buttonSelector);
-		}
-
-		let config = {
+		const button = field.querySelector('button.taxonomy-toggle');
+		const config = {
 			id: fieldId,
 			input: input,
 			container: field,
@@ -269,45 +244,31 @@
 			maxSelection: parseInt(button.dataset.max) || 0,
 			canSearch: 'search' in button.dataset,
 			hasAutocomplete: 'autocomplete' in button.dataset,
-			autocompleteDropdown: field.querySelector('.autocomplete-dropdown')??false,
+			autocompleteDropdown: field.querySelector('.autocomplete-dropdown') || null,
 			canCreate: 'creatable' in button.dataset,
 			isRequired: 'required' in button.dataset,
 			selectedTerms: new Set(),
 			toggle: button,
-			selectedContainer: (Object.hasOwn(options, 'selected')) ? options.selected : field.querySelector('.selected-items'),
-			...options
+			selectedContainer: field.querySelector('.selected-items'),
 		};
 
-		if (!this.hasAutocomplete && config.hasAutocomplete) {
-			this.hasAutocomplete = true;
-			this.initAutocomplete();
-		}
-
-		// Parse initial selected values
+		// Parse initial values
 		const value = input.value.trim();
-		if (value !== '') {
-			const selectedIds = value.split(',')
+		if (value) {
+			value.split(',')
 				.map(id => parseInt(id.trim()))
-				.filter(id => !isNaN(id));
-			selectedIds.forEach(id => config.selectedTerms.add(id));
-		}
-
-		if (Object.hasOwn(options, 'selectedItems')) {
-			options.selectedItems.forEach(id => {
-				config.selectedTerms.add(id);
-			});
+				.filter(id => !isNaN(id))
+				.forEach(id => config.selectedTerms.add(id));
 		}
 
 		this.fields.set(fieldId, config);
 
-		// Ensure store exists for this taxonomy
+		// Queue for batch fetch
 		if (this.isInitializing) {
 			this.taxonomiesToFetch.add(config.taxonomy);
-		} else {
-			// this.store.setFilter('taxonomy', config.taxonomy);
 		}
 
-		// Initialize display for any pre-selected values
+		// Initialize display
 		if (config.selectedTerms.size > 0) {
 			this.initFieldDisplay(fieldId);
 		}
@@ -315,78 +276,29 @@
 		return fieldId;
 	}
 
-	/**
-	 * Register a filter button (simplified registration for feed blocks)
-	 */
-	registerFilterButton(button, options = {}) {
-		const fieldId = this.createFieldId(button);
-		button.dataset.fieldId = fieldId;
-
-		if (options.buttonSelector) {
-			this.triggers.add(options.buttonSelector);
-		}
-
-		const config = {
-			id: fieldId,
-			input: null,
-			container: options.container || button.closest('.filters') || button.parentElement,
-			taxonomy: button.dataset.taxonomy,
-			name: `filter_${button.dataset.taxonomy}`,
-			maxSelection: parseInt(button.dataset.max) || 0,
-			canSearch: 'search' in button.dataset,
-			hasAutocomplete: false,
-			canCreate: false,
-			isRequired: false,
-			selectedTerms: new Set(options.selectedItems || []),
-			toggle: button,
-			selectedContainer: options.selected || null,
-			isFilterMode: true,
-			...options
-		};
-
-		this.fields.set(fieldId, config);
-
-		if (this.isInitializing) {
-			this.taxonomiesToFetch.add(config.taxonomy);
-		} else {
-			this.store.setFilter('taxonomy', config.taxonomy);
-		}
-
-		return fieldId;
-	}
-
-	/**
-	 * Create unique field ID
-	 */
 	createFieldId(field) {
 		this.index++;
 		return 'selector-' + this.index;
 	}
 
-	/**
-	 * Initialize display for a field with existing values
-	 */
 	async initFieldDisplay(fieldId) {
 		const field = this.fields.get(fieldId);
 		if (!field || field.selectedTerms.size === 0) return;
 
-		const selectedIds = Array.from(field.selectedTerms);
-
-		selectedIds.forEach(termId => {
-			const term = this.store.get(termId);  // Changed from getItem
+		Array.from(field.selectedTerms).forEach(termId => {
+			const term = this.store.get(termId);
 			if (term) {
-				this.addTermToDisplay(fieldId, term.id, term.name, term.path);
+				this.addTermDisplay(termId, term.name, term.path, 'field', fieldId);
 			}
 		});
 	}
 
-	/**
-	 * Initialize modal elements
-	 */
-	initModal() {
-		this.modalID = 'dialog#jvb-selector';
-		this.modal = document.querySelector(this.modalID);
+	/***********************************************************************
+	 * MODAL INITIALIZATION
+	 ***********************************************************************/
 
+	initModal() {
+		this.modal = document.querySelector('dialog#jvb-selector');
 		if (!this.modal) {
 			console.warn('Taxonomy selector modal not found');
 			return;
@@ -394,36 +306,24 @@
 
 		this.initModalElements();
 
-		// Initialize modal instance
 		this.modalInstance = new window.jvbModal(this.modal, {
-			handleForm: false,
-			save: null,
-			open: null
+			handleForm: false
 		});
-		this.modalInstance.subscribe((event, data) => {
-			switch (event) {
-				case 'modal-open':
-					this.openModal(data);
-					break;
-				case 'modal-close':
-					this.closeModal(data);
-					break;
-			}
+
+		this.modalInstance.subscribe((event) => {
+			if (event === 'modal-open') this.openModal();
+			if (event === 'modal-close') this.closeModal();
 		});
 	}
 
-	/**
-	 * Initialize modal element references
-	 */
 	initModalElements() {
-		this.selectors = {
+		const selectors = {
 			search: {
 				input: '[type=search]',
-				clear: '.clear-search',
 				container: '.search-wrapper'
 			},
-			termsList: 	'.items-container',
-			termsWrap:	'.items-wrap',
+			termsList: '.items-container',
+			termsWrap: '.items-wrap',
 			breadcrumbs: {
 				nav: 'nav.term-navigation',
 				back: '.back-to-parent',
@@ -436,25 +336,20 @@
 			sentinel: '.scroll-sentinel',
 			modal: {
 				title: '#modal-title',
-				content: '.modal-content'
 			},
 			create: {
 				details: '.create-new-term',
-				parent: '#select_parent',
 				summary: '.create-new-term summary',
-				name: '#term_name',
-				button: '.submit-term',
 				label: {
 					name: '[for=term_name]',
 					parent: '[for=select_parent]'
 				}
-			},
-			favouriteTerms: '.favourite-terms'
-		}
+			}
+		};
 
-		this.ui = window.uiFromSelectors(this.selectors);
+		this.ui = window.uiFromSelectors(selectors);
 
-		// Initialize intersection observer for infinite scroll
+		// Initialize infinite scroll observer
 		this.observer = new IntersectionObserver((entries) => {
 			entries.forEach(entry => {
 				if (entry.isIntersecting) {
@@ -467,58 +362,29 @@
 		});
 	}
 
-	/**
-	 * Set up global event delegation
-	 */
+	/***********************************************************************
+	 * GLOBAL EVENT LISTENERS
+	 ***********************************************************************/
+
 	initGlobalListeners() {
 		document.addEventListener('click', this.handleClick.bind(this));
 		document.addEventListener('change', this.handleChange.bind(this));
-		if (this.hasAutocomplete) {
-			this.initAutocomplete();
-		}
+		document.addEventListener('input', this.handleInput.bind(this));
+		document.addEventListener('focus', this.handleFocus.bind(this), true);
+		document.addEventListener('blur', this.handleBlur.bind(this), true);
 	}
 
-	initAutocomplete()
-	{
-		this.autocompleteHandler = (e) => {
-			window.debouncer.schedule(
-				'taxonomy-autocomplete',
-				() => this.handleAutocomplete(e),
-				300
-			);
-		};
-		document.addEventListener('input', this.autocompleteHandler);
-		document.addEventListener('blur', this.cleanupAutocomplete.bind(this));
-		// Preload taxonomy data on focus
-		document.addEventListener('focus', (e) => {
-			if (!('autocomplete' in e.target.dataset)) {
-				return;
-			}
-
+	handleClick(e) {
+		// Toggle button
+		if (window.targetCheck(e, '.taxonomy-toggle')) {
+			e.preventDefault();
 			const fieldId = this.getFieldId(e.target);
 			const field = this.fields.get(fieldId);
-
-			if (!field) return;
-
-			// Preload this taxonomy's data
-			this.preloadTaxonomy(field.taxonomy);
-		}, true); // Use capture phase
-	}
-
-	/**
-	 * Handle global click events
-	 */
-	handleClick(e) {
-		// Handle taxonomy toggle buttons
-		const toggleButton = window.targetCheck(e, Array.from(this.triggers));
-
-		if (toggleButton) {
-			e.preventDefault();
-			this.handleToggleClick(toggleButton);
+			if (field) this.setActiveField(fieldId, true);
 			return;
 		}
 
-		// Handle remove selected term buttons
+		// Remove selected term
 		const removeButton = window.targetCheck(e, 'button.remove-item');
 		if (removeButton && e.target.closest('.jvb-selector')) {
 			const fieldId = this.getFieldId(removeButton);
@@ -527,25 +393,20 @@
 			return;
 		}
 
-		// Handle modal close button
+		// Modal close
 		if (e.target.matches('.modal-close')) {
-			if (this.modalInstance) {
-				this.modalInstance.handleClose();
-			}
+			this.modalInstance?.handleClose();
 			return;
 		}
 
-		// Handle clicks within the modal
-		if (this.modal && this.modal.contains(e.target)) {
+		// Modal clicks
+		if (this.modal?.contains(e.target)) {
 			this.handleModalClick(e);
 		}
 	}
 
-	/**
-	 * Handle global change events
-	 */
 	handleChange(e) {
-		// Handle hidden input changes for taxonomy fields
+		// Hidden input changes
 		const taxonomyField = window.targetCheck(e, '.taxonomy.field, .post.field');
 		if (taxonomyField && e.target.type === 'hidden') {
 			const fieldId = this.getFieldId(e.target);
@@ -553,60 +414,122 @@
 			return;
 		}
 
-		// Handle modal changes
-		if (this.modal && this.modal.contains(e.target)) {
+		// Modal checkboxes
+		if (this.modal?.contains(e.target)) {
 			this.handleModalChange(e);
 		}
 	}
 
-	/**
-	 * Handle toggle button click
-	 */
-	handleToggleClick(toggle) {
-		try {
-			const fieldId = this.getFieldId(toggle);
+	handleInput(e) {
+		// Modal search
+		if (this.modal?.contains(e.target) && e.target.type === 'search') {
+			this.performSearch(e.target.value.trim(), 'modal');
+			return;
+		}
+
+		// Autocomplete
+		if ('autocomplete' in e.target.dataset) {
+			const fieldId = this.getFieldId(e.target);
 			const field = this.fields.get(fieldId);
-
-			if (!field) {
-				console.error('Field not found for toggle:', fieldId);
-				return;
-			}
-
-
-			this.setActiveField(fieldId, true);
-
-		} catch (error) {
-			console.error('Error handling toggle click:', error);
-			if (this.error?.log) {
-				this.error.log(error, {
-					component: 'TaxonomySelector',
-					action: 'handleToggleClick'
-				});
+			if (field?.hasAutocomplete) {
+				this.performSearch(e.target.value.trim(), 'autocomplete', fieldId);
 			}
 		}
 	}
 
-	/**
-	 * Set the active field for modal operations
-	 */
+	handleFocus(e) {
+		if (!('autocomplete' in e.target.dataset)) return;
+
+		const fieldId = this.getFieldId(e.target);
+		const field = this.fields.get(fieldId);
+
+		if (field?.hasAutocomplete) {
+			this.preloadTaxonomy(field.taxonomy);
+		}
+	}
+
+	handleBlur(e) {
+		if (!('autocomplete' in e.target.dataset)) return;
+
+		setTimeout(() => {
+			const fieldId = this.getFieldId(e.target);
+			const field = this.fields.get(fieldId);
+
+			if (field?.autocompleteDropdown) {
+				field.autocompleteDropdown.hidden = true;
+			}
+
+			this.searchContexts.delete(fieldId);
+		}, 200);
+	}
+
+	/***********************************************************************
+	 * UNIFIED SEARCH
+	 ***********************************************************************/
+
+	performSearch(query, context = 'modal', fieldId = null) {
+		const field = context === 'autocomplete'
+			? this.fields.get(fieldId)
+			: this.currentConfig;
+
+		if (!field) return;
+
+		// Autocomplete validation
+		if (context === 'autocomplete') {
+			field.currentAutocompleteQuery = query;
+
+			if (query.length < 2) {
+				if (field.autocompleteDropdown) {
+					field.autocompleteDropdown.hidden = true;
+				}
+				return;
+			}
+
+			this.searchContexts.set(fieldId, 'autocomplete');
+			this.activeField = fieldId;
+
+			if (field.autocompleteDropdown) {
+				field.autocompleteDropdown.hidden = false;
+			}
+		}
+
+		// Debounced search
+		window.debouncer.schedule(
+			`taxonomy-search-${context}-${fieldId || 'modal'}`,
+			async () => {
+				await this.store.setFilters({
+					taxonomy: field.taxonomy,
+					search: query,
+					page: 1,
+					parent: query ? 0 : (this.store.filters.parent || 0)
+				});
+
+				if (context === 'modal') {
+					window.removeChildren(this.ui.termsList);
+				}
+			},
+			300
+		);
+	}
+
+	/***********************************************************************
+	 * MODAL OPERATIONS
+	 ***********************************************************************/
+
 	setActiveField(fieldId, openModal = false) {
 		this.activeField = fieldId;
 		this.currentConfig = this.fields.get(fieldId);
 
-		this.currentSingular = this.getSingular(this.currentConfig.taxonomy);
-		this.currentPlural = this.getPlural(this.currentConfig.taxonomy);
-
 		if (openModal) {
 			this.modalInstance.handleOpen();
 		}
 
-		// Set taxonomy filter - store handles the rest
 		this.store.setFilter('taxonomy', this.currentConfig.taxonomy);
 
-		// Clear modal selection state
+		// Reset modal selection state
 		this.selectedTerms.clear();
 
-		// Copy field's current selections to modal state
+		// Copy field selections to modal
 		this.currentConfig.selectedTerms.forEach(termId => {
 			const term = this.store.get(termId);
 			if (term) {
@@ -619,147 +542,55 @@
 		});
 	}
 
-
-	/**
-	 * Handle clicks within modal
-	 */
 	handleModalClick(e) {
 		if (window.targetCheck(e, '.remove-item')) {
-			let selectedItem = window.targetCheck(e, '.selected-item');
+			const selectedItem = window.targetCheck(e, '.selected-item');
 			if (selectedItem) {
 				this.removeSelectedTermFromModal(selectedItem.dataset.id);
 			}
 		} else if (window.targetCheck(e, '.back-to-parent')) {
 			this.navigateToParent();
 		} else if (window.targetCheck(e, '.toggle-children')) {
-			let termItem = e.target.closest('li');
+			const termItem = e.target.closest('li');
 			this.navigateToChild(
 				parseInt(termItem.dataset.id),
 				termItem.querySelector('.term-name').textContent
 			);
 		} else if (window.targetCheck(e, '.path-level')) {
-			let pathLevel = window.targetCheck(e, '.path-level');
-			this.navigateToPath(pathLevel);
+			const pathLevel = window.targetCheck(e, '.path-level');
+			this.navigateToPath(parseInt(pathLevel.dataset.id) || 0);
 		}
 	}
 
-	/**
-	 * Handle changes within modal (checkboxes)
-	 */
 	handleModalChange(e) {
-		if (window.targetCheck(e, this.modalID) && e.target.type === 'checkbox') {
-			e.preventDefault();
-			e.stopPropagation();
+		if (e.target.type !== 'checkbox') return;
 
-			const termId = parseInt(e.target.closest('li').dataset.id);
-			const label = e.target.closest('li').querySelector('label');
+		e.preventDefault();
+		e.stopPropagation();
 
-			if (e.target.checked) {
-				this.addSelectedTermToModal(termId, label.title, label.dataset.path);
-			} else {
-				this.removeSelectedTermFromModal(termId);
-			}
+		const termId = parseInt(e.target.closest('li').dataset.id);
+		const label = e.target.closest('li').querySelector('label');
+
+		if (e.target.checked) {
+			this.addSelectedTermToModal(termId, label.title, label.dataset.path);
+		} else {
+			this.removeSelectedTermFromModal(termId);
 		}
 	}
 
-	/**
-	 * Open modal for filtering (without a field)
-	 * @param {string} taxonomy - The taxonomy to filter by
-	 * @param {Function} callback - Callback when terms are selected
-	 * @param {Array} preselected - Array of term IDs already selected
-	 */
-	openForFilter(taxonomy, callback, preselected = []) {
-		// Create a temporary virtual field config
-		const virtualFieldId = `filter-${taxonomy}-${Date.now()}`;
-
-		this.fields.set(virtualFieldId, {
-			id: virtualFieldId,
-			input: null, // No input for filter mode
-			container: null,
-			taxonomy: taxonomy,
-			name: `filter_${taxonomy}`,
-			maxSelection: 0, // No limit for filters
-			canSearch: true,
-			hasAutocomplete: false,
-			autocompleteDropdown: document.querySelector('.autocomplete-dropdown')??false,
-			canCreate: false, // Disable creation for filters
-			isRequired: false,
-			selectedTerms: new Set(preselected),
-			toggle: null,
-			selectedContainer: null,
-			isFilterMode: true, // Flag for filter mode
-			filterCallback: callback // Store the callback
-		});
-
-		this.setActiveField(virtualFieldId, true);
-		this.modalInstance.handleOpen();
-	}
-
-	/**
-	 * Open modal and initialize
-	 */
 	openModal() {
 		if (!this.currentConfig) {
 			console.error('No active field set');
 			return;
 		}
 
-		// Initialize creator if available
-		if (!this.creator && this.currentConfig.canCreate && 'jvbTaxCreator' in window) {
-			this.creator = new window.jvbTaxCreator(this);
-		}
-
-		// Update modal UI
-		this.updateModalForTaxonomy();
-
-		// Load selected terms display
+		this.updateModalUI();
 		this.updateModalSelections();
-		this.updateSelectionCount();
 
-		// Clear terms list and show loading
 		window.removeChildren(this.ui.termsList);
 		this.showLoading();
 	}
 
-	/**
-	 * Update selection count display in modal
-	 */
-	updateSelectionCount() {
-		if (!this.currentConfig) return;
-
-		const count = this.selectedTerms.size;
-		const max = this.currentConfig.maxSelection;
-
-		// Update any count display elements
-		const countElement = this.modal?.querySelector('.selection-count');
-		if (countElement) {
-			if (max > 0) {
-				countElement.textContent = `${count} of ${max} selected`;
-			} else {
-				countElement.textContent = `${count} selected`;
-			}
-		}
-	}
-
-
-
-	/**
-	 * Get singular label for taxonomy
-	 */
-	getSingular(taxonomy) {
-		return jvbSettings.labels[taxonomy]?.single || taxonomy;
-	}
-
-	/**
-	 * Get plural label for taxonomy
-	 */
-	getPlural(taxonomy) {
-		return jvbSettings.labels[taxonomy]?.plural || taxonomy;
-	}
-
-	/**
-	 * Close modal and save selections
-	 */
 	closeModal() {
 		this.observer.unobserve(this.ui.sentinel);
 		window.removeChildren(this.ui.termsList);
@@ -769,53 +600,19 @@
 			taxonomy: this.currentConfig.taxonomy
 		});
 
-		if (this.currentConfig?.isFilterMode) {
-			if (this.currentConfig.filterCallback) {
-				const selectedIds = Array.from(this.selectedTerms.keys());
-				this.currentConfig.filterCallback(selectedIds, this.currentConfig.taxonomy);
-			}
-			// this.fields.delete(this.activeField);
-		} else if (this.activeField) {
+		if (this.activeField) {
 			this.saveSelectionsToField(this.activeField);
 		}
 
-		// Cleanup
-		if (this.currentConfig?.canSearch && this.searchHandler) {
-			this.ui.search.input.removeEventListener('input', this.searchHandler);
-		}
-
-		if (!this.hasAutocomplete && this.creator) {
-			delete this.creator;
-		}
-
-		// Remove: this.activeStore = null;
 		this.activeField = null;
 		this.currentConfig = null;
 	}
 
-	/**
-	 * Reset modal state
-	 */
-	resetModalState() {
-		this.disabled = false;
+	updateModalUI() {
+		const singular = this.getLabel(this.currentConfig.taxonomy, 'single');
+		const plural = this.getLabel(this.currentConfig.taxonomy, 'plural');
 
-		window.removeChildren(this.ui.termsList);
-		window.removeChildren(this.ui.selectedTerms);
-		this.ui.search.input.value = '';
-
-		// Clear navigation breadcrumbs
-		window.removeChildren(this.ui.breadcrumbs.nav);
-		this.ui.breadcrumbs.nav.appendChild(this.ui.breadcrumbs.back);
-		this.ui.breadcrumbs.back.hidden = true;
-	}
-
-	/**
-	 * Update modal content for current taxonomy
-	 */
-	updateModalForTaxonomy() {
-		if (!this.currentConfig) return;
-
-		this.ui.modal.title.textContent = `Select ${this.currentPlural}`;
+		this.ui.modal.title.textContent = `Select ${plural}`;
 
 		if (this.ui.search.container) {
 			this.ui.search.container.style.display = this.currentConfig.canSearch ? 'block' : 'none';
@@ -826,185 +623,126 @@
 			this.ui.create.details.hidden = !this.currentConfig.canCreate;
 
 			if (this.ui.create.summary) {
-				this.ui.create.summary.textContent = `Add new ${this.currentSingular}`;
+				this.ui.create.summary.textContent = `Add new ${singular}`;
 			}
 
 			if (this.ui.create.label.name) {
-				this.ui.create.label.name.textContent = `Name this ${this.currentSingular}`;
+				this.ui.create.label.name.textContent = `Name this ${singular}`;
 			}
 			if (this.ui.create.label.parent) {
 				this.ui.create.label.parent.textContent = `Nest it under`;
 			}
-
-			if (this.ui.create.parent) {
-
-			}
 		}
 
-		const openMessage = `Opened ${this.currentSingular} selection. Choose from checkboxes or search to filter results.`;
-		this.a11y?.announce(openMessage);
+		this.a11y?.announce(`Opened ${singular} selection. Choose from checkboxes or search to filter results.`);
 	}
 
-	/**
-	 * Update modal selections display
-	 */
 	updateModalSelections() {
 		window.removeChildren(this.ui.selectedTerms);
 
 		this.selectedTerms.forEach((termData, id) => {
-			this.addTermToModalDisplay(id, termData.name, termData.path);
+			this.addTermDisplay(id, termData.name, termData.path, 'modal');
 		});
 
 		this.checkSelectionLimits();
 	}
 
-	/**
-	 * Add selected term to modal
-	 */
 	addSelectedTermToModal(id, name, path) {
-		this.selectedTerms.set(id, {
-			id: id,
-			name: name,
-			path: path
-		});
+		this.selectedTerms.set(id, { id, name, path });
 
-		this.addTermToModalDisplay(id, name, path);
+		this.addTermDisplay(id, name, path, 'modal');
 		this.checkSelectionLimits();
 
-		// Check the corresponding checkbox
 		const checkbox = this.ui.termsList.querySelector(`input[value="${id}"]`);
-		if (checkbox) {
-			checkbox.checked = true;
-		}
+		if (checkbox) checkbox.checked = true;
 	}
 
-	/**
-	 * Remove selected term from modal
-	 */
 	removeSelectedTermFromModal(id) {
 		this.selectedTerms.delete(parseInt(id));
 
-		// Remove from modal display
 		const selectedItem = this.ui.selectedTerms.querySelector(`[data-id="${id}"]`);
-		if (selectedItem) {
-			selectedItem.remove();
-		}
+		if (selectedItem) selectedItem.remove();
 
-		// Uncheck the corresponding checkbox
 		const checkbox = this.ui.termsList.querySelector(`input[value="${id}"]`);
-		if (checkbox) {
-			checkbox.checked = false;
-		}
+		if (checkbox) checkbox.checked = false;
 
 		this.checkSelectionLimits();
 	}
 
-	/**
-	 * Add term to modal display
-	 */
-	addTermToModalDisplay(id, name, path) {
-		const item = window.getTemplate('selectedTerm').cloneNode(true);
-		item.dataset.id = id;
-		item.dataset.path = path;
-		item.dataset.name = name;
-		item.dataset.taxonomy = this.currentConfig.taxonomy;
-		item.querySelector('span').textContent = path;
-		item.querySelector('button').title = `Remove ${name}`;
-
-		this.ui.selectedTerms.appendChild(item);
-	}
-
-	/**
-	 * Check selection limits and disable/enable checkboxes
-	 */
 	checkSelectionLimits() {
 		if (!this.currentConfig || this.currentConfig.maxSelection === 0) {
 			return;
 		}
 
 		this.disabled = this.selectedTerms.size >= this.currentConfig.maxSelection;
-		this.setCheckboxes(this.disabled);
-	}
 
-	/**
-	 * Set checkbox disabled state
-	 */
-	setCheckboxes(disabled) {
 		this.ui.termsList.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
 			if (!checkbox.checked) {
-				checkbox.disabled = disabled;
+				checkbox.disabled = this.disabled;
 			}
 		});
 	}
 
-	/**
-	 * Save modal selections to field
-	 */
 	saveSelectionsToField(fieldId) {
 		const field = this.fields.get(fieldId);
 		if (!field) return;
 
-		// Clear current field selections
 		field.selectedTerms.clear();
 		window.removeChildren(field.selectedContainer);
 
-		// Add modal selections to field
 		this.selectedTerms.forEach((termData, id) => {
 			field.selectedTerms.add(id);
-			this.addTermToDisplay(fieldId, id, termData.name, termData.path);
+			this.addTermDisplay(id, termData.name, termData.path, 'field', fieldId);
 		});
 
-		// Update hidden input
-		const selectedIds = Array.from(field.selectedTerms);
-		field.input.value = selectedIds.join(',');
+		field.input.value = Array.from(field.selectedTerms).join(',');
 		field.input.dispatchEvent(new Event('change', { bubbles: true }));
 	}
 
-	/**
-	 * Remove selected term from field
-	 */
+	/***********************************************************************
+	 * TERM DISPLAY
+	 ***********************************************************************/
+
+	addTermDisplay(termId, termName, termPath, context = 'field', fieldId = null) {
+		const config = context === 'field'
+			? this.fields.get(fieldId)
+			: this.currentConfig;
+
+		const container = context === 'field'
+			? config.selectedContainer
+			: this.ui.selectedTerms;
+
+		if (container.querySelector(`[data-id="${termId}"]`)) return;
+
+		const item = window.getTemplate('selectedTerm');
+		item.dataset.id = termId;
+		item.dataset.path = termPath;
+		item.dataset.name = termName;
+		item.dataset.taxonomy = config.taxonomy;
+		item.querySelector('.item-name').textContent = termPath;
+		item.querySelector('button').title = `Remove ${termName}`;
+
+		container.appendChild(item);
+
+		if (context === 'modal') {
+			const checkbox = this.ui.termsList.querySelector(`input[value="${termId}"]`);
+			if (checkbox) checkbox.checked = true;
+		}
+	}
+
 	removeSelectedTerm(fieldId, termId) {
 		const field = this.fields.get(fieldId);
 		if (!field) return;
 
-		const id = parseInt(termId);
-		field.selectedTerms.delete(id);
+		field.selectedTerms.delete(parseInt(termId));
 
-		// Remove from display
-		const selectedItem = field.selectedContainer.querySelector(`[data-id="${id}"]`);
-		if (selectedItem) {
-			selectedItem.remove();
-		}
+		const selectedItem = field.selectedContainer.querySelector(`[data-id="${termId}"]`);
+		if (selectedItem) selectedItem.remove();
 
-		// Update hidden input
-		const selectedIds = Array.from(field.selectedTerms);
-		field.input.value = selectedIds.join(',');
+		field.input.value = Array.from(field.selectedTerms).join(',');
 		field.input.dispatchEvent(new Event('change', { bubbles: true }));
 	}
 
-	/**
-	 * Add term to field display
-	 */
-	addTermToDisplay(fieldId, id, name, path) {
-		const field = this.fields.get(fieldId);
-		if (!field || field.selectedContainer.querySelector(`[data-id="${id}"]`)) {
-			return; // Already displayed
-		}
-
-		const item = window.getTemplate('selectedTerm').cloneNode(true);
-		item.dataset.id = id;
-		item.dataset.path = path;
-		item.dataset.name = name;
-		item.dataset.taxonomy = field.taxonomy;
-		item.querySelector('span').textContent = path;
-		item.querySelector('button').title = `Remove ${name}`;
-
-		field.selectedContainer.appendChild(item);
-	}
-
-	/**
-	 * Update field from hidden input value
-	 */
 	updateFieldFromInput(fieldId) {
 		const field = this.fields.get(fieldId);
 		if (!field) return;
@@ -1013,248 +751,71 @@
 		field.selectedTerms.clear();
 		window.removeChildren(field.selectedContainer);
 
-		if (value !== '') {
-			const selectedIds = value.split(',')
+		if (value) {
+			value.split(',')
 				.map(id => parseInt(id.trim()))
-				.filter(id => !isNaN(id));
+				.filter(id => !isNaN(id))
+				.forEach(id => field.selectedTerms.add(id));
 
-			selectedIds.forEach(id => field.selectedTerms.add(id));
 			this.initFieldDisplay(fieldId);
 		}
 	}
 
-	/**
-	 * Handle search input
-	 */
-	handleSearch(e) {
-		const query = e.target.value.trim();
+	/***********************************************************************
+	 * NAVIGATION
+	 ***********************************************************************/
 
-		// Clear existing debounce
-		if (this.searchHandler) {
-			clearTimeout(this.searchHandler);
-		}
-
-		this.searchHandler = setTimeout(() => {
-			// Single call - auto-fetches
-			this.store.setFilters({
-				search: query,
-				page: 1,
-				parent: query ? 0 : (this.store.filters.parent || 0)
-			});
-
-			window.removeChildren(this.ui.termsList);
-		}, 300);
-	}
-
-	async handleAutocomplete(e) {
-		if (!('autocomplete' in e.target.dataset)) {
-			return;
-		}
-
-		const fieldId = this.getFieldId(e.target);
-		const field = this.fields.get(fieldId);
-
-		if (!field) return;
-
-		// Store current value immediately (fixes fast typing issue)
-		const query = e.target.value.trim();
-		field.currentAutocompleteQuery = query;
-
-		if (query.length < 2) {
-			if (field.autocompleteDropdown) {
-				field.autocompleteDropdown.hidden = true;
-			}
-			this.isAutocompleteActive = false;
-			return;
-		}
-
-		this.activeField = fieldId;
-		this.isAutocompleteActive = true;
-
-		if (field.autocompleteDropdown) {
-			field.autocompleteDropdown.hidden = false;
-		}
-
-		this.store.setFilters({
-			taxonomy: field.taxonomy,
-			search: query,
-			page: 1
-		});
-	}
-
-	cleanupAutocomplete(e) {
-		if (!('autocomplete' in e.target.dataset)) {
-			return;
-		}
-
-		const fieldId = this.getFieldId(e.target);
-		const field = this.fields.get(fieldId);
-
-		if (!field) return;
-
-		if (this.creator) {
-			delete this.creator;
-		}
-	}
-
-	showAutocompleteError(fieldId) {
-
-		const field = this.fields.get(fieldId);
-		if (!field) {
-			return;
-		}
-		if (!field.config.autocompleteDropdown) {
-			field.config.autocompleteDropdown = field.element.querySelector('.autocomplete-dropdown');
-		}
-		const dropdown = field.config.autocompleteDropdown;
-		if (dropdown) {
-			window.removeChildren(dropdown);
-			this.showEmptyState('Hmmm... something went wrong', dropdown);
-		}
-	}
-
-	showAutocompleteResults(field, terms, query) {
-		if (!field || !field.autocompleteDropdown) {
-			return;
-		}
-
-		const dropdown = field.autocompleteDropdown;
-		window.removeChildren(dropdown);
-
-		if (terms.length === 0) {
-			this.showEmptyState('No items found.', dropdown);
-		} else {
-			terms.forEach(term => {
-				const element = this.createAutocompleteTermElement(field, term);
-				if (element) {
-					dropdown.appendChild(element);
-				}
-			});
-		}
-
-		// Only show create button if exact match doesn't exist
-		const currentQuery = field.currentAutocompleteQuery || query;
-		if (field.canCreate && currentQuery && window.jvbTaxCreator) {
-			const exactMatch = terms.find(term =>
-				term.name.toLowerCase() === currentQuery.toLowerCase()
-			);
-
-			if (!exactMatch) {
-				const createOption = this.createNewTermOption(currentQuery);
-				dropdown.appendChild(createOption);
-			}
-		}
-
-		dropdown.hidden = false;
-	}
-
-	createNewTermOption(query) {
-		const button = document.createElement('button');
-		button.type = 'button';
-		button.className = 'autocomplete-item create-term';
-		button.dataset.query = query;
-		button.innerHTML = `<strong>Create:</strong> "${query}"`;
-
-		return button;
-	}
-
-	createAutocompleteTermElement(field, term) {
-		const item = document.createElement('button');
-		item.type = 'button';
-		item.className = 'autocomplete-item';
-		item.dataset.id = term.id;
-		item.dataset.name = term.name;
-		item.dataset.path = term.path || term.name;
-		item.textContent = term.path || term.name;
-
-		item.addEventListener('click', () => {
-			// Add term to field
-			field.selectedTerms.add(parseInt(term.id));
-			this.addTermToDisplay(field.id, term.id, term.name, term.path);
-
-			// 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;
-			const input = field.container.querySelector('input[data-autocomplete]');
-			if (input) input.value = '';
-		});
-
-		return item;
-	}
-
-	/**
-	 * Navigate to parent term
-	 */
 	navigateToParent() {
-		// Store handles fetch automatically
-		this.store.setFilters({
-			parent: 0,
-			page: 1
-		});
-
+		this.store.setFilters({ parent: 0, page: 1 });
 		window.removeChildren(this.ui.termsList);
 		this.ui.breadcrumbs.back.hidden = true;
 	}
 
-	/**
-	 * Navigate to child term
-	 */
 	navigateToChild(termId, termName) {
-		// Store handles fetch automatically
-		this.store.setFilters({
-			parent: termId,
-			page: 1
-		});
-
+		this.store.setFilters({ parent: termId, page: 1 });
 		window.removeChildren(this.ui.termsList);
 		this.updateBreadcrumbs(termId, termName);
 		this.ui.breadcrumbs.back.hidden = false;
 	}
 
-	/**
-	 * Navigate to specific path level
-	 */
-	navigateToPath(pathLevel) {
-		const parentId = parseInt(pathLevel.dataset.id) || 0;
-
-		// Store handles fetch automatically
-		this.store.setFilters({
-			parent: parentId,
-			page: 1
-		});
-
+	navigateToPath(parentId) {
+		this.store.setFilters({ parent: parentId, page: 1 });
 		window.removeChildren(this.ui.termsList);
 		this.ui.breadcrumbs.back.hidden = parentId === 0;
 	}
 
-	/**
-	 * Load more terms (pagination)
-	 */
 	loadMoreTerms() {
 		const currentPage = this.store.filters.page || 1;
 		this.store.setFilter('page', currentPage + 1);
 	}
 
-	/**
-	 * Render terms list
-	 */
-	renderTerms(terms = null, append = false, showPath = false) {
-		// If no terms provided, get from store
-		if (!terms) {
-			terms = this.store.getFiltered();
-		}
+	updateBreadcrumbs(termId, termName) {
+		const breadcrumb = window.getTemplate('termBreadcrumb');
+		breadcrumb.dataset.id = termId;
+		breadcrumb.textContent = termName;
+		breadcrumb.title = termName;
 
-		if (!append) {
-			window.removeChildren(this.ui.termsList);
+		const existingCrumb = this.ui.breadcrumbs.nav.querySelector(`[data-id="${termId}"]`);
+		if (existingCrumb) {
+			while (existingCrumb.nextElementSibling) {
+				existingCrumb.nextElementSibling.remove();
+			}
+		} else {
+			this.ui.breadcrumbs.nav.appendChild(breadcrumb);
 		}
+	}
+
+	/***********************************************************************
+	 * RENDERING
+	 ***********************************************************************/
+
+	renderTerms(terms = null, append = false, showPath = false) {
+		if (!terms) terms = this.store.getFiltered();
+
+		if (!append) window.removeChildren(this.ui.termsList);
 
 		if (terms.length === 0) {
-			if (!append) {
-				this.showEmptyState();
-			}
+			if (!append) this.showEmptyState();
 			return;
 		}
 
@@ -1271,104 +832,145 @@
 				show: showPath
 			});
 
-			if (element) {
-				fragment.appendChild(element);
-			}
+			if (element) fragment.appendChild(element);
 		});
 
 		this.ui.termsList.appendChild(fragment);
 	}
 
-	/**
-	 * Create individual term element
-	 */
 	createTermElement(termData) {
-		if (!termData || !termData.name) return null;
+		if (!termData?.name) return null;
 
-		const listItem = window.getTemplate('termListItem').cloneNode(true);
+		const listItem = window.getTemplate('termListItem');
 		listItem.dataset.id = termData.id;
 
 		const isSelected = this.selectedTerms.has(termData.id);
 		const checkbox = listItem.querySelector('input');
 		const label = listItem.querySelector('label');
-		const nameSpan = listItem.querySelector('span, .term-name');
+		const nameSpan = listItem.querySelector('.term-name');
 
-		if (checkbox && label && nameSpan) {
-			checkbox.id = `${this.currentConfig.container.id}${termData.id}`;
-			checkbox.name = `${this.currentConfig.container.id}${this.currentConfig.taxonomy}-select`;
-			checkbox.value = termData.id;
-			checkbox.disabled = !isSelected && this.disabled;
-			checkbox.checked = isSelected;
+		checkbox.id = `${this.currentConfig.container.id}${termData.id}`;
+		checkbox.name = `${this.currentConfig.container.id}${this.currentConfig.taxonomy}-select`;
+		checkbox.value = termData.id;
+		checkbox.disabled = !isSelected && this.disabled;
+		checkbox.checked = isSelected;
 
-			label.htmlFor = checkbox.id;
-			label.title = termData.path || termData.name;
-			label.dataset.path = termData.path;
+		label.htmlFor = checkbox.id;
+		label.title = termData.path || termData.name;
+		label.dataset.path = termData.path;
 
-			nameSpan.textContent = termData.show ? termData.path : termData.name;
-		}
+		nameSpan.textContent = termData.show ? termData.path : termData.name;
 
 		if (termData.hasChildren) {
-			const childrenToggle = window.getTemplate ?
-				window.getTemplate('termChildrenToggle') :
-				this.createChildrenToggle();
-
-			if (childrenToggle) {
-				childrenToggle.ariaLabel = `View sub-terms of ${termData.name}`;
-				listItem.appendChild(childrenToggle);
-			}
+			const childrenToggle = window.getTemplate('termChildrenToggle');
+			childrenToggle.ariaLabel = `View sub-terms of ${termData.name}`;
+			listItem.appendChild(childrenToggle);
 		}
 
 		return listItem;
 	}
 
-	/**
-	 * Create children toggle button
-	 */
-	createChildrenToggle() {
+	/***********************************************************************
+	 * AUTOCOMPLETE
+	 ***********************************************************************/
+
+	showAutocompleteResults(field, terms, query) {
+		if (!field?.autocompleteDropdown) return;
+
+		const dropdown = field.autocompleteDropdown;
+		window.removeChildren(dropdown);
+
+		if (terms.length === 0) {
+			this.showEmptyState('No items found.', dropdown);
+		} else {
+			const fragment = document.createDocumentFragment();
+
+			terms.forEach(term => {
+				const item = this.createAutocompleteItem(field, term);
+				if (item) fragment.appendChild(item);
+			});
+
+			dropdown.appendChild(fragment);
+		}
+
+		// Create button if allowed and no exact match
+		const currentQuery = field.currentAutocompleteQuery || query;
+		if (field.canCreate && currentQuery) {
+			const exactMatch = terms.find(term =>
+				term.name.toLowerCase() === currentQuery.toLowerCase()
+			);
+
+			if (!exactMatch) {
+				dropdown.appendChild(this.createAutocompleteCreateButton(currentQuery));
+			}
+		}
+
+		dropdown.hidden = false;
+	}
+
+	createAutocompleteItem(field, term) {
 		const button = document.createElement('button');
 		button.type = 'button';
-		button.className = 'toggle-children';
-		button.innerHTML = '→';
+		button.className = 'autocomplete-item';
+		button.dataset.id = term.id;
+		button.dataset.name = term.name;
+		button.dataset.path = term.path || term.name;
+		button.textContent = term.path || term.name;
+
+		button.addEventListener('click', () => {
+			field.selectedTerms.add(parseInt(term.id));
+			this.addTermDisplay(term.id, term.name, term.path, 'field', field.id);
+
+			field.input.value = Array.from(field.selectedTerms).join(',');
+			field.input.dispatchEvent(new Event('change', { bubbles: true }));
+
+			field.autocompleteDropdown.hidden = true;
+			const input = field.container.querySelector('input[data-autocomplete]');
+			if (input) input.value = '';
+		});
+
 		return button;
 	}
 
-	/**
-	 * Update breadcrumb navigation
-	 */
-	updateBreadcrumbs(termId, termName) {
-		// This is a simplified version - you'd want to maintain a proper breadcrumb trail
-		const breadcrumb = window.getTemplate('termBreadcrumb').cloneNode(true);
-		breadcrumb.dataset.id = termId;
-		breadcrumb.textContent = termName;
-		breadcrumb.title = termName;
+	createAutocompleteCreateButton(query) {
+		const button = document.createElement('button');
+		button.type = 'button';
+		button.className = 'autocomplete-item create-term';
+		button.dataset.query = query;
 
-		// Remove any existing breadcrumbs after this level
-		const existingCrumb = this.ui.breadcrumbs.nav.querySelector(`[data-id="${termId}"]`);
-		if (existingCrumb) {
-			// Remove all breadcrumbs after this one
-			while (existingCrumb.nextElementSibling) {
-				existingCrumb.nextElementSibling.remove();
-			}
-		} else {
-			this.ui.breadcrumbs.nav.appendChild(breadcrumb);
-		}
+		const strong = document.createElement('strong');
+		strong.textContent = 'Create: ';
+
+		button.appendChild(strong);
+		button.appendChild(document.createTextNode(`"${query}"`));
+
+		return button;
 	}
 
-	/**
-	 * Show loading state
-	 */
+	showAutocompleteError(fieldId) {
+		const field = this.fields.get(fieldId);
+		if (!field?.autocompleteDropdown) return;
+
+		window.removeChildren(field.autocompleteDropdown);
+		this.showEmptyState('Hmmm... something went wrong', field.autocompleteDropdown);
+	}
+
+	/***********************************************************************
+	 * UI STATES
+	 ***********************************************************************/
+
 	showLoading() {
 		this.ui.loading.loading.hidden = false;
 		this.modal.classList.add('loading');
 
-		const searchQuery = this.store?.filters?.search || '';
-		const currentParent = this.store?.filters?.parent || 0;
+		const searchQuery = this.store.filters.search || '';
+		const currentParent = this.store.filters.parent || 0;
 
-		let message = searchQuery !== '' ?
-			`searching for "${searchQuery}" items` :
-			currentParent === 0 ?
-				'loading items' :
-				`loading child items`;
+		const message = searchQuery
+			? `searching for "${searchQuery}" items`
+			: currentParent === 0
+				? 'loading items'
+				: 'loading child items';
 
 		if (window.typeLoop) {
 			this.stopTyping = window.typeLoop(this.ui.loading.text, message);
@@ -1377,9 +979,6 @@
 		}
 	}
 
-	/**
-	 * Hide loading state
-	 */
 	hideLoading() {
 		this.ui.loading.loading.hidden = true;
 		this.modal.classList.remove('loading');
@@ -1389,47 +988,40 @@
 		}
 	}
 
-	/**
-	 * Show empty state message
-	 */
 	showEmptyState(message = 'No items found.', container = null) {
-		if (!container) {
-			container = this.ui.termsList;
-		}
-		const emptyElement = window.getTemplate('noResults').cloneNode(true);
+		if (!container) container = this.ui.termsList;
 
-		if (message && emptyElement.querySelector('span')) {
-			emptyElement.querySelector('span').textContent = message;
+		const emptyElement = window.getTemplate('noResults');
+		const messageSpan = emptyElement.querySelector('span');
+
+		if (message && messageSpan) {
+			messageSpan.textContent = message;
 		}
 
 		container.appendChild(emptyElement);
 	}
 
-	/**
-	 * Get field ID from any element within the field
-	 */
+	/***********************************************************************
+	 * UTILITIES
+	 ***********************************************************************/
+
 	getFieldId(element) {
-		if (element.dataset.fieldId) {
-			return element.dataset.fieldId;
-		}
+		if (element.dataset.fieldId) return element.dataset.fieldId;
 
 		const fieldContainer = element.closest('[data-field-id]');
-		if (fieldContainer) {
-			return fieldContainer.dataset.fieldId;
-		}
-
-		return null;
+		return fieldContainer?.dataset.fieldId || null;
 	}
-	/********************************************
-	BATCH FETCH: fetches first page for all taxonomies in one call
-	 ********************************************/
+
+	getLabel(taxonomy, type = 'single') {
+		return jvbSettings.labels[taxonomy]?.[type] || taxonomy;
+	}
+
 	async batchFetchTaxonomies() {
 		if (this.taxonomiesToFetch.size === 0) return;
 
 		const taxonomies = Array.from(this.taxonomiesToFetch);
 		this.taxonomiesToFetch.clear();
 
-		// Single fetch - the data-loaded event will handle cache splitting
 		this.store.setFilters({
 			taxonomy: taxonomies.join(','),
 			page: 1,
@@ -1437,85 +1029,31 @@
 			parent: 0
 		});
 	}
-	handleBatchDataLoaded(taxonomyString, data) {
-		const taxonomies = taxonomyString.split(',').map(t => t.trim());
-		const storeInstance = this.store.getStore(); // Access actual store instance
 
-		taxonomies.forEach(taxonomy => {
-			const filters = {
-				taxonomy: taxonomy,
-				page: 1,
-				search: '',
-				parent: 0
-			};
-
-			// Use the internal generateCacheKey method via store instance
-			const cacheKey = this.generateCacheKeyForFilters(filters);
-
-			// Filter items for this specific taxonomy
-			const items = Array.from(this.store.data.values())
-				.filter(item => item.taxonomy === taxonomy)
-				.map(item => item.id);
-
-			const cacheEntry = {
-				key: cacheKey,
-				items: items,
-				timestamp: Date.now(),
-				endpoint: storeInstance.config.endpoint,
-				filters: filters
-			};
-
-			// Set in both memory and IndexedDB cache
-			storeInstance.cache.set(cacheKey, cacheEntry);
-
-			// Persist to IndexedDB (if available)
-			if (storeInstance.db?.objectStoreNames.contains('cache')) {
-				const tx = storeInstance.db.transaction(['cache'], 'readwrite');
-				const objectStore = tx.objectStore('cache');
-				objectStore.put(cacheEntry);
-			}
-
-			// Update button states for this taxonomy
-			this.updateFieldsForTaxonomy(taxonomy);
-		});
-
-		// Initialize field displays
-		this.fields.forEach((config, fieldId) => {
-			if (config.selectedTerms.size > 0) {
-				this.initFieldDisplay(fieldId);
-			}
-		});
-	}
-
-	/**
-	 * Generate cache key for given filters (matching DataStore's internal logic)
-	 */
-	generateCacheKeyForFilters(filters) {
-		const normalized = Object.keys(filters)
-			.sort()
-			.reduce((acc, key) => {
-				acc[key] = filters[key];
-				return acc;
-			}, {});
-
-		return JSON.stringify(normalized);
-	}
-
-	/**
-	 * Preload taxonomy data on hover
-	 */
 	async preloadTaxonomy(taxonomy) {
-		// Trigger fetch for this taxonomy
-		this.store.setFilters({
+		await this.store.setFilters({
 			taxonomy: taxonomy,
 			page: 1,
 			search: '',
 			parent: 0
 		});
 	}
-	/*****************************************
-	SUBSCRIBERS
-	 *****************************************/
+
+	handleError(error, context, detail = null) {
+		console.error(`Taxonomy ${context} error:`, error, detail);
+
+		if (this.error?.log) {
+			this.error.log(error, {
+				component: 'TaxonomySelector',
+				action: context,
+				detail: detail
+			});
+		}
+
+		if (this.modal?.open) {
+			this.showEmptyState('Error loading. Please try again.');
+		}
+	}
 
 	subscribe(callback) {
 		this.subscribers.add(callback);
@@ -1523,7 +1061,7 @@
 	}
 
 	notify(event, data = {}) {
-		this.subscribers.forEach( callback => {
+		this.subscribers.forEach(callback => {
 			try {
 				callback(event, data);
 			} catch (error) {
@@ -1532,35 +1070,27 @@
 		});
 	}
 
-	/**
-	 * Clean up
-	 */
 	destroy() {
-		// Remove event listeners
 		document.removeEventListener('click', this.handleClick);
 		document.removeEventListener('change', this.handleChange);
+		document.removeEventListener('input', this.handleInput);
+		document.removeEventListener('focus', this.handleFocus);
+		document.removeEventListener('blur', this.handleBlur);
 
-		// Clear intervals and cleanup
 		this.observer?.disconnect();
-
-		// Destroy all stores
 		this.store.destroy();
-
 		this.subscribers.clear();
-		// Clear all maps
 		this.fields.clear();
 		this.selectedTerms.clear();
+		this.searchContexts.clear();
 	}
 }
 
-/**
- * Initialize singleton
- */
-document.addEventListener('DOMContentLoaded', function() {
+// Initialize on auth ready
+document.addEventListener('DOMContentLoaded', () => {
 	window.auth.subscribe((event) => {
 		if (event === 'auth-loaded') {
 			window.jvbSelector = new TaxonomySelector();
 		}
 	});
-
 });
diff --git a/assets/js/min/creator.min.js b/assets/js/min/creator.min.js
index d3303a7..8ea287c 100644
--- a/assets/js/min/creator.min.js
+++ b/assets/js/min/creator.min.js
@@ -1 +1 @@
-window.jvbTaxCreator=class{constructor(e){this.selector=e,e.modal&&(this.createNew=e.modal.querySelector(".create-new-term"),this.toggle=e.modal.querySelector(".new-term-toggle"),this.form=this.createNew?.querySelector(".create-new-term-section")),this.initListeners(),this.form&&this.initTermCreation()}initListeners(){this.clickHandler=this.handleClick.bind(this),document.addEventListener("click",this.clickHandler)}handleClick(e){window.targetCheck(e,".create-new-term summary")&&(this.createNew.open&&this.createNew.querySelector('input[name="term_name"]').focus(),this.resetParentOptions()),window.targetCheck(e,".submit-term")&&this.handleTermCreation(e).then((()=>{})),window.targetCheck(e,".create-term")&&this.handleAutocompleteCreate(e).then((()=>{}))}async handleTermCreation(e){const t=this.selector.currentConfig?.taxonomy;if(!t)return;const r=this.form.querySelector('input[name="term_name"]').value.trim(),n=parseInt(this.form.querySelector("input#select_parent")?.value)||0;if(r)try{const e=this.form.querySelector("button");e&&(e.disabled=!0);const o=await this.createTerm(r,n,t);if(o.success&&o.term){let e=o.term;const a=e.path||e.name;this.createNew.open=!1,await this.selector.store.clearCache(),this.selector.store.data.set(e.id,{id:e.id,name:e.name,path:a,taxonomy:t,parent:n,count:0,hasChildren:!1,slug:e.slug||r.toLowerCase().replace(/\s+/g,"-")}),this.selector.addSelectedTermToModal(e.id,e.name,a),(this.selector.store.filters.parent||0)===n&&await this.selector.store.setFilters({taxonomy:t,parent:n,page:1,search:""}),this.form.querySelector('input[name="term_name"]').value="";const s=this.createNew.querySelector(".term-suggestions");s&&(s.hidden=!0)}}catch(e){console.error("Error creating term:",e),this.selector.error?.log(e,{component:"TaxonomyCreator",action:"handleTermCreation"})}finally{this.form.querySelector("button").disabled=!1}}async handleAutocompleteCreate(e){const t=e.target.closest(".create-term"),r=this.selector.getFieldId(t),n=this.selector.fields.get(r);if(!n)return;const o=n.container.querySelector("input[data-autocomplete]"),a=o?.value.trim()||t.dataset.query;if(!a)return;const s=t.innerHTML;try{t.disabled=!0,t.textContent="Creating...";const e=await this.createTerm(a,0,n.taxonomy);if(e.success&&e.term){const t=e.term,r=t.path||t.name;n.selectedTerms.add(parseInt(t.id)),this.selector.store.data.set(t.id,{id:t.id,name:t.name,path:r,taxonomy:n.taxonomy,parent:0,count:0,hasChildren:!1,slug:t.slug||a.toLowerCase().replace(/\s+/g,"-")}),this.selector.addTermToDisplay(n.id,t.id,t.name,r),n.input.value=Array.from(n.selectedTerms).join(","),n.input.dispatchEvent(new Event("change",{bubbles:!0})),n.autocompleteDropdown.hidden=!0,o&&(o.value=""),await this.selector.store.clearCache()}else if("exists"===e.reason&&e.term){const t=e.term;n.selectedTerms.add(parseInt(t.id)),this.selector.addTermToDisplay(n.id,t.id,t.name,t.path||t.name),n.input.value=Array.from(n.selectedTerms).join(","),n.input.dispatchEvent(new Event("change",{bubbles:!0})),n.autocompleteDropdown.hidden=!0,o&&(o.value="")}}catch(e){console.error("Error creating term:",e),this.selector.error?.log(e,{component:"TaxonomyCreator",action:"handleAutocompleteCreate"})}finally{t.innerHTML=s,t.disabled=!1}}initTermCreation(){this.form&&this.form.addEventListener("change",(e=>{e.preventDefault(),e.stopPropagation()}))}resetParentOptions(){const e=this.selector.currentConfig?.taxonomy;if(!e)return;let t=this.createNew.querySelector("#select_parent");if(!t)return;let r=t.querySelector("option");if(!r)return;window.removeChildren(t),t.append(r.cloneNode(!0));const n=this.selector.store.filters.parent||0;if(0!==n){const e=this.selector.store.data.get(n);if(e){let n=r.cloneNode(!0);n.value=e.id,n.textContent=e.name,t.append(n)}}const o=[];this.selector.store.data.forEach((t=>{t.taxonomy===e&&t.parent===n&&o.push(t)})),o.sort(((e,t)=>e.name.localeCompare(t.name))),o.forEach((e=>{let n=r.cloneNode(!0);n.id=`select-parent-${e.id}`,n.value=e.id,n.textContent="  — "+e.name,t.append(n)}))}async createTerm(e,t=0,r){try{await this.selector.store.setFilters({taxonomy:r,search:e,page:1,parent:0}),await new Promise((e=>setTimeout(e,100)));const n=Array.from(this.selector.store.data.values()).find((t=>t.taxonomy===r&&t.name.toLowerCase()===e.toLowerCase()));if(n)return this.createNew&&this.showTermSuggestions([n],!0),{success:!1,reason:"exists",term:n};const o=await fetch(`${jvbSettings.api}terms`,{method:"POST",headers:{"Content-Type":"application/json","X-WP-Nonce":window.auth.getNonce()},body:JSON.stringify({taxonomy:r,name:e,parent:t})});if(!o.ok)throw new Error(`Server error: ${o.status}`);return await o.json()}catch(e){throw console.error("Error creating term:",e),e}}showTermSuggestions(e,t=!1){const r=this.createNew.querySelector(".term-suggestions")||this.createSuggestionContainer();window.removeChildren(r);const n=document.createElement("h4");n.textContent=t?"This term already exists:":"Similar terms already exist:",r.appendChild(n);const o=document.createElement("ul");o.className="term-suggestion-list",e.forEach((e=>{const t=document.createElement("li"),n=document.createElement("button");n.type="button",n.className="use-existing-term",n.setAttribute("data-id",e.id),n.textContent=e.path||e.name,n.addEventListener("click",(()=>{this.selector.addSelectedTermToModal(e.id,e.name,e.path||e.name),this.createNew.open=!1,r.hidden=!0,this.form.querySelector('input[name="term_name"]').value=""})),t.appendChild(n),o.appendChild(t)})),r.appendChild(o),r.hidden=!1}createSuggestionContainer(){const e=document.createElement("div");return e.className="term-suggestions",e.hidden=!0,this.createNew.querySelector("form").after(e),e}destroy(){this.clickHandler&&document.removeEventListener("click",this.clickHandler);const e=this.createNew?.querySelector(".loading-message.create-term");e&&(e.hidden=!0);const t=this.createNew?.querySelector(".term-suggestions");t&&(t.hidden=!0)}};
\ No newline at end of file
+window.jvbTaxCreator=class{constructor(e){this.selector=e,e.modal&&(this.createNew=e.modal.querySelector(".create-new-term"),this.toggle=e.modal.querySelector(".new-term-toggle"),this.form=this.createNew?.querySelector(".create-new-term-section")),this.initListeners(),this.form&&this.initTermCreation()}initListeners(){this.clickHandler=this.handleClick.bind(this),document.addEventListener("click",this.clickHandler)}handleClick(e){window.targetCheck(e,".create-new-term summary")&&(this.createNew.open&&this.createNew.querySelector('input[name="term_name"]').focus(),this.resetParentOptions()),window.targetCheck(e,".submit-term")&&this.handleTermCreation(e).then((()=>{})),window.targetCheck(e,".create-term")&&this.handleAutocompleteCreate(e).then((()=>{}))}async handleTermCreation(e){const t=this.selector.currentConfig?.taxonomy;if(!t)return;const r=this.form.querySelector('input[name="term_name"]').value.trim(),n=parseInt(this.form.querySelector("input#select_parent")?.value)||0;if(!r)return;const s=this.form.querySelector("button");try{s&&(s.disabled=!0);const e=await this.createTerm(r,n,t);e.success&&e.term&&(await this.handleSuccessfulCreation(e.term,t,n),this.clearForm())}catch(e){console.error("Error creating term:",e),this.selector.handleError(e,"handleTermCreation")}finally{s&&(s.disabled=!1)}}async handleSuccessfulCreation(e,t,r){const n=e.path||e.name;this.createNew.open=!1,await this.selector.store.clearCache(),this.selector.store.data.set(e.id,{id:e.id,name:e.name,path:n,taxonomy:t,parent:r,count:0,hasChildren:!1,slug:e.slug||termName.toLowerCase().replace(/\s+/g,"-")}),this.selector.addSelectedTermToModal(e.id,e.name,n),(this.selector.store.filters.parent||0)===r&&await this.selector.store.setFilters({taxonomy:t,parent:r,page:1,search:""})}async handleAutocompleteCreate(e){const t=e.target.closest(".create-term"),r=this.selector.getFieldId(t),n=this.selector.fields.get(r);if(!n)return;const s=n.container.querySelector("input[data-autocomplete]"),a=s?.value.trim()||t.dataset.query;if(!a)return;const o=t.innerHTML;try{t.disabled=!0,t.textContent="Creating...";const e=await this.createTerm(a,0,n.taxonomy);e.success&&e.term?await this.handleAutocompleteSuccess(e.term,n,s):"exists"===e.reason&&e.term&&this.handleExistingTerm(e.term,n,s)}catch(e){console.error("Error creating term:",e),this.selector.handleError(e,"handleAutocompleteCreate")}finally{t.innerHTML=o,t.disabled=!1}}async handleAutocompleteSuccess(e,t,r){const n=e.path||e.name;t.selectedTerms.add(parseInt(e.id)),this.selector.store.data.set(e.id,{id:e.id,name:e.name,path:n,taxonomy:t.taxonomy,parent:0,count:0,hasChildren:!1,slug:e.slug||e.name.toLowerCase().replace(/\s+/g,"-")}),this.selector.addTermDisplay(e.id,e.name,n,"field",t.id),t.input.value=Array.from(t.selectedTerms).join(","),t.input.dispatchEvent(new Event("change",{bubbles:!0})),t.autocompleteDropdown.hidden=!0,r&&(r.value=""),await this.selector.store.clearCache()}handleExistingTerm(e,t,r){t.selectedTerms.add(parseInt(e.id)),this.selector.addTermDisplay(e.id,e.name,e.path||e.name,"field",t.id),t.input.value=Array.from(t.selectedTerms).join(","),t.input.dispatchEvent(new Event("change",{bubbles:!0})),t.autocompleteDropdown.hidden=!0,r&&(r.value="")}initTermCreation(){this.form&&this.form.addEventListener("change",(e=>{e.preventDefault(),e.stopPropagation()}))}resetParentOptions(){const e=this.selector.currentConfig?.taxonomy;if(!e)return;let t=this.createNew.querySelector("#select_parent");if(!t)return;let r=t.querySelector("option");if(!r)return;window.removeChildren(t),t.append(r.cloneNode(!0));const n=this.selector.store.filters.parent||0;if(0!==n){const e=this.selector.store.data.get(n);if(e){let n=r.cloneNode(!0);n.value=e.id,n.textContent=e.name,t.append(n)}}const s=[];this.selector.store.data.forEach((t=>{t.taxonomy===e&&t.parent===n&&s.push(t)})),s.sort(((e,t)=>e.name.localeCompare(t.name))),s.forEach((e=>{let n=r.cloneNode(!0);n.id=`select-parent-${e.id}`,n.value=e.id,n.textContent="  — "+e.name,t.append(n)}))}async createTerm(e,t=0,r){try{await this.selector.store.setFilters({taxonomy:r,search:e,page:1,parent:0}),await new Promise((e=>setTimeout(e,100)));const n=Array.from(this.selector.store.data.values()).find((t=>t.taxonomy===r&&t.name.toLowerCase()===e.toLowerCase()));if(n)return this.createNew&&this.showTermSuggestions([n],!0),{success:!1,reason:"exists",term:n};const s=await fetch(`${jvbSettings.api}terms`,{method:"POST",headers:{"Content-Type":"application/json","X-WP-Nonce":window.auth.getNonce()},body:JSON.stringify({taxonomy:r,name:e,parent:t})});if(!s.ok)throw new Error(`Server error: ${s.status}`);return await s.json()}catch(e){throw console.error("Error creating term:",e),e}}showTermSuggestions(e,t=!1){const r=this.createNew.querySelector(".term-suggestions")||this.createSuggestionContainer();window.removeChildren(r);const n=document.createElement("h4");n.textContent=t?"This term already exists:":"Similar terms already exist:",r.appendChild(n);const s=document.createElement("ul");s.className="term-suggestion-list",e.forEach((e=>{const t=document.createElement("li"),r=this.createSuggestionButton(e);t.appendChild(r),s.appendChild(t)})),r.appendChild(s),r.hidden=!1}createSuggestionButton(e){const t=document.createElement("button");return t.type="button",t.className="use-existing-term",t.dataset.id=e.id,t.textContent=e.path||e.name,t.addEventListener("click",(()=>{this.selector.addSelectedTermToModal(e.id,e.name,e.path||e.name),this.createNew.open=!1;const t=this.createNew.querySelector(".term-suggestions");t&&(t.hidden=!0),this.clearForm()})),t}createSuggestionContainer(){const e=document.createElement("div");return e.className="term-suggestions",e.hidden=!0,this.createNew.querySelector("form").after(e),e}clearForm(){const e=this.form.querySelector('input[name="term_name"]');e&&(e.value="");const t=this.createNew.querySelector(".term-suggestions");t&&(t.hidden=!0)}destroy(){this.clickHandler&&document.removeEventListener("click",this.clickHandler);const e=this.createNew?.querySelector(".loading-message.create-term");e&&(e.hidden=!0);const t=this.createNew?.querySelector(".term-suggestions");t&&(t.hidden=!0)}};
\ No newline at end of file
diff --git a/assets/js/min/selector.min.js b/assets/js/min/selector.min.js
index bc8c622..74ecd45 100644
--- a/assets/js/min/selector.min.js
+++ b/assets/js/min/selector.min.js
@@ -1 +1 @@
-(()=>{class e{constructor(){this.a11y=window.jvbA11y,this.error=window.jvbError,this.index=-1,this.hasAutocomplete=!1,this.isInitializing=!0,this.taxonomiesToFetch=new Set,this.triggers=new Set([".taxonomy-toggle"]),this.subscribers=new Set;const e=window.jvbStore.register("taxonomies",{storeName:"terms",keyPath:"id",showLoading:!1,indexes:[{name:"taxonomy",keyPath:"taxonomy"},{name:"parent",keyPath:"parent"},{name:"slug",keyPath:"slug",unique:!0},{name:"count",keyPath:"count"}],endpoint:"terms",TTL:12e4,filters:{taxonomy:"",page:1,search:"",parent:0},required:"taxonomy",delayFetch:!0});this.store=e.terms,this.fields=new Map,this.selectedTerms=new Map,this.activeField=null,this.currentConfig=null,this.currentSingular=null,this.currentPlural=null,this.disabled=!1,this.searchHandler=null,this.autocompleteHandler=null,this.isAutocompleteActive=!1,this.init()}init(){this.initModal(),this.scanExistingFields(),this.initGlobalListeners(),this.hasAutocomplete&&window.jvbTaxCreator&&(this.creator=new window.jvbTaxCreator(this)),this.store.subscribe(this.handleStoreEvent.bind(this)),this.isInitializing=!1,this.batchFetchTaxonomies()}handleStoreEvent(e,t){switch(e){case"data-loaded":const e=this.store.filters.taxonomy;if(e?.includes(",")&&this.handleBatchDataLoaded(e,t),e){(e.includes(",")?e.split(",").map((e=>e.trim())):[e]).forEach((e=>{this.updateFieldsForTaxonomy(e)}))}if(this.modal?.open&&this.handleTermsLoaded(t),this.isAutocompleteActive&&this.activeField){const e=this.fields.get(this.activeField),i=t.data?.items||[],s=t.filters?.search||"";this.showAutocompleteResults(e,i,s),this.isAutocompleteActive=!1}break;case"filters-changed":this.modal?.open&&this.showLoading();break;case"fetch-error":this.isAutocompleteActive&&this.activeField&&(this.showAutocompleteError(this.activeField),this.isAutocompleteActive=!1),this.handleFetchError(t.error)}}handleTermsLoaded(e){this.hideLoading();const t=this.store.getFiltered(),i=this.store.lastResponse?.page||{},s=e.filters?.search&&e.filters.search.length>0,o=i.page>1;this.notify("terms-loaded",{terms:t,filters:e.filters}),0===t.length?(o||this.showEmptyState(s?"No results found.":"No items available."),this.observer.unobserve(this.ui.sentinel)):(this.renderTerms(t,o,s),i.has_more?this.observer.observe(this.ui.sentinel):this.observer.unobserve(this.ui.sentinel)),this.a11y?.announce(t.length,o)}handleFetchError(e){console.error("Taxonomy fetch error:",e),this.hideLoading(),this.error?.log?this.error.log(e,{component:"TaxonomySelector",action:"fetchTerms"},(()=>this.fetchCurrentTerms())):this.showEmptyState("Error loading terms. Please try again.")}updateFieldButtonState(e){const t=this.fields.get(e);if(!t)return;const i=Array.from(this.store.data.values()).some((e=>e.taxonomy===t.taxonomy));t.toggle&&(t.toggle.disabled=!i&&!t.canCreate,t.toggle.title=i?`Select ${this.getPlural(t.taxonomy)}`:`No ${this.getSingular(t.taxonomy)} available`)}updateFieldsForTaxonomy(e){this.getFieldsForTaxonomy(e).forEach((e=>{this.updateFieldButtonState(e.id)}))}getFieldsForTaxonomy(e){return Array.from(this.fields.values()).filter((t=>t.taxonomy===e))}scanExistingFields(e=null){e||(e=document.body);e.querySelectorAll(".field.taxonomy, .field.post").forEach((e=>{try{this.registerField(e)}catch(t){this.error.log(t,{component:"TaxonomySelector",action:"scanExistingFields",container:e.dataset.name})}}))}registerField(e,t={}){let i=e.querySelector("input[type=hidden]");if(!i)return!1;"fieldId"in e.dataset||(e.dataset.fieldId=this.createFieldId(e));let s=e.dataset.fieldId,o=Object.hasOwn(t,"button")?t.button:e.querySelector("button.taxonomy-toggle");Object.hasOwn(t,"buttonSelector")&&this.triggers.add(t.buttonSelector);let r={id:s,input:i,container:e,taxonomy:o.dataset.taxonomy,name:e.dataset.field,maxSelection:parseInt(o.dataset.max)||0,canSearch:"search"in o.dataset,hasAutocomplete:"autocomplete"in o.dataset,autocompleteDropdown:e.querySelector(".autocomplete-dropdown")??!1,canCreate:"creatable"in o.dataset,isRequired:"required"in o.dataset,selectedTerms:new Set,toggle:o,selectedContainer:Object.hasOwn(t,"selected")?t.selected:e.querySelector(".selected-items"),...t};!this.hasAutocomplete&&r.hasAutocomplete&&(this.hasAutocomplete=!0,this.initAutocomplete());const a=i.value.trim();if(""!==a){a.split(",").map((e=>parseInt(e.trim()))).filter((e=>!isNaN(e))).forEach((e=>r.selectedTerms.add(e)))}return Object.hasOwn(t,"selectedItems")&&t.selectedItems.forEach((e=>{r.selectedTerms.add(e)})),this.fields.set(s,r),this.isInitializing&&this.taxonomiesToFetch.add(r.taxonomy),r.selectedTerms.size>0&&this.initFieldDisplay(s),s}registerFilterButton(e,t={}){const i=this.createFieldId(e);e.dataset.fieldId=i,t.buttonSelector&&this.triggers.add(t.buttonSelector);const s={id:i,input:null,container:t.container||e.closest(".filters")||e.parentElement,taxonomy:e.dataset.taxonomy,name:`filter_${e.dataset.taxonomy}`,maxSelection:parseInt(e.dataset.max)||0,canSearch:"search"in e.dataset,hasAutocomplete:!1,canCreate:!1,isRequired:!1,selectedTerms:new Set(t.selectedItems||[]),toggle:e,selectedContainer:t.selected||null,isFilterMode:!0,...t};return this.fields.set(i,s),this.isInitializing?this.taxonomiesToFetch.add(s.taxonomy):this.store.setFilter("taxonomy",s.taxonomy),i}createFieldId(e){return this.index++,"selector-"+this.index}async initFieldDisplay(e){const t=this.fields.get(e);if(!t||0===t.selectedTerms.size)return;Array.from(t.selectedTerms).forEach((t=>{const i=this.store.get(t);i&&this.addTermToDisplay(e,i.id,i.name,i.path)}))}initModal(){this.modalID="dialog#jvb-selector",this.modal=document.querySelector(this.modalID),this.modal?(this.initModalElements(),this.modalInstance=new window.jvbModal(this.modal,{handleForm:!1,save:null,open:null}),this.modalInstance.subscribe(((e,t)=>{switch(e){case"modal-open":this.openModal(t);break;case"modal-close":this.closeModal(t)}}))):console.warn("Taxonomy selector modal not found")}initModalElements(){this.selectors={search:{input:"[type=search]",clear:".clear-search",container:".search-wrapper"},termsList:".items-container",termsWrap:".items-wrap",breadcrumbs:{nav:"nav.term-navigation",back:".back-to-parent"},loading:{loading:".loading",text:".loading span"},selectedTerms:".selected-items",sentinel:".scroll-sentinel",modal:{title:"#modal-title",content:".modal-content"},create:{details:".create-new-term",parent:"#select_parent",summary:".create-new-term summary",name:"#term_name",button:".submit-term",label:{name:"[for=term_name]",parent:"[for=select_parent]"}},favouriteTerms:".favourite-terms"},this.ui=window.uiFromSelectors(this.selectors),this.observer=new IntersectionObserver((e=>{e.forEach((e=>{e.isIntersecting&&this.loadMoreTerms()}))}),{root:this.ui.termsWrap,threshold:.5})}initGlobalListeners(){document.addEventListener("click",this.handleClick.bind(this)),document.addEventListener("change",this.handleChange.bind(this)),this.hasAutocomplete&&this.initAutocomplete()}initAutocomplete(){this.autocompleteHandler=e=>{window.debouncer.schedule("taxonomy-autocomplete",(()=>this.handleAutocomplete(e)),300)},document.addEventListener("input",this.autocompleteHandler),document.addEventListener("blur",this.cleanupAutocomplete.bind(this)),document.addEventListener("focus",(e=>{if(!("autocomplete"in e.target.dataset))return;const t=this.getFieldId(e.target),i=this.fields.get(t);i&&this.preloadTaxonomy(i.taxonomy)}),!0)}handleClick(e){const t=window.targetCheck(e,Array.from(this.triggers));if(t)return e.preventDefault(),void this.handleToggleClick(t);const i=window.targetCheck(e,"button.remove-item");if(i&&e.target.closest(".jvb-selector")){const e=this.getFieldId(i),t=i.closest(".selected-item").dataset.id;this.removeSelectedTerm(e,t)}else e.target.matches(".modal-close")?this.modalInstance&&this.modalInstance.handleClose():this.modal&&this.modal.contains(e.target)&&this.handleModalClick(e)}handleChange(e){if(window.targetCheck(e,".taxonomy.field, .post.field")&&"hidden"===e.target.type){const t=this.getFieldId(e.target);this.updateFieldFromInput(t)}else this.modal&&this.modal.contains(e.target)&&this.handleModalChange(e)}handleToggleClick(e){try{const t=this.getFieldId(e);if(!this.fields.get(t))return void console.error("Field not found for toggle:",t);this.setActiveField(t,!0)}catch(e){console.error("Error handling toggle click:",e),this.error?.log&&this.error.log(e,{component:"TaxonomySelector",action:"handleToggleClick"})}}setActiveField(e,t=!1){this.activeField=e,this.currentConfig=this.fields.get(e),this.currentSingular=this.getSingular(this.currentConfig.taxonomy),this.currentPlural=this.getPlural(this.currentConfig.taxonomy),t&&this.modalInstance.handleOpen(),this.store.setFilter("taxonomy",this.currentConfig.taxonomy),this.selectedTerms.clear(),this.currentConfig.selectedTerms.forEach((e=>{const t=this.store.get(e);t&&this.selectedTerms.set(e,{id:e,name:t.name,path:t.path})}))}handleModalClick(e){if(window.targetCheck(e,".remove-item")){let t=window.targetCheck(e,".selected-item");t&&this.removeSelectedTermFromModal(t.dataset.id)}else if(window.targetCheck(e,".back-to-parent"))this.navigateToParent();else if(window.targetCheck(e,".toggle-children")){let t=e.target.closest("li");this.navigateToChild(parseInt(t.dataset.id),t.querySelector(".term-name").textContent)}else if(window.targetCheck(e,".path-level")){let t=window.targetCheck(e,".path-level");this.navigateToPath(t)}}handleModalChange(e){if(window.targetCheck(e,this.modalID)&&"checkbox"===e.target.type){e.preventDefault(),e.stopPropagation();const t=parseInt(e.target.closest("li").dataset.id),i=e.target.closest("li").querySelector("label");e.target.checked?this.addSelectedTermToModal(t,i.title,i.dataset.path):this.removeSelectedTermFromModal(t)}}openForFilter(e,t,i=[]){const s=`filter-${e}-${Date.now()}`;this.fields.set(s,{id:s,input:null,container:null,taxonomy:e,name:`filter_${e}`,maxSelection:0,canSearch:!0,hasAutocomplete:!1,autocompleteDropdown:document.querySelector(".autocomplete-dropdown")??!1,canCreate:!1,isRequired:!1,selectedTerms:new Set(i),toggle:null,selectedContainer:null,isFilterMode:!0,filterCallback:t}),this.setActiveField(s,!0),this.modalInstance.handleOpen()}openModal(){this.currentConfig?(!this.creator&&this.currentConfig.canCreate&&"jvbTaxCreator"in window&&(this.creator=new window.jvbTaxCreator(this)),this.updateModalForTaxonomy(),this.updateModalSelections(),this.updateSelectionCount(),window.removeChildren(this.ui.termsList),this.showLoading()):console.error("No active field set")}updateSelectionCount(){if(!this.currentConfig)return;const e=this.selectedTerms.size,t=this.currentConfig.maxSelection,i=this.modal?.querySelector(".selection-count");i&&(i.textContent=t>0?`${e} of ${t} selected`:`${e} selected`)}getSingular(e){return jvbSettings.labels[e]?.single||e}getPlural(e){return jvbSettings.labels[e]?.plural||e}closeModal(){if(this.observer.unobserve(this.ui.sentinel),window.removeChildren(this.ui.termsList),this.notify("selected-terms",{terms:this.selectedTerms,taxonomy:this.currentConfig.taxonomy}),this.currentConfig?.isFilterMode){if(this.currentConfig.filterCallback){const e=Array.from(this.selectedTerms.keys());this.currentConfig.filterCallback(e,this.currentConfig.taxonomy)}}else this.activeField&&this.saveSelectionsToField(this.activeField);this.currentConfig?.canSearch&&this.searchHandler&&this.ui.search.input.removeEventListener("input",this.searchHandler),!this.hasAutocomplete&&this.creator&&delete this.creator,this.activeField=null,this.currentConfig=null}resetModalState(){this.disabled=!1,window.removeChildren(this.ui.termsList),window.removeChildren(this.ui.selectedTerms),this.ui.search.input.value="",window.removeChildren(this.ui.breadcrumbs.nav),this.ui.breadcrumbs.nav.appendChild(this.ui.breadcrumbs.back),this.ui.breadcrumbs.back.hidden=!0}updateModalForTaxonomy(){if(!this.currentConfig)return;this.ui.modal.title.textContent=`Select ${this.currentPlural}`,this.ui.search.container&&(this.ui.search.container.style.display=this.currentConfig.canSearch?"block":"none"),this.ui.create.details&&(this.ui.create.details.style.display=this.currentConfig.canCreate?"block":"none",this.ui.create.details.hidden=!this.currentConfig.canCreate,this.ui.create.summary&&(this.ui.create.summary.textContent=`Add new ${this.currentSingular}`),this.ui.create.label.name&&(this.ui.create.label.name.textContent=`Name this ${this.currentSingular}`),this.ui.create.label.parent&&(this.ui.create.label.parent.textContent="Nest it under"),this.ui.create.parent);const e=`Opened ${this.currentSingular} selection. Choose from checkboxes or search to filter results.`;this.a11y?.announce(e)}updateModalSelections(){window.removeChildren(this.ui.selectedTerms),this.selectedTerms.forEach(((e,t)=>{this.addTermToModalDisplay(t,e.name,e.path)})),this.checkSelectionLimits()}addSelectedTermToModal(e,t,i){this.selectedTerms.set(e,{id:e,name:t,path:i}),this.addTermToModalDisplay(e,t,i),this.checkSelectionLimits();const s=this.ui.termsList.querySelector(`input[value="${e}"]`);s&&(s.checked=!0)}removeSelectedTermFromModal(e){this.selectedTerms.delete(parseInt(e));const t=this.ui.selectedTerms.querySelector(`[data-id="${e}"]`);t&&t.remove();const i=this.ui.termsList.querySelector(`input[value="${e}"]`);i&&(i.checked=!1),this.checkSelectionLimits()}addTermToModalDisplay(e,t,i){const s=window.getTemplate("selectedTerm").cloneNode(!0);s.dataset.id=e,s.dataset.path=i,s.dataset.name=t,s.dataset.taxonomy=this.currentConfig.taxonomy,s.querySelector("span").textContent=i,s.querySelector("button").title=`Remove ${t}`,this.ui.selectedTerms.appendChild(s)}checkSelectionLimits(){this.currentConfig&&0!==this.currentConfig.maxSelection&&(this.disabled=this.selectedTerms.size>=this.currentConfig.maxSelection,this.setCheckboxes(this.disabled))}setCheckboxes(e){this.ui.termsList.querySelectorAll('input[type="checkbox"]').forEach((t=>{t.checked||(t.disabled=e)}))}saveSelectionsToField(e){const t=this.fields.get(e);if(!t)return;t.selectedTerms.clear(),window.removeChildren(t.selectedContainer),this.selectedTerms.forEach(((i,s)=>{t.selectedTerms.add(s),this.addTermToDisplay(e,s,i.name,i.path)}));const i=Array.from(t.selectedTerms);t.input.value=i.join(","),t.input.dispatchEvent(new Event("change",{bubbles:!0}))}removeSelectedTerm(e,t){const i=this.fields.get(e);if(!i)return;const s=parseInt(t);i.selectedTerms.delete(s);const o=i.selectedContainer.querySelector(`[data-id="${s}"]`);o&&o.remove();const r=Array.from(i.selectedTerms);i.input.value=r.join(","),i.input.dispatchEvent(new Event("change",{bubbles:!0}))}addTermToDisplay(e,t,i,s){const o=this.fields.get(e);if(!o||o.selectedContainer.querySelector(`[data-id="${t}"]`))return;const r=window.getTemplate("selectedTerm").cloneNode(!0);r.dataset.id=t,r.dataset.path=s,r.dataset.name=i,r.dataset.taxonomy=o.taxonomy,r.querySelector("span").textContent=s,r.querySelector("button").title=`Remove ${i}`,o.selectedContainer.appendChild(r)}updateFieldFromInput(e){const t=this.fields.get(e);if(!t)return;const i=t.input.value.trim();if(t.selectedTerms.clear(),window.removeChildren(t.selectedContainer),""!==i){i.split(",").map((e=>parseInt(e.trim()))).filter((e=>!isNaN(e))).forEach((e=>t.selectedTerms.add(e))),this.initFieldDisplay(e)}}handleSearch(e){const t=e.target.value.trim();this.searchHandler&&clearTimeout(this.searchHandler),this.searchHandler=setTimeout((()=>{this.store.setFilters({search:t,page:1,parent:t?0:this.store.filters.parent||0}),window.removeChildren(this.ui.termsList)}),300)}async handleAutocomplete(e){if(!("autocomplete"in e.target.dataset))return;const t=this.getFieldId(e.target),i=this.fields.get(t);if(!i)return;const s=e.target.value.trim();if(i.currentAutocompleteQuery=s,s.length<2)return i.autocompleteDropdown&&(i.autocompleteDropdown.hidden=!0),void(this.isAutocompleteActive=!1);this.activeField=t,this.isAutocompleteActive=!0,i.autocompleteDropdown&&(i.autocompleteDropdown.hidden=!1),this.store.setFilters({taxonomy:i.taxonomy,search:s,page:1})}cleanupAutocomplete(e){if(!("autocomplete"in e.target.dataset))return;const t=this.getFieldId(e.target);this.fields.get(t)&&this.creator&&delete this.creator}showAutocompleteError(e){const t=this.fields.get(e);if(!t)return;t.config.autocompleteDropdown||(t.config.autocompleteDropdown=t.element.querySelector(".autocomplete-dropdown"));const i=t.config.autocompleteDropdown;i&&(window.removeChildren(i),this.showEmptyState("Hmmm... something went wrong",i))}showAutocompleteResults(e,t,i){if(!e||!e.autocompleteDropdown)return;const s=e.autocompleteDropdown;window.removeChildren(s),0===t.length?this.showEmptyState("No items found.",s):t.forEach((t=>{const i=this.createAutocompleteTermElement(e,t);i&&s.appendChild(i)}));const o=e.currentAutocompleteQuery||i;if(e.canCreate&&o&&window.jvbTaxCreator){if(!t.find((e=>e.name.toLowerCase()===o.toLowerCase()))){const e=this.createNewTermOption(o);s.appendChild(e)}}s.hidden=!1}createNewTermOption(e){const t=document.createElement("button");return t.type="button",t.className="autocomplete-item create-term",t.dataset.query=e,t.innerHTML=`<strong>Create:</strong> "${e}"`,t}createAutocompleteTermElement(e,t){const i=document.createElement("button");return i.type="button",i.className="autocomplete-item",i.dataset.id=t.id,i.dataset.name=t.name,i.dataset.path=t.path||t.name,i.textContent=t.path||t.name,i.addEventListener("click",(()=>{e.selectedTerms.add(parseInt(t.id)),this.addTermToDisplay(e.id,t.id,t.name,t.path),e.input.value=Array.from(e.selectedTerms).join(","),e.input.dispatchEvent(new Event("change",{bubbles:!0})),e.autocompleteDropdown.hidden=!0;const i=e.container.querySelector("input[data-autocomplete]");i&&(i.value="")})),i}navigateToParent(){this.store.setFilters({parent:0,page:1}),window.removeChildren(this.ui.termsList),this.ui.breadcrumbs.back.hidden=!0}navigateToChild(e,t){this.store.setFilters({parent:e,page:1}),window.removeChildren(this.ui.termsList),this.updateBreadcrumbs(e,t),this.ui.breadcrumbs.back.hidden=!1}navigateToPath(e){const t=parseInt(e.dataset.id)||0;this.store.setFilters({parent:t,page:1}),window.removeChildren(this.ui.termsList),this.ui.breadcrumbs.back.hidden=0===t}loadMoreTerms(){const e=this.store.filters.page||1;this.store.setFilter("page",e+1)}renderTerms(e=null,t=!1,i=!1){if(e||(e=this.store.getFiltered()),t||window.removeChildren(this.ui.termsList),0===e.length)return void(t||this.showEmptyState());const s=this.store.filters.parent||0;this.ui.breadcrumbs.back.hidden=0===s;const o=document.createDocumentFragment();e.forEach((e=>{const t=this.createTermElement({id:parseInt(e.id),name:e.name,hasChildren:e.hasChildren,path:e.path||null,show:i});t&&o.appendChild(t)})),this.ui.termsList.appendChild(o)}createTermElement(e){if(!e||!e.name)return null;const t=window.getTemplate("termListItem").cloneNode(!0);t.dataset.id=e.id;const i=this.selectedTerms.has(e.id),s=t.querySelector("input"),o=t.querySelector("label"),r=t.querySelector("span, .term-name");if(s&&o&&r&&(s.id=`${this.currentConfig.container.id}${e.id}`,s.name=`${this.currentConfig.container.id}${this.currentConfig.taxonomy}-select`,s.value=e.id,s.disabled=!i&&this.disabled,s.checked=i,o.htmlFor=s.id,o.title=e.path||e.name,o.dataset.path=e.path,r.textContent=e.show?e.path:e.name),e.hasChildren){const i=window.getTemplate?window.getTemplate("termChildrenToggle"):this.createChildrenToggle();i&&(i.ariaLabel=`View sub-terms of ${e.name}`,t.appendChild(i))}return t}createChildrenToggle(){const e=document.createElement("button");return e.type="button",e.className="toggle-children",e.innerHTML="→",e}updateBreadcrumbs(e,t){const i=window.getTemplate("termBreadcrumb").cloneNode(!0);i.dataset.id=e,i.textContent=t,i.title=t;const s=this.ui.breadcrumbs.nav.querySelector(`[data-id="${e}"]`);if(s)for(;s.nextElementSibling;)s.nextElementSibling.remove();else this.ui.breadcrumbs.nav.appendChild(i)}showLoading(){this.ui.loading.loading.hidden=!1,this.modal.classList.add("loading");const e=this.store?.filters?.search||"",t=this.store?.filters?.parent||0;let i=""!==e?`searching for "${e}" items`:0===t?"loading items":"loading child items";window.typeLoop?this.stopTyping=window.typeLoop(this.ui.loading.text,i):this.ui.loading.text.textContent=i}hideLoading(){this.ui.loading.loading.hidden=!0,this.modal.classList.remove("loading"),this.stopTyping&&this.stopTyping()}showEmptyState(e="No items found.",t=null){t||(t=this.ui.termsList);const i=window.getTemplate("noResults").cloneNode(!0);e&&i.querySelector("span")&&(i.querySelector("span").textContent=e),t.appendChild(i)}getFieldId(e){if(e.dataset.fieldId)return e.dataset.fieldId;const t=e.closest("[data-field-id]");return t?t.dataset.fieldId:null}async batchFetchTaxonomies(){if(0===this.taxonomiesToFetch.size)return;const e=Array.from(this.taxonomiesToFetch);this.taxonomiesToFetch.clear(),this.store.setFilters({taxonomy:e.join(","),page:1,search:"",parent:0})}handleBatchDataLoaded(e,t){const i=e.split(",").map((e=>e.trim())),s=this.store.getStore();i.forEach((e=>{const t={taxonomy:e,page:1,search:"",parent:0},i=this.generateCacheKeyForFilters(t),o={key:i,items:Array.from(this.store.data.values()).filter((t=>t.taxonomy===e)).map((e=>e.id)),timestamp:Date.now(),endpoint:s.config.endpoint,filters:t};if(s.cache.set(i,o),s.db?.objectStoreNames.contains("cache")){s.db.transaction(["cache"],"readwrite").objectStore("cache").put(o)}this.updateFieldsForTaxonomy(e)})),this.fields.forEach(((e,t)=>{e.selectedTerms.size>0&&this.initFieldDisplay(t)}))}generateCacheKeyForFilters(e){const t=Object.keys(e).sort().reduce(((t,i)=>(t[i]=e[i],t)),{});return JSON.stringify(t)}async preloadTaxonomy(e){this.store.setFilters({taxonomy:e,page:1,search:"",parent:0})}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(e,t={}){this.subscribers.forEach((i=>{try{i(e,t)}catch(e){console.error("Subscriber error:",e)}}))}destroy(){document.removeEventListener("click",this.handleClick),document.removeEventListener("change",this.handleChange),this.observer?.disconnect(),this.store.destroy(),this.subscribers.clear(),this.fields.clear(),this.selectedTerms.clear()}}document.addEventListener("DOMContentLoaded",(function(){window.auth.subscribe((t=>{"auth-loaded"===t&&(window.jvbSelector=new e)}))}))})();
\ No newline at end of file
+(()=>{class e{constructor(){this.a11y=window.jvbA11y,this.error=window.jvbError,this.index=-1,this.isInitializing=!0,this.taxonomiesToFetch=new Set,this.subscribers=new Set;const e=window.jvbStore.register("taxonomies",{storeName:"terms",keyPath:"id",showLoading:!1,indexes:[{name:"taxonomy",keyPath:"taxonomy"},{name:"parent",keyPath:"parent"},{name:"slug",keyPath:"slug",unique:!0},{name:"count",keyPath:"count"}],endpoint:"terms",TTL:12e4,filters:{taxonomy:"",page:1,search:"",parent:0},required:"taxonomy",delayFetch:!0});this.store=e.terms,this.fields=new Map,this.selectedTerms=new Map,this.activeField=null,this.currentConfig=null,this.disabled=!1,this.searchContexts=new Map,this.init()}init(){this.initModal(),this.scanExistingFields(),this.initGlobalListeners(),this.needsCreator()&&window.jvbTaxCreator&&(this.creator=new window.jvbTaxCreator(this)),this.store.subscribe(this.handleStoreEvent.bind(this)),this.isInitializing=!1,this.batchFetchTaxonomies()}needsCreator(){return Array.from(this.fields.values()).some((e=>e.canCreate||e.hasAutocomplete))}handleStoreEvent(e,t){const i={"data-loaded":()=>this.handleDataLoaded(t),"filters-changed":()=>this.handleFiltersChanged(t),"fetch-error":()=>this.handleFetchError(t.error)};i[e]?.()}handleDataLoaded(e){const t=this.store.filters.taxonomy;if(t){(t.includes(",")?t.split(",").map((e=>e.trim())):[t]).forEach((e=>this.updateFieldsForTaxonomy(e)))}this.isInitializing&&this.fields.forEach(((e,t)=>{e.selectedTerms.size>0&&this.initFieldDisplay(t)})),this.renderSearchResults(e)}renderSearchResults(e){const t=this.getActiveSearchContext();"modal"===t?this.renderModalResults(e):"autocomplete"===t&&this.renderAutocompleteResults(e)}getActiveSearchContext(){return this.modal?.open?"modal":this.activeField&&this.searchContexts.has(this.activeField)?this.searchContexts.get(this.activeField):null}renderModalResults(e){this.hideLoading();const t=this.store.getFiltered(),i=this.store.lastResponse?.page||{},s=e.filters?.search?.length>0,o=i.page>1;this.notify("terms-loaded",{terms:t,filters:e.filters}),0===t.length?(o||this.showEmptyState(s?"No results found.":"No items available."),this.observer.unobserve(this.ui.sentinel)):(this.renderTerms(t,o,s),i.has_more?this.observer.observe(this.ui.sentinel):this.observer.unobserve(this.ui.sentinel)),this.a11y?.announce(t.length,o)}renderAutocompleteResults(e){const t=this.fields.get(this.activeField);if(!t?.autocompleteDropdown)return;const i=this.store.getFiltered(),s=e.filters?.search||"";this.showAutocompleteResults(t,i,s),this.searchContexts.delete(this.activeField)}handleFiltersChanged(e){this.modal?.open&&this.showLoading()}handleFetchError(e){this.hideLoading();"autocomplete"===this.getActiveSearchContext()?(this.showAutocompleteError(this.activeField),this.searchContexts.delete(this.activeField)):this.handleError(e,"fetch")}updateFieldsForTaxonomy(e){this.getFieldsForTaxonomy(e).forEach((e=>{this.updateFieldButtonState(e.id)}))}updateFieldButtonState(e){const t=this.fields.get(e);if(!t)return;const i=Array.from(this.store.data.values()).some((e=>e.taxonomy===t.taxonomy));t.toggle&&(t.toggle.disabled=!i&&!t.canCreate,t.toggle.title=i?`Select ${this.getLabel(t.taxonomy,"plural")}`:`No ${this.getLabel(t.taxonomy,"single")} available`)}getFieldsForTaxonomy(e){return Array.from(this.fields.values()).filter((t=>t.taxonomy===e))}scanExistingFields(e=document.body){e.querySelectorAll(".field.taxonomy, .field.post").forEach((e=>{try{this.registerField(e)}catch(t){this.handleError(t,"scanExistingFields",e.dataset.name)}}))}registerField(e){const t=e.querySelector("input[type=hidden]");if(!t)return!1;const i=this.createFieldId(e);e.dataset.fieldId=i;const s=e.querySelector("button.taxonomy-toggle"),o={id:i,input:t,container:e,taxonomy:s.dataset.taxonomy,name:e.dataset.field,maxSelection:parseInt(s.dataset.max)||0,canSearch:"search"in s.dataset,hasAutocomplete:"autocomplete"in s.dataset,autocompleteDropdown:e.querySelector(".autocomplete-dropdown")||null,canCreate:"creatable"in s.dataset,isRequired:"required"in s.dataset,selectedTerms:new Set,toggle:s,selectedContainer:e.querySelector(".selected-items")},a=t.value.trim();return a&&a.split(",").map((e=>parseInt(e.trim()))).filter((e=>!isNaN(e))).forEach((e=>o.selectedTerms.add(e))),this.fields.set(i,o),this.isInitializing&&this.taxonomiesToFetch.add(o.taxonomy),o.selectedTerms.size>0&&this.initFieldDisplay(i),i}createFieldId(e){return this.index++,"selector-"+this.index}async initFieldDisplay(e){const t=this.fields.get(e);t&&0!==t.selectedTerms.size&&Array.from(t.selectedTerms).forEach((t=>{const i=this.store.get(t);i&&this.addTermDisplay(t,i.name,i.path,"field",e)}))}initModal(){this.modal=document.querySelector("dialog#jvb-selector"),this.modal?(this.initModalElements(),this.modalInstance=new window.jvbModal(this.modal,{handleForm:!1}),this.modalInstance.subscribe((e=>{"modal-open"===e&&this.openModal(),"modal-close"===e&&this.closeModal()}))):console.warn("Taxonomy selector modal not found")}initModalElements(){this.ui=window.uiFromSelectors({search:{input:"[type=search]",container:".search-wrapper"},termsList:".items-container",termsWrap:".items-wrap",breadcrumbs:{nav:"nav.term-navigation",back:".back-to-parent"},loading:{loading:".loading",text:".loading span"},selectedTerms:".selected-items",sentinel:".scroll-sentinel",modal:{title:"#modal-title"},create:{details:".create-new-term",summary:".create-new-term summary",label:{name:"[for=term_name]",parent:"[for=select_parent]"}}}),this.observer=new IntersectionObserver((e=>{e.forEach((e=>{e.isIntersecting&&this.loadMoreTerms()}))}),{root:this.ui.termsWrap,threshold:.5})}initGlobalListeners(){document.addEventListener("click",this.handleClick.bind(this)),document.addEventListener("change",this.handleChange.bind(this)),document.addEventListener("input",this.handleInput.bind(this)),document.addEventListener("focus",this.handleFocus.bind(this),!0),document.addEventListener("blur",this.handleBlur.bind(this),!0)}handleClick(e){if(window.targetCheck(e,".taxonomy-toggle")){e.preventDefault();const t=this.getFieldId(e.target);return void(this.fields.get(t)&&this.setActiveField(t,!0))}const t=window.targetCheck(e,"button.remove-item");if(t&&e.target.closest(".jvb-selector")){const e=this.getFieldId(t),i=t.closest(".selected-item").dataset.id;this.removeSelectedTerm(e,i)}else e.target.matches(".modal-close")?this.modalInstance?.handleClose():this.modal?.contains(e.target)&&this.handleModalClick(e)}handleChange(e){if(window.targetCheck(e,".taxonomy.field, .post.field")&&"hidden"===e.target.type){const t=this.getFieldId(e.target);this.updateFieldFromInput(t)}else this.modal?.contains(e.target)&&this.handleModalChange(e)}handleInput(e){if(this.modal?.contains(e.target)&&"search"===e.target.type)this.performSearch(e.target.value.trim(),"modal");else if("autocomplete"in e.target.dataset){const t=this.getFieldId(e.target),i=this.fields.get(t);i?.hasAutocomplete&&this.performSearch(e.target.value.trim(),"autocomplete",t)}}handleFocus(e){if(!("autocomplete"in e.target.dataset))return;const t=this.getFieldId(e.target),i=this.fields.get(t);i?.hasAutocomplete&&this.preloadTaxonomy(i.taxonomy)}handleBlur(e){"autocomplete"in e.target.dataset&&setTimeout((()=>{const t=this.getFieldId(e.target),i=this.fields.get(t);i?.autocompleteDropdown&&(i.autocompleteDropdown.hidden=!0),this.searchContexts.delete(t)}),200)}performSearch(e,t="modal",i=null){const s="autocomplete"===t?this.fields.get(i):this.currentConfig;if(s){if("autocomplete"===t){if(s.currentAutocompleteQuery=e,e.length<2)return void(s.autocompleteDropdown&&(s.autocompleteDropdown.hidden=!0));this.searchContexts.set(i,"autocomplete"),this.activeField=i,s.autocompleteDropdown&&(s.autocompleteDropdown.hidden=!1)}window.debouncer.schedule(`taxonomy-search-${t}-${i||"modal"}`,(async()=>{await this.store.setFilters({taxonomy:s.taxonomy,search:e,page:1,parent:e?0:this.store.filters.parent||0}),"modal"===t&&window.removeChildren(this.ui.termsList)}),300)}}setActiveField(e,t=!1){this.activeField=e,this.currentConfig=this.fields.get(e),t&&this.modalInstance.handleOpen(),this.store.setFilter("taxonomy",this.currentConfig.taxonomy),this.selectedTerms.clear(),this.currentConfig.selectedTerms.forEach((e=>{const t=this.store.get(e);t&&this.selectedTerms.set(e,{id:e,name:t.name,path:t.path})}))}handleModalClick(e){if(window.targetCheck(e,".remove-item")){const t=window.targetCheck(e,".selected-item");t&&this.removeSelectedTermFromModal(t.dataset.id)}else if(window.targetCheck(e,".back-to-parent"))this.navigateToParent();else if(window.targetCheck(e,".toggle-children")){const t=e.target.closest("li");this.navigateToChild(parseInt(t.dataset.id),t.querySelector(".term-name").textContent)}else if(window.targetCheck(e,".path-level")){const t=window.targetCheck(e,".path-level");this.navigateToPath(parseInt(t.dataset.id)||0)}}handleModalChange(e){if("checkbox"!==e.target.type)return;e.preventDefault(),e.stopPropagation();const t=parseInt(e.target.closest("li").dataset.id),i=e.target.closest("li").querySelector("label");e.target.checked?this.addSelectedTermToModal(t,i.title,i.dataset.path):this.removeSelectedTermFromModal(t)}openModal(){this.currentConfig?(this.updateModalUI(),this.updateModalSelections(),window.removeChildren(this.ui.termsList),this.showLoading()):console.error("No active field set")}closeModal(){this.observer.unobserve(this.ui.sentinel),window.removeChildren(this.ui.termsList),this.notify("selected-terms",{terms:this.selectedTerms,taxonomy:this.currentConfig.taxonomy}),this.activeField&&this.saveSelectionsToField(this.activeField),this.activeField=null,this.currentConfig=null}updateModalUI(){const e=this.getLabel(this.currentConfig.taxonomy,"single"),t=this.getLabel(this.currentConfig.taxonomy,"plural");this.ui.modal.title.textContent=`Select ${t}`,this.ui.search.container&&(this.ui.search.container.style.display=this.currentConfig.canSearch?"block":"none"),this.ui.create.details&&(this.ui.create.details.style.display=this.currentConfig.canCreate?"block":"none",this.ui.create.details.hidden=!this.currentConfig.canCreate,this.ui.create.summary&&(this.ui.create.summary.textContent=`Add new ${e}`),this.ui.create.label.name&&(this.ui.create.label.name.textContent=`Name this ${e}`),this.ui.create.label.parent&&(this.ui.create.label.parent.textContent="Nest it under")),this.a11y?.announce(`Opened ${e} selection. Choose from checkboxes or search to filter results.`)}updateModalSelections(){window.removeChildren(this.ui.selectedTerms),this.selectedTerms.forEach(((e,t)=>{this.addTermDisplay(t,e.name,e.path,"modal")})),this.checkSelectionLimits()}addSelectedTermToModal(e,t,i){this.selectedTerms.set(e,{id:e,name:t,path:i}),this.addTermDisplay(e,t,i,"modal"),this.checkSelectionLimits();const s=this.ui.termsList.querySelector(`input[value="${e}"]`);s&&(s.checked=!0)}removeSelectedTermFromModal(e){this.selectedTerms.delete(parseInt(e));const t=this.ui.selectedTerms.querySelector(`[data-id="${e}"]`);t&&t.remove();const i=this.ui.termsList.querySelector(`input[value="${e}"]`);i&&(i.checked=!1),this.checkSelectionLimits()}checkSelectionLimits(){this.currentConfig&&0!==this.currentConfig.maxSelection&&(this.disabled=this.selectedTerms.size>=this.currentConfig.maxSelection,this.ui.termsList.querySelectorAll('input[type="checkbox"]').forEach((e=>{e.checked||(e.disabled=this.disabled)})))}saveSelectionsToField(e){const t=this.fields.get(e);t&&(t.selectedTerms.clear(),window.removeChildren(t.selectedContainer),this.selectedTerms.forEach(((i,s)=>{t.selectedTerms.add(s),this.addTermDisplay(s,i.name,i.path,"field",e)})),t.input.value=Array.from(t.selectedTerms).join(","),t.input.dispatchEvent(new Event("change",{bubbles:!0})))}addTermDisplay(e,t,i,s="field",o=null){const a="field"===s?this.fields.get(o):this.currentConfig,n="field"===s?a.selectedContainer:this.ui.selectedTerms;if(n.querySelector(`[data-id="${e}"]`))return;const r=window.getTemplate("selectedTerm");if(r.dataset.id=e,r.dataset.path=i,r.dataset.name=t,r.dataset.taxonomy=a.taxonomy,r.querySelector(".item-name").textContent=i,r.querySelector("button").title=`Remove ${t}`,n.appendChild(r),"modal"===s){const t=this.ui.termsList.querySelector(`input[value="${e}"]`);t&&(t.checked=!0)}}removeSelectedTerm(e,t){const i=this.fields.get(e);if(!i)return;i.selectedTerms.delete(parseInt(t));const s=i.selectedContainer.querySelector(`[data-id="${t}"]`);s&&s.remove(),i.input.value=Array.from(i.selectedTerms).join(","),i.input.dispatchEvent(new Event("change",{bubbles:!0}))}updateFieldFromInput(e){const t=this.fields.get(e);if(!t)return;const i=t.input.value.trim();t.selectedTerms.clear(),window.removeChildren(t.selectedContainer),i&&(i.split(",").map((e=>parseInt(e.trim()))).filter((e=>!isNaN(e))).forEach((e=>t.selectedTerms.add(e))),this.initFieldDisplay(e))}navigateToParent(){this.store.setFilters({parent:0,page:1}),window.removeChildren(this.ui.termsList),this.ui.breadcrumbs.back.hidden=!0}navigateToChild(e,t){this.store.setFilters({parent:e,page:1}),window.removeChildren(this.ui.termsList),this.updateBreadcrumbs(e,t),this.ui.breadcrumbs.back.hidden=!1}navigateToPath(e){this.store.setFilters({parent:e,page:1}),window.removeChildren(this.ui.termsList),this.ui.breadcrumbs.back.hidden=0===e}loadMoreTerms(){const e=this.store.filters.page||1;this.store.setFilter("page",e+1)}updateBreadcrumbs(e,t){const i=window.getTemplate("termBreadcrumb");i.dataset.id=e,i.textContent=t,i.title=t;const s=this.ui.breadcrumbs.nav.querySelector(`[data-id="${e}"]`);if(s)for(;s.nextElementSibling;)s.nextElementSibling.remove();else this.ui.breadcrumbs.nav.appendChild(i)}renderTerms(e=null,t=!1,i=!1){if(e||(e=this.store.getFiltered()),t||window.removeChildren(this.ui.termsList),0===e.length)return void(t||this.showEmptyState());const s=this.store.filters.parent||0;this.ui.breadcrumbs.back.hidden=0===s;const o=document.createDocumentFragment();e.forEach((e=>{const t=this.createTermElement({id:parseInt(e.id),name:e.name,hasChildren:e.hasChildren,path:e.path||null,show:i});t&&o.appendChild(t)})),this.ui.termsList.appendChild(o)}createTermElement(e){if(!e?.name)return null;const t=window.getTemplate("termListItem");t.dataset.id=e.id;const i=this.selectedTerms.has(e.id),s=t.querySelector("input"),o=t.querySelector("label"),a=t.querySelector(".term-name");if(s.id=`${this.currentConfig.container.id}${e.id}`,s.name=`${this.currentConfig.container.id}${this.currentConfig.taxonomy}-select`,s.value=e.id,s.disabled=!i&&this.disabled,s.checked=i,o.htmlFor=s.id,o.title=e.path||e.name,o.dataset.path=e.path,a.textContent=e.show?e.path:e.name,e.hasChildren){const i=window.getTemplate("termChildrenToggle");i.ariaLabel=`View sub-terms of ${e.name}`,t.appendChild(i)}return t}showAutocompleteResults(e,t,i){if(!e?.autocompleteDropdown)return;const s=e.autocompleteDropdown;if(window.removeChildren(s),0===t.length)this.showEmptyState("No items found.",s);else{const i=document.createDocumentFragment();t.forEach((t=>{const s=this.createAutocompleteItem(e,t);s&&i.appendChild(s)})),s.appendChild(i)}const o=e.currentAutocompleteQuery||i;if(e.canCreate&&o){t.find((e=>e.name.toLowerCase()===o.toLowerCase()))||s.appendChild(this.createAutocompleteCreateButton(o))}s.hidden=!1}createAutocompleteItem(e,t){const i=document.createElement("button");return i.type="button",i.className="autocomplete-item",i.dataset.id=t.id,i.dataset.name=t.name,i.dataset.path=t.path||t.name,i.textContent=t.path||t.name,i.addEventListener("click",(()=>{e.selectedTerms.add(parseInt(t.id)),this.addTermDisplay(t.id,t.name,t.path,"field",e.id),e.input.value=Array.from(e.selectedTerms).join(","),e.input.dispatchEvent(new Event("change",{bubbles:!0})),e.autocompleteDropdown.hidden=!0;const i=e.container.querySelector("input[data-autocomplete]");i&&(i.value="")})),i}createAutocompleteCreateButton(e){const t=document.createElement("button");t.type="button",t.className="autocomplete-item create-term",t.dataset.query=e;const i=document.createElement("strong");return i.textContent="Create: ",t.appendChild(i),t.appendChild(document.createTextNode(`"${e}"`)),t}showAutocompleteError(e){const t=this.fields.get(e);t?.autocompleteDropdown&&(window.removeChildren(t.autocompleteDropdown),this.showEmptyState("Hmmm... something went wrong",t.autocompleteDropdown))}showLoading(){this.ui.loading.loading.hidden=!1,this.modal.classList.add("loading");const e=this.store.filters.search||"",t=this.store.filters.parent||0,i=e?`searching for "${e}" items`:0===t?"loading items":"loading child items";window.typeLoop?this.stopTyping=window.typeLoop(this.ui.loading.text,i):this.ui.loading.text.textContent=i}hideLoading(){this.ui.loading.loading.hidden=!0,this.modal.classList.remove("loading"),this.stopTyping&&this.stopTyping()}showEmptyState(e="No items found.",t=null){t||(t=this.ui.termsList);const i=window.getTemplate("noResults"),s=i.querySelector("span");e&&s&&(s.textContent=e),t.appendChild(i)}getFieldId(e){if(e.dataset.fieldId)return e.dataset.fieldId;const t=e.closest("[data-field-id]");return t?.dataset.fieldId||null}getLabel(e,t="single"){return jvbSettings.labels[e]?.[t]||e}async batchFetchTaxonomies(){if(0===this.taxonomiesToFetch.size)return;const e=Array.from(this.taxonomiesToFetch);this.taxonomiesToFetch.clear(),this.store.setFilters({taxonomy:e.join(","),page:1,search:"",parent:0})}async preloadTaxonomy(e){await this.store.setFilters({taxonomy:e,page:1,search:"",parent:0})}handleError(e,t,i=null){console.error(`Taxonomy ${t} error:`,e,i),this.error?.log&&this.error.log(e,{component:"TaxonomySelector",action:t,detail:i}),this.modal?.open&&this.showEmptyState("Error loading. Please try again.")}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(e,t={}){this.subscribers.forEach((i=>{try{i(e,t)}catch(e){console.error("Subscriber error:",e)}}))}destroy(){document.removeEventListener("click",this.handleClick),document.removeEventListener("change",this.handleChange),document.removeEventListener("input",this.handleInput),document.removeEventListener("focus",this.handleFocus),document.removeEventListener("blur",this.handleBlur),this.observer?.disconnect(),this.store.destroy(),this.subscribers.clear(),this.fields.clear(),this.selectedTerms.clear(),this.searchContexts.clear()}}document.addEventListener("DOMContentLoaded",(()=>{window.auth.subscribe((t=>{"auth-loaded"===t&&(window.jvbSelector=new e)}))}))})();
\ No newline at end of file

--
Gitblit v1.10.0