Files
prestashop-entity-selector/sources/scss/components/_groups.scss
myprestarocks c6fd5cee13 refactor: entity selector full overhaul — Mar 2026
- Unified _setBadgeCount for ALL badge updates
- target-conditions-trait → entity-selector-trait
- target-* → es-* class rename (20+ classes)
- SCSS recompiled: zero duplicate selectors
- CSS transitions replace jQuery slideDown/slideUp
- Serialize cache, method swap cache
- Badge: no-matches gray, consistent hover, no blending
- Inline condition count always visible
- Preview popover refreshes in-place on sort change
- Categories add chips immediately
- Entity type icons on chips
- Consistent info_outline icons via buildHelpIcon
- Method dropdown text clipping fix (line-height)
- mpr-input-compact on all inputs
- Dropdown padding fixed in SCSS source
- Chips wrapper: same container always
- Reusable helpers: _buildEmptyState, _buildSearchBoxHtml, _buildInfoTooltip
- Asset path: uses $this->module->getPathUri() not reflection
- Debug logs removed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 18:05:43 +00:00

975 lines
24 KiB
SCSS

/**
* Groups Component
* Selection groups, include/exclude sections, method selectors
*/
@use '../variables' as *;
@use '../mixins' as *;
.entity-selector-trait {
// Group container
.es-group {
background: $es-white;
border: 1px solid $es-border-color;
border-radius: $es-radius-lg;
overflow: hidden;
}
// Group header
.es-group-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: $es-spacing-md;
padding: $es-spacing-sm $es-spacing-md;
background: $es-bg-header;
border-bottom: 1px solid $es-border-color;
}
.es-group-title {
display: flex;
align-items: center;
gap: $es-spacing-sm;
font-size: $es-font-size-sm;
font-weight: $es-font-weight-semibold;
color: $es-text-primary;
.group-number {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 20px;
height: 20px;
padding: 0 0.25rem;
background: $es-primary;
color: $es-white;
font-size: $es-font-size-xs;
font-weight: $es-font-weight-bold;
border-radius: $es-radius-full;
}
}
.es-group-actions {
display: flex;
align-items: center;
gap: $es-spacing-xs;
}
.group-action-btn {
@include button-reset;
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
color: $es-text-muted;
border-radius: $es-radius-sm;
transition: all $es-transition-fast;
&:hover {
background: $es-slate-200;
color: $es-text-secondary;
}
&.danger:hover {
background: $es-danger-light;
color: $es-danger;
}
}
// Group body
.es-group-body,
.group-body {
padding: $es-spacing-md;
}
// Include section
.include-section {
margin-bottom: $es-spacing-md;
}
.section-label {
display: flex;
align-items: center;
gap: $es-spacing-xs;
margin-bottom: $es-spacing-sm;
font-size: $es-font-size-xs;
font-weight: $es-font-weight-semibold;
text-transform: uppercase;
letter-spacing: 0.05em;
&.label-include {
color: $es-success-dark;
i {
color: $es-success;
}
}
&.label-exclude {
color: $es-danger;
i {
color: $es-danger;
}
}
}
// Method selector
.method-selector {
display: flex;
align-items: center;
gap: $es-spacing-sm;
margin-bottom: $es-spacing-sm;
}
.method-selector-wrapper {
flex: 1;
position: relative;
}
.method-select {
@include input-base;
padding-right: 2rem;
cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");
background-position: right 0.5rem center;
background-repeat: no-repeat;
background-size: 1.5em 1.5em;
}
.method-help-btn {
@include button-reset;
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
color: $es-text-muted;
border-radius: $es-radius-full;
transition: all $es-transition-fast;
&:hover {
background: $es-slate-100;
color: $es-primary;
}
}
// Value picker (search trigger)
.value-picker {
position: relative;
}
.value-picker-trigger {
@include button-reset;
display: flex;
align-items: center;
gap: $es-spacing-sm;
width: 100%;
padding: $es-spacing-sm $es-spacing-md;
background: $es-white;
border: 1px solid $es-border-color;
border-radius: $es-radius-md;
color: $es-text-muted;
font-size: $es-font-size-sm;
text-align: left;
transition: all $es-transition-fast;
&:hover {
border-color: $es-slate-300;
}
&:focus {
border-color: $es-primary;
@include focus-ring($es-primary);
}
i {
color: $es-text-light;
}
}
// Pattern input (text input for patterns)
.pattern-input-wrapper {
position: relative;
}
.pattern-input {
@include input-base;
font-family: monospace;
}
.pattern-add-btn {
@include button-reset;
position: absolute;
right: 0.25rem;
top: 50%;
transform: translateY(-50%);
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
color: $es-primary;
border-radius: $es-radius-sm;
transition: all $es-transition-fast;
&:hover {
background: $es-primary-light;
}
}
// Multi-range input (price ranges)
.multi-range-container {
display: flex;
flex-direction: column;
gap: $es-spacing-sm;
}
.range-row {
display: flex;
align-items: center;
gap: $es-spacing-sm;
}
.range-input {
@include input-base;
width: 100px;
text-align: center;
}
.range-separator {
color: $es-text-muted;
font-size: $es-font-size-sm;
}
.range-remove-btn {
@include button-reset;
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
color: $es-text-muted;
border-radius: $es-radius-sm;
transition: all $es-transition-fast;
&:hover {
background: $es-danger-light;
color: $es-danger;
}
}
.range-add-btn {
@include button-reset;
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.25rem 0.5rem;
color: $es-primary;
font-size: $es-font-size-xs;
font-weight: $es-font-weight-medium;
border-radius: $es-radius-sm;
transition: all $es-transition-fast;
&:hover {
background: $es-primary-light;
}
}
// Multi-select tiles (stock status, etc.)
.multi-select-tiles {
display: flex;
flex-wrap: wrap;
gap: $es-spacing-xs;
}
.multi-select-tile {
@include button-reset;
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.375rem 0.75rem;
background: $es-slate-100;
color: $es-text-secondary;
font-size: $es-font-size-xs;
font-weight: $es-font-weight-medium;
border: 1px solid transparent;
border-radius: $es-radius-full;
transition: all $es-transition-fast;
&:hover {
background: $es-slate-200;
}
&.selected {
background: $es-primary-light;
color: $es-primary;
border-color: $es-primary;
}
}
// Exclude section
.exclude-section {
margin-top: $es-spacing-md;
padding-top: $es-spacing-md;
border-top: 1px dashed $es-border-color;
}
// Legacy exclude-rows (if used elsewhere)
.exclude-rows {
display: flex;
flex-direction: column;
gap: $es-spacing-sm;
}
.exclude-row-content {
flex: 1;
}
.exclude-remove-btn {
@include button-reset;
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
color: $es-text-muted;
border-radius: $es-radius-sm;
flex-shrink: 0;
transition: all $es-transition-fast;
&:hover {
background: $es-danger-light;
color: $es-danger;
}
}
.add-exclude-btn {
@include button-reset;
display: inline-flex;
align-items: center;
gap: 0.25rem;
margin-top: $es-spacing-sm;
padding: 0.25rem 0.5rem;
color: $es-danger;
font-size: $es-font-size-xs;
font-weight: $es-font-weight-medium;
border: 1px dashed $es-danger;
border-radius: $es-radius-sm;
transition: all $es-transition-fast;
&:hover {
background: $es-danger-light;
}
}
// Add group button (used in block-footer)
.btn-add-group {
@include button-reset;
display: inline-flex;
align-items: center;
gap: 0.375rem;
padding: 0.5rem 0.875rem;
color: $es-primary;
font-size: $es-font-size-sm;
font-weight: $es-font-weight-medium;
background: rgba($es-primary, 0.05);
border: 1px dashed $es-primary;
border-radius: 0.375rem;
cursor: pointer;
transition: all $es-transition-fast;
&:hover {
background: rgba($es-primary, 0.1);
}
i {
font-size: 12px;
}
}
// Block footer
.block-footer {
display: flex;
align-items: center;
gap: $es-spacing-sm;
padding: $es-spacing-md;
border-top: 1px solid $es-border-color;
}
// Block body
.block-body {
padding: 0;
}
// Groups container
.groups-container {
padding: $es-spacing-md;
}
// Groups empty state
.groups-empty-state {
display: flex;
align-items: center;
justify-content: center;
padding: $es-spacing-xl;
color: $es-text-muted;
font-size: $es-font-size-sm;
}
// Selection group
.selection-group {
background: $es-white;
border: 1px solid $es-border-color;
border-radius: $es-radius-lg;
margin-bottom: $es-spacing-md;
&:last-child {
margin-bottom: 0;
}
&.collapsed {
.group-body {
display: none;
}
.group-collapse-toggle i {
transform: rotate(-90deg);
}
}
}
// Group header
.group-header {
display: flex;
align-items: center;
gap: $es-spacing-sm;
padding: $es-spacing-sm $es-spacing-md;
background: $es-bg-header;
border-bottom: 1px solid $es-border-color;
border-radius: $es-radius-lg $es-radius-lg 0 0;
cursor: pointer;
&.group-header-single {
padding: $es-spacing-xs $es-spacing-md;
background: transparent;
border-bottom: none;
}
}
.group-collapse-toggle {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
color: $es-text-muted;
i {
transition: transform $es-transition-fast;
}
}
.group-name-wrapper {
flex: 1;
display: flex;
align-items: center;
gap: $es-spacing-sm;
}
.group-name-input {
flex: 1;
padding: 0.25rem 0.5rem;
font-size: $es-font-size-sm;
font-weight: $es-font-weight-semibold;
color: $es-text-primary;
background: transparent;
border: 1px solid transparent;
border-radius: $es-radius-sm;
transition: all $es-transition-fast;
&:hover,
&:focus {
background: $es-white;
border-color: $es-border-color;
outline: none;
}
&::placeholder {
color: $es-text-muted;
font-weight: $es-font-weight-medium;
}
}
.group-count-badge {
@include count-badge($es-primary);
}
.btn-remove-group {
@include button-reset;
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
color: $es-text-muted;
border-radius: $es-radius-sm;
transition: all $es-transition-fast;
&:hover {
background: $es-danger-light;
color: $es-danger;
}
}
// Group include section - green accent to distinguish from exclude
.group-include {
margin-bottom: $es-spacing-md;
padding: $es-spacing-sm;
background: rgba($es-success, 0.03);
border: 1px solid rgba($es-success, 0.2);
border-radius: $es-radius-md;
}
.section-row {
display: flex;
flex-direction: column;
gap: $es-spacing-sm;
}
// Method selector wrapper (from PHP)
.method-selector-wrapper {
display: flex;
align-items: center;
gap: $es-spacing-sm;
}
.method-info-placeholder {
display: flex;
align-items: center;
min-width: 20px;
}
.include-method-select,
.exclude-method-select {
flex: 1;
@include input-base;
cursor: pointer;
}
// Lock indicator for method selector (when excludes are present)
.selector-locked {
.include-method-select {
opacity: 0.7;
cursor: not-allowed;
}
}
.lock-indicator {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
color: $es-warning;
cursor: help;
i {
font-size: 14px;
}
.mpr-tooltip {
display: none;
position: absolute;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
padding: $es-spacing-xs $es-spacing-sm;
background: $es-slate-800;
color: $es-white;
font-size: $es-font-size-xs;
font-weight: $es-font-weight-normal;
white-space: nowrap;
border-radius: $es-radius-sm;
z-index: 100;
}
&:hover .mpr-tooltip {
display: block;
}
}
// Group excludes section
.group-excludes {
margin-top: $es-spacing-md;
}
.except-separator {
display: flex;
align-items: center;
gap: $es-spacing-sm;
margin: 0 0 $es-spacing-sm 0;
// Lines on both sides
&::before,
&::after {
content: '';
flex: 1;
height: 1px;
background: rgba($es-danger, 0.3);
}
}
.except-label {
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.25rem 0.75rem;
background: $es-danger-light;
color: $es-danger;
font-size: $es-font-size-xs;
font-weight: $es-font-weight-semibold;
border-radius: $es-radius-full;
white-space: nowrap;
flex-shrink: 0;
i {
font-size: 10px;
}
}
.exclude-rows-container {
display: flex;
flex-direction: column;
gap: $es-spacing-sm;
}
.exclude-row {
display: flex;
flex-direction: column;
padding: $es-spacing-sm;
background: rgba($es-danger, 0.03);
border: 1px solid rgba($es-danger, 0.15);
border-radius: $es-radius-md;
// Value picker inside exclude row - full width
.value-picker {
width: 100%;
margin-top: $es-spacing-sm;
}
}
.exclude-header-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: $es-spacing-sm;
width: 100%;
.method-selector-wrapper {
flex: 1;
}
// Delete button at the far right
.btn-remove-exclude-row {
flex-shrink: 0;
margin-left: auto;
}
}
.btn-remove-exclude-row {
@include button-reset;
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
color: $es-text-muted;
border-radius: $es-radius-sm;
transition: all $es-transition-fast;
&:hover {
background: $es-danger-light;
color: $es-danger;
}
}
.btn-add-exclude,
.btn-add-another-exclude {
@include button-reset;
display: inline-flex;
align-items: center;
gap: 0.25rem;
margin-top: $es-spacing-sm;
padding: 0.375rem 0.625rem;
color: $es-danger;
font-size: $es-font-size-xs;
font-weight: $es-font-weight-medium;
background: transparent;
border: 1px dashed rgba($es-danger, 0.5);
border-radius: $es-radius-sm;
transition: all $es-transition-fast;
&:hover {
background: $es-danger-light;
border-color: $es-danger;
}
i {
font-size: 10px;
}
}
// Group modifiers (inline version from PHP)
// Uses negative margins to break out of .group-body padding
.group-modifiers {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: $es-spacing-md;
padding: $es-spacing-sm $es-spacing-md;
margin: $es-spacing-md (-$es-spacing-md) (-$es-spacing-md);
background: $es-slate-50;
border-top: 1px solid $es-border-color;
border-radius: 0 0 $es-radius-lg $es-radius-lg;
}
.modifier-inline {
display: flex;
align-items: center;
gap: 0.375rem;
}
// Common height for all modifier controls
$modifier-height: 26px;
.group-modifier-limit {
width: 50px;
max-width: 50px;
height: $modifier-height;
padding: 0 0.375rem;
font-size: $es-font-size-xs;
text-align: center;
border: 1px solid $es-border-color;
border-radius: $es-radius-sm;
&:focus {
border-color: $es-primary;
outline: none;
}
}
// Sort modifier - input group style (select + button glued together)
.modifier-sort {
gap: 0; // Remove gap to glue select + button together
.modifier-label {
margin-right: 0.375rem; // Keep space between label and input group
}
.group-modifier-sort {
height: $modifier-height;
padding: 0 0.5rem;
font-size: $es-font-size-xs;
border: 1px solid $es-border-color;
border-radius: $es-radius-sm 0 0 $es-radius-sm;
border-right: none;
cursor: pointer;
&:focus {
border-color: $es-primary;
outline: none;
position: relative;
z-index: 1;
}
}
.btn-sort-dir {
@include button-reset;
display: flex;
align-items: center;
justify-content: center;
width: $modifier-height;
height: $modifier-height;
color: $es-text-muted;
background: $es-slate-100;
border: 1px solid $es-border-color;
border-radius: 0 $es-radius-sm $es-radius-sm 0;
transition: all $es-transition-fast;
&:hover {
background: $es-slate-200;
color: $es-text-secondary;
}
i {
font-size: 11px;
}
}
}
// Fallback for elements outside .modifier-sort context
.group-modifier-sort {
height: $modifier-height;
padding: 0 0.5rem;
font-size: $es-font-size-xs;
border: 1px solid $es-border-color;
border-radius: $es-radius-sm;
cursor: pointer;
&:focus {
border-color: $es-primary;
outline: none;
}
}
.btn-sort-dir {
@include button-reset;
display: flex;
align-items: center;
justify-content: center;
width: $modifier-height;
height: $modifier-height;
color: $es-text-muted;
border: 1px solid $es-border-color;
border-radius: $es-radius-sm;
transition: all $es-transition-fast;
&:hover {
background: $es-slate-100;
color: $es-text-secondary;
}
}
.group-preview-badge {
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.25rem 0.5rem;
background: $es-slate-100;
color: $es-text-muted;
font-size: $es-font-size-xs;
font-weight: $es-font-weight-medium;
border-radius: $es-radius-full;
transition: all $es-transition-fast;
&.clickable {
cursor: pointer;
&:hover {
background: $es-primary-light;
color: $es-primary;
}
}
}
// OR separator between groups
.group-separator {
display: flex;
align-items: center;
justify-content: center;
padding: $es-spacing-sm 0;
color: $es-text-muted;
font-size: $es-font-size-xs;
font-weight: $es-font-weight-semibold;
text-transform: uppercase;
letter-spacing: 0.1em;
&::before,
&::after {
content: '';
flex: 1;
height: 1px;
background: $es-border-color;
margin: 0 $es-spacing-md;
}
}
// Group modifiers (limit, sort)
.group-modifiers {
display: flex;
flex-wrap: wrap;
gap: $es-spacing-md;
padding-top: $es-spacing-md;
margin-top: $es-spacing-md;
border-top: 1px solid $es-border-color;
}
.modifier-group {
display: flex;
align-items: center;
gap: $es-spacing-sm;
}
.modifier-label {
font-size: $es-font-size-xs;
font-weight: $es-font-weight-medium;
color: $es-text-muted;
white-space: nowrap;
}
.modifier-input {
@include input-base;
width: 80px;
padding: 0.25rem 0.5rem;
font-size: $es-font-size-xs;
}
.modifier-select {
@include input-base;
width: auto;
padding: 0.25rem 1.5rem 0.25rem 0.5rem;
font-size: $es-font-size-xs;
cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");
background-position: right 0.25rem center;
background-repeat: no-repeat;
background-size: 1.25em 1.25em;
}
// Condition match count badge
.condition-match-count {
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.125rem 0.375rem;
background: $es-slate-100;
color: $es-text-muted;
font-size: $es-font-size-xs;
font-weight: $es-font-weight-medium;
border-radius: $es-radius-full;
cursor: pointer;
transition: all $es-transition-fast;
&:hover {
background: $es-slate-200;
}
&.has-results {
background: $es-primary-light;
color: $es-primary;
}
// Country holidays variant - use calendar icon style
&.country-holidays {
background: rgba(139, 92, 246, 0.1);
color: #8b5cf6;
&:hover {
background: rgba(139, 92, 246, 0.2);
}
&.clickable {
background: rgba(139, 92, 246, 0.15);
}
}
i {
font-size: 10px;
}
}
}