@gem-sdk/components 2.1.36 → 2.1.38

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.
@@ -4,15 +4,41 @@ const AutoPlayPlugin = (slider)=>{
4
4
  let autoplay = slider.options.autoplay ?? {};
5
5
  let timeout;
6
6
  let mouseOver = false;
7
+ let inViewport = false;
8
+ let observer = null;
9
+ // Thêm sự kiện cho dot nếu có
10
+ const $dots = slider?.container?.closest('[data-component-type="component"]')?.querySelector('.carousel-dots');
11
+ // Callback của IntersectionObserver
12
+ function handleIntersection(entries) {
13
+ entries.forEach((entry)=>{
14
+ // Với threshold 0, chỉ cần một phần bất kỳ hiển thị sẽ đánh dấu isIntersecting
15
+ inViewport = entry.isIntersecting;
16
+ if (inViewport) {
17
+ nextTimeout();
18
+ } else {
19
+ clearNextTimeout();
20
+ }
21
+ });
22
+ }
23
+ function initIntersectionObserver() {
24
+ // Nếu đã có observer hoặc không có container thì không khởi tạo lại
25
+ if (observer || !slider.container) return;
26
+ observer = new IntersectionObserver(handleIntersection, {
27
+ root: null,
28
+ threshold: 0
29
+ });
30
+ observer.observe(slider.container);
31
+ }
7
32
  function clearNextTimeout() {
8
33
  clearTimeout(timeout);
9
34
  }
10
35
  function nextTimeout() {
11
- clearTimeout(timeout);
36
+ clearNextTimeout();
12
37
  timeout = setTimeout(()=>{
13
- if (mouseOver || !autoplay.enable) return;
38
+ // Nếu đang hover, autoplay bị tắt hoặc slider không trong viewport thì không chuyển slide
39
+ if (mouseOver || !autoplay.enable || !inViewport) return;
14
40
  slider.next();
15
- }, autoplay?.delay ?? 2000);
41
+ }, autoplay.delay ?? 2000);
16
42
  }
