@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.
Files changed (118) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc.json +153 -0
  3. package/.husky/pre-commit +5 -0
  4. package/.lintstagedrc.json +4 -0
  5. package/.prettierignore +7 -0
  6. package/.prettierrc.json +19 -0
  7. package/.vscode/launch.json +15 -0
  8. package/custom1.html +52 -0
  9. package/custom2.html +44 -0
  10. package/gulpfile.js +73 -0
  11. package/index.html +39 -0
  12. package/package.json +57 -0
  13. package/src/assets/icons/chevron-back.svg +4 -0
  14. package/src/assets/icons/chevron-down.svg +3 -0
  15. package/src/assets/icons/chevron-forward.svg +4 -0
  16. package/src/assets/icons/chevron-left.svg +3 -0
  17. package/src/assets/icons/chevron-right.svg +3 -0
  18. package/src/assets/icons/chevron-up.svg +3 -0
  19. package/src/assets/icons/cross.svg +3 -0
  20. package/src/assets/icons/help.svg +4 -0
  21. package/src/assets/icons/minus.svg +3 -0
  22. package/src/assets/icons/plus.svg +7 -0
  23. package/src/assets/images/icons.svg +1 -0
  24. package/src/assets/images/placeholder.png +0 -0
  25. package/src/components/autocomplete/autocomplete.component.hbs +60 -0
  26. package/src/components/autocomplete/autocomplete.component.ts +124 -0
  27. package/src/components/base.component.ts +120 -0
  28. package/src/components/facet-types/base-facet.component.ts +114 -0
  29. package/src/components/facet-types/checkbox-list-facet/checkbox-list-facet.component.hbs +6 -0
  30. package/src/components/facet-types/checkbox-list-facet/checkbox-list-facet.component.ts +73 -0
  31. package/src/components/facet-types/checkbox-list-facet/checkbox-list.partial.hbs +42 -0
  32. package/src/components/facet-types/color-facet/color-facet.component.hbs +29 -0
  33. package/src/components/facet-types/color-facet/color-facet.component.ts +26 -0
  34. package/src/components/facet-types/index.ts +6 -0
  35. package/src/components/facet-types/linked-list-facet/link-list-facet.component.hbs +17 -0
  36. package/src/components/facet-types/linked-list-facet/link-list-facet.component.ts +32 -0
  37. package/src/components/facet-types/range-slider-facet/range-slider-facet.component.hbs +11 -0
  38. package/src/components/facet-types/range-slider-facet/range-slider-facet.component.ts +107 -0
  39. package/src/components/facet-types/search-within-facet/search-within-facet.component.hbs +3 -0
  40. package/src/components/facet-types/search-within-facet/search-within-facet.component.ts +33 -0
  41. package/src/components/facet-types/size-facet/size-facet.component.hbs +29 -0
  42. package/src/components/facet-types/size-facet/size-facet.component.ts +30 -0
  43. package/src/components/facet-wrapper/facet-wrapper.component.hbs +36 -0
  44. package/src/components/facet-wrapper/facet-wrapper.component.ts +62 -0
  45. package/src/components/facets-list/facets-list.component.hbs +11 -0
  46. package/src/components/facets-list/facets-list.component.ts +92 -0
  47. package/src/components/icon/icon.component.hbs +3 -0
  48. package/src/components/icon/icon.component.ts +40 -0
  49. package/src/components/index.ts +18 -0
  50. package/src/components/modified-query/modified-query.component.hbs +7 -0
  51. package/src/components/modified-query/modified-query.component.ts +38 -0
  52. package/src/components/page-size/page-size.component.hbs +7 -0
  53. package/src/components/page-size/page-size.component.ts +35 -0
  54. package/src/components/pagination/pagination.component.hbs +34 -0
  55. package/src/components/pagination/pagination.component.ts +85 -0
  56. package/src/components/query-suggestions/query-suggestions.component.hbs +3 -0
  57. package/src/components/query-suggestions/query-suggestions.component.ts +66 -0
  58. package/src/components/range-slider/range-slider.component.hbs +9 -0
  59. package/src/components/range-slider/range-slider.component.ts +190 -0
  60. package/src/components/search/search.component.hbs +4 -0
  61. package/src/components/search/search.component.ts +93 -0
  62. package/src/components/search-results/search-results.component.hbs +29 -0
  63. package/src/components/search-results/search-results.component.ts +17 -0
  64. package/src/components/search-results-item/search-results-item.component.hbs +23 -0
  65. package/src/components/search-results-item/search-results-item.component.ts +99 -0
  66. package/src/components/search-results-list/search-results-list.component.hbs +15 -0
  67. package/src/components/search-results-list/search-results-list.component.ts +24 -0
  68. package/src/components/selected-facets/selected-facets.component.hbs +11 -0
  69. package/src/components/selected-facets/selected-facets.component.ts +81 -0
  70. package/src/components/sorting/sorting.component.hbs +7 -0
  71. package/src/components/sorting/sorting.component.ts +33 -0
  72. package/src/components/tabs/tabs.component.hbs +7 -0
  73. package/src/components/tabs/tabs.component.ts +35 -0
  74. package/src/components/tooltip/tooltip.component.hbs +6 -0
  75. package/src/components/tooltip/tooltip.component.ts +48 -0
  76. package/src/helpers/formatters.ts +15 -0
  77. package/src/helpers/index.ts +1 -0
  78. package/src/index.ts +125 -0
  79. package/src/models/autocomplete.models.ts +71 -0
  80. package/src/models/component.models.ts +27 -0
  81. package/src/models/global.models.ts +78 -0
  82. package/src/models/index.ts +7 -0
  83. package/src/models/raw-autocomplete.models.ts +46 -0
  84. package/src/models/raw-search.models.ts +130 -0
  85. package/src/models/search.models.ts +144 -0
  86. package/src/models/tracking.models.ts +33 -0
  87. package/src/scss/components/_autocomplete.scss +55 -0
  88. package/src/scss/components/_facets.scss +287 -0
  89. package/src/scss/components/_icons.scss +17 -0
  90. package/src/scss/components/_modified-query.scss +11 -0
  91. package/src/scss/components/_page-size.scss +2 -0
  92. package/src/scss/components/_pagination.scss +23 -0
  93. package/src/scss/components/_query-suggestions.scss +7 -0
  94. package/src/scss/components/_range-slider.scss +50 -0
  95. package/src/scss/components/_search-results-item.scss +64 -0
  96. package/src/scss/components/_search.scss +4 -0
  97. package/src/scss/components/_selected-facets.scss +24 -0
  98. package/src/scss/components/_sorting.scss +2 -0
  99. package/src/scss/components/_tabs.scss +51 -0
  100. package/src/scss/components/_tooltips.scss +29 -0
  101. package/src/scss/general/_forms.scss +82 -0
  102. package/src/scss/general/_grid-layout.scss +44 -0
  103. package/src/scss/general/_images.scss +3 -0
  104. package/src/scss/general/_typography.scss +15 -0
  105. package/src/scss/hawksearch.scss +35 -0
  106. package/src/scss/includes/_breakpoints.scss +51 -0
  107. package/src/scss/includes/_components.scss +14 -0
  108. package/src/scss/includes/_general.scss +4 -0
  109. package/src/scss/includes/_reset.scss +203 -0
  110. package/src/scss/includes/_utility-classes.scss +85 -0
  111. package/src/services/autocomplete.service.ts +104 -0
  112. package/src/services/base.service.ts +93 -0
  113. package/src/services/index.ts +3 -0
  114. package/src/services/search.service.ts +754 -0
  115. package/src/services/tracking.service.ts +136 -0
  116. package/tsconfig.json +22 -0
  117. package/typings.d.ts +21 -0
  118. 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
+ }
@@ -0,0 +1,3 @@
1
+ export * from './autocomplete.service';
2
+ export * from './search.service';
3
+ export * from './tracking.service';