@bridgeline-digital/hawksearch-handlebars-ui 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +2 -0
- package/.eslintrc.json +153 -0
- package/.husky/pre-commit +5 -0
- package/.lintstagedrc.json +4 -0
- package/.prettierignore +7 -0
- package/.prettierrc.json +19 -0
- package/.vscode/launch.json +15 -0
- package/custom1.html +52 -0
- package/custom2.html +44 -0
- package/gulpfile.js +73 -0
- package/index.html +39 -0
- package/package.json +57 -0
- package/src/assets/icons/chevron-back.svg +4 -0
- package/src/assets/icons/chevron-down.svg +3 -0
- package/src/assets/icons/chevron-forward.svg +4 -0
- package/src/assets/icons/chevron-left.svg +3 -0
- package/src/assets/icons/chevron-right.svg +3 -0
- package/src/assets/icons/chevron-up.svg +3 -0
- package/src/assets/icons/cross.svg +3 -0
- package/src/assets/icons/help.svg +4 -0
- package/src/assets/icons/minus.svg +3 -0
- package/src/assets/icons/plus.svg +7 -0
- package/src/assets/images/icons.svg +1 -0
- package/src/assets/images/placeholder.png +0 -0
- package/src/components/autocomplete/autocomplete.component.hbs +60 -0
- package/src/components/autocomplete/autocomplete.component.ts +124 -0
- package/src/components/base.component.ts +120 -0
- package/src/components/facet-types/base-facet.component.ts +114 -0
- package/src/components/facet-types/checkbox-list-facet/checkbox-list-facet.component.hbs +6 -0
- package/src/components/facet-types/checkbox-list-facet/checkbox-list-facet.component.ts +73 -0
- package/src/components/facet-types/checkbox-list-facet/checkbox-list.partial.hbs +42 -0
- package/src/components/facet-types/color-facet/color-facet.component.hbs +29 -0
- package/src/components/facet-types/color-facet/color-facet.component.ts +26 -0
- package/src/components/facet-types/index.ts +6 -0
- package/src/components/facet-types/linked-list-facet/link-list-facet.component.hbs +17 -0
- package/src/components/facet-types/linked-list-facet/link-list-facet.component.ts +32 -0
- package/src/components/facet-types/range-slider-facet/range-slider-facet.component.hbs +11 -0
- package/src/components/facet-types/range-slider-facet/range-slider-facet.component.ts +107 -0
- package/src/components/facet-types/search-within-facet/search-within-facet.component.hbs +3 -0
- package/src/components/facet-types/search-within-facet/search-within-facet.component.ts +33 -0
- package/src/components/facet-types/size-facet/size-facet.component.hbs +29 -0
- package/src/components/facet-types/size-facet/size-facet.component.ts +30 -0
- package/src/components/facet-wrapper/facet-wrapper.component.hbs +36 -0
- package/src/components/facet-wrapper/facet-wrapper.component.ts +62 -0
- package/src/components/facets-list/facets-list.component.hbs +11 -0
- package/src/components/facets-list/facets-list.component.ts +92 -0
- package/src/components/icon/icon.component.hbs +3 -0
- package/src/components/icon/icon.component.ts +40 -0
- package/src/components/index.ts +18 -0
- package/src/components/modified-query/modified-query.component.hbs +7 -0
- package/src/components/modified-query/modified-query.component.ts +38 -0
- package/src/components/page-size/page-size.component.hbs +7 -0
- package/src/components/page-size/page-size.component.ts +35 -0
- package/src/components/pagination/pagination.component.hbs +34 -0
- package/src/components/pagination/pagination.component.ts +85 -0
- package/src/components/query-suggestions/query-suggestions.component.hbs +3 -0
- package/src/components/query-suggestions/query-suggestions.component.ts +66 -0
- package/src/components/range-slider/range-slider.component.hbs +9 -0
- package/src/components/range-slider/range-slider.component.ts +190 -0
- package/src/components/search/search.component.hbs +4 -0
- package/src/components/search/search.component.ts +93 -0
- package/src/components/search-results/search-results.component.hbs +29 -0
- package/src/components/search-results/search-results.component.ts +17 -0
- package/src/components/search-results-item/search-results-item.component.hbs +23 -0
- package/src/components/search-results-item/search-results-item.component.ts +99 -0
- package/src/components/search-results-list/search-results-list.component.hbs +15 -0
- package/src/components/search-results-list/search-results-list.component.ts +24 -0
- package/src/components/selected-facets/selected-facets.component.hbs +11 -0
- package/src/components/selected-facets/selected-facets.component.ts +81 -0
- package/src/components/sorting/sorting.component.hbs +7 -0
- package/src/components/sorting/sorting.component.ts +33 -0
- package/src/components/tabs/tabs.component.hbs +7 -0
- package/src/components/tabs/tabs.component.ts +35 -0
- package/src/components/tooltip/tooltip.component.hbs +6 -0
- package/src/components/tooltip/tooltip.component.ts +48 -0
- package/src/helpers/formatters.ts +15 -0
- package/src/helpers/index.ts +1 -0
- package/src/index.ts +125 -0
- package/src/models/autocomplete.models.ts +71 -0
- package/src/models/component.models.ts +27 -0
- package/src/models/global.models.ts +78 -0
- package/src/models/index.ts +7 -0
- package/src/models/raw-autocomplete.models.ts +46 -0
- package/src/models/raw-search.models.ts +130 -0
- package/src/models/search.models.ts +144 -0
- package/src/models/tracking.models.ts +33 -0
- package/src/scss/components/_autocomplete.scss +55 -0
- package/src/scss/components/_facets.scss +287 -0
- package/src/scss/components/_icons.scss +17 -0
- package/src/scss/components/_modified-query.scss +11 -0
- package/src/scss/components/_page-size.scss +2 -0
- package/src/scss/components/_pagination.scss +23 -0
- package/src/scss/components/_query-suggestions.scss +7 -0
- package/src/scss/components/_range-slider.scss +50 -0
- package/src/scss/components/_search-results-item.scss +64 -0
- package/src/scss/components/_search.scss +4 -0
- package/src/scss/components/_selected-facets.scss +24 -0
- package/src/scss/components/_sorting.scss +2 -0
- package/src/scss/components/_tabs.scss +51 -0
- package/src/scss/components/_tooltips.scss +29 -0
- package/src/scss/general/_forms.scss +82 -0
- package/src/scss/general/_grid-layout.scss +44 -0
- package/src/scss/general/_images.scss +3 -0
- package/src/scss/general/_typography.scss +15 -0
- package/src/scss/hawksearch.scss +35 -0
- package/src/scss/includes/_breakpoints.scss +51 -0
- package/src/scss/includes/_components.scss +14 -0
- package/src/scss/includes/_general.scss +4 -0
- package/src/scss/includes/_reset.scss +203 -0
- package/src/scss/includes/_utility-classes.scss +85 -0
- package/src/services/autocomplete.service.ts +104 -0
- package/src/services/base.service.ts +93 -0
- package/src/services/index.ts +3 -0
- package/src/services/search.service.ts +754 -0
- package/src/services/tracking.service.ts +136 -0
- package/tsconfig.json +22 -0
- package/typings.d.ts +21 -0
- package/webpack.config.js +75 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
@use '../includes/breakpoints';
|
|
2
|
+
|
|
3
|
+
.display {
|
|
4
|
+
@include breakpoints.all {
|
|
5
|
+
&-block {
|
|
6
|
+
display: block !important;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
&-none {
|
|
10
|
+
display: none !important;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.flex-vertical {
|
|
16
|
+
@include breakpoints.all {
|
|
17
|
+
&-center {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.flex-gap {
|
|
26
|
+
@include breakpoints.all {
|
|
27
|
+
display: flex;
|
|
28
|
+
flex-wrap: wrap;
|
|
29
|
+
gap: var(--padding);
|
|
30
|
+
|
|
31
|
+
&--none {
|
|
32
|
+
gap: 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&--xs {
|
|
36
|
+
gap: var(--padding-xs);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
&--sm {
|
|
40
|
+
gap: var(--padding-sm);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.margin {
|
|
46
|
+
@include breakpoints.all {
|
|
47
|
+
margin-bottom: var(--margin) !important;
|
|
48
|
+
|
|
49
|
+
&-small {
|
|
50
|
+
margin-bottom: var(--margin-sm) !important;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
&-none {
|
|
54
|
+
margin-bottom: 0 !important;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.width {
|
|
60
|
+
@include breakpoints.all {
|
|
61
|
+
&-auto {
|
|
62
|
+
width: auto;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
&-full {
|
|
66
|
+
width: 100%;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.text-align {
|
|
72
|
+
@include breakpoints.all {
|
|
73
|
+
&-left {
|
|
74
|
+
text-align: left;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
&-center {
|
|
78
|
+
text-align: center;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
&-right {
|
|
82
|
+
text-align: right;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { AutocompleteRequest, AutocompleteResponse, HawksearchGlobal, RawAutocompleteRequest, RawAutocompleteResponse } from '../models';
|
|
2
|
+
import { BaseService } from './base.service';
|
|
3
|
+
|
|
4
|
+
declare let Hawksearch: HawksearchGlobal;
|
|
5
|
+
|
|
6
|
+
export class AutocompleteService extends BaseService {
|
|
7
|
+
protected baseUrl = Hawksearch.config.endpoints?.search || 'https://searchapi-dev.hawksearch.net/api/v2';
|
|
8
|
+
|
|
9
|
+
// #region Search Operations
|
|
10
|
+
|
|
11
|
+
async query(query: string): Promise<AutocompleteResponse> {
|
|
12
|
+
const request: AutocompleteRequest = {
|
|
13
|
+
query: query
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const response = await this.executeAutocomplete(request);
|
|
17
|
+
|
|
18
|
+
return response;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private executeAutocomplete(request: AutocompleteRequest): Promise<AutocompleteResponse> {
|
|
22
|
+
const rawRequest: RawAutocompleteRequest = {
|
|
23
|
+
ClientGuid: Hawksearch.config.clientId,
|
|
24
|
+
Keyword: request.query,
|
|
25
|
+
DisplayFullResponse: true
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return this.httpPost<RawAutocompleteResponse>('/autocomplete', rawRequest).then((rawSearchResults) => {
|
|
29
|
+
const autocompleteResponse = this.convertAutocompleteResponse(request.query, rawSearchResults);
|
|
30
|
+
|
|
31
|
+
this.bindComponent(autocompleteResponse);
|
|
32
|
+
|
|
33
|
+
return autocompleteResponse;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private convertAutocompleteResponse(query: string, autocompleteResponse: RawAutocompleteResponse): AutocompleteResponse {
|
|
38
|
+
return {
|
|
39
|
+
query: query,
|
|
40
|
+
categories: {
|
|
41
|
+
title: autocompleteResponse.CategoryHeading,
|
|
42
|
+
results: autocompleteResponse.Categories.map((c) => ({
|
|
43
|
+
title: this.stripHtml(c.Value),
|
|
44
|
+
field: c.FieldQSName,
|
|
45
|
+
value: c.FieldQSValue,
|
|
46
|
+
url: `?${this.queryStringParams.facet}-${c.FieldQSName}=${encodeURIComponent(c.FieldQSValue)}`
|
|
47
|
+
}))
|
|
48
|
+
},
|
|
49
|
+
content: {
|
|
50
|
+
title: autocompleteResponse.ContentHeading,
|
|
51
|
+
results: autocompleteResponse.Content.map((c) => ({
|
|
52
|
+
id: c.Results.DocId,
|
|
53
|
+
pinned: c.Results.IsPin,
|
|
54
|
+
score: c.Results.Score,
|
|
55
|
+
attributes: c.Results.Document,
|
|
56
|
+
title: this.stripHtml(c.Value),
|
|
57
|
+
url: c.Url
|
|
58
|
+
})),
|
|
59
|
+
totalRecords: autocompleteResponse.ContentCount
|
|
60
|
+
},
|
|
61
|
+
queries: {
|
|
62
|
+
title: autocompleteResponse.PopularHeading,
|
|
63
|
+
results: autocompleteResponse.Popular.map((r) => ({
|
|
64
|
+
query: r,
|
|
65
|
+
url: `?${this.queryStringParams.query}=${encodeURIComponent(r)}`
|
|
66
|
+
}))
|
|
67
|
+
},
|
|
68
|
+
products: {
|
|
69
|
+
title: autocompleteResponse.ProductHeading,
|
|
70
|
+
results: autocompleteResponse.Products.map((p) => ({
|
|
71
|
+
id: p.Results.DocId,
|
|
72
|
+
pinned: p.Results.IsPin,
|
|
73
|
+
score: p.Results.Score,
|
|
74
|
+
attributes: p.Results.Document,
|
|
75
|
+
title: this.stripHtml(p.ProductName),
|
|
76
|
+
sku: p.Sku || undefined,
|
|
77
|
+
imageUrl: p.Thumb?.Url,
|
|
78
|
+
url: p.Url
|
|
79
|
+
})),
|
|
80
|
+
totalRecords: autocompleteResponse.ProductCount
|
|
81
|
+
},
|
|
82
|
+
totalRecords: autocompleteResponse.Count,
|
|
83
|
+
viewAllText: autocompleteResponse.ViewAllButtonLabel
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// #endregion Search Operations
|
|
88
|
+
|
|
89
|
+
// #region Events
|
|
90
|
+
|
|
91
|
+
bindComponent(autocompleteResponse: AutocompleteResponse): void {
|
|
92
|
+
this.triggerEvent('hawksearch:bind-autocomplete', autocompleteResponse);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private triggerEvent<T>(name: string, data: T): void {
|
|
96
|
+
const event = new CustomEvent(name, { detail: data });
|
|
97
|
+
|
|
98
|
+
dispatchEvent(event);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// #endregion Events
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const autocompleteService = new AutocompleteService();
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { unescape } from 'lodash';
|
|
2
|
+
import { HawksearchGlobal } from '@models';
|
|
3
|
+
|
|
4
|
+
declare let Hawksearch: HawksearchGlobal;
|
|
5
|
+
|
|
6
|
+
export abstract class BaseService {
|
|
7
|
+
protected abstract baseUrl: string;
|
|
8
|
+
|
|
9
|
+
protected fieldMappings = {
|
|
10
|
+
imageUrl: Hawksearch.config.fieldMappings?.imageUrl || 'image',
|
|
11
|
+
price: Hawksearch.config.fieldMappings?.price || 'price',
|
|
12
|
+
salePrice: Hawksearch.config.fieldMappings?.salePrice || 'saleprice',
|
|
13
|
+
title: Hawksearch.config.fieldMappings?.title || 'itemname',
|
|
14
|
+
type: Hawksearch.config.fieldMappings?.type || 'contenttype',
|
|
15
|
+
url: Hawksearch.config.fieldMappings?.url || 'url'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
protected queryStringParams = {
|
|
19
|
+
disableSpellcheck: Hawksearch.config.queryStringMappings?.disableSpellcheck || 'disableSpellcheck',
|
|
20
|
+
facet: Hawksearch.config.queryStringMappings?.facet || 'facet',
|
|
21
|
+
page: Hawksearch.config.queryStringMappings?.page || 'page',
|
|
22
|
+
pageSize: Hawksearch.config.queryStringMappings?.pageSize || 'pageSize',
|
|
23
|
+
query: Hawksearch.config.queryStringMappings?.query || 'query',
|
|
24
|
+
searchWithin: Hawksearch.config.queryStringMappings?.searchWithin || 'searchWithin',
|
|
25
|
+
sort: Hawksearch.config.queryStringMappings?.sort || 'sort'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
protected async httpPost<T>(relativeUrl: string, body?: any): Promise<T> {
|
|
29
|
+
const url = this.baseUrl + relativeUrl;
|
|
30
|
+
const headers = this.getHeaders();
|
|
31
|
+
|
|
32
|
+
const response = await fetch(url, {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: headers,
|
|
35
|
+
body: JSON.stringify(body)
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const contentType = response.headers.get('content-type');
|
|
39
|
+
|
|
40
|
+
if (contentType?.startsWith('application/json')) {
|
|
41
|
+
return response.json();
|
|
42
|
+
} else {
|
|
43
|
+
return undefined as unknown as T;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
protected stripHtml(input: string): string {
|
|
48
|
+
let output = input.replace(/<[^>]+>/g, '');
|
|
49
|
+
|
|
50
|
+
output = unescape(output);
|
|
51
|
+
|
|
52
|
+
return output;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private getHeaders(): Headers {
|
|
56
|
+
const headers = new Headers();
|
|
57
|
+
|
|
58
|
+
headers.set('Content-Type', 'application/json');
|
|
59
|
+
headers.set('X-Requested-With', 'XMLHttpRequest');
|
|
60
|
+
|
|
61
|
+
return headers;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
protected getVisitorId(): string {
|
|
65
|
+
const key = 'VisitorId';
|
|
66
|
+
let value = localStorage.getItem(key);
|
|
67
|
+
|
|
68
|
+
if (!value) {
|
|
69
|
+
value = this.generateGuid();
|
|
70
|
+
|
|
71
|
+
localStorage.setItem(key, value);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
protected getVisitId(): string {
|
|
78
|
+
const key = 'VisitId';
|
|
79
|
+
let value = sessionStorage.getItem(key);
|
|
80
|
+
|
|
81
|
+
if (!value) {
|
|
82
|
+
value = this.generateGuid();
|
|
83
|
+
|
|
84
|
+
sessionStorage.setItem(key, value);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
protected generateGuid(): string {
|
|
91
|
+
return crypto.randomUUID();
|
|
92
|
+
}
|
|
93
|
+
}
|