react-swipe-action 0.2.1 → 0.3.2

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/README.md CHANGED
@@ -36,6 +36,7 @@ Use the `SwipeAction` component. The `main` prop is a function that receives a `
36
36
  {handle}
37
37
  </div>
38
38
  )}
39
+ inertia
39
40
  startAction={...}
40
41
  endAction={...}
41
42
  />
@@ -51,6 +52,14 @@ An action is an object with the following properties:
51
52
  - `background`: The background that is shown behind the content.
52
53
  - `onLongSwipe`: A function to be called when a "long swipe" is performed.
53
54
 
55
+ ### Inertia
56
+
57
+ Pass the `inertia` prop to enable momentum-based snapping — the element will spring-animate to the nearest snap point after release.
58
+
59
+ ```jsx
60
+ <SwipeAction inertia .../>
61
+ ```
62
+
54
63
  Action and its properties are optional, so you can choose to only implement one side or just the long swipe.
55
64
 
56
65
  ```jsx
@@ -105,6 +114,7 @@ const ActionButton = ({ onClick, children }) => {
105
114
  | Prop | Type | Description |
106
115
  | ------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
107
116
  | `main` | `(handle: ReactNode) => ReactNode` | **Required.** A function that returns the main content. It receives a `handle` which should be rendered on the draggable element. |
117
+ | `inertia` | `boolean` | Enables momentum-based snapping — the element spring-animates to the nearest snap point after release. |
108
118
  | `startAction` | `Action` | An action to be performed when swiping from left to right. |
109
119
  | `endAction` | `Action` | An action to be performed when swiping from right to left. |
110
120
 
@@ -12,6 +12,7 @@ export type Action = {
12
12
  });
13
13
  export type SwipeActionProps = {
14
14
  main: (handle: ReactNode) => ReactNode;
15
+ inertia?: boolean;
15
16
  startAction?: Action;
16
17
  endAction?: Action;
17
18
  };
@@ -1,103 +1,97 @@
1
1
  "use client";
2
- import { __assign } from './_virtual/_tslib.js';
3
2
  import { jsx, jsxs } from 'react/jsx-runtime';
4
- import { createContext, useState, useCallback, useRef, useMemo, useContext } from 'react';
3
+ import { createContext, useState, useRef, useCallback, useMemo, useContext } from 'react';
5
4
  import { useDrag } from 'react-use-drag';
6
5
  import styles from './SwipeAction.module.css.js';
6
+ import { useElementWidth } from './useElementWidth.js';
7
7
 
