Skip to content

Commit 273a002

Browse files
committed
Instant search enhancements:
* :New option to display product thumbnails (NOTE: the feature can be enabled via Configuration > Settings > Catalog Settings > Search) * The first item is not autoselected anymore, so that ENTER press navigates to the search result instead of the product detail page * NOTE: if thumbnails are enabled, the product short description is also displayed * NOTE: you should reduce the number of displayed items if thumbnails are activated (was 16, should be 10, fits better)
1 parent 9bf20e7 commit 273a002

12 files changed

Lines changed: 141 additions & 35 deletions

File tree

changelog.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
# Release Notes
22

3-
## SmartStore.NET 2.5
3+
## SmartStore.NET 2.2.1
44

55
### New Features
66
* #582 GMC feed: Option to include\exclude a product
77
* #652 Web API: Support for file upload and multipart mime
8+
* New option to display product thumbnails in instant search (NOTE: the feature can be enabled via _Configuration > Settings > Catalog Settings > Search_)
89

9-
## SmartStore.NET 2.2
10+
## SmartStore.NET 2.2.0
1011

1112
### New Features
1213
* New mobile theme: _MobileLight_ (a light variant of the default mobile theme)

src/Libraries/SmartStore.Core/Domain/Catalog/CatalogSettings.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ public CatalogSettings()
2828
MaxFilterItemsToDisplay = 4;
2929
ShowSubcategoriesAboveProductLists = true;
3030
ProductSearchAutoCompleteEnabled = true;
31-
ProductSearchAutoCompleteNumberOfProducts = 16;
31+
ShowProductImagesInSearchAutoComplete = true;
32+
ProductSearchAutoCompleteNumberOfProducts = 10;
3233
ProductSearchTermMinimumLength = 3;
3334
NumberOfBestsellersOnHomepage = 6;
3435
SearchPageProductsPerPage = 6;

src/Presentation/SmartStore.Web/Administration/Infrastructure/AutoMapperStartupTask.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,6 @@ public void Execute()
606606
.ForMember(dest => dest.DisplayTierPricesWithDiscounts, mo => mo.Ignore())
607607
.ForMember(dest => dest.FileUploadMaximumSizeBytes, mo => mo.Ignore())
608608
.ForMember(dest => dest.FileUploadAllowedExtensions, mo => mo.Ignore())
609-
.ForMember(dest => dest.ShowProductImagesInSearchAutoComplete, mo => mo.Ignore())
610609
.ForMember(dest => dest.ProductSearchPageSize, mo => mo.Ignore())
611610
.ForMember(dest => dest.ManufacturersBlockItemsToDisplay, mo => mo.Ignore())
612611
.ForMember(dest => dest.MostRecentlyUsedCategoriesMaxSize, mo => mo.Ignore())

src/Presentation/SmartStore.Web/Administration/Models/Settings/CatalogSettingsModel.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,9 @@ public CatalogSettingsModel()
244244
[SmartResourceDisplayName("Admin.Configuration.Settings.Catalog.ProductSearchAutoCompleteEnabled")]
245245
public bool ProductSearchAutoCompleteEnabled { get; set; }
246246

247+
[SmartResourceDisplayName("Admin.Configuration.Settings.Catalog.ShowProductImagesInSearchAutoComplete")]
248+
public bool ShowProductImagesInSearchAutoComplete { get; set; }
249+
247250
[SmartResourceDisplayName("Admin.Configuration.Settings.Catalog.ProductSearchAutoCompleteNumberOfProducts")]
248251
public int ProductSearchAutoCompleteNumberOfProducts { get; set; }
249252

