@automattic/jetpack-components 0.71.0 → 0.72.1

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 (40) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/build/components/action-button/index.d.ts +10 -10
  3. package/build/components/action-button/index.js +1 -1
  4. package/build/components/action-popover/types.d.ts +0 -1
  5. package/build/components/admin-page/types.d.ts +0 -1
  6. package/build/components/admin-section/types.d.ts +0 -1
  7. package/build/components/boost-score-graph/uplot-line-chart.js +2 -2
  8. package/build/components/dot-pager/index.d.ts +14 -0
  9. package/build/components/dot-pager/index.js +52 -0
  10. package/build/components/icon-tooltip/index.js +1 -1
  11. package/build/components/icon-tooltip/types.d.ts +0 -1
  12. package/build/components/layout/types.d.ts +0 -1
  13. package/build/components/number-control/index.js +1 -1
  14. package/build/components/popover/index.d.ts +0 -1
  15. package/build/components/pricing-table/types.d.ts +0 -1
  16. package/build/components/product-offer/types.d.ts +0 -1
  17. package/build/components/product-price/types.d.ts +0 -1
  18. package/build/components/radio-control/index.d.ts +0 -1
  19. package/build/components/spinner/index.d.ts +3 -3
  20. package/build/components/spinner/index.js +1 -1
  21. package/build/components/split-button/types.d.ts +0 -1
  22. package/build/components/stat-card/types.d.ts +0 -1
  23. package/build/components/status/index.d.ts +0 -1
  24. package/build/components/swipeable/index.d.ts +12 -0
  25. package/build/components/swipeable/index.js +293 -0
  26. package/build/components/terms-of-service/index.d.ts +0 -1
  27. package/build/components/theme-provider/types.d.ts +0 -1
  28. package/build/components/toggle-control/index.d.ts +0 -1
  29. package/build/index.d.ts +1 -0
  30. package/build/index.js +1 -0
  31. package/components/action-button/index.jsx +1 -1
  32. package/components/dot-pager/README.md +20 -0
  33. package/components/dot-pager/index.tsx +147 -0
  34. package/components/dot-pager/style.scss +80 -0
  35. package/components/number-control/index.jsx +3 -1
  36. package/components/swipeable/README.md +34 -0
  37. package/components/swipeable/index.tsx +425 -0
  38. package/components/swipeable/style.scss +34 -0
  39. package/index.ts +1 -0
  40. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -2,9 +2,23 @@
2
2
 
3
3
  ### This is a list detailing changes for the Jetpack RNA Components package releases.
4
4
 