8
- var context = createContext({
9
- reset: function () {
8
+ const context = createContext({
9
+ reset: () => {
10
10
  throw new Error("Can't call reset outside SwipeAction component.");
11
11
  },
12
12
  });
13
- var SwipeAction = function (_a) {
14
- var startAction = _a.startAction, endAction = _a.endAction, main = _a.main;
15
- var _b = useState(0), position = _b[0], setPosition = _b[1];
16
- var _c = useState(0), positionOffset = _c[0], setPositionOffset = _c[1];
17
- var _d = useState(false), isSwiping = _d[0], setIsSwiping = _d[1];
18
- var onRelativePositionChange = useCallback(function (x) {
13
+ const SwipeAction = ({ startAction, endAction, main, inertia, }) => {
14
+ const [position, setPosition] = useState(0);
15
+ const [positionOffset, setPositionOffset] = useState(0);
16
+ const [isSwiping, setIsSwiping] = useState(false);
17
+ const [startActionContentRef, startWidth] = useElementWidth();
18
+ const [endActionContentRef, endWidth] = useElementWidth();
19
+ const [mainRef, mainWidth] = useElementWidth();
20
+ const isLongSwipeEnabled = useRef(true); // Prevents long swipe from being triggered twice in React strict mode
21
+ const onRelativePositionChange = useCallback(({ x }) => {
19
22
  if (Math.abs(x) > 5) {
20
23
  setIsSwiping(true);
21
24
  }
22
25
  setPositionOffset(x);
23
26
  }, []);
24
- var isLongSwipeEnabled = useRef(true); // Prevents long swipe from being triggered twice in React strict mode
25
- var onEnd = useCallback(function (x) {
27
+ const onEnd = useCallback(({ x }) => {
28
+ const newPosition = position + x;
29
+ setPosition(newPosition);
30
+ setPositionOffset(0);
26
31
  if (x === 0) {
27
32
  setIsSwiping(false);
28
33
  }
29
- else {
30
- setPosition(function (position) {
31
- var _a, _b, _c, _d;
32
- var newPosition = position + x;
33
- var mainWidth = (_b = (_a = mainRef.current) === null || _a === void 0 ? void 0 : _a.offsetWidth) !== null && _b !== void 0 ? _b : 0;
34
- var handleContent = function (ref, position, onLongSwipe) {
35
- var _a;
36
- var contentWidth = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.offsetWidth;
37
- var positionSign = position === 'start' ? 1 : -1;
38
- var normalizedSwipePosition = positionSign * newPosition;
39
- if (onLongSwipe &&
40
- contentWidth !== undefined &&
41
- normalizedSwipePosition >
42
- Math.max(mainWidth / 4, contentWidth) * 1.6) {
43
- if (isLongSwipeEnabled.current) {
44
- Promise.resolve(onLongSwipe()).then(function () {
45
- setPosition(0);
46
- });
47
- isLongSwipeEnabled.current = false;
48
- }
49
- return positionSign * mainWidth;
50
- }
51
- if (contentWidth !== 0 &&
52
- contentWidth !== undefined &&
53
- normalizedSwipePosition > contentWidth * 0.7) {
54
- return positionSign * contentWidth;
55
- }
56
- if (Math.sign(newPosition) === positionSign) {
57
- setTimeout(function () {
58
- setIsSwiping(false);
59
- }, 200); // Delay to ignore immediate click
60
- return 0;
61
- }
62
- };
63
- return ((_d = (_c = handleContent(startActionContentRef, 'start', startAction === null || startAction === void 0 ? void 0 : startAction.onLongSwipe)) !== null && _c !== void 0 ? _c : handleContent(endActionContentRef, 'end', endAction === null || endAction === void 0 ? void 0 : endAction.onLongSwipe)) !== null && _d !== void 0 ? _d : newPosition);
64
- });
34
+ else if (newPosition === 0) {
35
+ setTimeout(() => {
36
+ setIsSwiping(false);
37
+ }, 200); // Delay to ignore immediate click
65
38
  }
66
- setPositionOffset(0);
67
- }, [startAction === null || startAction === void 0 ? void 0 : startAction.onLongSwipe, endAction === null || endAction === void 0 ? void 0 : endAction.onLongSwipe]);
68
- var onStart = useCallback(function () {
39
+ if (mainWidth > 0 && Math.abs(newPosition) >= mainWidth - 0.5) {
40
+ const onLongSwipe = newPosition > 0 ? startAction === null || startAction === void 0 ? void 0 : startAction.onLongSwipe : endAction === null || endAction === void 0 ? void 0 : endAction.onLongSwipe;
41
+ if (onLongSwipe && isLongSwipeEnabled.current) {
42
+ isLongSwipeEnabled.current = false;
43
+ Promise.resolve(onLongSwipe()).then(() => {
44
+ setPosition(0);
45
+ setIsSwiping(false);
46
+ });
47
+ }
48
+ }
49
+ }, [position, mainWidth, startAction === null || startAction === void 0 ? void 0 : startAction.onLongSwipe, endAction === null || endAction === void 0 ? void 0 : endAction.onLongSwipe]);
50
+ const onStart = useCallback(() => {
69
51
  isLongSwipeEnabled.current = true;
70
52
  }, []);
71
- var elementProps = useDrag({
72
- onStart: onStart,
73
- onRelativePositionChange: onRelativePositionChange,
74
- onEnd: onEnd,
75
- }).elementProps;
76
- var mainRef = useRef(null);
77
- var startActionContentRef = useRef(null);
78
- var endActionContentRef = useRef(null);
79
- var x = useMemo(function () {
80
- return Math.max(endAction ? Number.NEGATIVE_INFINITY : 0, Math.min(startAction ? Number.POSITIVE_INFINITY : 0, position + positionOffset));
81
- }, [position, positionOffset, startAction, endAction]);
82
- var reset = useCallback(function () {
53
+ const snapPoints = useMemo(() => {
54
+ const points = [{ x: -position, y: 0 }];
55
+ if (startAction) {
56
+ if (startWidth > 0)
57
+ points.push({ x: startWidth - position, y: 0 });
58
+ if (startAction.onLongSwipe && mainWidth > 0)
59
+ points.push({ x: mainWidth - position, y: 0 });
60
+ }
61
+ if (endAction) {
62
+ if (endWidth > 0)
63
+ points.push({ x: -endWidth - position, y: 0 });
64
+ if (endAction.onLongSwipe && mainWidth > 0)
65
+ points.push({ x: -mainWidth - position, y: 0 });
66
+ }
67
+ return points;
68
+ }, [position, mainWidth, startWidth, endWidth, startAction, endAction]);
69
+ const { elementProps } = useDrag({
70
+ onStart,
71
+ onRelativePositionChange,
72
+ onEnd,
73
+ inertia,
74
+ snapPoints,
75
+ });
76
+ const x = useMemo(() => Math.max(endAction ? Number.NEGATIVE_INFINITY : 0, Math.min(startAction ? Number.POSITIVE_INFINITY : 0, position + positionOffset)), [position, positionOffset, startAction, endAction]);
77
+ const reset = useCallback(() => {
83
78
  setPosition(0);
84
79
  setPositionOffset(0);
85
80
  setIsSwiping(false);
86
81
  }, []);
87
- return (jsx(context.Provider, { value: { reset: reset }, children: jsxs("div", { className: styles.wrapper, children: [startAction && x > 0 && (jsx(Action, { position: "start", content: startAction.content, background: startAction.background, contentRef: startActionContentRef })), endAction && x < 0 && (jsx(Action, { position: "end", content: endAction.content, background: endAction.background, contentRef: endActionContentRef })), jsx("div", { className: styles.main, ref: mainRef, style: {
88
- '--x': "".concat(x, "px"),
89
- }, children: main(jsx("div", __assign({ className: styles.handle }, elementProps, { onClick: function (event) {
82
+ return (jsx(context.Provider, { value: { reset }, children: jsxs("div", { className: styles.wrapper, children: [startAction && x > 0 && (jsx(Action, { position: "start", content: startAction.content, background: startAction.background, contentRef: startActionContentRef })), endAction && x < 0 && (jsx(Action, { position: "end", content: endAction.content, background: endAction.background, contentRef: endActionContentRef })), jsx("div", { className: styles.main, ref: mainRef, style: {
83
+ '--x': `${x}px`,
84
+ }, children: main(jsx("div", Object.assign({ className: styles.handle }, elementProps, { onClick: (event) => {
90
85
  if (isSwiping) {
91
86
  event.stopPropagation();
92
87
  }
93
88
  } }))) })] }) }));
94
89
  };
95
- var Action = function (_a) {
96
- var content = _a.content, background = _a.background, position = _a.position, contentRef = _a.contentRef;
90
+ const Action = ({ content, background, position, contentRef }) => {
97
91
  // @TODO: allow focus by tab key before visible
98
- return (jsxs("div", { className: "".concat(styles.action, " ").concat(styles["is_position_".concat(position)]), children: [jsx("div", { className: styles.action_background, children: background }), jsx("div", { className: styles.action_content, ref: contentRef, children: content })] }));
92
+ return (jsxs("div", { className: `${styles.action} ${styles[`is_position_${position}`]}`, children: [jsx("div", { className: styles.action_background, children: background }), jsx("div", { className: styles.action_content, ref: contentRef, children: content })] }));
99
93
  };
100
- var useSwipeActionReset = function () { return useContext(context).reset; };
94
+ const useSwipeActionReset = () => useContext(context).reset;
101
95
 
102
96
  export { SwipeAction, useSwipeActionReset };
103
97
  //# sourceMappingURL=SwipeAction.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SwipeAction.js","sources":["../src/SwipeAction.tsx"],"sourcesContent":["'use client'\n\nimport type {\n\tCSSProperties,\n\tFunctionComponent,\n\tReactNode,\n\tRefObject,\n} from 'react'\nimport {\n\tcreateContext,\n\tuseCallback,\n\tuseContext,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from 'react'\nimport { useDrag } from 'react-use-drag'\nimport styles from './SwipeAction.module.css'\n\n// @TODO: add inertia\n// @TODO: animate snapping\n// @TODO: add threshold - maybe to react-use-drag\n\ntype OnLongSwipe = () => void | Promise<void>\ntype Content = ReactNode\n\nexport type Action = {\n\tbackground?: ReactNode\n\tcontent?: Content\n\tonLongSwipe?: OnLongSwipe\n} & (\n\t| {\n\t\t\tcontent: Content\n\t }\n\t| {\n\t\t\tonLongSwipe: OnLongSwipe\n\t }\n)\n\nconst context = createContext({\n\treset: (): void => {\n\t\tthrow new Error(\"Can't call reset outside SwipeAction component.\")\n\t},\n})\n\nexport type SwipeActionProps = {\n\tmain: (handle: ReactNode) => ReactNode\n\tstartAction?: Action\n\tendAction?: Action\n}\n\nexport const SwipeAction: FunctionComponent<SwipeActionProps> = ({\n\tstartAction,\n\tendAction,\n\tmain,\n}) => {\n\tconst [position, setPosition] = useState(0)\n\tconst [positionOffset, setPositionOffset] = useState(0)\n\tconst [isSwiping, setIsSwiping] = useState(false)\n\tconst onRelativePositionChange = useCallback((x: number) => {\n\t\tif (Math.abs(x) > 5) {\n\t\t\tsetIsSwiping(true)\n\t\t}\n\t\tsetPositionOffset(x)\n\t}, [])\n\tconst isLongSwipeEnabled = useRef(true) // Prevents long swipe from being triggered twice in React strict mode\n\tconst onEnd = useCallback(\n\t\t(x: number) => {\n\t\t\tif (x === 0) {\n\t\t\t\tsetIsSwiping(false)\n\t\t\t} else {\n\t\t\t\tsetPosition((position) => {\n\t\t\t\t\tconst newPosition = position + x\n\t\t\t\t\tconst mainWidth = mainRef.current?.offsetWidth ?? 0\n\n\t\t\t\t\tconst handleContent = (\n\t\t\t\t\t\tref: RefObject<HTMLDivElement>,\n\t\t\t\t\t\tposition: Position,\n\t\t\t\t\t\tonLongSwipe: undefined | OnLongSwipe,\n\t\t\t\t\t) => {\n\t\t\t\t\t\tconst contentWidth = ref.current?.offsetWidth\n\t\t\t\t\t\tconst positionSign = position === 'start' ? 1 : -1\n\t\t\t\t\t\tconst normalizedSwipePosition = positionSign * newPosition\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tonLongSwipe &&\n\t\t\t\t\t\t\tcontentWidth !== undefined &&\n\t\t\t\t\t\t\tnormalizedSwipePosition >\n\t\t\t\t\t\t\t\tMath.max(mainWidth / 4, contentWidth) * 1.6\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tif (isLongSwipeEnabled.current) {\n\t\t\t\t\t\t\t\tPromise.resolve(onLongSwipe()).then(() => {\n\t\t\t\t\t\t\t\t\tsetPosition(0)\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\tisLongSwipeEnabled.current = false\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn positionSign * mainWidth\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tcontentWidth !== 0 &&\n\t\t\t\t\t\t\tcontentWidth !== undefined &&\n\t\t\t\t\t\t\tnormalizedSwipePosition > contentWidth * 0.7\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn positionSign * contentWidth\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (Math.sign(newPosition) === positionSign) {\n\t\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\t\tsetIsSwiping(false)\n\t\t\t\t\t\t\t}, 200) // Delay to ignore immediate click\n\t\t\t\t\t\t\treturn 0\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\thandleContent(\n\t\t\t\t\t\t\tstartActionContentRef,\n\t\t\t\t\t\t\t'start',\n\t\t\t\t\t\t\tstartAction?.onLongSwipe,\n\t\t\t\t\t\t) ??\n\t\t\t\t\t\thandleContent(endActionContentRef, 'end', endAction?.onLongSwipe) ??\n\t\t\t\t\t\tnewPosition\n\t\t\t\t\t)\n\t\t\t\t})\n\t\t\t}\n\t\t\tsetPositionOffset(0)\n\t\t},\n\t\t[startAction?.onLongSwipe, endAction?.onLongSwipe],\n\t)\n\tconst onStart = useCallback(() => {\n\t\tisLongSwipeEnabled.current = true\n\t}, [])\n\tconst { elementProps } = useDrag({\n\t\tonStart,\n\t\tonRelativePositionChange,\n\t\tonEnd,\n\t})\n\tconst mainRef = useRef<HTMLDivElement>(null)\n\tconst startActionContentRef = useRef<HTMLDivElement>(null)\n\tconst endActionContentRef = useRef<HTMLDivElement>(null)\n\n\tconst x = useMemo(\n\t\t() =>\n\t\t\tMath.max(\n\t\t\t\tendAction ? Number.NEGATIVE_INFINITY : 0,\n\t\t\t\tMath.min(\n\t\t\t\t\tstartAction ? Number.POSITIVE_INFINITY : 0,\n\t\t\t\t\tposition + positionOffset,\n\t\t\t\t),\n\t\t\t),\n\t\t[position, positionOffset, startAction, endAction],\n\t)\n\n\tconst reset = useCallback(() => {\n\t\tsetPosition(0)\n\t\tsetPositionOffset(0)\n\t\tsetIsSwiping(false)\n\t}, [])\n\n\treturn (\n\t\t<context.Provider value={{ reset }}>\n\t\t\t<div className={styles.wrapper}>\n\t\t\t\t{startAction && x > 0 && (\n\t\t\t\t\t<Action\n\t\t\t\t\t\tposition=\"start\"\n\t\t\t\t\t\tcontent={startAction.content}\n\t\t\t\t\t\tbackground={startAction.background}\n\t\t\t\t\t\tcontentRef={startActionContentRef}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t\t{endAction && x < 0 && (\n\t\t\t\t\t<Action\n\t\t\t\t\t\tposition=\"end\"\n\t\t\t\t\t\tcontent={endAction.content}\n\t\t\t\t\t\tbackground={endAction.background}\n\t\t\t\t\t\tcontentRef={endActionContentRef}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t\t<div\n\t\t\t\t\tclassName={styles.main}\n\t\t\t\t\tref={mainRef}\n\t\t\t\t\tstyle={\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t'--x': `${x}px`,\n\t\t\t\t\t\t} as CSSProperties\n\t\t\t\t\t}\n\t\t\t\t>\n\t\t\t\t\t{main(\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={styles.handle}\n\t\t\t\t\t\t\t{...elementProps}\n\t\t\t\t\t\t\tonClick={(event) => {\n\t\t\t\t\t\t\t\tif (isSwiping) {\n\t\t\t\t\t\t\t\t\tevent.stopPropagation()\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</context.Provider>\n\t)\n}\n\ntype Position = 'start' | 'end'\n\nconst Action: FunctionComponent<{\n\tposition: Position\n\tcontent: ReactNode\n\tbackground: ReactNode\n\tcontentRef: RefObject<HTMLDivElement>\n}> = ({ content, background, position, contentRef }) => {\n\t// @TODO: allow focus by tab key before visible\n\treturn (\n\t\t<div className={`${styles.action} ${styles[`is_position_${position}`]}`}>\n\t\t\t<div className={styles.action_background}>{background}</div>\n\t\t\t<div className={styles.action_content} ref={contentRef}>\n\t\t\t\t{content}\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nexport const useSwipeActionReset = () => useContext(context).reset\n"],"names":[],"mappings":";;;;;;;AAuCA;AACC;AACC;;AAED;AAQM;AACN;;;;AAOA;;;;;;;AAOA;AAEE;;;;;;AAIE;;AAGA;;;AAMC;AACA;AACA;AAEC;;AAEC;AAED;;;AAGC;AACA;;;;;AAMD;AACA;;;;AAKA;;AAEA;AACA;;AAEF;AAEA;AASD;;;AAGF;;AAIA;;;AAGA;AACA;AACA;AACA;AACD;AACA;AACA;;AAIE;;;;;;;;;AAyCqB;;;;AAWjB;AAOP;AAIA;;;AAOC;AAQD;AAEO;;"}
1
+ {"version":3,"file":"SwipeAction.js","sources":["../src/SwipeAction.tsx"],"sourcesContent":["'use client'\n\nimport type {\n\tCSSProperties,\n\tFunctionComponent,\n\tReactNode,\n\tRefCallback,\n} from 'react'\nimport {\n\tcreateContext,\n\tuseCallback,\n\tuseContext,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from 'react'\nimport {\n\tuseDrag,\n\ttype Position,\n\ttype PositionWithVelocity,\n} from 'react-use-drag'\nimport styles from './SwipeAction.module.css'\nimport { useElementWidth } from './useElementWidth'\n\ntype OnLongSwipe = () => void | Promise<void>\ntype Content = ReactNode\n\nexport type Action = {\n\tbackground?: ReactNode\n\tcontent?: Content\n\tonLongSwipe?: OnLongSwipe\n} & (\n\t| {\n\t\t\tcontent: Content\n\t }\n\t| {\n\t\t\tonLongSwipe: OnLongSwipe\n\t }\n)\n\nconst context = createContext({\n\treset: (): void => {\n\t\tthrow new Error(\"Can't call reset outside SwipeAction component.\")\n\t},\n})\n\nexport type SwipeActionProps = {\n\tmain: (handle: ReactNode) => ReactNode\n\tinertia?: boolean\n\tstartAction?: Action\n\tendAction?: Action\n}\n\nexport const SwipeAction: FunctionComponent<SwipeActionProps> = ({\n\tstartAction,\n\tendAction,\n\tmain,\n\tinertia,\n}) => {\n\tconst [position, setPosition] = useState(0)\n\tconst [positionOffset, setPositionOffset] = useState(0)\n\tconst [isSwiping, setIsSwiping] = useState(false)\n\tconst [startActionContentRef, startWidth] = useElementWidth()\n\tconst [endActionContentRef, endWidth] = useElementWidth()\n\tconst [mainRef, mainWidth] = useElementWidth()\n\tconst isLongSwipeEnabled = useRef(true) // Prevents long swipe from being triggered twice in React strict mode\n\tconst onRelativePositionChange = useCallback(\n\t\t({ x }: PositionWithVelocity) => {\n\t\t\tif (Math.abs(x) > 5) {\n\t\t\t\tsetIsSwiping(true)\n\t\t\t}\n\t\t\tsetPositionOffset(x)\n\t\t},\n\t\t[],\n\t)\n\tconst onEnd = useCallback(\n\t\t({ x }: PositionWithVelocity) => {\n\t\t\tconst newPosition = position + x\n\t\t\tsetPosition(newPosition)\n\t\t\tsetPositionOffset(0)\n\t\t\tif (x === 0) {\n\t\t\t\tsetIsSwiping(false)\n\t\t\t} else if (newPosition === 0) {\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tsetIsSwiping(false)\n\t\t\t\t}, 200) // Delay to ignore immediate click\n\t\t\t}\n\t\t\tif (mainWidth > 0 && Math.abs(newPosition) >= mainWidth - 0.5) {\n\t\t\t\tconst onLongSwipe =\n\t\t\t\t\tnewPosition > 0 ? startAction?.onLongSwipe : endAction?.onLongSwipe\n\t\t\t\tif (onLongSwipe && isLongSwipeEnabled.current) {\n\t\t\t\t\tisLongSwipeEnabled.current = false\n\t\t\t\t\tPromise.resolve(onLongSwipe()).then(() => {\n\t\t\t\t\t\tsetPosition(0)\n\t\t\t\t\t\tsetIsSwiping(false)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[position, mainWidth, startAction?.onLongSwipe, endAction?.onLongSwipe],\n\t)\n\tconst onStart = useCallback(() => {\n\t\tisLongSwipeEnabled.current = true\n\t}, [])\n\tconst snapPoints = useMemo((): Position[] => {\n\t\tconst points: Position[] = [{ x: -position, y: 0 }]\n\t\tif (startAction) {\n\t\t\tif (startWidth > 0) points.push({ x: startWidth - position, y: 0 })\n\t\t\tif (startAction.onLongSwipe && mainWidth > 0)\n\t\t\t\tpoints.push({ x: mainWidth - position, y: 0 })\n\t\t}\n\t\tif (endAction) {\n\t\t\tif (endWidth > 0) points.push({ x: -endWidth - position, y: 0 })\n\t\t\tif (endAction.onLongSwipe && mainWidth > 0)\n\t\t\t\tpoints.push({ x: -mainWidth - position, y: 0 })\n\t\t}\n\t\treturn points\n\t}, [position, mainWidth, startWidth, endWidth, startAction, endAction])\n\tconst { elementProps } = useDrag({\n\t\tonStart,\n\t\tonRelativePositionChange,\n\t\tonEnd,\n\t\tinertia,\n\t\tsnapPoints,\n\t})\n\n\tconst x = useMemo(\n\t\t() =>\n\t\t\tMath.max(\n\t\t\t\tendAction ? Number.NEGATIVE_INFINITY : 0,\n\t\t\t\tMath.min(\n\t\t\t\t\tstartAction ? Number.POSITIVE_INFINITY : 0,\n\t\t\t\t\tposition + positionOffset,\n\t\t\t\t),\n\t\t\t),\n\t\t[position, positionOffset, startAction, endAction],\n\t)\n\n\tconst reset = useCallback(() => {\n\t\tsetPosition(0)\n\t\tsetPositionOffset(0)\n\t\tsetIsSwiping(false)\n\t}, [])\n\n\treturn (\n\t\t<context.Provider value={{ reset }}>\n\t\t\t<div className={styles.wrapper}>\n\t\t\t\t{startAction && x > 0 && (\n\t\t\t\t\t<Action\n\t\t\t\t\t\tposition=\"start\"\n\t\t\t\t\t\tcontent={startAction.content}\n\t\t\t\t\t\tbackground={startAction.background}\n\t\t\t\t\t\tcontentRef={startActionContentRef}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t\t{endAction && x < 0 && (\n\t\t\t\t\t<Action\n\t\t\t\t\t\tposition=\"end\"\n\t\t\t\t\t\tcontent={endAction.content}\n\t\t\t\t\t\tbackground={endAction.background}\n\t\t\t\t\t\tcontentRef={endActionContentRef}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t\t<div\n\t\t\t\t\tclassName={styles.main}\n\t\t\t\t\tref={mainRef}\n\t\t\t\t\tstyle={\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t'--x': `${x}px`,\n\t\t\t\t\t\t} as CSSProperties\n\t\t\t\t\t}\n\t\t\t\t>\n\t\t\t\t\t{main(\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={styles.handle}\n\t\t\t\t\t\t\t{...elementProps}\n\t\t\t\t\t\t\tonClick={(event) => {\n\t\t\t\t\t\t\t\tif (isSwiping) {\n\t\t\t\t\t\t\t\t\tevent.stopPropagation()\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t/>,\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</context.Provider>\n\t)\n}\n\ntype ActionPosition = 'start' | 'end'\n\nconst Action: FunctionComponent<{\n\tposition: ActionPosition\n\tcontent: ReactNode\n\tbackground: ReactNode\n\tcontentRef: RefCallback<HTMLDivElement>\n}> = ({ content, background, position, contentRef }) => {\n\t// @TODO: allow focus by tab key before visible\n\treturn (\n\t\t<div className={`${styles.action} ${styles[`is_position_${position}`]}`}>\n\t\t\t<div className={styles.action_background}>{background}</div>\n\t\t\t<div className={styles.action_content} ref={contentRef}>\n\t\t\t\t{content}\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nexport const useSwipeActionReset = () => useContext(context).reset\n"],"names":[],"mappings":";;;;;;;AAwCA;;AAEE;;AAED;AASM;;;;;;;;;;;;;;;AAwBJ;;;AAGA;;;AAEO;;;AAGN;;AAED;;AAGC;AACC;;;;AAIA;;;;AAMJ;AACC;;AAED;AACC;;;AAEqB;AACpB;AACC;;;;AAGiB;AAClB;AACC;;AAEF;AACD;AACA;;;;;;AAMC;;AAcD;;;;;;;AA+BuB;;;;AAWjB;AAOP;AAIA;;AAOC;AAQD;AAEO;;"}
@@ -0,0 +1,2 @@
1
+ import { type RefCallback } from 'react';
2
+ export declare const useElementWidth: () => [RefCallback<HTMLElement | null>, number];
@@ -0,0 +1,23 @@
1
+ import { useState, useRef, useCallback } from 'react';
2
+
3
+ const useElementWidth = () => {
4
+ const [width, setWidth] = useState(0);
5
+ const observerRef = useRef(null);
6
+ const ref = useCallback((element) => {
7
+ var _a;
8
+ (_a = observerRef.current) === null || _a === void 0 ? void 0 : _a.disconnect();
9
+ observerRef.current = null;
10
+ if (!element) {
11
+ return;
12
+ }
13
+ observerRef.current = new ResizeObserver(([entry]) => {
14
+ var _a, _b, _c;
15
+ setWidth((_c = (_b = (_a = entry.borderBoxSize) === null || _a === void 0 ? void 0 : _a.at(0)) === null || _b === void 0 ? void 0 : _b.inlineSize) !== null && _c !== void 0 ? _c : entry.contentRect.width);
16
+ });
17
+ observerRef.current.observe(element);
18
+ }, []);
19
+ return [ref, width];
20
+ };
21
+
22
+ export { useElementWidth };
23
+ //# sourceMappingURL=useElementWidth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useElementWidth.js","sources":["../src/useElementWidth.ts"],"sourcesContent":["import { type RefCallback, useCallback, useRef, useState } from 'react'\n\nexport const useElementWidth = (): [\n\tRefCallback<HTMLElement | null>,\n\tnumber,\n] => {\n\tconst [width, setWidth] = useState(0)\n\tconst observerRef = useRef<ResizeObserver | null>(null)\n\n\tconst ref = useCallback((element: HTMLElement | null) => {\n\t\tobserverRef.current?.disconnect()\n\t\tobserverRef.current = null\n\t\tif (!element) {\n\t\t\treturn\n\t\t}\n\t\tobserverRef.current = new ResizeObserver(([entry]) => {\n\t\t\tsetWidth(\n\t\t\t\tentry.borderBoxSize?.at(0)?.inlineSize ?? entry.contentRect.width,\n\t\t\t)\n\t\t})\n\t\tobserverRef.current.observe(element)\n\t}, [])\n\n\treturn [ref, width]\n}\n"],"names":[],"mappings":";;AAEO,MAAM,eAAe,GAAG,MAG3B;IACH,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;AACrC,IAAA,MAAM,WAAW,GAAG,MAAM,CAAwB,IAAI,CAAC,CAAA;AAEvD,IAAA,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,OAA2B,KAAI;;AACvD,QAAA,CAAA,EAAA,GAAA,WAAW,CAAC,OAAO,MAAE,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAA,UAAU,EAAE,CAAA;AACjC,QAAA,WAAW,CAAC,OAAO,GAAG,IAAI,CAAA;QAC1B,IAAI,CAAC,OAAO,EAAE;YACb,OAAM;SACN;QACD,WAAW,CAAC,OAAO,GAAG,IAAI,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,KAAI;;YACpD,QAAQ,CACP,MAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,KAAK,CAAC,aAAa,0CAAE,EAAE,CAAC,CAAC,CAAC,MAAA,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAE,UAAU,MAAI,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,EAAA,GAAA,KAAK,CAAC,WAAW,CAAC,KAAK,CACjE,CAAA;AACF,SAAC,CAAC,CAAA;AACF,QAAA,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;KACpC,EAAE,EAAE,CAAC,CAAA;AAEN,IAAA,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;AACpB;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-swipe-action",
3
- "version": "0.2.1",
3
+ "version": "0.3.2",
4
4
  "description": "Swipe to reveal or perform an action.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -8,6 +8,7 @@
8
8
  "scripts": {
9
9
  "start": "rollup -c -w",
10
10
  "build": "rollup -c",
11
+ "dev": "npm run storybook",
11
12
  "prepare": "npm run build",
12
13
  "storybook": "storybook dev -p 6006",
13
14
  "build-storybook": "storybook build"
@@ -59,7 +60,10 @@
59
60
  "files": [
60
61
  "/dist/"
61
62
  ],
63
+ "publishConfig": {
64
+ "provenance": true
65
+ },
62
66
  "dependencies": {
63
- "react-use-drag": "^0.3.2"
67
+ "react-use-drag": "^1.7.1"
64
68
  }
65
69
  }
@@ -1,12 +0,0 @@
1
- import type { StoryObj } from '@storybook/react';
2
- import './SwipeAction.stories.css';
3
- declare const meta: {
4
- title: string;
5
- tags: string[];
6
- parameters: {
7
- layout: string;
8
- };
9
- };
10
- export default meta;
11
- type Story = StoryObj<typeof meta>;
12
- export declare const All: Story;
@@ -1,35 +0,0 @@
1
- /******************************************************************************
2
- Copyright (c) Microsoft Corporation.
3
-
4
- Permission to use, copy, modify, and/or distribute this software for any
5
- purpose with or without fee is hereby granted.
6
-
7
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
8
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
10
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
12
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13
- PERFORMANCE OF THIS SOFTWARE.
14
- ***************************************************************************** */
15
- /* global Reflect, Promise, SuppressedError, Symbol */
16
-
17
-
18
- var __assign = function() {
19
- __assign = Object.assign || function __assign(t) {
20
- for (var s, i = 1, n = arguments.length; i < n; i++) {
21
- s = arguments[i];
22
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
23
- }
24
- return t;
25
- };
26
- return __assign.apply(this, arguments);
27
- };
28
-
29
- typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
30
- var e = new Error(message);
31
- return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
32
- };
33
-
34
- export { __assign };
35
- //# sourceMappingURL=_tslib.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"_tslib.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}