src/Presentation/SmartStore.Web/Administration/Views/Setting/Catalog.cshtml

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,12 @@
117117
118118
function toggleProductSearchAutoComplete() {
119119
if ($('#@Html.FieldIdFor(model => model.ProductSearchAutoCompleteEnabled)').is(':checked')) {
120-
$('#pnlProductSearchAutoCompleteNumberOfProducts').show();
120+
$('#pnlProductSearchAutoCompleteNumberOfProducts').show();
121+
$('#pnlShowProductImagesInSearchAutoComplete').show();
121122
}
122123
else {
123-
$('#pnlProductSearchAutoCompleteNumberOfProducts').hide();
124+
$('#pnlProductSearchAutoCompleteNumberOfProducts').hide();
125+
$('#pnlShowProductImagesInSearchAutoComplete').hide();
124126
}
125127
}
126128
@@ -797,6 +799,20 @@
797799
@Html.ValidationMessageFor(model => model.ProductSearchPageSizeOptions)
798800
</td>
799801
</tr>
802+
<tr>
803+
<td class="adminTitle">
804+
@Html.SmartLabelFor(model => model.SuppressSkuSearch)
805+
</td>
806+
<td class="adminData">
807+
@Html.SettingEditorFor(model => model.SuppressSkuSearch)
808+
@Html.ValidationMessageFor(model => model.SuppressSkuSearch)
809+
</td>
810+
</tr>
811+
<tr>
812+
<td colspan="2">
813+
<hr />
814+
</td>
815+
</tr>
800816
<tr>
801817
<td class="adminTitle">
802818
@Html.SmartLabelFor(model => model.ProductSearchAutoCompleteEnabled)
@@ -806,6 +822,15 @@
806822
@Html.ValidationMessageFor(model => model.ProductSearchAutoCompleteEnabled)
807823
</td>
808824
</tr>
825+
<tr id="pnlShowProductImagesInSearchAutoComplete">
826+
<td class="adminTitle">
827+
@Html.SmartLabelFor(model => model.ShowProductImagesInSearchAutoComplete)
828+
</td>
829+
<td class="adminData">
830+
@Html.SettingEditorFor(model => model.ShowProductImagesInSearchAutoComplete)
831+
@Html.ValidationMessageFor(model => model.ShowProductImagesInSearchAutoComplete)
832+
</td>
833+
</tr>
809834
<tr id="pnlProductSearchAutoCompleteNumberOfProducts">
810835
<td class="adminTitle">
811836
@Html.SmartLabelFor(model => model.ProductSearchAutoCompleteNumberOfProducts)
@@ -815,15 +840,6 @@
815840
@Html.ValidationMessageFor(model => model.ProductSearchAutoCompleteNumberOfProducts)
816841
</td>
817842
</tr>
818-
<tr>
819-
<td class="adminTitle">
820-
@Html.SmartLabelFor(model => model.SuppressSkuSearch)
821-
</td>
822-
<td class="adminData">
823-
@Html.SettingEditorFor(model => model.SuppressSkuSearch)
824-
@Html.ValidationMessageFor(model => model.SuppressSkuSearch)
825-
</td>
826-
</tr>
827843
</table>
828844
}
829845
}

src/Presentation/SmartStore.Web/Content/bootstrap/js/bootstrap-typeahead.js

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
constructor: Typeahead
4747

