mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-24 06:32:12 -03:00
feat(filter): add tag logic toggle (OR/AND) for include tags filtering
Add a segmented toggle in the Filter Panel to switch between 'Any' (OR) and 'All' (AND) logic when filtering by multiple include tags. Changes: - Backend: Add tag_logic field to FilterCriteria and ModelFilterSet - Backend: Parse tag_logic parameter in model handlers - Frontend: Add segmented toggle UI in filter panel header - Frontend: Add interaction logic and state management for tag logic - Add translations for all supported languages - Add comprehensive tests for the new feature Closes #802
This commit is contained in:
@@ -673,6 +673,57 @@
|
||||
|
||||
|
||||
|
||||
/* Tag Logic Toggle Styles */
|
||||
.filter-section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.filter-section-header h4 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tag-logic-toggle {
|
||||
display: flex;
|
||||
background-color: var(--lora-surface);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius-sm);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tag-logic-option {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 2px 8px;
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
color: var(--text-color);
|
||||
opacity: 0.7;
|
||||
transition: all 0.2s ease;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tag-logic-option:hover {
|
||||
opacity: 1;
|
||||
background-color: var(--lora-surface-hover);
|
||||
}
|
||||
|
||||
.tag-logic-option.active {
|
||||
background-color: var(--lora-accent);
|
||||
color: white;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tag-logic-option:first-child {
|
||||
border-right: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.tag-logic-option.active:first-child {
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
/* Mobile adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.search-options-panel,
|
||||
|
||||
@@ -924,6 +924,11 @@ export class BaseModelApiClient {
|
||||
params.append('model_type', type);
|
||||
});
|
||||
}
|
||||
|
||||
// Add tag logic parameter (any = OR, all = AND)
|
||||
if (pageState.filters.tagLogic) {
|
||||
params.append('tag_logic', pageState.filters.tagLogic);
|
||||
}
|
||||
}
|
||||
|
||||
this._addModelSpecificParams(params, pageState);
|
||||
|
||||
@@ -63,6 +63,9 @@ export class FilterManager {
|
||||
this.initializeLicenseFilters();
|
||||
}
|
||||
|
||||
// Initialize tag logic toggle
|
||||
this.initializeTagLogicToggle();
|
||||
|
||||
// Add click handler for filter button
|
||||
if (this.filterButton) {
|
||||
this.filterButton.addEventListener('click', () => {
|
||||
@@ -84,6 +87,45 @@ export class FilterManager {
|
||||
this.loadFiltersFromStorage();
|
||||
}
|
||||
|
||||
initializeTagLogicToggle() {
|
||||
const toggleContainer = document.getElementById('tagLogicToggle');
|
||||
if (!toggleContainer) return;
|
||||
|
||||
const options = toggleContainer.querySelectorAll('.tag-logic-option');
|
||||
|
||||
options.forEach(option => {
|
||||
option.addEventListener('click', async () => {
|
||||
const value = option.dataset.value;
|
||||
if (this.filters.tagLogic === value) return;
|
||||
|
||||
this.filters.tagLogic = value;
|
||||
this.updateTagLogicToggleUI();
|
||||
|
||||
// Auto-apply filter when logic changes
|
||||
await this.applyFilters(false);
|
||||
});
|
||||
});
|
||||
|
||||
// Set initial state
|
||||
this.updateTagLogicToggleUI();
|
||||
}
|
||||
|
||||
updateTagLogicToggleUI() {
|
||||
const toggleContainer = document.getElementById('tagLogicToggle');
|
||||
if (!toggleContainer) return;
|
||||
|
||||
const options = toggleContainer.querySelectorAll('.tag-logic-option');
|
||||
const currentLogic = this.filters.tagLogic || 'any';
|
||||
|
||||
options.forEach(option => {
|
||||
if (option.dataset.value === currentLogic) {
|
||||
option.classList.add('active');
|
||||
} else {
|
||||
option.classList.remove('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async loadTopTags() {
|
||||
try {
|
||||
// Show loading state
|
||||
@@ -573,9 +615,13 @@ export class FilterManager {
|
||||
baseModel: [],
|
||||
tags: {},
|
||||
license: {},
|
||||
modelTypes: []
|
||||
modelTypes: [],
|
||||
tagLogic: 'any'
|
||||
});
|
||||
|
||||
// Update tag logic toggle UI
|
||||
this.updateTagLogicToggleUI();
|
||||
|
||||
// Update state
|
||||
const pageState = getCurrentPageState();
|
||||
pageState.filters = this.cloneFilters();
|
||||
@@ -620,6 +666,7 @@ export class FilterManager {
|
||||
pageState.filters = this.cloneFilters();
|
||||
|
||||
this.updateTagSelections();
|
||||
this.updateTagLogicToggleUI();
|
||||
this.updateActiveFiltersCount();
|
||||
|
||||
if (this.hasActiveFilters()) {
|
||||
@@ -655,7 +702,8 @@ export class FilterManager {
|
||||
baseModel: Array.isArray(source.baseModel) ? [...source.baseModel] : [],
|
||||
tags: this.normalizeTagFilters(source.tags),
|
||||
license: this.shouldShowLicenseFilters() ? this.normalizeLicenseFilters(source.license) : {},
|
||||
modelTypes: this.normalizeModelTypeFilters(source.modelTypes)
|
||||
modelTypes: this.normalizeModelTypeFilters(source.modelTypes),
|
||||
tagLogic: source.tagLogic || 'any'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -737,7 +785,8 @@ export class FilterManager {
|
||||
baseModel: [...(this.filters.baseModel || [])],
|
||||
tags: { ...(this.filters.tags || {}) },
|
||||
license: { ...(this.filters.license || {}) },
|
||||
modelTypes: [...(this.filters.modelTypes || [])]
|
||||
modelTypes: [...(this.filters.modelTypes || [])],
|
||||
tagLogic: this.filters.tagLogic || 'any'
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user