@ecomplus/storefront-components 1.0.0-beta.18 → 1.0.0-beta.180

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 (125) hide show
  1. package/CHANGELOG.md +1271 -133
  2. package/README.md +9 -4
  3. package/all.js +3 -1
  4. package/dist/1.storefront-components.min.js +2 -0
  5. package/dist/1.storefront-components.min.js.map +1 -0
  6. package/dist/2.storefront-components.min.js +5 -0
  7. package/dist/2.storefront-components.min.js.map +1 -0
  8. package/dist/3.storefront-components.min.js +5 -0
  9. package/dist/3.storefront-components.min.js.map +1 -0
  10. package/dist/storefront-components.min.js +33 -12
  11. package/dist/storefront-components.min.js.map +1 -1
  12. package/package.json +17 -12
  13. package/src/APagination.vue +2 -0
  14. package/src/AShare.vue +2 -0
  15. package/src/AccountAddresses.vue +3 -0
  16. package/src/AccountForm.vue +3 -0
  17. package/src/AccountPoints.vue +3 -0
  18. package/src/BuyTogether.vue +3 -0
  19. package/src/EarnPointsProgress.vue +3 -0
  20. package/src/ItemCustomizations.vue +2 -0
  21. package/src/KitProductVariations.vue +3 -0
  22. package/src/PointsApplier.vue +2 -0
  23. package/src/ProductQuickview.vue +3 -0
  24. package/src/QuantitySelector.vue +3 -0
  25. package/src/RecommendedItems.vue +3 -0
  26. package/src/ShippingLine.vue +1 -0
  27. package/src/TheCart.vue +3 -0
  28. package/src/html/APagination.html +90 -0
  29. package/src/html/APrices.html +24 -4
  30. package/src/html/AShare.html +31 -0
  31. package/src/html/AccountAddresses.html +90 -0
  32. package/src/html/AccountForm.html +269 -0
  33. package/src/html/AccountPoints.html +39 -0
  34. package/src/html/AddressForm.html +9 -7
  35. package/src/html/BuyTogether.html +60 -0
  36. package/src/html/CartItem.html +86 -38
  37. package/src/html/CartQuickview.html +28 -5
  38. package/src/html/DiscountApplier.html +2 -2
  39. package/src/html/EarnPointsProgress.html +28 -0
  40. package/src/html/InputDate.html +1 -1
  41. package/src/html/InputDocNumber.html +1 -0
  42. package/src/html/InputPhone.html +1 -1
  43. package/src/html/InstantSearch.html +3 -3
  44. package/src/html/ItemCustomizations.html +14 -0
  45. package/src/html/KitProductVariations.html +92 -0
  46. package/src/html/LoginBlock.html +34 -32
  47. package/src/html/LoginModal.html +9 -4
  48. package/src/html/PaymentOption.html +7 -5
  49. package/src/html/PointsApplier.html +26 -0
  50. package/src/html/ProductCard.html +56 -8
  51. package/src/html/ProductGallery.html +21 -3
  52. package/src/html/ProductQuickview.html +64 -0
  53. package/src/html/ProductVariations.html +30 -3
  54. package/src/html/QuantitySelector.html +85 -0
  55. package/src/html/RecommendedItems.html +48 -0
  56. package/src/html/SearchEngine.html +100 -24
  57. package/src/html/ShippingCalculator.html +83 -3
  58. package/src/html/ShippingLine.html +5 -2
  59. package/src/html/TheAccount.html +43 -9
  60. package/src/html/TheCart.html +156 -0
  61. package/src/html/TheProduct.html +416 -138
  62. package/src/js/APagination.js +74 -0
  63. package/src/js/APicture.js +27 -7
  64. package/src/js/APrices.js +80 -41
  65. package/src/js/AShare.js +83 -0
  66. package/src/js/AccountAddresses.js +192 -0
  67. package/src/js/AccountForm.js +312 -0
  68. package/src/js/AccountPoints.js +63 -0
  69. package/src/js/AddressForm.js +80 -35
  70. package/src/js/BuyTogether.js +246 -0
  71. package/src/js/CartItem.js +67 -14
  72. package/src/js/CartQuickview.js +21 -2
  73. package/src/js/DiscountApplier.js +132 -42
  74. package/src/js/EarnPointsProgress.js +77 -0
  75. package/src/js/InputDate.js +8 -8
  76. package/src/js/InputDocNumber.js +20 -0
  77. package/src/js/ItemCustomizations.js +10 -0
  78. package/src/js/KitProductVariations.js +218 -0
  79. package/src/js/LoginBlock.js +46 -5
  80. package/src/js/LoginModal.js +17 -4
  81. package/src/js/PaymentOption.js +28 -1
  82. package/src/js/PointsApplier.js +110 -0
  83. package/src/js/ProductCard.js +97 -11
  84. package/src/js/ProductGallery.js +32 -12
  85. package/src/js/ProductQuickview.js +72 -0
  86. package/src/js/ProductVariations.js +76 -19
  87. package/src/js/QuantitySelector.js +175 -0
  88. package/src/js/RecommendedItems.js +178 -0
  89. package/src/js/SearchEngine.js +185 -55
  90. package/src/js/ShippingCalculator.js +174 -35
  91. package/src/js/ShippingLine.js +35 -5
  92. package/src/js/TheAccount.js +97 -6
  93. package/src/js/TheCart.js +146 -0
  94. package/src/js/TheProduct.js +387 -43
  95. package/src/js/helpers/add-idle-callback.js +7 -0
  96. package/src/js/helpers/check-form-validity.js +3 -0
  97. package/src/js/helpers/favorite-products.js +24 -0
  98. package/src/js/helpers/scroll-to-element.js +10 -0
  99. package/src/js/helpers/sort-apps.js +14 -0
  100. package/src/js/helpers/wait-storefront-info.js +21 -0
  101. package/src/scss/APicture.scss +2 -0
  102. package/src/scss/APrices.scss +13 -1
  103. package/src/scss/AccountAddresses.scss +27 -0
  104. package/src/scss/AccountForm.scss +5 -0
  105. package/src/scss/AccountPoints.scss +17 -0
  106. package/src/scss/BuyTogether.scss +38 -0
  107. package/src/scss/CartItem.scss +17 -1
  108. package/src/scss/EarnPointsProgress.scss +6 -0
  109. package/src/scss/InstantSearch.scss +1 -0
  110. package/src/scss/KitProductVariations.scss +72 -0
  111. package/src/scss/LoginBlock.scss +5 -0
  112. package/src/scss/PaymentOption.scss +10 -1
  113. package/src/scss/ProductCard.scss +66 -28
  114. package/src/scss/ProductGallery.scss +4 -2
  115. package/src/scss/ProductQuickview.scss +36 -0
  116. package/src/scss/ProductVariations.scss +20 -4
  117. package/src/scss/QuantitySelector.scss +39 -0
  118. package/src/scss/RecommendedItems.scss +28 -0
  119. package/src/scss/SearchEngine.scss +9 -5
  120. package/src/scss/ShippingCalculator.scss +42 -1
  121. package/src/scss/ShippingLine.scss +24 -0
  122. package/src/scss/TheAccount.scss +4 -0
  123. package/src/scss/TheCart.scss +54 -0
  124. package/src/scss/TheProduct.scss +146 -1
  125. package/webpack.config.js +20 -6
