Add clickable preview popover to filter group toggles

- Add showFilterGroupPreviewPopover method in _preview.js
- Make toggle-count badges clickable with data attributes
- Add event binding for .toggle-count.clickable in _events.js
- Add hover/active/loading styles for clickable toggle-count
- Requires previewFilterGroupProducts AJAX handler in PHP backend

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-28 10:18:42 +00:00
parent aa9f28bb7e
commit 6ebf94e15b
9 changed files with 306 additions and 14 deletions

View File

@@ -231,6 +231,23 @@
}
});
// Filter group toggle count badge click for preview popover
$(document).on('click', '.filter-group-toggle .toggle-count.clickable', function(e) {
e.stopPropagation();
e.preventDefault();
var $badge = $(this);
var groupId = $badge.data('groupId');
var groupType = $badge.data('type');
var groupName = $badge.data('groupName');
if ($badge.hasClass('popover-open')) {
self.hidePreviewPopover();
} else {
self.showFilterGroupPreviewPopover($badge, groupId, groupType, groupName);
}
});
// Close popover when clicking outside
$(document).on('click', function(e) {
if (!$(e.target).closest('.target-preview-popover').length &&
@@ -238,7 +255,8 @@
!$(e.target).closest('.condition-match-count').length &&
!$(e.target).closest('.group-count-badge').length &&
!$(e.target).closest('.group-modifiers').length &&
!$(e.target).closest('.group-preview-badge').length) {
!$(e.target).closest('.group-preview-badge').length &&
!$(e.target).closest('.toggle-count.clickable').length) {
self.hidePreviewPopover();
}
});
@@ -3501,10 +3519,10 @@
if (this.filterableData.attributes && this.filterableData.attributes.length > 0) {
this.filterableData.attributes.forEach(function(group) {
var html = '<button type="button" class="filter-group-toggle" data-group-id="' + group.id + '" data-type="attribute">';
var html = '<button type="button" class="filter-group-toggle" data-group-id="' + group.id + '" data-type="attribute" data-group-name="' + self.escapeAttr(group.name) + '">';
html += '<span class="toggle-name">' + group.name + '</span>';
if (group.count !== undefined) {
html += '<span class="toggle-count"><i class="icon-eye"></i> (' + group.count + ')</span>';
html += '<span class="toggle-count clickable" data-group-id="' + group.id + '" data-type="attribute" data-group-name="' + self.escapeAttr(group.name) + '"><i class="icon-eye"></i> ' + group.count + '</span>';
}
html += '</button>';
$attrContainer.append(html);
@@ -3518,10 +3536,10 @@
if (this.filterableData.features && this.filterableData.features.length > 0) {
this.filterableData.features.forEach(function(group) {
var html = '<button type="button" class="filter-group-toggle" data-group-id="' + group.id + '" data-type="feature">';
var html = '<button type="button" class="filter-group-toggle" data-group-id="' + group.id + '" data-type="feature" data-group-name="' + self.escapeAttr(group.name) + '">';
html += '<span class="toggle-name">' + group.name + '</span>';
if (group.count !== undefined) {
html += '<span class="toggle-count"><i class="icon-eye"></i> (' + group.count + ')</span>';
html += '<span class="toggle-count clickable" data-group-id="' + group.id + '" data-type="feature" data-group-name="' + self.escapeAttr(group.name) + '"><i class="icon-eye"></i> ' + group.count + '</span>';
}
html += '</button>';
$featContainer.append(html);
@@ -7580,6 +7598,118 @@
price = parseFloat(price) || 0;
}
return price.toFixed(2) + ' €';
},
/**
* Show preview popover for filter group toggle (attribute/feature groups)
*/
showFilterGroupPreviewPopover: function($badge, groupId, groupType, groupName) {
var self = this;
this.hidePreviewPopover();
$badge.addClass('popover-open loading');
this.$activeBadge = $badge;
var trans = this.config.trans || {};
var entityLabelPlural = 'products';
// Fetch products matching this attribute/feature group
$.ajax({
url: this.config.ajaxUrl,
type: 'POST',
dataType: 'json',
data: {
ajax: 1,
action: 'previewFilterGroupProducts',
trait: 'EntitySelector',
group_id: groupId,
group_type: groupType,
limit: 10
},
success: function(response) {
$badge.removeClass('loading');
if (response.success) {
var items = response.items || [];
var totalCount = response.count || 0;
var hasMore = response.hasMore || false;
self.showFilterGroupItemsPopover($badge, items, totalCount, hasMore, entityLabelPlural, groupName, groupType);
} else {
$badge.removeClass('popover-open');
self.$activeBadge = null;
}
},
error: function() {
$badge.removeClass('loading popover-open');
self.$activeBadge = null;
}
});
},
/**
* Show popover for filter group preview items
*/
showFilterGroupItemsPopover: function($badge, items, totalCount, hasMore, entityLabel, groupName, groupType) {
var self = this;
var trans = this.config.trans || {};
var typeLabel = groupType === 'attribute' ? (trans.attribute || 'Attribute') : (trans.feature || 'Feature');
var html = '<div class="target-preview-popover preview-type-filter-group">';
html += '<div class="preview-header">';
html += '<span class="preview-count">' + totalCount + ' ' + entityLabel + '</span>';
html += '<button type="button" class="preview-close"><i class="icon-times"></i></button>';
html += '</div>';
if (items.length > 0) {
html += '<div class="preview-list">';
html += this.renderPreviewItems(items);
html += '</div>';
if (hasMore) {
var remaining = totalCount - items.length;
html += '<div class="preview-footer">';
html += '<span class="preview-more-info">+ ' + remaining + ' ' + (trans.more || 'more') + '</span>';
html += '</div>';
}
} else {
html += '<div class="preview-empty">' + (trans.no_preview || 'No items to preview') + '</div>';
}
html += '</div>';
var $popover = $(html);
$('body').append($popover);
$popover.find('.preview-close').on('click', function() {
self.hidePreviewPopover();
});
// Position popover below badge
var badgeOffset = $badge.offset();
var badgeHeight = $badge.outerHeight();
var badgeWidth = $badge.outerWidth();
var popoverWidth = $popover.outerWidth();
var leftPos = badgeOffset.left + (badgeWidth / 2) - (popoverWidth / 2);
var minLeft = 10;
var maxLeft = $(window).width() - popoverWidth - 10;
leftPos = Math.max(minLeft, Math.min(leftPos, maxLeft));
var topPos = badgeOffset.top + badgeHeight + 8;
$popover.css({
position: 'absolute',
top: topPos,
left: leftPos,
zIndex: 10000
});
$popover.addClass('show');
this.$previewPopover = $popover;
}
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long