5
+ ## [0.72.1] - 2025-04-01
6
+ ### Changed
7
+ - Update package dependencies. [#42762]
8
+
9
+ ## [0.72.0] - 2025-03-31
10
+ ### Added
11
+ - Add dot-page component. [#42625]
12
+
13
+ ### Changed
14
+ - Implement a continuous loop in testimonial slider. [#42781]
15
+
16
+ ### Fixed
17
+ - Components: Prevent deprecation notices by adding `__next40pxDefaultSize` to controls. [#42677]
18
+
5
19
  ## [0.71.0] - 2025-03-26
6
20
  ### Removed
7
- - Removed threat components [#41654]
21
+ - Remove threat components. [#41654]
8
22
 
9
23
  ### Fixed
10
24
  - Split Button: Increase compatibility of components, preventing console warnings. [#42627]
@@ -1333,6 +1347,8 @@
1333
1347
  ### Changed
1334
1348
  - Update node version requirement to 14.16.1
1335
1349
 
1350
+ [0.72.1]: https://github.com/Automattic/jetpack-components/compare/0.72.0...0.72.1
1351
+ [0.72.0]: https://github.com/Automattic/jetpack-components/compare/0.71.0...0.72.0
1336
1352
  [0.71.0]: https://github.com/Automattic/jetpack-components/compare/0.70.1...0.71.0
1337
1353
  [0.70.1]: https://github.com/Automattic/jetpack-components/compare/0.70.0...0.70.1
1338
1354
  [0.70.0]: https://github.com/Automattic/jetpack-components/compare/0.69.1...0.70.0
@@ -7,19 +7,19 @@ export default ActionButton;
7
7
  * It is useful to async actions when the user has to wait the result of a request or process.
8
8
  *
9
9
  * @param {object} props - The properties.
10
- * @return {React.Component} The `ActionButton` component.
10
+ * @return {React.ReactNode} The `ActionButton` component.
11
11
  */
12
- declare function ActionButton(props: object): React.Component;
12
+ declare function ActionButton(props: object): React.ReactNode;
13
13
  declare namespace ActionButton {
14
14
  namespace propTypes {
15
- const label: any;
16
- const onClick: any;
17
- const isLoading: any;
18
- const isDisabled: any;
19
- const displayError: any;
20
- const errorMessage: any;
21
- const variant: any;
22
- const isExternalLink: any;
15
+ let label: any;
16
+ let onClick: any;
17
+ let isLoading: any;
18
+ let isDisabled: any;
19
+ let displayError: any;
20
+ let errorMessage: any;
21
+ let variant: any;
22
+ let isExternalLink: any;
23
23
  }
24
24
  }
25
25
  import React from 'react';
@@ -20,7 +20,7 @@ import styles from './style.module.scss';
20
20
  * It is useful to async actions when the user has to wait the result of a request or process.
21
21
  *
22
22
  * @param {object} props - The properties.
23
- * @return {React.Component} The `ActionButton` component.
23
+ * @return {React.ReactNode} The `ActionButton` component.
24
24
  */
25
25
  const ActionButton = props => {
26
26
  const { label, onClick, isLoading = false, loadingText, isDisabled, displayError = false, errorMessage = __('An error occurred. Please try again.', 'jetpack-components'), variant = 'primary', isExternalLink = false, customClass, } = props;
@@ -1,4 +1,3 @@
1
- /// <reference types="react" resolution-mode="require"/>
2
1
  /**
3
2
  * Types
4
3
  */
@@ -1,4 +1,3 @@
1
- /// <reference types="react" resolution-mode="require"/>
2
1
  import type { JetpackFooterMenuItem } from '../jetpack-footer/types.js';
3
2
  export type AdminPageProps = {
4
3
  /**
@@ -1,4 +1,3 @@
1
- /// <reference types="react" resolution-mode="require"/>
2
1
  export type AdminSectionBaseProps = {
3
2
  /**
4
3
  * The section content
@@ -33,7 +33,7 @@ function createSerieInfo(label, score) {
33
33
  gradient.addColorStop(0, getColor(score, '44'));
34
34
  gradient.addColorStop(1, getColor(score, '11'));
35
35
  return gradient;
36
- },
36
+ }, // use the gradient as fill for the series
37
37
  width: 2,
38
38
  paths: (u, seriesIdx, idx0, idx1) => {
39
39
  return spline?.()(u, seriesIdx, idx0, idx1) || null;
@@ -115,7 +115,7 @@ export default function UplotLineChart({ range, periods, annotations = [] }) {
115
115
  space: 100,
116
116
  size: 30,
117
117
  grid: {
118
- stroke: 'rgba(220, 220, 222, 0.5)',
118
+ stroke: 'rgba(220, 220, 222, 0.5)', // #DCDCDE with 0.5 opacity
119
119
  width: 1,
120
120
  },
121
121
  ticks: {
@@ -0,0 +1,14 @@
1
+ import { ReactNode } from 'react';
2
+ import './style.scss';
3
+ type DotPagerProps = {
4
+ hasDynamicHeight?: boolean;
5
+ children: ReactNode;
6
+ className?: string;
7
+ onPageSelected?: (index: number) => void;
8
+ isClickEnabled?: boolean;
9
+ rotateTime?: number;
10
+ tracksPrefix?: string;
11
+ tracksFn?: (eventName: string, data?: Record<string, unknown>) => void;
12
+ };
13
+ declare const DotPager: ({ hasDynamicHeight, children, className, onPageSelected, isClickEnabled, rotateTime, tracksPrefix, tracksFn, ...props }: DotPagerProps) => import("react/jsx-runtime").JSX.Element;
14
+ export default DotPager;
@@ -0,0 +1,52 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button } from '@wordpress/components';
3
+ import { __, sprintf } from '@wordpress/i18n';
4
+ import clsx from 'clsx';
5
+ import { Children, useState, useEffect, useCallback, useMemo } from 'react';
6
+ import { Swipeable } from '../swipeable/index.js';
7
+ import './style.scss';
8
+ const Controls = ({ currentPage, numberOfPages, setCurrentPage, tracksPrefix, tracksFn, }) => {
9
+ // Create a map of memoized handlers for each page
10
+ const pageHandlers = useMemo(() => Array.from({ length: numberOfPages }, (_, page) => () => {
11
+ tracksFn(tracksPrefix + '_dot_click', {
12
+ current_page: currentPage,
13
+ destination_page: page,
14
+ });
15
+ setCurrentPage(page);
16
+ }), [numberOfPages, currentPage, tracksFn, tracksPrefix, setCurrentPage]);
17
+ if (numberOfPages < 2) {
18
+ return null;
19
+ }
20
+ return (_jsx("ul", { className: "dot-pager__controls", "aria-label": __('Pager controls', 'jetpack-components'), children: Array.from({ length: numberOfPages }, (_, page) => (_jsx("li", { "aria-current": page === currentPage ? 'page' : undefined, children: _jsx(Button, { className: clsx('dot-pager__control-choose-page', {
21
+ 'dot-pager__control-current': page === currentPage,
22
+ }), disabled: page === currentPage, "aria-label": sprintf(
23
+ /* translators: %1$d: current page number, %2$d: total number of pages */
24
+ __('Page %1$d of %2$d', 'jetpack-components'), page + 1, numberOfPages), onClick: pageHandlers[page] }, page.toString()) }, `page-${page}`))) }));
25
+ };
26
+ const DotPager = ({ hasDynamicHeight = false, children, className = '', onPageSelected, isClickEnabled = false, rotateTime = 0, tracksPrefix = '', tracksFn = () => { }, ...props }) => {
27
+ const normalizedChildren = Children.toArray(children).filter(Boolean);
28
+ const [currentPage, setCurrentPage] = useState(0);
29
+ const [isPaused, setIsPaused] = useState(false);
30
+ const numPages = Children.count(normalizedChildren);
31
+ useEffect(() => {
32
+ if (rotateTime > 0 && numPages > 1 && !isPaused) {
33
+ const timerId = setTimeout(() => {
34
+ // Add 1 to numPages to account for the clones
35
+ setCurrentPage((currentPage + 1) % (numPages + 1));
36
+ }, rotateTime * 1000);
37
+ return () => clearTimeout(timerId);
38
+ }
39
+ }, [currentPage, numPages, rotateTime, isPaused]);
40
+ const handleSelectPage = useCallback((index) => {
41
+ setCurrentPage(index);
42
+ onPageSelected?.(index);
43
+ }, [onPageSelected]);
44
+ const handleMouseEnter = useCallback(() => {
45
+ setIsPaused(true);
46
+ }, []);
47
+ const handleMouseLeave = useCallback(() => {
48
+ setIsPaused(false);
49
+ }, []);
50
+ return (_jsxs("div", { className: clsx('dot-pager', className), onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, ...props, children: [_jsx(Controls, { currentPage: currentPage, numberOfPages: numPages, setCurrentPage: handleSelectPage, tracksPrefix: tracksPrefix, tracksFn: tracksFn }), _jsx(Swipeable, { hasDynamicHeight: hasDynamicHeight, onPageSelect: handleSelectPage, currentPage: currentPage, pageClassName: "dot-pager__page", containerClassName: "dot-pager__pages", isClickEnabled: isClickEnabled, children: normalizedChildren })] }));
51
+ };
52
+ export default DotPager;
@@ -39,7 +39,7 @@ const IconTooltip = ({ className = '', iconClassName = '', placement = 'bottom-e
39
39
  noArrow: false,
40
40
  resize: false,
41
41
  flip: false,
42
- offset,
42
+ offset, // The distance (in px) between the anchor and the popover.
43
43
  focusOnMount: 'container',
44
44
  onClose: hideTooltip,
45
45
  className: 'icon-tooltip-container',
@@ -1,4 +1,3 @@
1
- /// <reference types="react" resolution-mode="require"/>
2
1
  export type Placement = 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end';
3
2
  export type Position = 'top left' | 'top center' | 'top right' | 'bottom left' | 'bottom center' | 'bottom right';
4
3
  export type IconTooltipProps = {
@@ -1,4 +1,3 @@
1
- /// <reference types="react" resolution-mode="require"/>
2
1
  type ColSpan = number | {
3
2
  start: number;
4
3
  end: number;
@@ -11,6 +11,6 @@ TextControl, } from '@wordpress/components';
11
11
  */
12
12
  const NumberControl = ExperimentalNumberControl ||
13
13
  function CustomNumberControl(props) {
14
- return _jsx(TextControl, { type: "number", inputMode: "numeric", ...props });
14
+ return (_jsx(TextControl, { type: "number", inputMode: "numeric", ...props, __next40pxDefaultSize: true }));
15
15
  };
16
16
  export default NumberControl;
@@ -1,4 +1,3 @@
1
- /// <reference types="react" resolution-mode="require"/>
2
1
  import './style.scss';
3
2
  type PopoverProps = {
4
3
  action: React.ReactNode;
@@ -1,4 +1,3 @@
1
- /// <reference types="react" resolution-mode="require"/>
2
1
  import type { Placement } from '../icon-tooltip/types.js';
3
2
  export type PricingTableProps = {
4
3
  /**
@@ -1,4 +1,3 @@
1
- /// <reference types="react" resolution-mode="require"/>
2
1
  import { ProductPriceProps } from '../product-price/types.js';
3
2
  import type { JetpackIconSlug } from '../icons/index.js';
4
3
  export type ProductOfferProps = {
@@ -1,4 +1,3 @@
1
- /// <reference types="react" resolution-mode="require"/>
2
1
  export type ProductPriceProps = {
3
2
  /**
4
3
  * Product price.
@@ -1,4 +1,3 @@
1
- /// <reference types="react" resolution-mode="require"/>
2
1
  interface RadioControlProps {
3
2
  /** The current value. */
4
3
  selected: string;
@@ -6,8 +6,8 @@ declare function Spinner({ color, className, size }: {
6
6
  }): import("react/jsx-runtime").JSX.Element;
7
7
  declare namespace Spinner {
8
8
  namespace propTypes {
9
- const color: any;
10
- const className: any;
11
- const size: any;
9
+ let color: any;
10
+ let className: any;
11
+ let size: any;
12
12
  }
13
13
  }
@@ -7,7 +7,7 @@ const Spinner = ({ color = '#FFFFFF', className = '', size = 20 }) => {
7
7
  const styleOuter = {
8
8
  width: size,
9
9
  height: size,
10
- fontSize: size,
10
+ fontSize: size, // allows border-width to be specified in em units
11
11
  borderTopColor: color,
12
12
  };
13
13
  const styleInner = {
@@ -1,4 +1,3 @@
1
- /// <reference types="react" resolution-mode="require"/>
2
1
  /**
3
2
  * External dependencies
4
3
  */
@@ -1,4 +1,3 @@
1
- /// <reference types="react" resolution-mode="require"/>
2
1
  export type StatCardProps = {
3
2
  /**
4
3
  * Custom className to be inserted.
@@ -1,4 +1,3 @@
1
- /// <reference types="react" resolution-mode="require"/>
2
1
  interface StatusProps {
3
2
  status?: 'active' | 'error' | 'inactive' | 'action' | 'initializing';
4
3
  label?: string;
@@ -0,0 +1,12 @@
1
+ import './style.scss';
2
+ export declare const Swipeable: ({ hasDynamicHeight, children, currentPage, onPageSelect, pageClassName, containerClassName, isClickEnabled, ...otherProps }: {
3
+ [x: string]: any;
4
+ hasDynamicHeight?: boolean;
5
+ children: any;
6
+ currentPage?: number;
7
+ onPageSelect: any;
8
+ pageClassName: any;
9
+ containerClassName: any;
10
+ isClickEnabled: any;
11
+ }) => import("react/jsx-runtime").JSX.Element;
12
+ export default Swipeable;
@@ -0,0 +1,293 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import clsx from 'clsx';
3
+ import { Children, useState, useLayoutEffect, useRef, useCallback, useEffect } from 'react';
4
+ import './style.scss';
5
+ const OFFSET_THRESHOLD_PERCENTAGE = 0.35; // Percentage of width to travel before we trigger the slider to move to the desired slide.
6
+ const VELOCITY_THRESHOLD = 0.2; // Speed of drag above, before we trigger the slider to move to the desired slide.
7
+ const VERTICAL_THRESHOLD_ANGLE = 55;
8
+ const TRANSITION_DURATION = '300ms';
9
+ /**
10
+ * Custom hook to observe and handle resize events on a DOM element.
11
+ *
12
+ * @return {[React.Dispatch<React.SetStateAction<HTMLElement | null>>, ResizeObserverEntry | null]} Tuple containing setter and entry
13
+ */
14
+ function useResizeObserver() {
15
+ const [observerEntry, setObserverEntry] = useState(null);
16
+ const [node, setNode] = useState(null);
17
+ const observer = useRef(null);
18
+ const disconnect = useCallback(() => observer.current?.disconnect(), []);
19
+ const observe = useCallback(() => {
20
+ observer.current = new ResizeObserver(([entry]) => setObserverEntry(entry));
21
+ if (node) {
22
+ observer.current.observe(node);
23
+ }
24
+ }, [node]);
25
+ useLayoutEffect(() => {
26
+ observe();
27
+ return () => disconnect();
28
+ }, [disconnect, observe]);
29
+ return [setNode, observerEntry];
30
+ }
31
+ /**
32
+ * Gets the drag position and timestamp from a mouse, touch, or pointer event.
33
+ *
34
+ * @param {Event} event - The event object from the drag/touch/pointer interaction
35
+ * @return {object} Object containing x, y coordinates and timestamp of the event
36
+ */
37
+ function getDragPositionAndTime(event) {
38
+ const { timeStamp } = event;
39
+ if (Object.prototype.hasOwnProperty.call(event, 'clientX')) {
40
+ return { x: event.clientX, y: event.clientY, timeStamp };
41
+ }
42
+ if (event.targetTouches[0]) {
43
+ return {
44
+ x: event.targetTouches[0].clientX,
45
+ y: event.targetTouches[0].clientY,
46
+ timeStamp,
47
+ };
48
+ }
49
+ const touch = event.changedTouches[0];
50
+ return { x: touch.clientX, y: touch.clientY, timeStamp };
51
+ }
52
+ /**
53
+ * Calculates the total width needed for all pages in the swipeable component.
54
+ *
55
+ * @param {number} pageWidth - The width of a single page
56
+ * @param {number} numPages - The total number of pages
57
+ * @return {number|null} The total width of all pages or null if pageWidth is not available
58
+ */
59
+ function getPagesWidth(pageWidth, numPages) {
60
+ if (!pageWidth) {
61
+ return null;
62
+ }
63
+ return pageWidth * numPages;
64
+ }
65
+ export const Swipeable = ({ hasDynamicHeight = false, children, currentPage = 0, onPageSelect, pageClassName, containerClassName, isClickEnabled, ...otherProps }) => {
66
+ const prevPageRef = useRef(currentPage);
67
+ const [swipeableArea, setSwipeableArea] = useState(null);
68
+ // TODO: Needs to be added RTL support
69
+ const isRtl = false;
70
+ const [resizeObserverRef, entry] = useResizeObserver();
71
+ const [isTransitioning, setIsTransitioning] = useState(false);
72
+ const [pagesStyle, setPagesStyle] = useState({
73
+ transitionDuration: TRANSITION_DURATION,
74
+ });
75
+ const [dragData, setDragData] = useState(null);
76
+ const pagesRef = useRef(null);
77
+ const numPages = Children.count(children);
78
+ const containerWidth = entry?.contentRect?.width;
79
+ useEffect(() => {
80
+ let timeoutId;
81
+ if ((currentPage === 0 && prevPageRef.current === numPages) ||
82
+ (currentPage === numPages - 1 && prevPageRef.current === -1)) {
83
+ // We are in a real slide after being transitioned from a clone
84
+ // we need to set again the transitionDuration to TRANSITION_DURATION
85
+ // But we need to wait a little bit to avoid enabling it before
86
+ // we moved to the real slide
87
+ timeoutId = setTimeout(() => {
88
+ setPagesStyle(prev => ({ ...prev, transitionDuration: TRANSITION_DURATION }));
89
+ }, 500);
90
+ }
91
+ else if (currentPage === numPages || currentPage < 0) {
92
+ // In a clone slide. Start the transition to the real slide
93
+ setIsTransitioning(true);
94
+ }
95
+ prevPageRef.current = currentPage;
96
+ return () => {
97
+ if (timeoutId) {
98
+ clearTimeout(timeoutId);
99
+ }
100
+ };
101
+ }, [currentPage, numPages]);
102
+ const getOffset = useCallback(index => {
103
+ // Adjust offset to account for the cloned element at the beginning
104
+ const adjustedIndex = index + 1;
105
+ const offset = containerWidth * adjustedIndex;
106
+ return isRtl ? offset : -offset;
107
+ }, [isRtl, containerWidth]);
108
+ const updateEnabled = hasDynamicHeight && numPages > 1;
109
+ // Generate a property that denotes the order of the cards, in order to recalculate height whenever the card order changes.
110
+ const childrenOrder = children.reduce((acc, child) => acc + child.key, '');
111
+ useLayoutEffect(() => {
112
+ if (!updateEnabled) {
113
+ // This is a fix for a bug when you have >1 pages and it update the component to just one but the height is still
114
+ // Related to https://github.com/Automattic/dotcom-forge/issues/2033
115
+ if (pagesStyle?.height) {
116
+ setPagesStyle({ ...pagesStyle, height: undefined });
117
+ }
118
+ return;
119
+ }
120
+ const targetHeight = pagesRef.current?.querySelector('.is-current')
121
+ ?.offsetHeight;
122
+ if (targetHeight && pagesStyle?.height !== targetHeight) {
123
+ setPagesStyle({ ...pagesStyle, height: targetHeight });
124
+ }
125
+ }, [pagesRef, currentPage, pagesStyle, updateEnabled, containerWidth, childrenOrder]);
126
+ const resetDragData = useCallback(() => {
127
+ setPagesStyle(prev => ({
128
+ ...prev,
129
+ transitionDuration: TRANSITION_DURATION,
130
+ }));
131
+ setDragData(null);
132
+ }, [setPagesStyle, setDragData]);
133
+ const handleDragStart = useCallback(event => {
134
+ const position = getDragPositionAndTime(event);
135
+ setSwipeableArea(pagesRef.current?.getBoundingClientRect());
136
+ setDragData({ start: position });
137
+ setPagesStyle(prev => ({ ...prev, transitionDuration: `0ms` })); // Set transition Duration to 0 for smooth dragging.
138
+ }, [setPagesStyle, setDragData]);
139
+ const hasSwipedToNextPage = useCallback(delta => (isRtl ? delta > 0 : delta < 0), [isRtl]);
140
+ const hasSwipedToPreviousPage = useCallback(delta => (isRtl ? delta < 0 : delta > 0), [isRtl]);
141
+ const handleTransitionEnd = useCallback(() => {
142
+ if (!isTransitioning) {
143
+ return;
144
+ }
145
+ setIsTransitioning(false);
146
+ // If we're on the clone slides, jump to the corresponding real slide
147
+ // We set the transitionDuration to 0ms to make invisible the
148
+ // change from the clone to the real slide
149
+ if (currentPage >= numPages) {
150
+ setPagesStyle(prev => ({ ...prev, transitionDuration: '0ms' }));
151
+ onPageSelect(0);
152
+ }
153
+ else if (currentPage < 0) {
154
+ setPagesStyle(prev => ({ ...prev, transitionDuration: '0ms' }));
155
+ onPageSelect(numPages - 1);
156
+ }
157
+ }, [currentPage, numPages, onPageSelect, isTransitioning]);
158
+ const handleDragEnd = useCallback(event => {
159
+ if (!dragData) {
160
+ return; // End early if we are not dragging any more.
161
+ }
162
+ let dragPosition = getDragPositionAndTime(event);
163
+ if (dragPosition.x === 0) {
164
+ dragPosition = dragData.last;
165
+ }
166
+ const delta = dragPosition.x - dragData.start.x;
167
+ const absoluteDelta = Math.abs(delta);
168
+ const velocity = absoluteDelta / (dragPosition.timeStamp - dragData.start.timeStamp);
169
+ const verticalAbsoluteDelta = Math.abs(dragPosition.y - dragData.start.y);
170
+ const angle = (Math.atan2(verticalAbsoluteDelta, absoluteDelta) * 180) / Math.PI;
171
+ // Is click or tap?
172
+ if (velocity === 0 && isClickEnabled) {
173
+ onPageSelect((currentPage + 1) % numPages);
174
+ resetDragData();
175
+ return;
176
+ }
177
+ // Is vertical scroll detected?
178
+ if (angle > VERTICAL_THRESHOLD_ANGLE) {
179
+ resetDragData();
180
+ return;
181
+ }
182
+ const hasMetThreshold = absoluteDelta > OFFSET_THRESHOLD_PERCENTAGE * containerWidth ||
183
+ velocity > VELOCITY_THRESHOLD;
184
+ let newIndex = currentPage;
185
+ if (hasMetThreshold) {
186
+ if (hasSwipedToNextPage(delta)) {
187
+ newIndex = currentPage + 1;
188
+ if (newIndex >= numPages) {
189
+ setIsTransitioning(true);
190
+ }
191
+ }
192
+ else if (hasSwipedToPreviousPage(delta)) {
193
+ newIndex = currentPage - 1;
194
+ if (newIndex < 0) {
195
+ setIsTransitioning(true);
196
+ }
197
+ }
198
+ }
199
+ setPagesStyle(prev => ({
200
+ ...prev,
201
+ transform: `translate3d(${getOffset(newIndex)}px, 0px, 0px)`,
202
+ transitionDuration: TRANSITION_DURATION,
203
+ }));
204
+ onPageSelect(newIndex);
205
+ setDragData(null);
206
+ }, [
207
+ currentPage,
208
+ dragData,
209
+ hasSwipedToNextPage,
210
+ hasSwipedToPreviousPage,
211
+ numPages,
212
+ onPageSelect,
213
+ setPagesStyle,
214
+ containerWidth,
215
+ isClickEnabled,
216
+ resetDragData,
217
+ getOffset,
218
+ ]);
219
+ const handleDrag = useCallback(event => {
220
+ if (!dragData) {
221
+ return;
222
+ }
223
+ const dragPosition = getDragPositionAndTime(event);
224
+ const delta = dragPosition.x - dragData.start.x;
225
+ const absoluteDelta = Math.abs(delta);
226
+ const offset = getOffset(currentPage) + delta;
227
+ setDragData({ ...dragData, last: dragPosition });
228
+ // The user needs to swipe horizontally more then 2 px in order for the canvase to be dragging.
229
+ // We do this so that the user can scroll vertically smother.
230
+ if (absoluteDelta < 3) {
231
+ return;
232
+ }
233
+ setPagesStyle(prev => ({
234
+ ...prev,
235
+ transform: `translate3d(${offset}px, 0px, 0px)`,
236
+ transitionDuration: '0ms',
237
+ }));
238
+ if (!swipeableArea) {
239
+ return;
240
+ }
241
+ // Did the user swipe out of the swipeable area?
242
+ if (dragPosition.x < swipeableArea.left ||
243
+ dragPosition.x > swipeableArea.right ||
244
+ dragPosition.y > swipeableArea.bottom ||
245
+ dragPosition.y < swipeableArea.top) {
246
+ handleDragEnd(event);
247
+ }
248
+ }, [dragData, getOffset, currentPage, swipeableArea, handleDragEnd]);
249
+ const getTouchEvents = useCallback(() => {
250
+ if ('onpointerup' in document) {
251
+ return {
252
+ onPointerDown: handleDragStart,
253
+ onPointerMove: handleDrag,
254
+ onPointerUp: handleDragEnd,
255
+ onPointerLeave: handleDragEnd,
256
+ };
257
+ }
258
+ if ('ondragend' in document) {
259
+ return {
260
+ onDragStart: handleDragStart,
261
+ onDrag: handleDrag,
262
+ onDragEnd: handleDragEnd,
263
+ onDragExit: handleDragEnd,
264
+ };
265
+ }
266
+ if ('ontouchend' in document) {
267
+ return {
268
+ onTouchStart: handleDragStart,
269
+ onTouchMove: handleDrag,
270
+ onTouchEnd: handleDragEnd,
271
+ onTouchCancel: handleDragEnd,
272
+ };
273
+ }
274
+ return null;
275
+ }, [handleDragStart, handleDrag, handleDragEnd]);
276
+ const offset = getOffset(currentPage);
277
+ return (_jsxs(_Fragment, { children: [_jsx("div", { ...getTouchEvents(), className: "swipeable__container", ref: pagesRef, ...otherProps, children: _jsxs("div", { className: clsx('swipeable__pages', containerClassName), style: {
278
+ ...pagesStyle,
279
+ width: getPagesWidth(containerWidth, numPages + 2),
280
+ transform: `translate3d(${offset}px, 0px, 0px)`,
281
+ }, onTransitionEnd: handleTransitionEnd, children: [_jsx("div", { style: { width: `${containerWidth}px` }, className: clsx('swipeable__page', pageClassName, {
282
+ 'is-clone': true,
283
+ 'is-prev': currentPage === 0,
284
+ }), children: Children.toArray(children)[numPages - 1] }, `clone-prev-${numPages - 1}`), Children.map(children, (child, index) => (_jsx("div", { style: { width: `${containerWidth}px` }, className: clsx('swipeable__page', pageClassName, {
285
+ 'is-current': index === currentPage,
286
+ 'is-prev': index < currentPage,
287
+ 'is-next': index > currentPage,
288
+ }), "data-testid": `swipeable-page-${index + 1}`, children: child }, `page-${index}`))), _jsx("div", { style: { width: `${containerWidth}px` }, className: clsx('swipeable__page', pageClassName, {
289
+ 'is-clone': true,
290
+ 'is-next': currentPage === numPages - 1,
291
+ }), children: Children.toArray(children)[0] }, `clone-next-0`)] }) }), _jsx("div", { ref: resizeObserverRef, className: "swipeable__resize-observer" })] }));
292
+ };
293
+ export default Swipeable;
@@ -1,4 +1,3 @@
1
- /// <reference types="react" resolution-mode="require"/>
2
1
  import type { TermsOfServiceProps } from './types.js';
3
2
  import './styles.scss';
4
3
  declare const TermsOfService: React.FC<TermsOfServiceProps>;
@@ -1,4 +1,3 @@
1
- /// <reference types="react" resolution-mode="require"/>
2
1
  export type ThemeProviderProps = {
3
2
  /**
4
3
  * An optional id to register and identify the provider instance.
@@ -1,4 +1,3 @@
1
- /// <reference types="react" resolution-mode="require"/>
2
1
  interface ToggleControlProps {
3
2
  /** Whether or not the toggle is currently enabled. */
4
3
  checked?: boolean;
package/build/index.d.ts CHANGED
@@ -45,6 +45,7 @@ export { default as BoostScoreGraph } from './components/boost-score-graph/index
45
45
  export { default as ProductPrice } from './components/product-price/index.js';
46
46
  export { default as ProductOffer, IconsCard } from './components/product-offer/index.js';
47
47
  export { default as Dialog } from './components/dialog/index.js';
48
+ export { default as DotPager } from './components/dot-pager/index.js';
48
49
  export { default as DonutMeter } from './components/donut-meter/index.js';
49
50
  export { default as RecordMeterBar } from './components/record-meter-bar/index.js';
50
51
  export { default as ContextualUpgradeTrigger } from './components/contextual-upgrade-trigger/index.js';
package/build/index.js CHANGED
@@ -59,6 +59,7 @@ export { default as BoostScoreGraph } from './components/boost-score-graph/index
59
59
  export { default as ProductPrice } from './components/product-price/index.js';
60
60
  export { default as ProductOffer, IconsCard } from './components/product-offer/index.js';
61
61
  export { default as Dialog } from './components/dialog/index.js';
62
+ export { default as DotPager } from './components/dot-pager/index.js';
62
63
  export { default as DonutMeter } from './components/donut-meter/index.js';
63
64
  export { default as RecordMeterBar } from './components/record-meter-bar/index.js';
64
65
  export { default as ContextualUpgradeTrigger } from './components/contextual-upgrade-trigger/index.js';
@@ -20,7 +20,7 @@ import styles from './style.module.scss';
20
20
  * It is useful to async actions when the user has to wait the result of a request or process.
21
21
  *
22
22
  * @param {object} props - The properties.
23
- * @return {React.Component} The `ActionButton` component.
23
+ * @return {React.ReactNode} The `ActionButton` component.
24
24
  */
25
25
  const ActionButton = props => {
26
26
  const {