@@ -1,4 +1,6 @@
1
1
  import {
2
+ i19all,
3
+ i19asOf,
2
4
  i19brands,
3
5
  i19categories,
4
6
  i19clearFilters,
@@ -9,21 +11,31 @@ import {
9
11
  i19highestPrice,
10
12
  i19itemsFound,
11
13
  i19lowestPrice,
14
+ i19name,
12
15
  i19noResultsFor,
13
16
  i19popularProducts,
17
+ i19price,
14
18
  i19refineSearch,
19
+ i19releases,
15
20
  i19relevance,
16
21
  i19results,
17
22
  i19sales,
18
23
  i19searchAgain,
19
24
  i19searchingFor,
20
25
  i19searchOfflineErrorMsg,
21
- i19sort
26
+ i19sort,
27
+ i19upTo
22
28
  } from '@ecomplus/i18n'
23
29
 
24
- import { i18n } from '@ecomplus/utils'
30
+ import {
31
+ i18n,
32
+ formatMoney
33
+ } from '@ecomplus/utils'
34
+
25
35
  import lozad from 'lozad'
26
36
  import EcomSearch from '@ecomplus/search-engine'
37
+ import { Portal } from '@linusborg/vue-simple-portal'
38
+ import scrollToElement from './helpers/scroll-to-element'
27
39
  import ABackdrop from '../ABackdrop.vue'
28
40
  import ProductCard from '../ProductCard.vue'
29
41
 
@@ -44,6 +56,7 @@ export default {
44
56
  name: 'SearchEngine',
45
57
 
46
58
  components: {
59
+ Portal,
47
60
  ABackdrop,
48
61
  ProductCard
49
62
  },
@@ -63,6 +76,7 @@ export default {
63
76
  isFixedBrands: Boolean,
64
77
  isFixedCategories: Boolean,
65
78
  defaultSort: String,
79
+ defaultFilters: Object,
66
80
  autoFixScore: {
67
81
  type: Number,
68
82
  default: 0.6
@@ -79,10 +93,15 @@ export default {
79
93
  type: Boolean,
80
94
  default: true
81
95
  },
96
+ loadMoreSelector: String,
82
97
  canRetry: {
83
98
  type: Boolean,
84
99
  default: true
85
100
  },
101
+ canShowItems: {
102
+ type: Boolean,
103
+ default: true
104
+ },
86
105
  productCardProps: Object,
87
106
  gridsData: {
88
107
  type: Array,
@@ -103,6 +122,9 @@ export default {
103
122
  noResultsTerm: '',
104
123
  keepNoResultsTerm: false,
105
124
  filters: [],
125
+ priceRange: {},
126
+ priceOptions: [],
127
+ hasSetPriceRange: false,
106
128
  lastSelectedFilter: null,
107
129
  selectedOptions: {},
108
130
  selectedSortOption: null,
@@ -110,6 +132,7 @@ export default {
110
132
  lastRequestId: null,
111
133
  isScheduled: false,
112
134
  isLoadingMore: false,
135
+ mustSkipLoadMore: false,
113
136
  hasNetworkError: false,
114
137
  popularItems: [],
115
138
  hasSetPopularItems: false,
@@ -119,6 +142,7 @@ export default {
119
142
  },
120
143
 
121
144
  computed: {
145
+ i19all: () => i18n(i19all),
122
146
  i19clearFilters: () => i18n(i19clearFilters),
123
147
  i19closeFilters: () => i18n(i19closeFilters),
124
148
  i19didYouMean: () => i18n(i19didYouMean),
@@ -127,8 +151,9 @@ export default {
127
151
  i19itemsFound: () => i18n(i19itemsFound),
128
152
  i19noResultsFor: () => i18n(i19noResultsFor),
129
153
  i19popularProducts: () => i18n(i19popularProducts),
130
- i19relevance: () => i18n(i19relevance),
154
+ i19price: () => i18n(i19price),
131
155
  i19refineSearch: () => i18n(i19refineSearch),
156
+ i19relevance: () => i18n(i19relevance),
132
157
  i19results: () => i18n(i19results),
133
158
  i19searchAgain: () => i18n(i19searchAgain),
134
159
  i19searchingFor: () => i18n(i19searchingFor),
@@ -158,6 +183,12 @@ export default {
158
183
  }, {
159
184
  value: 'highest_price',
160
185
  label: i18n(i19highestPrice)
186
+ }, {
187
+ value: 'news',
188
+ label: i18n(i19releases)
189
+ }, {
190
+ value: 'slug',
191
+ label: i18n(i19name)
161
192
  }
162
193
  ],
163
194
 
@@ -172,7 +203,10 @@ export default {
172
203
 
173
204
  isNavVisible () {
174
205
  return this.hasSearched && this.isFilterable &&
175
- (this.isSearching || this.totalSearchResults > 8 || this.hasSelectedOptions)
206
+ (this.isSearching ||
207
+ this.totalSearchResults > 8 ||
208
+ this.hasSelectedOptions ||
209
+ this.hasSetPriceRange)
176
210
  },
177
211
 
178
212
  isResultsVisible () {
@@ -181,7 +215,8 @@ export default {
181
215
 
182
216
  hasFilters () {
183
217
  return this.hasSelectedOptions ||
184
- this.filters.find(({ options }) => options.length)
218
+ this.filters.find(({ options }) => options.length) ||
219
+ this.hasSetPriceRange
185
220
  },
186
221
 
187
222
  suggestedItems () {
@@ -191,10 +226,18 @@ export default {
191
226
  loadObserver () {
192
227
  return this.canLoadMore && lozad('#search-engine-load-more', {
193
228
  load: () => {
194
- this.isLoadingMore = true
195
- this.fetchItems()
229
+ if (!this.mustSkipLoadMore) {
230
+ this.mustSkipLoadMore = this.isLoadingMore = true
231
+ this.fetchItems()
232
+ }
196
233
  }
197
234
  })
235
+ },
236
+
237
+ pageAnchorIndex () {
238
+ const count = this.suggestedItems.length
239
+ const rest = count % this.pageSize
240
+ return (rest === 0 ? count - this.pageSize : count - rest) - 1
198
241
  }
199
242
  },
200
243
 
@@ -208,7 +251,7 @@ export default {
208
251
  ecomSearch.setPageNumber(this.page + Math.ceil(this.resultItems.length / this.pageSize))
209
252
  }
210
253
  const fetching = ecomSearch.setPageSize(this.pageSize).fetch()
211
- .then(() => {
254
+ .then(result => {
212
255
  if (this.lastRequestId === requestId) {
213
256
  this.hasNetworkError = false
214
257
  if (!isPopularItems) {
@@ -219,6 +262,7 @@ export default {
219
262
  this.hasSetPopularItems = true
220
263
  this.popularItems = ecomSearch.getItems()
221
264
  }
265
+ return result
222
266
  })
223
267
  .catch(err => {
224
268
  console.error(err)
@@ -232,9 +276,15 @@ export default {
232
276
  })
233
277
  .finally(() => {
234
278
  this.countOpenRequests--
235
- this.isLoadingMore = false
279
+ if (this.isLoadingMore) {
280
+ this.isLoadingMore = false
281
+ this.$nextTick(() => setTimeout(() => {
282
+ this.mustSkipLoadMore = false
283
+ this.loadObserver.observe()
284
+ }, 300))
285
+ }
236
286
  })
237
- this.$emit('fetch', { ecomSearch, fetching })
287
+ this.$emit('fetch', { ecomSearch, fetching, isPopularItems })
238
288
  },
239
289
 
240
290
  updateFilters () {
@@ -256,7 +306,8 @@ export default {
256
306
  options,
257
307
  isSpec
258
308
  }
259
- const optionsList = !this.selectedOptions[filter] ? []
309
+ const optionsList = !this.selectedOptions[filter]
310
+ ? []
260
311
  : this.selectedOptions[filter]
261
312
  .filter(option => options.find(({ key }) => key === option))
262
313
  this.$set(this.selectedOptions, filter, optionsList)
@@ -265,46 +316,75 @@ export default {
265
316
  }
266
317
  addFilter('Brands', this.ecomSearch.getBrands())
267
318
  addFilter('Categories', this.ecomSearch.getCategories())
268
- this.ecomSearch.getSpecs().forEach(({ key, options }, index) => {
319
+ this.ecomSearch.getSpecs().forEach(({ key, options }) => {
269
320
  addFilter(key, options, true)
270
321
  })
271
322
  this.filters = this.filters.filter((_, i) => updatedFilters.includes(i))
272
323
  this.searchFilterId = Date.now()
273
324
  },
274
325
 
275
- handleSuggestions () {
276
- const { ecomSearch, term } = this
277
- let suggestTerm = term
278
- let canAutoFix = false
279
- this.suggestedTerm = ''
280
- ecomSearch.getTermSuggestions().forEach(({ options, text }) => {
281
- if (options.length) {
282
- const opt = options[0]
283
- if (
284
- !this.totalSearchResults &&
285
- this.autoFixScore > 0 &&
286
- opt.score >= this.autoFixScore &&
287
- opt.text.indexOf(term) === -1
288
- ) {
289
- canAutoFix = true
290
- }
291
- suggestTerm = suggestTerm.replace(text, opt.text)
326
+ updatePriceOptions () {
327
+ this.priceRange = this.ecomSearch.getPriceRange()
328
+ if (Math.round(this.priceRange.min) < Math.round(this.priceRange.avg)) {
329
+ const price1 = Math.ceil(Math.max(this.priceRange.min * 1.5, this.priceRange.avg / 2))
330
+ const price2 = Math.ceil(Math.min(this.priceRange.max / 1.5, this.priceRange.avg * 2))
331
+ if (price1 !== price2) {
332
+ this.priceOptions = [Math.min(price1, price2), Math.max(price1, price2), null]
333
+ .map((max, i, prices) => {
334
+ const min = prices[i - 1]
335
+ return {
336
+ min,
337
+ max,
338
+ label: !min
339
+ ? `${i18n(i19upTo)} ${formatMoney(max)}`
340
+ : i < 2
341
+ ? `${formatMoney(min)} - ${formatMoney(max)}`
342
+ : `${i18n(i19asOf)} ${formatMoney(min)}`
343
+ }
344
+ })
345
+ return
292
346
  }
293
- })
294
- if (!this.keepNoResultsTerm) {
295
- this.noResultsTerm = ''
296
- } else {
297
- this.keepNoResultsTerm = false
298
347
  }
299
- if (suggestTerm !== term) {
300
- if (canAutoFix) {
301
- this.noResultsTerm = term
302
- this.keepNoResultsTerm = true
303
- this.$emit('update:term', suggestTerm)
348
+ this.priceOptions = []
349
+ },
350
+
351
+ handleSuggestions () {
352
+ if (this.term) {
353
+ const { ecomSearch } = this
354
+ const term = this.term.toLowerCase()
355
+ let suggestTerm = term
356
+ let canAutoFix = false
357
+ this.suggestedTerm = ''
358
+ ecomSearch.getTermSuggestions().forEach(({ options, text }) => {
359
+ if (options.length) {
360
+ const opt = options[0]
361
+ const optTerm = opt.text.toLowerCase()
362
+ if (
363
+ !this.totalSearchResults &&
364
+ this.autoFixScore > 0 &&
365
+ opt.score >= this.autoFixScore &&
366
+ optTerm.indexOf(term) === -1
367
+ ) {
368
+ canAutoFix = true
369
+ }
370
+ suggestTerm = suggestTerm.replace(new RegExp(text, 'i'), optTerm)
371
+ }
372
+ })
373
+ if (!this.keepNoResultsTerm) {
374
+ this.noResultsTerm = ''
304
375
  } else {
305
- this.suggestedTerm = suggestTerm
376
+ this.keepNoResultsTerm = false
377
+ }
378
+ if (suggestTerm !== term) {
379
+ if (canAutoFix) {
380
+ this.noResultsTerm = term
381
+ this.keepNoResultsTerm = true
382
+ this.$emit('update:term', suggestTerm)
383
+ } else {
384
+ this.suggestedTerm = suggestTerm
385
+ }
386
+ ecomSearch.history.shift()
306
387
  }
307
- ecomSearch.history.shift()
308
388
  }
309
389
  },
310
390
 
@@ -315,6 +395,17 @@ export default {
315
395
  ? this.resultItems.concat(ecomSearch.getItems())
316
396
  : ecomSearch.getItems()
317
397
  this.updateFilters()
398
+ if (!this.hasSearched && this.defaultFilters) {
399
+ for (const filter in this.defaultFilters) {
400
+ const options = this.defaultFilters[filter]
401
+ if (Array.isArray(options)) {
402
+ options.forEach(option => this.setFilterOption(filter, option, true))
403
+ } else if (typeof options === 'string') {
404
+ this.setFilterOption(filter, options, true)
405
+ }
406
+ }
407
+ }
408
+ this.updatePriceOptions()
318
409
  this.handleSuggestions()
319
410
  if (!this.totalSearchResults && this.hasPopularItems && !this.hasSetPopularItems) {
320
411
  this.fetchItems(false, true)
@@ -349,7 +440,8 @@ export default {
349
440
 
350
441
  toggleFilters (isVisible) {
351
442
  this.isAsideVisible = typeof isVisible === 'boolean'
352
- ? isVisible : !this.isAsideVisible
443
+ ? isVisible
444
+ : !this.isAsideVisible
353
445
  },
354
446
 
355
447
  getFilterLabel (filter) {
@@ -409,23 +501,53 @@ export default {
409
501
  }
410
502
  },
411
503
 
504
+ handlePriceInputs () {
505
+ const { inputMinPrice, inputMaxPrice } = this.$refs
506
+ const min = Number(inputMinPrice.value) || null
507
+ const max = Number(inputMaxPrice.value) || null
508
+ if ((min && !max) || min <= max) {
509
+ this.setPriceRange(min, max)
510
+ }
511
+ inputMinPrice.value = (min || '')
512
+ inputMaxPrice.value = (max || '')
513
+ },
514
+
515
+ setPriceRange (min, max) {
516
+ if (
517
+ (min && min !== this.priceRange.min) ||
518
+ (max && max !== this.priceRange.max)
519
+ ) {
520
+ this.hasSetPriceRange = true
521
+ } else if (this.hasSetPriceRange) {
522
+ this.hasSetPriceRange = false
523
+ } else {
524
+ return
525
+ }
526
+ this.ecomSearch.setPriceRange(min, max)
527
+ this.scheduleFetch()
528
+ },
529
+
412
530
  setFilterOption (filter, option, isSet) {
413
531
  const { selectedOptions } = this
414
532
  const optionsList = selectedOptions[filter]
415
- if (isSet) {
416
- this.lastSelectedFilter = filter
417
- optionsList.push(option)
418
- } else {
533
+ if (optionsList) {
419
534
  const optionIndex = optionsList.indexOf(option)
420
- if (optionIndex > -1) {
421
- optionsList.splice(optionIndex, 1)
422
- }
423
- if (!optionsList.length && this.lastSelectedFilter === filter) {
424
- this.lastSelectedFilter = null
535
+ if (isSet) {
536
+ if (optionIndex === -1) {
537
+ this.lastSelectedFilter = filter
538
+ optionsList.push(option)
539
+ }
540
+ } else {
541
+ if (optionIndex > -1) {
542
+ optionsList.splice(optionIndex, 1)
543
+ }
544
+ if (!optionsList.length && this.lastSelectedFilter === filter) {
545
+ this.lastSelectedFilter = null
546
+ }
425
547
  }
548
+ this.updateSearchFilter(filter)
549
+ this.scheduleFetch()
426
550
  }
427
- this.updateSearchFilter(filter)
428
- this.scheduleFetch()
429
551
  },
430
552
 
431
553
  clearFilters () {
@@ -442,7 +564,11 @@ export default {
442
564
  setSortOrder (sort) {
443
565
  this.selectedSortOption = sort
444
566
  this.ecomSearch.setSortOrder(sort)
445
- this.scheduleFetch()
567
+ if (this.page > 1) {
568
+ this.page = 1
569
+ } else {
570
+ this.scheduleFetch()
571
+ }
446
572
  }
447
573
  },
448
574
 
@@ -465,7 +591,11 @@ export default {
465
591
  isSearching (isSearching) {
466
592
  if (!isSearching && this.loadObserver) {
467
593
  this.$nextTick(() => {
468
- this.loadObserver.observe()
594
+ if (!this.mustSkipLoadMore) {
595
+ this.loadObserver.observe()
596
+ } else {
597
+ setTimeout(() => scrollToElement(this.$refs.pageAnchor[0], 40), 100)
598
+ }
469
599
  })
470
600
  }
471
601
  }