4848
, select: function () {
49-
var val = this.$menu.find('.active').attr('data-value')
49+
var val = this.$menu.find('.active').attr('data-value')
50+
5051
// codehint: deleted ('change' results in js error)
5152
/*this.$element
5253
.val(this.updater(val))
@@ -66,10 +67,12 @@
6667
var pos = $.extend({}, this.$element.offset(), {
6768
height: this.$element[0].offsetHeight
6869
})
69-
70+
7071
this.$menu.css({
7172
top: pos.top + pos.height
7273
, left: pos.left
74+
, boxSizing: "border-box" // codehint: added
75+
, minWidth: this.$element.outerWidth(false) + "px" // codehint: added
7376
})
7477

7578
this.$menu.show()
@@ -135,8 +138,8 @@
135138
, highlighter: function (item) {
136139
// codehint: sm-edit
137140
// Original:
138-
// var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
139-
141+
// var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
142+
140143
// codehint: the above query transforms the whole query to a regex pattern, thus only highlighting 'exact' matches.
141144
// But we also want to highlight 'far' words. So we create a pattern like "word1|word2|wordn..." (instead of "word1\ word2\ wordn...")
142145
var query = _.map(_.str.words(this.query), function (val, i) { return val.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&') }).join("|");
@@ -153,8 +156,12 @@
153156
i.find('a').html(that.highlighter(item))
154157
return i[0]
155158
})
159+
160+
// codehint: edit
161+
if (this.options.autoSelectFirstItem) {
162+
items.first().addClass('active')
163+
}
156164

157-
items.first().addClass('active')
158165
this.$menu.html(items)
159166
return this
160167
}
@@ -213,11 +220,16 @@
213220

214221
switch(e.keyCode) {
215222
case 9: // tab
216-
case 13: // enter
217223
case 27: // escape
218224
e.preventDefault()
219225
break
220226

227+
case 13: // enter
228+
if (this.$menu.find('.active').length > 0) {
229+
e.preventDefault();
230+
}
231+
break
232+
221233
case 38: // up arrow
222234
e.preventDefault()
223235
this.prev()
@@ -330,6 +342,8 @@
330342
, minLength: 1
331343
// codehint: sm-add (throttled KeyPress)
332344
, keyUpDelay: 0
345+
// codehint: sm-add
346+
, autoSelectFirstItem: true
333347
}
334348

335349
$.fn.typeahead.Constructor = Typeahead

src/Presentation/SmartStore.Web/Controllers/CatalogController.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,7 +1223,7 @@ public ActionResult Search(SearchModel model, SearchPagingFilteringModel command
12231223
[ChildActionOnly]
12241224
public ActionResult SearchBox()
12251225
{
1226-
var model = new SearchBoxModel()
1226+
var model = new SearchBoxModel
12271227
{
12281228
AutoCompleteEnabled = _catalogSettings.ProductSearchAutoCompleteEnabled,
12291229
ShowProductImagesInSearchAutoComplete = _catalogSettings.ShowProductImagesInSearchAutoComplete,
@@ -1237,16 +1237,15 @@ public ActionResult SearchTermAutoComplete(string term)
12371237
if (String.IsNullOrWhiteSpace(term) || term.Length < _catalogSettings.ProductSearchTermMinimumLength)
12381238
return Content("");
12391239

1240-
//products
1241-
var productNumber = _catalogSettings.ProductSearchAutoCompleteNumberOfProducts > 0 ?
1242-
_catalogSettings.ProductSearchAutoCompleteNumberOfProducts : 16;
1240+
// products
1241+
var pageSize = _catalogSettings.ProductSearchAutoCompleteNumberOfProducts > 0 ? _catalogSettings.ProductSearchAutoCompleteNumberOfProducts : 10;
12431242

12441243
var ctx = new ProductSearchContext();
12451244
ctx.LanguageId = _services.WorkContext.WorkingLanguage.Id;
12461245
ctx.Keywords = term;
12471246
ctx.SearchSku = !_catalogSettings.SuppressSkuSearch;
12481247
ctx.OrderBy = ProductSortingEnum.Position;
1249-
ctx.PageSize = productNumber;
1248+
ctx.PageSize = pageSize;
12501249
ctx.StoreId = _services.StoreContext.CurrentStoreIdIfMultiStoreMode;
12511250
ctx.VisibleIndividuallyOnly = true;
12521251

@@ -1256,12 +1255,13 @@ public ActionResult SearchTermAutoComplete(string term)
12561255
products,
12571256
false,
12581257
_catalogSettings.ShowProductImagesInSearchAutoComplete,
1259-
_mediaSettings.AutoCompleteSearchThumbPictureSize).ToList();
1258+
_mediaSettings.ProductThumbPictureSizeOnProductDetailsPage).ToList();
12601259

12611260
var result = (from p in models
12621261
select new
12631262
{
12641263
label = p.Name,
1264+
secondary = p.ShortDescription.Truncate(70, "...") ?? "",
12651265
producturl = Url.RouteUrl("Product", new { SeName = p.SeName }),
12661266
productpictureurl = p.DefaultPictureModel.ImageUrl
12671267
})

src/Presentation/SmartStore.Web/Scripts/smartstore.instantsearch.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,24 @@
22

33
// on document ready
44
$(function () {
5+
6+
var _baseHighlighter = $.fn.typeahead.Constructor.prototype.highlighter;
7+
58
var searchBox = $('#instantsearch');
69
if (searchBox.length == 0)
710
return;
811

912
var searchResult,
1013
minLength = searchBox.data("minlength"),
14+
showThumbs = searchBox.data("showthumbs"),
1115
url = searchBox.data("url");
1216

1317
searchBox.typeahead({
1418
items: 16,
1519
minLength: minLength,
1620
keyUpDelay: 400,
21+
menu: '<ul class="typeahead dropdown-menu dropdown-instantsearch{0}"></ul>'.format(showThumbs ? " rich" : ""),
22+
autoSelectFirstItem: false,
1723
source: function (query, process) {
1824
$('#instantsearch-progress').removeClass('hide');
1925
return $.ajax({
@@ -46,6 +52,25 @@
4652
matcher: function (item) {
4753
// items are filtered already. Do not filter again!
4854
return true;
55+
},
56+
highlighter: function (label) {
57+
var inner = _baseHighlighter.call(this, label);
58+
if (!showThumbs) {
59+
return inner;
60+
}
61+
62+
var item = _.find(searchResult, function (x) { return x.label == label });
63+
64+
var html = ''
65+
+ "<div class='item-wrapper'>"
66+
+ "<div class='item-thumb'><img src='" + item.productpictureurl + "' /></div>"
67+
+ "<div class='item-labels'>"
68+
+ "<div class='item-primary text-overflow'>" + inner + "</div>"
69+
+ "<div class='item-secondary text-overflow'>" + item.secondary + "</div>"
70+
+ "</div>"
71+
+ "</div>";
72+
73+
return html;
4974
}
5075
});
5176

src/Presentation/SmartStore.Web/Themes/Alpha/Content/catalog.less

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,15 @@
9292
.text-overflow();
9393
}
9494

95+
//
96+
// Search panel
97+
// --------------------------------------------------
98+
99+
.search-page .basic-search {
100+
border-bottom: 1px solid #ddd;
101+
border-bottom-color: rgba(0,0,0, 0.12);
102+
margin-bottom: 6px;
103+
}
104+
95105

96106

src/Presentation/SmartStore.Web/Themes/Alpha/Content/shopbar.less

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,43 @@
7070
z-index: 10;
7171
}
7272

73+
.typeahead {
74+
@baseHeight: 36px;
75+
&.dropdown-instantsearch.rich li {
76+
border-bottom: 1px solid rgba(0,0,0, 0.12);
77+
&:last-child { border-bottom: none }
78+
}
79+
.item-wrapper {
80+
display: block;
81+
height: @baseHeight;
82+
padding: 5px 0;
83+
}
84+
.item-thumb {
85+
float: left;
86+
width: @baseHeight;
87+
height: @baseHeight;
88+
margin-right: 8px;
89+
text-align: center;
90+
}
91+
.item-thumb > img {
92+
max-height: 100% ;
93+
}
94+
.item-labels {
95+
float: left;
96+
height: @baseHeight;
97+
}
98+
.item-primary {
99+
font-size: 13px;
100+
line-height: @baseHeight / 2;
101+
}
102+
.item-secondary {
103+
font-size: 11px;
104+
line-height: (@baseHeight / 2) - 2px;
105+
padding-top: 2px;
106+
opacity: 0.7
107+
}
108+
}
109+
73110
#shopbar-account .shopbar-flyout-container .register {
74111
text-align: center;
75112
}

0 commit comments

Comments
 (0)