17
43
  function onMouseOver() {
18
44
  mouseOver = true;
@@ -23,11 +49,15 @@ const AutoPlayPlugin = (slider)=>{
23
49
  nextTimeout();
24
50
  }
25
51
  slider.on('created', ()=>{
26
- if (autoplay.pauseOnHover) {
52
+ $dots?.addEventListener('mouseover', onMouseOver);
53
+ $dots?.addEventListener('mouseout', onMouseOut);
54
+ // Nếu tùy chọn pauseOnHover bật thì thêm sự kiện hover cho container slider
55
+ if (autoplay.pauseOnHover && slider.container) {
27
56
  slider.container.addEventListener('mouseover', onMouseOver);
28
57
  slider.container.addEventListener('mouseout', onMouseOut);
29
58
  }
30
- if (slider.options.autoplay?.enable) {
59
+ if (autoplay.enable) {
60
+ initIntersectionObserver();
31
61
  nextTimeout();
32
62
  }
33
63
  });
@@ -35,29 +65,40 @@ const AutoPlayPlugin = (slider)=>{
35
65
  slider.on('animationEnded', nextTimeout);
36
66
  slider.on('updated', (newOptions)=>{
37
67
  autoplay = newOptions.options?.autoplay ?? {};
38
- nextTimeout();
39
- });
40
- slider.on('destroyed', ()=>{
41
- slider.container.removeEventListener('mouseover', onMouseOver);
42
- slider.container.removeEventListener('mouseout', onMouseOut);
68
+ if (autoplay.enable) {
69
+ initIntersectionObserver();
70
+ nextTimeout();
71
+ }
43
72
  });
44
73
  slider.on('optionsChanged', (newOptions)=>{
45
- const $dots = slider?.container?.closest('[data-component-type="component"]')?.querySelector('.carousel-dots');
46
- $dots?.addEventListener('mouseover', onMouseOver);
47
- $dots?.addEventListener('mouseout', onMouseOut);
48
- if (newOptions.options.autoplay?.pauseOnHover !== autoplay.pauseOnHover) {
74
+ if (newOptions.options.autoplay?.pauseOnHover !== autoplay.pauseOnHover && slider.container) {
49
75
  if (newOptions.options.autoplay?.pauseOnHover) {
50
76
  slider.container.addEventListener('mouseover', onMouseOver);
51
77
  slider.container.addEventListener('mouseout', onMouseOut);
52
- }
53
- if (!newOptions.options.autoplay?.pauseOnHover) {
78
+ } else {
54
79
  slider.container.removeEventListener('mouseover', onMouseOver);
55
80
  slider.container.removeEventListener('mouseout', onMouseOut);
56
81
  mouseOver = false;
57
82
  }
58
83
  }
59
- nextTimeout();
60
84
  autoplay = newOptions.options.autoplay ?? {};
85
+ if (autoplay.enable) {
86
+ initIntersectionObserver();
87
+ nextTimeout();
88
+ }
89
+ });
90
+ slider.on('destroyed', ()=>{
91
+ if (slider.container) {
92
+ slider.container.removeEventListener('mouseover', onMouseOver);
93
+ slider.container.removeEventListener('mouseout', onMouseOut);
94
+ }
95
+ $dots?.removeEventListener('mouseover', onMouseOver);
96
+ $dots?.removeEventListener('mouseout', onMouseOut);
97
+ clearNextTimeout();
98
+ if (observer) {
99
+ observer.disconnect();
100
+ observer = null;
101
+ }
61
102
  });
62
103
  };
63
104
 
@@ -7,6 +7,7 @@ var Head = require('next/head');
7
7
  var Img = require('./Img.js');
8
8
 
9
9
  const AdaptiveImage = ({ srcSet, priority, pictureClass, imagePlaceholder, ...rest })=>{
10
+ const fallBackImage = 'https://cdn.shopify.com/s/assets/no-image-2048-5e88c1b20e087fb7bbe9a3771824e743c244f437e4f8ba93bbf7b11b53f7824c_large.gif';
10
11
  return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
11
12
  children: [
12
13
  /*#__PURE__*/ jsxRuntime.jsxs(Head, {
@@ -36,17 +37,17 @@ const AdaptiveImage = ({ srcSet, priority, pictureClass, imagePlaceholder, ...re
36
37
  children: [
37
38
  !!srcSet?.mobile?.src && /*#__PURE__*/ jsxRuntime.jsx("source", {
38
39
  media: "(max-width: 767px)",
39
- srcSet: srcSet.mobile.src
40
+ srcSet: srcSet.mobile.src || fallBackImage
40
41
  }),
41
42
  !!srcSet?.tablet?.src && /*#__PURE__*/ jsxRuntime.jsx("source", {
42
43
  media: "(max-width: 1024px)",
43
- srcSet: srcSet?.tablet?.src
44
+ srcSet: srcSet?.tablet?.src || fallBackImage
44
45
  }),
45
46
  /*#__PURE__*/ jsxRuntime.jsx(Img.default, {
46
47
  ...rest,
47
48
  loading: priority ? 'eager' : 'lazy',
48
49
  image: {
49
- src: srcSet?.desktop?.src || imagePlaceholder?.['desktop']
50
+ src: srcSet?.desktop?.src || imagePlaceholder?.['desktop'] || fallBackImage
50
51
  }
51
52
  })
52
53
  ]
@@ -18,9 +18,17 @@ const ProductTag = ({ setting, styles, builderProps, ...passProps })=>{
18
18
  const mode = core.useEditorMode();
19
19
  const product = core.useProduct();
20
20
  const currentVariant = core.useCurrentVariant();
21
+ const { quantity, hasUpdatePrice } = core.useQuantity();
22
+ const quantityProduct = React.useMemo(()=>{
23
+ return hasUpdatePrice && customContent?.unit !== 'percentage' ? quantity || 1 : 1;
24
+ }, [
25
+ hasUpdatePrice,
26
+ customContent?.unit,
27
+ quantity
28
+ ]);
21
29
  const price = currentVariant?.price ?? 0;
22
30
  const salePrice = currentVariant?.salePrice ?? 0;
23
- const priceSave = salePrice - price;
31
+ const priceSave = (salePrice - price) * quantityProduct;
24
32
  const moneyFormatted = core.useFormatMoney(priceSave < 0 ? 0 : priceSave, setting?.moneyWithCurrentFormat || false);
25
33
  const customDisplayWithUnit = React.useMemo(()=>{
26
34
  if (!salePrice && salePrice !== 0 || !price && price !== 0) return `${customContent?.unit === 'percentage' ? '0%' : moneyFormatted}`;
@@ -2,15 +2,41 @@ const AutoPlayPlugin = (slider)=>{
2
2
  let autoplay = slider.options.autoplay ?? {};
3
3
  let timeout;
4
4
  let mouseOver = false;
5
+ let inViewport = false;
6
+ let observer = null;
7
+ // Thêm sự kiện cho dot nếu có
8
+ const $dots = slider?.container?.closest('[data-component-type="component"]')?.querySelector('.carousel-dots');
9
+ // Callback của IntersectionObserver
10
+ function handleIntersection(entries) {
11
+ entries.forEach((entry)=>{
12
+ // Với threshold 0, chỉ cần một phần bất kỳ hiển thị sẽ đánh dấu isIntersecting
13
+ inViewport = entry.isIntersecting;
14
+ if (inViewport) {
15
+ nextTimeout();
16
+ } else {
17
+ clearNextTimeout();
18
+ }
19
+ });
20
+ }
21
+ function initIntersectionObserver() {
22
+ // Nếu đã có observer hoặc không có container thì không khởi tạo lại
23
+ if (observer || !slider.container) return;
24
+ observer = new IntersectionObserver(handleIntersection, {
25
+ root: null,
26
+ threshold: 0
27
+ });
28
+ observer.observe(slider.container);
29
+ }
5
30
  function clearNextTimeout() {
6
31
  clearTimeout(timeout);
7
32
  }
8
33
  function nextTimeout() {
9
- clearTimeout(timeout);
34
+ clearNextTimeout();
10
35
  timeout = setTimeout(()=>{
11
- if (mouseOver || !autoplay.enable) return;
36
+ // Nếu đang hover, autoplay bị tắt hoặc slider không trong viewport thì không chuyển slide
37
+ if (mouseOver || !autoplay.enable || !inViewport) return;
12
38
  slider.next();
13
- }, autoplay?.delay ?? 2000);
39
+ }, autoplay.delay ?? 2000);
14
40
  }
15
41
  function onMouseOver() {
16
42
  mouseOver = true;
@@ -21,11 +47,15 @@ const AutoPlayPlugin = (slider)=>{
21
47
  nextTimeout();
22
48
  }
23
49
  slider.on('created', ()=>{
24
- if (autoplay.pauseOnHover) {
50
+ $dots?.addEventListener('mouseover', onMouseOver);
51
+ $dots?.addEventListener('mouseout', onMouseOut);
52
+ // Nếu tùy chọn pauseOnHover bật thì thêm sự kiện hover cho container slider
53
+ if (autoplay.pauseOnHover && slider.container) {
25
54
  slider.container.addEventListener('mouseover', onMouseOver);
26
55
  slider.container.addEventListener('mouseout', onMouseOut);
27
56
  }
28
- if (slider.options.autoplay?.enable) {
57
+ if (autoplay.enable) {
58
+ initIntersectionObserver();
29
59
  nextTimeout();
30
60
  }
31
61
  });
@@ -33,29 +63,40 @@ const AutoPlayPlugin = (slider)=>{
33
63
  slider.on('animationEnded', nextTimeout);
34
64
  slider.on('updated', (newOptions)=>{
35
65
  autoplay = newOptions.options?.autoplay ?? {};
36
- nextTimeout();
37
- });
38
- slider.on('destroyed', ()=>{
39
- slider.container.removeEventListener('mouseover', onMouseOver);
40
- slider.container.removeEventListener('mouseout', onMouseOut);
66
+ if (autoplay.enable) {
67
+ initIntersectionObserver();
68
+ nextTimeout();
69
+ }
41
70
  });
42
71
  slider.on('optionsChanged', (newOptions)=>{
43
- const $dots = slider?.container?.closest('[data-component-type="component"]')?.querySelector('.carousel-dots');
44
- $dots?.addEventListener('mouseover', onMouseOver);
45
- $dots?.addEventListener('mouseout', onMouseOut);
46
- if (newOptions.options.autoplay?.pauseOnHover !== autoplay.pauseOnHover) {
72
+ if (newOptions.options.autoplay?.pauseOnHover !== autoplay.pauseOnHover && slider.container) {
47
73
  if (newOptions.options.autoplay?.pauseOnHover) {
48
74
  slider.container.addEventListener('mouseover', onMouseOver);
49
75
  slider.container.addEventListener('mouseout', onMouseOut);
50
- }
51
- if (!newOptions.options.autoplay?.pauseOnHover) {
76
+ } else {
52
77
  slider.container.removeEventListener('mouseover', onMouseOver);
53
78
  slider.container.removeEventListener('mouseout', onMouseOut);
54
79
  mouseOver = false;
55
80
  }
56
81
  }
57
- nextTimeout();
58
82
  autoplay = newOptions.options.autoplay ?? {};
83
+ if (autoplay.enable) {
84
+ initIntersectionObserver();
85
+ nextTimeout();
86
+ }
87
+ });
88
+ slider.on('destroyed', ()=>{
89
+ if (slider.container) {
90
+ slider.container.removeEventListener('mouseover', onMouseOver);
91
+ slider.container.removeEventListener('mouseout', onMouseOut);
92
+ }
93
+ $dots?.removeEventListener('mouseover', onMouseOver);
94
+ $dots?.removeEventListener('mouseout', onMouseOut);
95
+ clearNextTimeout();
96
+ if (observer) {
97
+ observer.disconnect();
98
+ observer = null;
99
+ }
59
100
  });
60
101
  };
61
102
 
@@ -3,6 +3,7 @@ import Head from 'next/head';
3
3
  import Img from './Img.js';
4
4
 
5
5
  const AdaptiveImage = ({ srcSet, priority, pictureClass, imagePlaceholder, ...rest })=>{
6
+ const fallBackImage = 'https://cdn.shopify.com/s/assets/no-image-2048-5e88c1b20e087fb7bbe9a3771824e743c244f437e4f8ba93bbf7b11b53f7824c_large.gif';
6
7
  return /*#__PURE__*/ jsxs(Fragment, {
7
8
  children: [
8
9
  /*#__PURE__*/ jsxs(Head, {
@@ -32,17 +33,17 @@ const AdaptiveImage = ({ srcSet, priority, pictureClass, imagePlaceholder, ...re
32
33
  children: [
33
34
  !!srcSet?.mobile?.src && /*#__PURE__*/ jsx("source", {
34
35
  media: "(max-width: 767px)",
35
- srcSet: srcSet.mobile.src
36
+ srcSet: srcSet.mobile.src || fallBackImage
36
37
  }),
37
38
  !!srcSet?.tablet?.src && /*#__PURE__*/ jsx("source", {
38
39
  media: "(max-width: 1024px)",
39
- srcSet: srcSet?.tablet?.src
40
+ srcSet: srcSet?.tablet?.src || fallBackImage
40
41
  }),
41
42
  /*#__PURE__*/ jsx(Img, {
42
43
  ...rest,
43
44
  loading: priority ? 'eager' : 'lazy',
44
45
  image: {
45
- src: srcSet?.desktop?.src || imagePlaceholder?.['desktop']
46
+ src: srcSet?.desktop?.src || imagePlaceholder?.['desktop'] || fallBackImage
46
47
  }
47
48
  })
48
49
  ]
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
- import { useMoneyFormat, makeGlobalSize, useEditorMode, useProduct, useCurrentVariant, useFormatMoney, useProductBundleDiscount, formatMoney, getGlobalColorClass, cls, makeStyleResponsive, makeStyle, getRadiusCSSFromGlobal, getCustomRadius } from '@gem-sdk/core';
2
+ import { useMoneyFormat, makeGlobalSize, useEditorMode, useProduct, useCurrentVariant, useQuantity, useFormatMoney, useProductBundleDiscount, formatMoney, getGlobalColorClass, cls, makeStyleResponsive, makeStyle, getRadiusCSSFromGlobal, getCustomRadius } from '@gem-sdk/core';
3
3
  import { useMemo } from 'react';
4
4
  import Text from '../../text/components/Text.js';
5
5
  import { DiscountType } from './constants/product-bundle.js';
@@ -14,9 +14,17 @@ const ProductTag = ({ setting, styles, builderProps, ...passProps })=>{
14
14
  const mode = useEditorMode();
15
15
  const product = useProduct();
16
16
  const currentVariant = useCurrentVariant();
17
+ const { quantity, hasUpdatePrice } = useQuantity();
18
+ const quantityProduct = useMemo(()=>{
19
+ return hasUpdatePrice && customContent?.unit !== 'percentage' ? quantity || 1 : 1;
20
+ }, [
21
+ hasUpdatePrice,
22
+ customContent?.unit,
23
+ quantity
24
+ ]);
17
25
  const price = currentVariant?.price ?? 0;
18
26
  const salePrice = currentVariant?.salePrice ?? 0;
19
- const priceSave = salePrice - price;
27
+ const priceSave = (salePrice - price) * quantityProduct;
20
28
  const moneyFormatted = useFormatMoney(priceSave < 0 ? 0 : priceSave, setting?.moneyWithCurrentFormat || false);
21
29
  const customDisplayWithUnit = useMemo(()=>{
22
30
  if (!salePrice && salePrice !== 0 || !price && price !== 0) return `${customContent?.unit === 'percentage' ? '0%' : moneyFormatted}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gem-sdk/components",
3
- "version": "2.1.36",
3
+ "version": "2.1.38",
4
4
  "license": "MIT",
5
5
  "sideEffects": false,
6
6
  "main": "dist/cjs/index.js",