@aiquants/swipe-overlay 0.0.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.
- package/LICENSE +21 -0
- package/README.md +35 -0
- package/dist/index.d.mts +45 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 fehde-k
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# @aiquants/swipe-overlay
|
|
2
|
+
|
|
3
|
+
React hooks and context for swipe-to-dismiss overlay interactions.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Swipe to dismiss**: Smooth swipe gestures to close overlays
|
|
8
|
+
- **Touch & Mouse support**: Works with both touch devices and mouse interactions
|
|
9
|
+
- **Configurable thresholds**: Customize swipe thresholds and velocity
|
|
10
|
+
- **TypeScript support**: Fully typed for better developer experience
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @aiquants/swipe-overlay
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { useSwipeOverlay } from '@aiquants/swipe-overlay'
|
|
22
|
+
|
|
23
|
+
const MyOverlay = () => {
|
|
24
|
+
const { overlayRef, overlayStyle, attemptClose } = useSwipeOverlay({
|
|
25
|
+
onClose: () => console.log('Closed'),
|
|
26
|
+
isOpen: true
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div ref={overlayRef} style={overlayStyle}>
|
|
31
|
+
Content
|
|
32
|
+
</div>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
```
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { CSSProperties } from 'react';
|
|
3
|
+
|
|
4
|
+
type SwipeCloseGuardContextValue = {
|
|
5
|
+
isBlocked: boolean;
|
|
6
|
+
requestBlock: () => () => void;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Provides swipe-dismiss guard controls for mobile overlays.
|
|
10
|
+
* モバイルオーバーレイのスワイプクローズ操作を制御するガード機構を提供。
|
|
11
|
+
*/
|
|
12
|
+
declare const SwipeCloseGuardContext: react.Context<SwipeCloseGuardContextValue | null>;
|
|
13
|
+
/**
|
|
14
|
+
* Returns the swipe-dismiss guard handlers for overlay content.
|
|
15
|
+
* オーバーレイコンテンツ用のスワイプクローズガード制御を取得するフック。
|
|
16
|
+
*/
|
|
17
|
+
declare const useSwipeCloseGuard: () => SwipeCloseGuardContextValue;
|
|
18
|
+
type OverlayEscapeEntry = {
|
|
19
|
+
id: string;
|
|
20
|
+
onEscape: (e: KeyboardEvent) => boolean;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Manages a LIFO stack for Escape key handling in overlays.
|
|
24
|
+
* オーバーレイでの Escape キー処理を LIFO (後入れ先出し) で管理するフック。
|
|
25
|
+
*/
|
|
26
|
+
declare const useOverlayEscapeStack: () => (entry: OverlayEscapeEntry) => () => void;
|
|
27
|
+
type SwipeDismissHookResult = {
|
|
28
|
+
overlayRef: (node: HTMLDivElement | null) => void;
|
|
29
|
+
overlayStyle: CSSProperties;
|
|
30
|
+
attemptClose: () => boolean;
|
|
31
|
+
isSwiping: boolean;
|
|
32
|
+
element: HTMLDivElement | null;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Manages swipe-to-dismiss interactions on the mobile overlay panel.
|
|
36
|
+
* モバイルオーバーレイパネルのスワイプクローズ操作を管理。
|
|
37
|
+
*/
|
|
38
|
+
declare const useSwipeToDismissOverlay: ({ isOpen, onClose, isCloseBlocked, skipWhenTextSelected, }: {
|
|
39
|
+
isOpen: boolean;
|
|
40
|
+
onClose: () => void;
|
|
41
|
+
isCloseBlocked: () => boolean;
|
|
42
|
+
skipWhenTextSelected?: boolean;
|
|
43
|
+
}) => SwipeDismissHookResult;
|
|
44
|
+
|
|
45
|
+
export { SwipeCloseGuardContext, type SwipeCloseGuardContextValue, type SwipeDismissHookResult, useOverlayEscapeStack, useSwipeCloseGuard, useSwipeToDismissOverlay };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { CSSProperties } from 'react';
|
|
3
|
+
|
|
4
|
+
type SwipeCloseGuardContextValue = {
|
|
5
|
+
isBlocked: boolean;
|
|
6
|
+
requestBlock: () => () => void;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Provides swipe-dismiss guard controls for mobile overlays.
|
|
10
|
+
* モバイルオーバーレイのスワイプクローズ操作を制御するガード機構を提供。
|
|
11
|
+
*/
|
|
12
|
+
declare const SwipeCloseGuardContext: react.Context<SwipeCloseGuardContextValue | null>;
|
|
13
|
+
/**
|
|
14
|
+
* Returns the swipe-dismiss guard handlers for overlay content.
|
|
15
|
+
* オーバーレイコンテンツ用のスワイプクローズガード制御を取得するフック。
|
|
16
|
+
*/
|
|
17
|
+
declare const useSwipeCloseGuard: () => SwipeCloseGuardContextValue;
|
|
18
|
+
type OverlayEscapeEntry = {
|
|
19
|
+
id: string;
|
|
20
|
+
onEscape: (e: KeyboardEvent) => boolean;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Manages a LIFO stack for Escape key handling in overlays.
|
|
24
|
+
* オーバーレイでの Escape キー処理を LIFO (後入れ先出し) で管理するフック。
|
|
25
|
+
*/
|
|
26
|
+
declare const useOverlayEscapeStack: () => (entry: OverlayEscapeEntry) => () => void;
|
|
27
|
+
type SwipeDismissHookResult = {
|
|
28
|
+
overlayRef: (node: HTMLDivElement | null) => void;
|
|
29
|
+
overlayStyle: CSSProperties;
|
|
30
|
+
attemptClose: () => boolean;
|
|
31
|
+
isSwiping: boolean;
|
|
32
|
+
element: HTMLDivElement | null;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Manages swipe-to-dismiss interactions on the mobile overlay panel.
|
|
36
|
+
* モバイルオーバーレイパネルのスワイプクローズ操作を管理。
|
|
37
|
+
*/
|
|
38
|
+
declare const useSwipeToDismissOverlay: ({ isOpen, onClose, isCloseBlocked, skipWhenTextSelected, }: {
|
|
39
|
+
isOpen: boolean;
|
|
40
|
+
onClose: () => void;
|
|
41
|
+
isCloseBlocked: () => boolean;
|
|
42
|
+
skipWhenTextSelected?: boolean;
|
|
43
|
+
}) => SwipeDismissHookResult;
|
|
44
|
+
|
|
45
|
+
export { SwipeCloseGuardContext, type SwipeCloseGuardContextValue, type SwipeDismissHookResult, useOverlayEscapeStack, useSwipeCloseGuard, useSwipeToDismissOverlay };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var v=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var M=Object.prototype.hasOwnProperty;var k=(t,o)=>{for(var i in o)v(t,i,{get:o[i],enumerable:!0})},X=(t,o,i,l)=>{if(o&&typeof o=="object"||typeof o=="function")for(let e of P(o))!M.call(t,e)&&e!==i&&v(t,e,{get:()=>o[e],enumerable:!(l=D(o,e))||l.enumerable});return t};var H=t=>X(v({},"__esModule",{value:!0}),t);var T={};k(T,{SwipeCloseGuardContext:()=>I,useOverlayEscapeStack:()=>w,useSwipeCloseGuard:()=>R,useSwipeToDismissOverlay:()=>A});module.exports=H(T);var s=require("react"),O=120,I=(0,s.createContext)(null),R=()=>(0,s.useContext)(I)??{isBlocked:!1,requestBlock:()=>()=>{}},a=[],g=t=>{if(t.key!=="Escape")return;a[a.length-1]?.onEscape(t)&&(t.preventDefault(),t.stopPropagation())},w=()=>(0,s.useCallback)(t=>{let o=a.findIndex(i=>i.id===t.id);return o>=0&&a.splice(o,1),a.push(t),a.length===1&&typeof document<"u"&&document.addEventListener("keydown",g,!0),()=>{let i=a.findIndex(l=>l.id===t.id);i>=0&&a.splice(i,1),a.length===0&&typeof document<"u"&&document.removeEventListener("keydown",g,!0)}},[]),A=({isOpen:t,onClose:o,isCloseBlocked:i,skipWhenTextSelected:l=!1})=>{let[e,h]=(0,s.useState)(null),[x,d]=(0,s.useState)(!1),C=w(),E=(0,s.useRef)(`overlay-${Math.random().toString(36).slice(2)}`).current,u=(0,s.useRef)({pointerId:null,startX:0,startY:0,isActive:!1,isClosing:!1,lastDelta:0,hasCapture:!1}),p=(0,s.useCallback)(()=>i()?!1:(o(),!0),[i,o]);(0,s.useEffect)(()=>{if(t)return C({id:E,onEscape:p})},[t,p,C,E]),(0,s.useEffect)(()=>{if(!(e&&t))return;let c=()=>{let n=u.current;n.hasCapture&&n.pointerId!==null&&e.releasePointerCapture?.(n.pointerId),Object.assign(n,{pointerId:null,isActive:!1,isClosing:!1,hasCapture:!1}),e.style.transform="",e.style.transition="",d(!1)};u.current={pointerId:null,startX:0,startY:0,isActive:!1,isClosing:!1,lastDelta:0,hasCapture:!1},d(!1);let m=n=>{n.pointerType==="mouse"&&n.button!==0||n.target?.closest('[data-overlay-interactive="true"]')||l&&window.getSelection()?.toString().trim()||(e.style.transition="none",d(!0),u.current={pointerId:n.pointerId,startX:n.clientX,startY:n.clientY,isActive:!0,isClosing:!1,lastDelta:0,hasCapture:!1})},y=n=>{let r=u.current;if(!r.isActive||r.pointerId!==n.pointerId)return;if(l&&window.getSelection()?.toString().trim()){c();return}let f=n.clientX-r.startX,L=n.clientY-r.startY;if(!r.isClosing){if(Math.abs(f)<4)return;if(Math.abs(f)<Math.abs(L)*1.3||f<=0){c();return}r.isClosing=!0,r.hasCapture||(e.setPointerCapture?.(n.pointerId),r.hasCapture=!0)}r.isClosing&&(n.preventDefault(),r.lastDelta=Math.max(0,f),e.style.transform=`translateX(${Math.min(r.lastDelta,520)}px)`)},S=n=>{let r=u.current;r.pointerId===n.pointerId&&(r.isClosing&&r.lastDelta>O?(e.style.transition="",e.style.transform="translateX(100%)",r.hasCapture&&r.pointerId!==null&&e.releasePointerCapture?.(r.pointerId),Object.assign(r,{pointerId:null,isActive:!1,isClosing:!1,hasCapture:!1}),d(!1),p()):c())};return e.addEventListener("pointerdown",m),e.addEventListener("pointermove",y),e.addEventListener("pointerup",S),e.addEventListener("pointercancel",c),()=>{e.removeEventListener("pointerdown",m),e.removeEventListener("pointermove",y),e.removeEventListener("pointerup",S),e.removeEventListener("pointercancel",c)}},[t,e,p,l]);let b=(0,s.useMemo)(()=>({transform:t?"translateX(0px)":"translateX(100%)"}),[t]);return{overlayRef:h,overlayStyle:b,attemptClose:p,isSwiping:x,element:e}};0&&(module.exports={SwipeCloseGuardContext,useOverlayEscapeStack,useSwipeCloseGuard,useSwipeToDismissOverlay});
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { type CSSProperties, createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from \"react\"\n\nconst MOBILE_SWIPE_CLOSE_THRESHOLD = 120\n\n/* --- Contexts & Hooks for Overlay Management --- */\n\nexport type SwipeCloseGuardContextValue = { isBlocked: boolean; requestBlock: () => () => void }\n/**\n * Provides swipe-dismiss guard controls for mobile overlays.\n * モバイルオーバーレイのスワイプクローズ操作を制御するガード機構を提供。\n */\nexport const SwipeCloseGuardContext = createContext<SwipeCloseGuardContextValue | null>(null)\n\n/**\n * Returns the swipe-dismiss guard handlers for overlay content.\n * オーバーレイコンテンツ用のスワイプクローズガード制御を取得するフック。\n */\nexport const useSwipeCloseGuard = (): SwipeCloseGuardContextValue => {\n return useContext(SwipeCloseGuardContext) ?? { isBlocked: false, requestBlock: () => () => {} }\n}\n\ntype OverlayEscapeEntry = { id: string; onEscape: (e: KeyboardEvent) => boolean }\nconst overlayEscapeStack: OverlayEscapeEntry[] = []\n\nconst handleOverlayEscape = (e: KeyboardEvent) => {\n if (e.key !== \"Escape\") return\n const top = overlayEscapeStack[overlayEscapeStack.length - 1]\n if (top?.onEscape(e)) {\n e.preventDefault()\n e.stopPropagation()\n }\n}\n\n/**\n * Manages a LIFO stack for Escape key handling in overlays.\n * オーバーレイでの Escape キー処理を LIFO (後入れ先出し) で管理するフック。\n */\nexport const useOverlayEscapeStack = () => {\n return useCallback((entry: OverlayEscapeEntry) => {\n // 登録: 既存があれば削除して末尾に追加 (最優先)\n const idx = overlayEscapeStack.findIndex((x) => x.id === entry.id)\n if (idx >= 0) overlayEscapeStack.splice(idx, 1)\n overlayEscapeStack.push(entry)\n\n if (overlayEscapeStack.length === 1 && typeof document !== \"undefined\") {\n document.addEventListener(\"keydown\", handleOverlayEscape, true)\n }\n\n // 解除関数\n return () => {\n const index = overlayEscapeStack.findIndex((x) => x.id === entry.id)\n if (index >= 0) overlayEscapeStack.splice(index, 1)\n if (overlayEscapeStack.length === 0 && typeof document !== \"undefined\") {\n document.removeEventListener(\"keydown\", handleOverlayEscape, true)\n }\n }\n }, [])\n}\n\nexport type SwipeDismissHookResult = {\n overlayRef: (node: HTMLDivElement | null) => void\n overlayStyle: CSSProperties\n attemptClose: () => boolean\n isSwiping: boolean\n element: HTMLDivElement | null\n}\n\n/**\n * Manages swipe-to-dismiss interactions on the mobile overlay panel.\n * モバイルオーバーレイパネルのスワイプクローズ操作を管理。\n */\nexport const useSwipeToDismissOverlay = ({\n isOpen,\n onClose,\n isCloseBlocked,\n skipWhenTextSelected = false,\n}: {\n isOpen: boolean\n onClose: () => void\n isCloseBlocked: () => boolean\n skipWhenTextSelected?: boolean\n}): SwipeDismissHookResult => {\n const [element, setElement] = useState<HTMLDivElement | null>(null)\n const [isSwiping, setIsSwiping] = useState(false)\n const registerEscape = useOverlayEscapeStack()\n const overlayId = useRef(`overlay-${Math.random().toString(36).slice(2)}`).current\n\n // State ref to avoid re-renders during high-frequency pointer events\n const stateRef = useRef({\n pointerId: null as number | null,\n startX: 0,\n startY: 0,\n isActive: false,\n isClosing: false,\n lastDelta: 0,\n hasCapture: false,\n })\n\n const attemptClose = useCallback(() => {\n if (isCloseBlocked()) return false\n onClose()\n return true\n }, [isCloseBlocked, onClose])\n\n /**\n * Registers Escape key handler when open.\n * 開いている時に Escape キーハンドラを登録。\n */\n useEffect(() => {\n if (!isOpen) return\n return registerEscape({ id: overlayId, onEscape: attemptClose })\n }, [isOpen, attemptClose, registerEscape, overlayId])\n\n /**\n * Handles pointer events for swipe interaction.\n * スワイプ操作のためのポインターイベントを処理。\n */\n useEffect(() => {\n if (!(element && isOpen)) return\n\n const resetState = () => {\n const s = stateRef.current\n if (s.hasCapture && s.pointerId !== null) element.releasePointerCapture?.(s.pointerId)\n Object.assign(s, { pointerId: null, isActive: false, isClosing: false, hasCapture: false })\n element.style.transform = \"\"\n element.style.transition = \"\"\n setIsSwiping(false)\n }\n\n // Reset on open\n stateRef.current = { pointerId: null, startX: 0, startY: 0, isActive: false, isClosing: false, lastDelta: 0, hasCapture: false }\n setIsSwiping(false)\n\n const onPointerDown = (e: PointerEvent) => {\n if ((e.pointerType === \"mouse\" && e.button !== 0) || (e.target as HTMLElement)?.closest('[data-overlay-interactive=\"true\"]')) return\n if (skipWhenTextSelected && window.getSelection()?.toString().trim()) return\n\n element.style.transition = \"none\"\n setIsSwiping(true)\n stateRef.current = { pointerId: e.pointerId, startX: e.clientX, startY: e.clientY, isActive: true, isClosing: false, lastDelta: 0, hasCapture: false }\n }\n\n const onPointerMove = (e: PointerEvent) => {\n const s = stateRef.current\n if (!s.isActive || s.pointerId !== e.pointerId) return\n if (skipWhenTextSelected && window.getSelection()?.toString().trim()) { resetState(); return }\n\n const dx = e.clientX - s.startX\n const dy = e.clientY - s.startY\n\n if (!s.isClosing) {\n if (Math.abs(dx) < 4) return\n // Cancel if vertical scroll is dominant or moving left\n if (Math.abs(dx) < Math.abs(dy) * 1.3 || dx <= 0) { resetState(); return }\n s.isClosing = true\n if (!s.hasCapture) { element.setPointerCapture?.(e.pointerId); s.hasCapture = true }\n }\n\n if (s.isClosing) {\n e.preventDefault()\n s.lastDelta = Math.max(0, dx)\n element.style.transform = `translateX(${Math.min(s.lastDelta, 520)}px)`\n }\n }\n\n const onPointerUp = (e: PointerEvent) => {\n const s = stateRef.current\n if (s.pointerId !== e.pointerId) return\n\n if (s.isClosing && s.lastDelta > MOBILE_SWIPE_CLOSE_THRESHOLD) {\n element.style.transition = \"\"\n element.style.transform = \"translateX(100%)\"\n if (s.hasCapture && s.pointerId !== null) element.releasePointerCapture?.(s.pointerId)\n Object.assign(s, { pointerId: null, isActive: false, isClosing: false, hasCapture: false })\n setIsSwiping(false)\n attemptClose()\n } else {\n resetState()\n }\n }\n\n element.addEventListener(\"pointerdown\", onPointerDown)\n element.addEventListener(\"pointermove\", onPointerMove)\n element.addEventListener(\"pointerup\", onPointerUp)\n element.addEventListener(\"pointercancel\", resetState)\n\n return () => {\n element.removeEventListener(\"pointerdown\", onPointerDown)\n element.removeEventListener(\"pointermove\", onPointerMove)\n element.removeEventListener(\"pointerup\", onPointerUp)\n element.removeEventListener(\"pointercancel\", resetState)\n }\n }, [isOpen, element, attemptClose, skipWhenTextSelected])\n\n const overlayStyle = useMemo<CSSProperties>(() => ({\n transform: isOpen ? \"translateX(0px)\" : \"translateX(100%)\",\n }), [isOpen])\n\n return { overlayRef: setElement, overlayStyle, attemptClose, isSwiping, element }\n}\n"],"mappings":"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","names":["index_exports","__export","SwipeCloseGuardContext","useOverlayEscapeStack","useSwipeCloseGuard","useSwipeToDismissOverlay","__toCommonJS","import_react","MOBILE_SWIPE_CLOSE_THRESHOLD","overlayEscapeStack","handleOverlayEscape","e","entry","idx","x","index","isOpen","onClose","isCloseBlocked","skipWhenTextSelected","element","setElement","isSwiping","setIsSwiping","registerEscape","overlayId","stateRef","attemptClose","resetState","s","onPointerDown","onPointerMove","dx","dy","onPointerUp","overlayStyle"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createContext as D,useCallback as w,useContext as P,useEffect as y,useMemo as M,useRef as S,useState as g}from"react";var k=120,X=D(null),R=()=>P(X)??{isBlocked:!1,requestBlock:()=>()=>{}},s=[],I=r=>{if(r.key!=="Escape")return;s[s.length-1]?.onEscape(r)&&(r.preventDefault(),r.stopPropagation())},H=()=>w(r=>{let i=s.findIndex(o=>o.id===r.id);return i>=0&&s.splice(i,1),s.push(r),s.length===1&&typeof document<"u"&&document.addEventListener("keydown",I,!0),()=>{let o=s.findIndex(a=>a.id===r.id);o>=0&&s.splice(o,1),s.length===0&&typeof document<"u"&&document.removeEventListener("keydown",I,!0)}},[]),A=({isOpen:r,onClose:i,isCloseBlocked:o,skipWhenTextSelected:a=!1})=>{let[e,h]=g(null),[x,c]=g(!1),f=H(),v=S(`overlay-${Math.random().toString(36).slice(2)}`).current,l=S({pointerId:null,startX:0,startY:0,isActive:!1,isClosing:!1,lastDelta:0,hasCapture:!1}),u=w(()=>o()?!1:(i(),!0),[o,i]);y(()=>{if(r)return f({id:v,onEscape:u})},[r,u,f,v]),y(()=>{if(!(e&&r))return;let p=()=>{let t=l.current;t.hasCapture&&t.pointerId!==null&&e.releasePointerCapture?.(t.pointerId),Object.assign(t,{pointerId:null,isActive:!1,isClosing:!1,hasCapture:!1}),e.style.transform="",e.style.transition="",c(!1)};l.current={pointerId:null,startX:0,startY:0,isActive:!1,isClosing:!1,lastDelta:0,hasCapture:!1},c(!1);let C=t=>{t.pointerType==="mouse"&&t.button!==0||t.target?.closest('[data-overlay-interactive="true"]')||a&&window.getSelection()?.toString().trim()||(e.style.transition="none",c(!0),l.current={pointerId:t.pointerId,startX:t.clientX,startY:t.clientY,isActive:!0,isClosing:!1,lastDelta:0,hasCapture:!1})},E=t=>{let n=l.current;if(!n.isActive||n.pointerId!==t.pointerId)return;if(a&&window.getSelection()?.toString().trim()){p();return}let d=t.clientX-n.startX,L=t.clientY-n.startY;if(!n.isClosing){if(Math.abs(d)<4)return;if(Math.abs(d)<Math.abs(L)*1.3||d<=0){p();return}n.isClosing=!0,n.hasCapture||(e.setPointerCapture?.(t.pointerId),n.hasCapture=!0)}n.isClosing&&(t.preventDefault(),n.lastDelta=Math.max(0,d),e.style.transform=`translateX(${Math.min(n.lastDelta,520)}px)`)},m=t=>{let n=l.current;n.pointerId===t.pointerId&&(n.isClosing&&n.lastDelta>k?(e.style.transition="",e.style.transform="translateX(100%)",n.hasCapture&&n.pointerId!==null&&e.releasePointerCapture?.(n.pointerId),Object.assign(n,{pointerId:null,isActive:!1,isClosing:!1,hasCapture:!1}),c(!1),u()):p())};return e.addEventListener("pointerdown",C),e.addEventListener("pointermove",E),e.addEventListener("pointerup",m),e.addEventListener("pointercancel",p),()=>{e.removeEventListener("pointerdown",C),e.removeEventListener("pointermove",E),e.removeEventListener("pointerup",m),e.removeEventListener("pointercancel",p)}},[r,e,u,a]);let b=M(()=>({transform:r?"translateX(0px)":"translateX(100%)"}),[r]);return{overlayRef:h,overlayStyle:b,attemptClose:u,isSwiping:x,element:e}};export{X as SwipeCloseGuardContext,H as useOverlayEscapeStack,R as useSwipeCloseGuard,A as useSwipeToDismissOverlay};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { type CSSProperties, createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from \"react\"\n\nconst MOBILE_SWIPE_CLOSE_THRESHOLD = 120\n\n/* --- Contexts & Hooks for Overlay Management --- */\n\nexport type SwipeCloseGuardContextValue = { isBlocked: boolean; requestBlock: () => () => void }\n/**\n * Provides swipe-dismiss guard controls for mobile overlays.\n * モバイルオーバーレイのスワイプクローズ操作を制御するガード機構を提供。\n */\nexport const SwipeCloseGuardContext = createContext<SwipeCloseGuardContextValue | null>(null)\n\n/**\n * Returns the swipe-dismiss guard handlers for overlay content.\n * オーバーレイコンテンツ用のスワイプクローズガード制御を取得するフック。\n */\nexport const useSwipeCloseGuard = (): SwipeCloseGuardContextValue => {\n return useContext(SwipeCloseGuardContext) ?? { isBlocked: false, requestBlock: () => () => {} }\n}\n\ntype OverlayEscapeEntry = { id: string; onEscape: (e: KeyboardEvent) => boolean }\nconst overlayEscapeStack: OverlayEscapeEntry[] = []\n\nconst handleOverlayEscape = (e: KeyboardEvent) => {\n if (e.key !== \"Escape\") return\n const top = overlayEscapeStack[overlayEscapeStack.length - 1]\n if (top?.onEscape(e)) {\n e.preventDefault()\n e.stopPropagation()\n }\n}\n\n/**\n * Manages a LIFO stack for Escape key handling in overlays.\n * オーバーレイでの Escape キー処理を LIFO (後入れ先出し) で管理するフック。\n */\nexport const useOverlayEscapeStack = () => {\n return useCallback((entry: OverlayEscapeEntry) => {\n // 登録: 既存があれば削除して末尾に追加 (最優先)\n const idx = overlayEscapeStack.findIndex((x) => x.id === entry.id)\n if (idx >= 0) overlayEscapeStack.splice(idx, 1)\n overlayEscapeStack.push(entry)\n\n if (overlayEscapeStack.length === 1 && typeof document !== \"undefined\") {\n document.addEventListener(\"keydown\", handleOverlayEscape, true)\n }\n\n // 解除関数\n return () => {\n const index = overlayEscapeStack.findIndex((x) => x.id === entry.id)\n if (index >= 0) overlayEscapeStack.splice(index, 1)\n if (overlayEscapeStack.length === 0 && typeof document !== \"undefined\") {\n document.removeEventListener(\"keydown\", handleOverlayEscape, true)\n }\n }\n }, [])\n}\n\nexport type SwipeDismissHookResult = {\n overlayRef: (node: HTMLDivElement | null) => void\n overlayStyle: CSSProperties\n attemptClose: () => boolean\n isSwiping: boolean\n element: HTMLDivElement | null\n}\n\n/**\n * Manages swipe-to-dismiss interactions on the mobile overlay panel.\n * モバイルオーバーレイパネルのスワイプクローズ操作を管理。\n */\nexport const useSwipeToDismissOverlay = ({\n isOpen,\n onClose,\n isCloseBlocked,\n skipWhenTextSelected = false,\n}: {\n isOpen: boolean\n onClose: () => void\n isCloseBlocked: () => boolean\n skipWhenTextSelected?: boolean\n}): SwipeDismissHookResult => {\n const [element, setElement] = useState<HTMLDivElement | null>(null)\n const [isSwiping, setIsSwiping] = useState(false)\n const registerEscape = useOverlayEscapeStack()\n const overlayId = useRef(`overlay-${Math.random().toString(36).slice(2)}`).current\n\n // State ref to avoid re-renders during high-frequency pointer events\n const stateRef = useRef({\n pointerId: null as number | null,\n startX: 0,\n startY: 0,\n isActive: false,\n isClosing: false,\n lastDelta: 0,\n hasCapture: false,\n })\n\n const attemptClose = useCallback(() => {\n if (isCloseBlocked()) return false\n onClose()\n return true\n }, [isCloseBlocked, onClose])\n\n /**\n * Registers Escape key handler when open.\n * 開いている時に Escape キーハンドラを登録。\n */\n useEffect(() => {\n if (!isOpen) return\n return registerEscape({ id: overlayId, onEscape: attemptClose })\n }, [isOpen, attemptClose, registerEscape, overlayId])\n\n /**\n * Handles pointer events for swipe interaction.\n * スワイプ操作のためのポインターイベントを処理。\n */\n useEffect(() => {\n if (!(element && isOpen)) return\n\n const resetState = () => {\n const s = stateRef.current\n if (s.hasCapture && s.pointerId !== null) element.releasePointerCapture?.(s.pointerId)\n Object.assign(s, { pointerId: null, isActive: false, isClosing: false, hasCapture: false })\n element.style.transform = \"\"\n element.style.transition = \"\"\n setIsSwiping(false)\n }\n\n // Reset on open\n stateRef.current = { pointerId: null, startX: 0, startY: 0, isActive: false, isClosing: false, lastDelta: 0, hasCapture: false }\n setIsSwiping(false)\n\n const onPointerDown = (e: PointerEvent) => {\n if ((e.pointerType === \"mouse\" && e.button !== 0) || (e.target as HTMLElement)?.closest('[data-overlay-interactive=\"true\"]')) return\n if (skipWhenTextSelected && window.getSelection()?.toString().trim()) return\n\n element.style.transition = \"none\"\n setIsSwiping(true)\n stateRef.current = { pointerId: e.pointerId, startX: e.clientX, startY: e.clientY, isActive: true, isClosing: false, lastDelta: 0, hasCapture: false }\n }\n\n const onPointerMove = (e: PointerEvent) => {\n const s = stateRef.current\n if (!s.isActive || s.pointerId !== e.pointerId) return\n if (skipWhenTextSelected && window.getSelection()?.toString().trim()) { resetState(); return }\n\n const dx = e.clientX - s.startX\n const dy = e.clientY - s.startY\n\n if (!s.isClosing) {\n if (Math.abs(dx) < 4) return\n // Cancel if vertical scroll is dominant or moving left\n if (Math.abs(dx) < Math.abs(dy) * 1.3 || dx <= 0) { resetState(); return }\n s.isClosing = true\n if (!s.hasCapture) { element.setPointerCapture?.(e.pointerId); s.hasCapture = true }\n }\n\n if (s.isClosing) {\n e.preventDefault()\n s.lastDelta = Math.max(0, dx)\n element.style.transform = `translateX(${Math.min(s.lastDelta, 520)}px)`\n }\n }\n\n const onPointerUp = (e: PointerEvent) => {\n const s = stateRef.current\n if (s.pointerId !== e.pointerId) return\n\n if (s.isClosing && s.lastDelta > MOBILE_SWIPE_CLOSE_THRESHOLD) {\n element.style.transition = \"\"\n element.style.transform = \"translateX(100%)\"\n if (s.hasCapture && s.pointerId !== null) element.releasePointerCapture?.(s.pointerId)\n Object.assign(s, { pointerId: null, isActive: false, isClosing: false, hasCapture: false })\n setIsSwiping(false)\n attemptClose()\n } else {\n resetState()\n }\n }\n\n element.addEventListener(\"pointerdown\", onPointerDown)\n element.addEventListener(\"pointermove\", onPointerMove)\n element.addEventListener(\"pointerup\", onPointerUp)\n element.addEventListener(\"pointercancel\", resetState)\n\n return () => {\n element.removeEventListener(\"pointerdown\", onPointerDown)\n element.removeEventListener(\"pointermove\", onPointerMove)\n element.removeEventListener(\"pointerup\", onPointerUp)\n element.removeEventListener(\"pointercancel\", resetState)\n }\n }, [isOpen, element, attemptClose, skipWhenTextSelected])\n\n const overlayStyle = useMemo<CSSProperties>(() => ({\n transform: isOpen ? \"translateX(0px)\" : \"translateX(100%)\",\n }), [isOpen])\n\n return { overlayRef: setElement, overlayStyle, attemptClose, isSwiping, element }\n}\n"],"mappings":"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","names":["createContext","useCallback","useContext","useEffect","useMemo","useRef","useState","MOBILE_SWIPE_CLOSE_THRESHOLD","SwipeCloseGuardContext","useSwipeCloseGuard","overlayEscapeStack","handleOverlayEscape","e","useOverlayEscapeStack","entry","idx","x","index","useSwipeToDismissOverlay","isOpen","onClose","isCloseBlocked","skipWhenTextSelected","element","setElement","isSwiping","setIsSwiping","registerEscape","overlayId","stateRef","attemptClose","resetState","s","onPointerDown","onPointerMove","dx","dy","onPointerUp","overlayStyle"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aiquants/swipe-overlay",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "React hooks and context for swipe-to-dismiss overlay interactions",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react",
|
|
7
|
+
"hook",
|
|
8
|
+
"swipe",
|
|
9
|
+
"overlay",
|
|
10
|
+
"modal",
|
|
11
|
+
"dismiss",
|
|
12
|
+
"gesture",
|
|
13
|
+
"mobile"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "fehde-k",
|
|
17
|
+
"sideEffects": false,
|
|
18
|
+
"main": "dist/index.js",
|
|
19
|
+
"module": "dist/index.mjs",
|
|
20
|
+
"types": "dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.mjs",
|
|
25
|
+
"require": "./dist/index.js",
|
|
26
|
+
"default": "./dist/index.mjs"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE"
|
|
33
|
+
],
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"react": ">=16.8.0",
|
|
36
|
+
"react-dom": ">=16.8.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@biomejs/biome": "^2.3.7",
|
|
40
|
+
"@types/react": "^18.3.27",
|
|
41
|
+
"@types/react-dom": "^18.3.7",
|
|
42
|
+
"react": "^18.3.1",
|
|
43
|
+
"react-dom": "^18.3.1",
|
|
44
|
+
"rimraf": "^6.1.2",
|
|
45
|
+
"tsup": "^8.5.1",
|
|
46
|
+
"typescript": "^5.9.3",
|
|
47
|
+
"vitest": "^3.2.4"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup",
|
|
54
|
+
"build:watch": "tsup --watch",
|
|
55
|
+
"dev": "tsup --watch",
|
|
56
|
+
"watch": "tsup --watch",
|
|
57
|
+
"type-check": "tsc --noEmit",
|
|
58
|
+
"typecheck": "npm run type-check",
|
|
59
|
+
"clean": "rimraf dist",
|
|
60
|
+
"publish:patch": "npm version patch && npm publish",
|
|
61
|
+
"publish:minor": "npm version minor && npm publish",
|
|
62
|
+
"publish:major": "npm version major && npm publish",
|
|
63
|
+
"lint": "biome lint src/",
|
|
64
|
+
"lint:fix": "biome lint --write src/",
|
|
65
|
+
"format": "biome format src/",
|
|
66
|
+
"format:fix": "biome format --write src/",
|
|
67
|
+
"check": "biome check src/",
|
|
68
|
+
"check:fix": "biome check --write src/",
|
|
69
|
+
"license-check": "pnpm dlx license-checker --production --onlyAllow \"MIT;Apache-2.0;BSD-2-Clause;BSD-3-Clause;ISC;Unlicense\"",
|
|
70
|
+
"license-check:json": "pnpm dlx license-checker --production --onlyAllow \"MIT;Apache-2.0;BSD-2-Clause;BSD-3-Clause;ISC;Unlicense\" --json",
|
|
71
|
+
"test": "vitest run --passWithNoTests",
|
|
72
|
+
"test:coverage": "vitest run --coverage --passWithNoTests"
|
|
73
|
+
}
|
|
74
|
+
}
|