Style: chips toolbar compact design with bootstrap specificity fix

- Add bootstrap specificity overrides for input/select in chips toolbar
- Use #content.bootstrap selector with doubled classes for specificity
- Reduce toolbar height with consistent padding (0.2rem vertical)
- Change border-radius from pill to subtle rounded corners
- Simplify input structure (embedded search icon via background)
- Align all toolbar elements (input, select, count, clear button)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-31 22:40:57 +01:00
parent acbcf55b1c
commit 603be61f71
10 changed files with 375 additions and 221 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1408,7 +1408,7 @@
});
// Save - commit pending selections to chips
this.$dropdown.on('click', '.btn-confirm-dropdown', function(e) {
this.$dropdown.on('click', '.btn-save', function(e) {
e.preventDefault();
if (self.pendingPicker && self.pendingSelections) {
@@ -1437,7 +1437,7 @@
});
// Cancel - discard pending selections (no changes to chips)
this.$dropdown.on('click', '.btn-cancel-dropdown', function(e) {
this.$dropdown.on('click', '.btn-cancel', function(e) {
e.preventDefault();
// Just discard pending - chips remain unchanged
@@ -2519,21 +2519,27 @@
// Results
html += '<div class="dropdown-results"></div>';
// Footer
// Footer - unified load more + actions
html += '<div class="dropdown-footer">';
html += '<div class="load-more-controls" style="display:none;">';
html += '<span class="load-more-label">' + (trans.load || 'Load') + '</span>';
// Left side: load more
html += '<div class="dropdown-footer-left" style="visibility:hidden;">';
html += '<span class="load-label">' + (trans.load || 'Load') + '</span>';
html += '<select class="load-more-select">';
html += '<option value="10">10</option>';
html += '<option value="20" selected>20</option>';
html += '<option value="20">20</option>';
html += '<option value="50">50</option>';
html += '<option value="100">100</option>';
html += '<option value="all">' + (trans.all || 'All') + '</option>';
html += '</select>';
html += '<span class="load-more-of">' + (trans.of || 'of') + ' <span class="remaining-count">0</span> ' + (trans.remaining || 'remaining') + '</span>';
html += '<button type="button" class="btn-load-more"><i class="icon-plus"></i></button>';
html += '<span class="remaining-text">' + (trans.of || 'of') + ' <strong class="remaining-count">0</strong> ' + (trans.remaining || 'remaining') + '</span>';
html += '</div>';
html += '<button type="button" class="btn-cancel-dropdown"><i class="icon-times"></i> ' + (trans.cancel || 'Cancel') + ' <kbd>Esc</kbd></button>';
html += '<button type="button" class="btn-confirm-dropdown"><i class="icon-check"></i> ' + (trans.save || 'Save') + ' <kbd>⏎</kbd></button>';
// Right side: action buttons
html += '<div class="dropdown-footer-right">';
html += '<button type="button" class="dropdown-action-btn btn-cancel"><i class="icon-times"></i> ' + (trans.cancel || 'Cancel') + ' <span class="btn-shortcut">Esc</span></button>';
html += '<button type="button" class="dropdown-action-btn btn-save"><i class="icon-check"></i> ' + (trans.save || 'Save') + ' <span class="btn-shortcut">⏎</span></button>';
html += '</div>';
html += '</div>';
html += '</div>';
@@ -3963,10 +3969,7 @@
// Create wrapper structure - integrated filter toolbar with sort
var wrapperHtml = '<div class="chips-wrapper">' +
'<div class="chips-toolbar">' +
'<span class="chips-filter-group">' +
'<i class="icon-search"></i>' +
'<input type="text" class="chips-search-input" placeholder="' + (trans.filter_selected || 'Filter selected') + '...">' +
'</span>' +
'<select class="chips-sort-select" title="' + (trans.sort || 'Sort') + '">' +
'<option value="added">' + (trans.sort_added || 'Order added') + '</option>' +
'<option value="name_asc">' + (trans.sort_name_asc || 'Name A-Z') + '</option>' +

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -185,10 +185,7 @@
// Create wrapper structure - integrated filter toolbar with sort
var wrapperHtml = '<div class="chips-wrapper">' +
'<div class="chips-toolbar">' +
'<span class="chips-filter-group">' +
'<i class="icon-search"></i>' +
'<input type="text" class="chips-search-input" placeholder="' + (trans.filter_selected || 'Filter selected') + '...">' +
'</span>' +
'<select class="chips-sort-select" title="' + (trans.sort || 'Sort') + '">' +
'<option value="added">' + (trans.sort_added || 'Order added') + '</option>' +
'<option value="name_asc">' + (trans.sort_name_asc || 'Name A-Z') + '</option>' +

View File

@@ -342,21 +342,27 @@
// Results
html += '<div class="dropdown-results"></div>';
// Footer
// Footer - unified load more + actions
html += '<div class="dropdown-footer">';
html += '<div class="load-more-controls" style="display:none;">';
html += '<span class="load-more-label">' + (trans.load || 'Load') + '</span>';
// Left side: load more
html += '<div class="dropdown-footer-left" style="visibility:hidden;">';
html += '<span class="load-label">' + (trans.load || 'Load') + '</span>';
html += '<select class="load-more-select">';
html += '<option value="10">10</option>';
html += '<option value="20" selected>20</option>';
html += '<option value="20">20</option>';
html += '<option value="50">50</option>';
html += '<option value="100">100</option>';
html += '<option value="all">' + (trans.all || 'All') + '</option>';
html += '</select>';
html += '<span class="load-more-of">' + (trans.of || 'of') + ' <span class="remaining-count">0</span> ' + (trans.remaining || 'remaining') + '</span>';
html += '<button type="button" class="btn-load-more"><i class="icon-plus"></i></button>';
html += '<span class="remaining-text">' + (trans.of || 'of') + ' <strong class="remaining-count">0</strong> ' + (trans.remaining || 'remaining') + '</span>';
html += '</div>';
html += '<button type="button" class="btn-cancel-dropdown"><i class="icon-times"></i> ' + (trans.cancel || 'Cancel') + ' <kbd>Esc</kbd></button>';
html += '<button type="button" class="btn-confirm-dropdown"><i class="icon-check"></i> ' + (trans.save || 'Save') + ' <kbd>⏎</kbd></button>';
// Right side: action buttons
html += '<div class="dropdown-footer-right">';
html += '<button type="button" class="dropdown-action-btn btn-cancel"><i class="icon-times"></i> ' + (trans.cancel || 'Cancel') + ' <span class="btn-shortcut">Esc</span></button>';
html += '<button type="button" class="dropdown-action-btn btn-save"><i class="icon-check"></i> ' + (trans.save || 'Save') + ' <span class="btn-shortcut">⏎</span></button>';
html += '</div>';
html += '</div>';
html += '</div>';

View File

@@ -1243,7 +1243,7 @@
});
// Save - commit pending selections to chips
this.$dropdown.on('click', '.btn-confirm-dropdown', function(e) {
this.$dropdown.on('click', '.btn-save', function(e) {
e.preventDefault();
if (self.pendingPicker && self.pendingSelections) {
@@ -1272,7 +1272,7 @@
});
// Cancel - discard pending selections (no changes to chips)
this.$dropdown.on('click', '.btn-cancel-dropdown', function(e) {
this.$dropdown.on('click', '.btn-cancel', function(e) {
e.preventDefault();
// Just discard pending - chips remain unchanged

View File

@@ -22,97 +22,75 @@
// Chips toolbar - integrated filter bar inside chips area
.chips-toolbar {
display: none; // Hidden by default, shown via JS when chips exist
display: none;
align-items: center;
gap: $es-spacing-xs;
flex-wrap: nowrap;
gap: $es-spacing-sm;
padding: $es-spacing-sm $es-spacing-md;
padding-bottom: 0; // No bottom padding - chips will provide spacing
background: transparent; // Same as chips area
padding-bottom: 0;
background: transparent;
&.has-chips {
display: flex;
}
}
// Filter input styled as a search chip - takes available space
.chips-filter-group {
display: flex;
align-items: center;
gap: 0.25rem;
flex: 1; // Take available space
min-width: 0; // Allow shrinking
padding: 0.25rem 0.5rem;
background: $es-white;
border: 1px solid $es-border-color;
border-radius: $es-radius-full;
transition: all $es-transition-fast;
// Filter input - takes available space, icon embedded as background
// Using [type="text"] for specificity over .bootstrap input[type="text"]
input[type="text"].chips-search-input {
all: unset;
display: block;
flex: 1 1 auto;
min-width: 80px;
width: auto;
height: auto;
padding: 0.2rem 0.5rem 0.2rem 1.5rem;
background: $es-white url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.35-4.35'/%3E%3C/svg%3E") no-repeat 0.375rem center;
background-size: 10px;
border: 1px solid $es-slate-300;
border-radius: $es-radius-sm;
font-size: 11px;
line-height: 1.4;
color: $es-text-primary;
box-sizing: border-box;
transition: all $es-transition-fast;
&:focus-within {
border-color: $es-primary;
box-shadow: 0 0 0 2px rgba($es-primary, 0.1);
&::placeholder {
color: $es-text-muted;
font-size: 11px;
}
&:focus {
outline: none;
border-color: $es-primary;
box-shadow: 0 0 0 2px rgba($es-primary, 0.1);
}
}
// Search icon inside the pill
> i {
color: $es-text-muted;
font-size: 12px;
flex-shrink: 0;
}
}
// Sort dropdown - compact, auto width
select.chips-sort-select {
all: unset;
flex: 0 0 auto;
padding: 0.2rem 1.25rem 0.2rem 0.5rem;
border: 1px solid $es-border-color;
border-radius: $es-radius-sm;
background: $es-white url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3E%3Cpath fill='%23666' d='M0 2l4 4 4-4z'/%3E%3C/svg%3E") no-repeat right 0.375rem center;
background-size: 8px;
font-size: 10px;
line-height: 1.4;
color: $es-text-secondary;
cursor: pointer;
box-sizing: border-box;
white-space: nowrap;
.chips-search-input,
input.chips-search-input,
input.chips-search-input[type="text"] {
flex: 1 !important;
min-width: 60px !important;
max-width: none !important; // No max - use available space
width: 100% !important;
height: auto !important;
padding: 0.125rem 0 !important;
border: none !important;
border-radius: 0 !important;
background: transparent !important;
font-size: $es-font-size-xs !important;
color: $es-text-primary;
box-shadow: none !important;
&:hover {
border-color: $es-primary;
}
&:focus {
outline: none !important;
border: none !important;
box-shadow: none !important;
}
&::placeholder {
color: $es-text-muted;
font-size: $es-font-size-xs;
}
}
// Sort dropdown for chips
.chips-sort-select,
select.chips-sort-select {
appearance: none;
padding: 0.25rem 1.5rem 0.25rem 0.5rem !important;
border: 1px solid $es-border-color !important;
border-radius: $es-radius-full !important;
background: $es-white url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3E%3Cpath fill='%23666' d='M0 2l4 4 4-4z'/%3E%3C/svg%3E") no-repeat right 0.5rem center !important;
background-size: 8px !important;
font-size: 11px !important;
color: $es-text-secondary;
cursor: pointer;
transition: all $es-transition-fast;
height: auto !important;
min-height: 0 !important;
line-height: 1.2 !important;
&:hover {
border-color: $es-primary !important;
}
&:focus {
outline: none !important;
border-color: $es-primary !important;
box-shadow: 0 0 0 2px rgba($es-primary, 0.1) !important;
&:focus {
outline: none;
border-color: $es-primary;
box-shadow: 0 0 0 2px rgba($es-primary, 0.1);
}
}
}
@@ -120,14 +98,16 @@
.chips-count {
display: inline-flex;
align-items: center;
flex-shrink: 0; // Don't shrink
gap: 0.125rem;
padding: 0.125rem 0.5rem;
padding: 0.2rem 0.5rem;
background: $es-slate-200;
color: $es-text-secondary;
font-size: 11px;
font-size: 10px;
font-weight: $es-font-weight-semibold;
border-radius: $es-radius-full;
border-radius: $es-radius-sm;
white-space: nowrap;
line-height: 1.4;
&.has-filter {
background: $es-cyan-100;
@@ -154,16 +134,19 @@
// Clear button - subtle, chip-like
.btn-chips-clear {
@include button-reset;
display: flex;
display: inline-flex;
align-items: center;
flex-shrink: 0; // Don't shrink
gap: 0.25rem;
padding: 0.25rem 0.5rem;
padding: 0.2rem 0.5rem;
color: $es-danger;
font-size: 11px;
font-size: 10px;
font-weight: $es-font-weight-medium;
background: rgba($es-danger, 0.1);
border-radius: $es-radius-full;
border-radius: $es-radius-sm;
transition: all $es-transition-fast;
white-space: nowrap; // Prevent text wrapping
line-height: 1.4;
&:hover {
background: $es-danger;
@@ -171,7 +154,8 @@
}
i {
font-size: 10px;
font-size: 9px;
flex-shrink: 0;
}
.clear-text {
@@ -730,3 +714,74 @@
gap: $es-spacing-xs;
}
}
// Bootstrap specificity overrides for chips toolbar form elements
// PrestaShop admin uses #content .mpr-config-form... with high specificity
// We need to match or exceed that specificity
#content.bootstrap,
#content .bootstrap,
.bootstrap #content {
.target-conditions-trait,
.entity-selector-trait {
.chips-wrapper .chips-toolbar {
// Double class for extra specificity
input[type="text"].chips-search-input.chips-search-input {
all: unset;
display: block;
flex: 1 1 auto;
min-width: 80px;
width: auto;
height: auto;
padding: 0.2rem 0.5rem 0.2rem 1.5rem;
background: $es-white url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.35-4.35'/%3E%3C/svg%3E") no-repeat 0.375rem center;
background-size: 10px;
border: 1px solid $es-slate-300;
border-radius: $es-radius-sm;
font-size: 11px;
line-height: 1.4;
color: $es-text-primary;
box-sizing: border-box;
transition: all $es-transition-fast;
&::placeholder {
color: $es-text-muted;
font-size: 11px;
}
&:focus {
outline: none;
border-color: $es-primary;
box-shadow: 0 0 0 2px rgba($es-primary, 0.1);
}
}
// Double class for extra specificity
select.chips-sort-select.chips-sort-select {
all: unset;
flex: 0 0 auto;
padding: 0.2rem 1.25rem 0.2rem 0.5rem;
border: 1px solid $es-border-color;
border-radius: $es-radius-sm;
background: $es-white url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3E%3Cpath fill='%23666' d='M0 2l4 4 4-4z'/%3E%3C/svg%3E") no-repeat right 0.375rem center;
background-size: 8px;
font-size: 10px;
line-height: 1.4;
color: $es-text-secondary;
cursor: pointer;
box-sizing: border-box;
white-space: nowrap;
height: auto;
&:hover {
border-color: $es-primary;
}
&:focus {
outline: none;
border-color: $es-primary;
box-shadow: 0 0 0 2px rgba($es-primary, 0.1);
}
}
}
}
}

View File

@@ -388,114 +388,140 @@
}
}
// Load more controls
.load-more-controls {
display: flex;
align-items: center;
justify-content: center;
gap: $es-spacing-sm;
padding: $es-spacing-sm $es-spacing-md;
border-top: 1px solid $es-border-color;
font-size: $es-font-size-xs;
color: $es-text-muted;
.remaining-count {
font-weight: $es-font-weight-semibold;
color: $es-text-secondary;
}
.load-more-select {
@include input-base;
padding: 0.25rem 0.5rem;
font-size: $es-font-size-xs;
min-width: 70px;
}
}
// Load more button
.dropdown-load-more {
display: flex;
justify-content: center;
padding: $es-spacing-md;
border-top: 1px solid $es-border-color;
.load-more-btn {
@include button-reset;
display: inline-flex;
align-items: center;
gap: $es-spacing-xs;
padding: $es-spacing-sm $es-spacing-md;
font-size: $es-font-size-sm;
font-weight: $es-font-weight-medium;
color: $es-primary;
background: $es-primary-light;
border-radius: $es-radius-md;
transition: all $es-transition-fast;
&:hover {
background: rgba($es-primary, 0.2);
}
&.loading {
opacity: 0.7;
cursor: wait;
}
}
}
// Dropdown footer
// Unified dropdown footer - combines load more and actions
.dropdown-footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: $es-spacing-sm;
gap: $es-spacing-md;
padding: $es-spacing-sm $es-spacing-md;
background: $es-bg-header;
background: $es-slate-50;
border-top: 1px solid $es-border-color;
border-radius: 0 0 $es-radius-lg $es-radius-lg;
}
.dropdown-selection-count {
// Left side: load more controls
.dropdown-footer-left {
display: flex;
align-items: center;
gap: $es-spacing-xs;
font-size: $es-font-size-xs;
color: $es-text-muted;
strong {
.load-label {
color: $es-text-muted;
}
.load-more-select,
select.load-more-select {
appearance: none;
padding: 0.25rem 1.5rem 0.25rem 0.5rem;
border: 1px solid $es-border-color;
border-radius: $es-radius-sm;
background: $es-white url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3E%3Cpath fill='%23666' d='M0 2l4 4 4-4z'/%3E%3C/svg%3E") no-repeat right 0.4rem center;
background-size: 8px;
font-size: $es-font-size-xs;
color: $es-text-secondary;
cursor: pointer;
min-width: 55px;
&:hover {
border-color: $es-primary;
}
&:focus {
outline: none;
border-color: $es-primary;
}
}
.remaining-text {
color: $es-text-muted;
strong {
color: $es-text-secondary;
font-weight: $es-font-weight-semibold;
}
}
.btn-load-all {
@include button-reset;
padding: 0.25rem 0.5rem;
font-size: $es-font-size-xs;
color: $es-primary;
background: transparent;
border-radius: $es-radius-sm;
transition: all $es-transition-fast;
&:hover {
background: $es-primary-light;
}
}
}
.dropdown-actions {
// Right side: action buttons
.dropdown-footer-right {
display: flex;
align-items: center;
gap: $es-spacing-sm;
}
.dropdown-action-btn {
@include button-reset;
padding: $es-spacing-sm $es-spacing-md;
font-size: $es-font-size-sm;
display: inline-flex;
align-items: center;
gap: 0.375rem;
padding: 0.375rem 0.75rem;
font-size: $es-font-size-xs;
font-weight: $es-font-weight-medium;
border-radius: $es-radius-md;
border-radius: $es-radius-sm;
transition: all $es-transition-fast;
.btn-shortcut {
font-size: 10px;
padding: 0.125rem 0.25rem;
background: rgba(0, 0, 0, 0.08);
border-radius: 3px;
font-weight: $es-font-weight-normal;
}
&.btn-cancel {
color: $es-text-secondary;
background: $es-slate-100;
background: $es-white;
border: 1px solid $es-border-color;
&:hover {
background: $es-slate-200;
background: $es-slate-100;
border-color: $es-border-dark;
}
}
&.btn-apply {
&.btn-apply,
&.btn-save {
color: $es-white;
background: $es-primary;
border: 1px solid $es-primary;
&:hover {
background: $es-primary-hover;
border-color: $es-primary-hover;
}
.btn-shortcut {
background: rgba(255, 255, 255, 0.2);
}
}
}
// Legacy support - hide old load more section when new footer exists
.dropdown-load-more {
display: none;
}
.load-more-controls {
display: none;
}
// Filter panel
.dropdown-filter-panel {
padding: $es-spacing-md;
@@ -1432,56 +1458,123 @@ body > .target-search-dropdown,
@include text-truncate;
}
// Dropdown footer
// Dropdown footer - unified structure
.dropdown-footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: $es-spacing-md;
padding: $es-spacing-sm $es-spacing-md;
background: $es-bg-header;
background: $es-slate-50;
border-top: 1px solid $es-border-color;
border-radius: 0 0 $es-radius-lg $es-radius-lg;
}
.dropdown-footer-left {
display: flex;
align-items: center;
gap: $es-spacing-xs;
font-size: $es-font-size-xs;
color: $es-text-muted;
.load-label {
color: $es-text-muted;
}
.load-more-select,
select.load-more-select {
appearance: none;
padding: 0.25rem 1.5rem 0.25rem 0.5rem;
border: 1px solid $es-border-color;
border-radius: $es-radius-sm;
background: $es-white url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3E%3Cpath fill='%23666' d='M0 2l4 4 4-4z'/%3E%3C/svg%3E") no-repeat right 0.4rem center;
background-size: 8px;
font-size: $es-font-size-xs;
color: $es-text-secondary;
cursor: pointer;
min-width: 55px;
&:hover {
border-color: $es-primary;
}
&:focus {
outline: none;
border-color: $es-primary;
}
}
.remaining-text {
color: $es-text-muted;
strong {
color: $es-text-secondary;
font-weight: $es-font-weight-semibold;
}
}
}
.dropdown-footer-right {
display: flex;
align-items: center;
gap: $es-spacing-sm;
}
.dropdown-action-btn {
@include button-reset;
display: inline-flex;
align-items: center;
gap: 0.375rem;
padding: 0.375rem 0.75rem;
font-size: $es-font-size-xs;
font-weight: $es-font-weight-medium;
border-radius: $es-radius-sm;
transition: all $es-transition-fast;
white-space: nowrap;
.btn-shortcut {
font-size: 10px;
padding: 0.125rem 0.25rem;
background: rgba(0, 0, 0, 0.08);
border-radius: 3px;
font-weight: $es-font-weight-normal;
}
&.btn-cancel {
color: $es-text-secondary;
background: $es-white;
border: 1px solid $es-border-color;
&:hover {
background: $es-slate-100;
border-color: $es-border-dark;
}
}
&.btn-apply,
&.btn-save {
color: $es-white;
background: $es-primary;
border: 1px solid $es-primary;
&:hover {
background: $es-primary-hover;
border-color: $es-primary-hover;
}
.btn-shortcut {
background: rgba(255, 255, 255, 0.2);
}
}
}
.dropdown-footer-info {
font-size: $es-font-size-xs;
color: $es-text-muted;
}
// Legacy button support
.btn-cancel-dropdown {
@include button-reset;
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.375rem 0.75rem;
font-size: $es-font-size-sm;
color: $es-text-secondary;
background: $es-white;
border: 1px solid $es-border-color;
border-radius: $es-radius-sm;
cursor: pointer;
&:hover {
background: $es-bg-hover;
color: $es-danger;
border-color: $es-danger;
}
i {
font-size: 10px;
}
kbd {
font-size: 0.65rem;
padding: 0.125rem 0.25rem;
background: $es-slate-100;
border-radius: 2px;
color: $es-text-muted;
margin-left: 0.25rem;
}
}
.btn-confirm-dropdown {
@include button-reset;
display: inline-flex;
align-items: center;