react-dismiss 0.1.0 → 0.1.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 -21
- package/README.md +53 -192
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 react-dismiss
|
|
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.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 react-dismiss
|
|
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
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# react-dismiss
|
|
2
2
|
|
|
3
|
-
A tiny React hook for dismissing UI
|
|
4
|
-
|
|
5
|
-
Tiny, dependency-free, SSR-safe, StrictMode-safe, TypeScript-first. Ideal for dropdowns, popovers, modals, menus, and overlays.
|
|
3
|
+
A tiny React hook for dismissing UI when users interact outside or press Escape.
|
|
6
4
|
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
@@ -18,223 +16,86 @@ pnpm add react-dismiss
|
|
|
18
16
|
yarn add react-dismiss
|
|
19
17
|
```
|
|
20
18
|
|
|
21
|
-
Requires `react >= 18` (peer dependency).
|
|
22
|
-
|
|
23
19
|
## Quick Start
|
|
24
20
|
|
|
25
21
|
```tsx
|
|
26
22
|
import { useRef, useState } from "react";
|
|
27
23
|
import { useDismiss } from "react-dismiss";
|
|
28
24
|
|
|
29
|
-
function Dropdown() {
|
|
30
|
-
const [
|
|
31
|
-
const
|
|
25
|
+
export function Dropdown() {
|
|
26
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
27
|
+
const menuRef = useRef<HTMLDivElement>(null);
|
|
32
28
|
|
|
33
|
-
useDismiss(
|
|
34
|
-
setOpen(false);
|
|
35
|
-
});
|
|
29
|
+
useDismiss(menuRef, () => setIsOpen(false));
|
|
36
30
|
|
|
37
31
|
return (
|
|
38
|
-
<div ref={
|
|
39
|
-
<button onClick={() =>
|
|
40
|
-
{
|
|
32
|
+
<div ref={menuRef} style={{ position: "relative", display: "inline-block" }}>
|
|
33
|
+
<button onClick={() => setIsOpen((prev) => !prev)}>Toggle Menu</button>
|
|
34
|
+
{isOpen && (
|
|
35
|
+
<ul
|
|
36
|
+
style={{
|
|
37
|
+
position: "absolute",
|
|
38
|
+
top: "100%",
|
|
39
|
+
left: 0,
|
|
40
|
+
background: "#fff",
|
|
41
|
+
border: "1px solid #ccc",
|
|
42
|
+
padding: "8px",
|
|
43
|
+
listStyle: "none",
|
|
44
|
+
}}
|
|
45
|
+
>
|
|
46
|
+
<li>Profile</li>
|
|
47
|
+
<li>Settings</li>
|
|
48
|
+
<li>Log out</li>
|
|
49
|
+
</ul>
|
|
50
|
+
)}
|
|
41
51
|
</div>
|
|
42
52
|
);
|
|
43
53
|
}
|
|
44
54
|
```
|
|
45
55
|
|
|
46
|
-
## How It Works
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
Interaction inside element → ignore
|
|
50
|
-
Interaction outside element → onDismiss()
|
|
51
|
-
Escape key → onDismiss()
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
- Outside detection uses `element.contains(event.target)` — descendants are considered inside. Safe when `ref.current === null`.
|
|
55
|
-
- Escape handling listens for `keydown` with `key === "Escape"`.
|
|
56
|
-
- Listeners are attached to `document` inside `useEffect` (no SSR side-effects).
|
|
57
|
-
|
|
58
56
|
## API
|
|
59
57
|
|
|
60
58
|
```ts
|
|
61
|
-
useDismiss
|
|
59
|
+
useDismiss<T extends HTMLElement>(
|
|
60
|
+
ref: React.RefObject<T | null>,
|
|
61
|
+
onDismiss: () => void,
|
|
62
|
+
options?: UseDismissOptions
|
|
63
|
+
): void
|
|
62
64
|
```
|
|
63
65
|
|
|
64
|
-
###
|
|
65
|
-
|
|
66
|
-
`React.RefObject<T extends HTMLElement>` — the element considered "inside". Attach it to the root of the dismissible UI.
|
|
67
|
-
|
|
68
|
-
### `onDismiss`
|
|
69
|
-
|
|
70
|
-
`() => void` — called on dismissal. Latest callback is always used (ref-stable). Errors are not swallowed.
|
|
71
|
-
|
|
72
|
-
### `options`
|
|
66
|
+
### Arguments
|
|
73
67
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
68
|
+
- `ref`: `React.RefObject<T | null>` — React ref attached to the element considered "inside".
|
|
69
|
+
- `onDismiss`: `() => void` — Callback invoked when dismissal triggers. Ref-stable internally so inline callbacks do not re-bind listeners.
|
|
70
|
+
- `options`: `UseDismissOptions` (optional object):
|
|
71
|
+
- `enabled` (`boolean`, default: `true`): Master toggle to enable or disable dismissal listeners.
|
|
72
|
+
- `outside` (`boolean`, default: `true`): Whether outside pointer/click interactions trigger dismissal.
|
|
73
|
+
- `escape` (`boolean`, default: `true`): Whether pressing the `Escape` key triggers dismissal.
|
|
74
|
+
- `event` (`"pointerdown" | "click"`, default: `"pointerdown"`): DOM event used for outside interaction detection.
|
|
80
75
|
|
|
81
|
-
|
|
76
|
+
### Returns
|
|
82
77
|
|
|
83
|
-
|
|
78
|
+
`void`
|
|
84
79
|
|
|
85
|
-
|
|
86
|
-
// Disabled
|
|
87
|
-
useDismiss(ref, close, { enabled: false });
|
|
88
|
-
|
|
89
|
-
// Escape only
|
|
90
|
-
useDismiss(ref, close, { outside: false });
|
|
91
|
-
|
|
92
|
-
// Outside only
|
|
93
|
-
useDismiss(ref, close, { escape: false });
|
|
94
|
-
|
|
95
|
-
// Use click instead of pointerdown
|
|
96
|
-
useDismiss(ref, close, { event: "click" });
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
## Examples
|
|
100
|
-
|
|
101
|
-
### Dropdown
|
|
102
|
-
|
|
103
|
-
```tsx
|
|
104
|
-
const ref = useRef<HTMLDivElement>(null);
|
|
105
|
-
useDismiss(ref, () => setOpen(false));
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### Modal
|
|
109
|
-
|
|
110
|
-
```tsx
|
|
111
|
-
const ref = useRef<HTMLDivElement>(null);
|
|
112
|
-
useDismiss(ref, closeModal);
|
|
113
|
-
// Renders inside an overlay; Escape and outside click both close.
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### Popover
|
|
117
|
-
|
|
118
|
-
```tsx
|
|
119
|
-
const ref = useRef<HTMLDivElement>(null);
|
|
120
|
-
useDismiss(ref, () => setPopoverOpen(false));
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### Search suggestions
|
|
124
|
-
|
|
125
|
-
```tsx
|
|
126
|
-
const ref = useRef<HTMLDivElement>(null);
|
|
127
|
-
useDismiss(ref, () => setSuggestions([]));
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Escape-only (e.g., controlled modal where backdrop handles clicks)
|
|
131
|
-
|
|
132
|
-
```tsx
|
|
133
|
-
useDismiss(ref, close, { outside: false });
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### Click event (when pointerdown is too eager)
|
|
137
|
-
|
|
138
|
-
```tsx
|
|
139
|
-
useDismiss(ref, close, { event: "click" });
|
|
140
|
-
```
|
|
80
|
+
## What It Handles
|
|
141
81
|
|
|
142
|
-
|
|
82
|
+
- Outside interactions (`pointerdown` or `click`) while ignoring clicks on descendants inside `ref`.
|
|
83
|
+
- Escape key presses (`keydown` where `event.key === "Escape"`).
|
|
84
|
+
- Null or unmounted refs without throwing errors.
|
|
85
|
+
- Automatic event listener cleanup on component unmount or when options change.
|
|
86
|
+
- SSR safe: guards document access and attaches listeners only inside client-side `useEffect`.
|
|
143
87
|
|
|
144
88
|
## TypeScript
|
|
145
89
|
|
|
146
|
-
|
|
90
|
+
Ships with built-in TypeScript declarations and generics (`dist/index.d.ts`), no extra `@types` packages required.
|
|
147
91
|
|
|
148
|
-
|
|
149
|
-
const divRef = useRef<HTMLDivElement>(null);
|
|
150
|
-
useDismiss(divRef, onDismiss); // infers HTMLDivElement
|
|
151
|
-
|
|
152
|
-
const btnRef = useRef<HTMLButtonElement>(null);
|
|
153
|
-
useDismiss(btnRef, onDismiss); // infers HTMLButtonElement
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
## SSR
|
|
157
|
-
|
|
158
|
-
Safe for Next.js, Remix, Vite SSR, React Router. No browser globals are accessed at import time; listeners are registered inside `useEffect` only on the client.
|
|
159
|
-
|
|
160
|
-
```tsx
|
|
161
|
-
// This import never touches `document` at module init:
|
|
162
|
-
import { useDismiss } from "react-dismiss";
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
## Accessibility
|
|
166
|
-
|
|
167
|
-
> `react-dismiss` provides interaction detection, not complete accessibility behavior.
|
|
168
|
-
|
|
169
|
-
You still need to handle:
|
|
170
|
-
|
|
171
|
-
- Focus management / focus trapping for dialogs
|
|
172
|
-
- `aria-expanded`, `aria-controls`, correct `role`
|
|
173
|
-
- Keyboard navigation (arrow keys, tab order)
|
|
174
|
-
- Screen-reader announcements
|
|
175
|
-
|
|
176
|
-
Escape handling in this hook complements but does not replace those patterns. Pair it with proper ARIA:
|
|
177
|
-
|
|
178
|
-
```tsx
|
|
179
|
-
<button aria-expanded={open} aria-controls="menu">
|
|
180
|
-
Menu
|
|
181
|
-
</button>
|
|
182
|
-
<div id="menu" role="menu">{/* ... */}</div>
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
## Browser Support
|
|
186
|
-
|
|
187
|
-
- Outside detection uses `PointerEvent` (`pointerdown`). All modern browsers support it (Chrome 55+, Firefox 59+, Safari 13+).
|
|
188
|
-
- Fallback: pass `{ event: "click" }` if you must target older browsers.
|
|
189
|
-
- Escape handling uses `keydown` + `key === "Escape"` (universally supported).
|
|
190
|
-
|
|
191
|
-
## Bundle Size
|
|
192
|
-
|
|
193
|
-
- ~1.3 KB uncompressed (`dist/index.js`), < 1 KB minified + gzipped.
|
|
194
|
-
- Zero runtime dependencies.
|
|
195
|
-
- Tree-shakeable (`sideEffects: false`).
|
|
196
|
-
|
|
197
|
-
## Portals
|
|
198
|
-
|
|
199
|
-
By default "inside" means descendants of `ref.current` (`element.contains`). Content rendered in a React portal outside that subtree is considered outside and will dismiss. If you need multiple "inside" roots, compose refs or lift the dismiss root to a common ancestor. A dedicated multi-root API is intentionally out of scope for v1.
|
|
200
|
-
|
|
201
|
-
## Development
|
|
202
|
-
|
|
203
|
-
```bash
|
|
204
|
-
npm install
|
|
205
|
-
npm run typecheck
|
|
206
|
-
npm run lint
|
|
207
|
-
npm run test:run
|
|
208
|
-
npm run build
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
## Testing
|
|
212
|
-
|
|
213
|
-
```bash
|
|
214
|
-
npm run test # watch
|
|
215
|
-
npm run test:run # single run
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
Tests cover outside/inside, Escape, other keys, null ref, enabled/outside/escape toggles, dynamic enable, event switching, stale closures, unmount cleanup, StrictMode, nested elements, multiple instances, and error propagation.
|
|
219
|
-
|
|
220
|
-
## Building
|
|
221
|
-
|
|
222
|
-
```bash
|
|
223
|
-
npm run build # tsup → dist/index.js, dist/index.cjs, dist/index.d.ts
|
|
224
|
-
npm pack --dry-run # inspect published contents
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
## Publishing
|
|
228
|
-
|
|
229
|
-
```bash
|
|
230
|
-
npm run check # typecheck + lint + tests
|
|
231
|
-
npm run build
|
|
232
|
-
npm pack --dry-run # verify files: dist/, README.md, LICENSE
|
|
233
|
-
npm publish # or OIDC trusted publishing via GitHub Actions
|
|
234
|
-
```
|
|
92
|
+
## Why This One
|
|
235
93
|
|
|
236
|
-
|
|
94
|
+
- **Zero dependencies**: Only requires `react` (>= 18) as a peer dependency.
|
|
95
|
+
- **Tiny footprint**: ~1.3 KB uncompressed ESM/CJS build (< 1 KB minified + gzipped).
|
|
96
|
+
- **Ref-stable callback**: Passing inline callback functions will not re-bind document listeners on re-render.
|
|
237
97
|
|
|
238
|
-
##
|
|
98
|
+
## Links
|
|
239
99
|
|
|
240
|
-
|
|
100
|
+
- **npm**: [npmjs.com/package/react-dismiss](https://www.npmjs.com/package/react-dismiss)
|
|
101
|
+
- **GitHub**: [github.com/parasraju/react-dismiss](https://github.com/parasraju/react-dismiss)
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/useDismiss.ts"],"names":["useRef","useEffect"],"mappings":";;;;;AAGO,SAAS,UAAA,CACd,GAAA,EACA,SAAA,EACA,OAAA,GAA6B,EAAC,EACxB;AACN,EAAA,MAAM;AAAA,IACJ,OAAA,GAAU,IAAA;AAAA,IACV,OAAA,GAAU,IAAA;AAAA,IACV,MAAA,GAAS,IAAA;AAAA,IACT,KAAA,GAAQ;AAAA,GACV,GAAI,OAAA;AAEJ,EAAA,MAAM,YAAA,GAAeA,aAAO,SAAS,CAAA;AACrC,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,YAAA,CAAa,OAAA,GAAU,SAAA;AAAA,EACzB,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAAA,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,EAAS;AACd,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,MAAA,EAAQ;AAIzB,IAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AAErC,IAAA,MAAM,aAAA,GAAgB,CAAC,CAAA,KAAa;AAClC,MAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,MAAA,IAAI,CAAC,EAAA,EAAI;AACT,MAAA,MAAM,SAAS,CAAA,CAAE,MAAA;AACjB,MAAA,IAAI,EAAE,kBAAkB,IAAA,CAAA,EAAO;AAC/B,MAAA,IAAI,EAAA,CAAG,QAAA,CAAS,MAAM,CAAA,EAAG;AACzB,MAAA,YAAA,CAAa,OAAA,EAAQ;AAAA,IACvB,CAAA;AAEA,IAAA,MAAM,aAAA,GAAgB,CAAC,CAAA,KAAqB;AAC1C,MAAA,IAAI,CAAA,CAAE,QAAQ,QAAA,EAAU;AACtB,QAAA,YAAA,CAAa,OAAA,EAAQ;AAAA,MACvB;AAAA,IACF,CAAA;AAEA,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,QAAA,CAAS,gBAAA,CAAiB,OAAO,aAAa,CAAA;AAAA,IAChD;AACA,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,QAAA,CAAS,gBAAA,CAAiB,WAAW,aAAa,CAAA;AAAA,IACpD;AAEA,IAAA,OAAO,MAAM;AACX,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,QAAA,CAAS,mBAAA,CAAoB,OAAO,aAAa,CAAA;AAAA,MACnD;AACA,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,QAAA,CAAS,mBAAA,CAAoB,WAAW,aAAa,CAAA;AAAA,MACvD;AAAA,IACF,CAAA;AAAA,EACF,GAAG,CAAC,GAAA,EAAK,SAAS,OAAA,EAAS,MAAA,EAAQ,KAAK,CAAC,CAAA;AAC3C","file":"index.cjs","sourcesContent":["import { useEffect, useRef } from \"react\";\nimport type { UseDismissOptions } from \"./types.js\";\n\nexport function useDismiss<T extends HTMLElement>(\n ref: React.RefObject<T | null>,\n onDismiss: () => void,\n options: UseDismissOptions = {},\n): void {\n const {\n enabled = true,\n outside = true,\n escape = true,\n event = \"pointerdown\",\n } = options;\n\n const onDismissRef = useRef(onDismiss);\n useEffect(() => {\n onDismissRef.current = onDismiss;\n }, [onDismiss]);\n\n useEffect(() => {\n if (!enabled) return;\n if (!outside && !escape) return;\n\n // SSR guard: document is unavailable on server. Effect only runs on client,\n // but guard keeps logic safe if environment is non-DOM.\n if (typeof document === \"undefined\") return;\n\n const handleOutside = (e: Event) => {\n const el = ref.current;\n if (!el) return;\n const target = e.target;\n if (!(target instanceof Node)) return;\n if (el.contains(target)) return;\n onDismissRef.current();\n };\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n onDismissRef.current();\n }\n };\n\n if (outside) {\n document.addEventListener(event, handleOutside);\n }\n if (escape) {\n document.addEventListener(\"keydown\", handleKeyDown);\n }\n\n return () => {\n if (outside) {\n document.removeEventListener(event, handleOutside);\n }\n if (escape) {\n document.removeEventListener(\"keydown\", handleKeyDown);\n }\n };\n }, [ref, enabled, outside, escape, event]);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/useDismiss.ts"],"names":["useRef","useEffect"],"mappings":";;;;;AAGO,SAAS,UAAA,CACd,GAAA,EACA,SAAA,EACA,OAAA,GAA6B,EAAC,EACxB;AACN,EAAA,MAAM;AAAA,IACJ,OAAA,GAAU,IAAA;AAAA,IACV,OAAA,GAAU,IAAA;AAAA,IACV,MAAA,GAAS,IAAA;AAAA,IACT,KAAA,GAAQ;AAAA,GACV,GAAI,OAAA;AAEJ,EAAA,MAAM,YAAA,GAAeA,aAAO,SAAS,CAAA;AACrC,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,YAAA,CAAa,OAAA,GAAU,SAAA;AAAA,EACzB,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAAA,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,EAAS;AACd,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,MAAA,EAAQ;AAIzB,IAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AAErC,IAAA,MAAM,aAAA,GAAgB,CAAC,CAAA,KAAa;AAClC,MAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,MAAA,IAAI,CAAC,EAAA,EAAI;AACT,MAAA,MAAM,SAAS,CAAA,CAAE,MAAA;AACjB,MAAA,IAAI,EAAE,kBAAkB,IAAA,CAAA,EAAO;AAC/B,MAAA,IAAI,EAAA,CAAG,QAAA,CAAS,MAAM,CAAA,EAAG;AACzB,MAAA,YAAA,CAAa,OAAA,EAAQ;AAAA,IACvB,CAAA;AAEA,IAAA,MAAM,aAAA,GAAgB,CAAC,CAAA,KAAqB;AAC1C,MAAA,IAAI,CAAA,CAAE,QAAQ,QAAA,EAAU;AACtB,QAAA,YAAA,CAAa,OAAA,EAAQ;AAAA,MACvB;AAAA,IACF,CAAA;AAEA,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,QAAA,CAAS,gBAAA,CAAiB,OAAO,aAAa,CAAA;AAAA,IAChD;AACA,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,QAAA,CAAS,gBAAA,CAAiB,WAAW,aAAa,CAAA;AAAA,IACpD;AAEA,IAAA,OAAO,MAAM;AACX,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,QAAA,CAAS,mBAAA,CAAoB,OAAO,aAAa,CAAA;AAAA,MACnD;AACA,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,QAAA,CAAS,mBAAA,CAAoB,WAAW,aAAa,CAAA;AAAA,MACvD;AAAA,IACF,CAAA;AAAA,EACF,GAAG,CAAC,GAAA,EAAK,SAAS,OAAA,EAAS,MAAA,EAAQ,KAAK,CAAC,CAAA;AAC3C","file":"index.cjs","sourcesContent":["import { useEffect, useRef } from \"react\";\r\nimport type { UseDismissOptions } from \"./types.js\";\r\n\r\nexport function useDismiss<T extends HTMLElement>(\r\n ref: React.RefObject<T | null>,\r\n onDismiss: () => void,\r\n options: UseDismissOptions = {},\r\n): void {\r\n const {\r\n enabled = true,\r\n outside = true,\r\n escape = true,\r\n event = \"pointerdown\",\r\n } = options;\r\n\r\n const onDismissRef = useRef(onDismiss);\r\n useEffect(() => {\r\n onDismissRef.current = onDismiss;\r\n }, [onDismiss]);\r\n\r\n useEffect(() => {\r\n if (!enabled) return;\r\n if (!outside && !escape) return;\r\n\r\n // SSR guard: document is unavailable on server. Effect only runs on client,\r\n // but guard keeps logic safe if environment is non-DOM.\r\n if (typeof document === \"undefined\") return;\r\n\r\n const handleOutside = (e: Event) => {\r\n const el = ref.current;\r\n if (!el) return;\r\n const target = e.target;\r\n if (!(target instanceof Node)) return;\r\n if (el.contains(target)) return;\r\n onDismissRef.current();\r\n };\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n onDismissRef.current();\r\n }\r\n };\r\n\r\n if (outside) {\r\n document.addEventListener(event, handleOutside);\r\n }\r\n if (escape) {\r\n document.addEventListener(\"keydown\", handleKeyDown);\r\n }\r\n\r\n return () => {\r\n if (outside) {\r\n document.removeEventListener(event, handleOutside);\r\n }\r\n if (escape) {\r\n document.removeEventListener(\"keydown\", handleKeyDown);\r\n }\r\n };\r\n }, [ref, enabled, outside, escape, event]);\r\n}\r\n"]}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/useDismiss.ts"],"names":[],"mappings":";;;AAGO,SAAS,UAAA,CACd,GAAA,EACA,SAAA,EACA,OAAA,GAA6B,EAAC,EACxB;AACN,EAAA,MAAM;AAAA,IACJ,OAAA,GAAU,IAAA;AAAA,IACV,OAAA,GAAU,IAAA;AAAA,IACV,MAAA,GAAS,IAAA;AAAA,IACT,KAAA,GAAQ;AAAA,GACV,GAAI,OAAA;AAEJ,EAAA,MAAM,YAAA,GAAe,OAAO,SAAS,CAAA;AACrC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,YAAA,CAAa,OAAA,GAAU,SAAA;AAAA,EACzB,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,EAAS;AACd,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,MAAA,EAAQ;AAIzB,IAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AAErC,IAAA,MAAM,aAAA,GAAgB,CAAC,CAAA,KAAa;AAClC,MAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,MAAA,IAAI,CAAC,EAAA,EAAI;AACT,MAAA,MAAM,SAAS,CAAA,CAAE,MAAA;AACjB,MAAA,IAAI,EAAE,kBAAkB,IAAA,CAAA,EAAO;AAC/B,MAAA,IAAI,EAAA,CAAG,QAAA,CAAS,MAAM,CAAA,EAAG;AACzB,MAAA,YAAA,CAAa,OAAA,EAAQ;AAAA,IACvB,CAAA;AAEA,IAAA,MAAM,aAAA,GAAgB,CAAC,CAAA,KAAqB;AAC1C,MAAA,IAAI,CAAA,CAAE,QAAQ,QAAA,EAAU;AACtB,QAAA,YAAA,CAAa,OAAA,EAAQ;AAAA,MACvB;AAAA,IACF,CAAA;AAEA,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,QAAA,CAAS,gBAAA,CAAiB,OAAO,aAAa,CAAA;AAAA,IAChD;AACA,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,QAAA,CAAS,gBAAA,CAAiB,WAAW,aAAa,CAAA;AAAA,IACpD;AAEA,IAAA,OAAO,MAAM;AACX,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,QAAA,CAAS,mBAAA,CAAoB,OAAO,aAAa,CAAA;AAAA,MACnD;AACA,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,QAAA,CAAS,mBAAA,CAAoB,WAAW,aAAa,CAAA;AAAA,MACvD;AAAA,IACF,CAAA;AAAA,EACF,GAAG,CAAC,GAAA,EAAK,SAAS,OAAA,EAAS,MAAA,EAAQ,KAAK,CAAC,CAAA;AAC3C","file":"index.js","sourcesContent":["import { useEffect, useRef } from \"react\";\nimport type { UseDismissOptions } from \"./types.js\";\n\nexport function useDismiss<T extends HTMLElement>(\n ref: React.RefObject<T | null>,\n onDismiss: () => void,\n options: UseDismissOptions = {},\n): void {\n const {\n enabled = true,\n outside = true,\n escape = true,\n event = \"pointerdown\",\n } = options;\n\n const onDismissRef = useRef(onDismiss);\n useEffect(() => {\n onDismissRef.current = onDismiss;\n }, [onDismiss]);\n\n useEffect(() => {\n if (!enabled) return;\n if (!outside && !escape) return;\n\n // SSR guard: document is unavailable on server. Effect only runs on client,\n // but guard keeps logic safe if environment is non-DOM.\n if (typeof document === \"undefined\") return;\n\n const handleOutside = (e: Event) => {\n const el = ref.current;\n if (!el) return;\n const target = e.target;\n if (!(target instanceof Node)) return;\n if (el.contains(target)) return;\n onDismissRef.current();\n };\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n onDismissRef.current();\n }\n };\n\n if (outside) {\n document.addEventListener(event, handleOutside);\n }\n if (escape) {\n document.addEventListener(\"keydown\", handleKeyDown);\n }\n\n return () => {\n if (outside) {\n document.removeEventListener(event, handleOutside);\n }\n if (escape) {\n document.removeEventListener(\"keydown\", handleKeyDown);\n }\n };\n }, [ref, enabled, outside, escape, event]);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/useDismiss.ts"],"names":[],"mappings":";;;AAGO,SAAS,UAAA,CACd,GAAA,EACA,SAAA,EACA,OAAA,GAA6B,EAAC,EACxB;AACN,EAAA,MAAM;AAAA,IACJ,OAAA,GAAU,IAAA;AAAA,IACV,OAAA,GAAU,IAAA;AAAA,IACV,MAAA,GAAS,IAAA;AAAA,IACT,KAAA,GAAQ;AAAA,GACV,GAAI,OAAA;AAEJ,EAAA,MAAM,YAAA,GAAe,OAAO,SAAS,CAAA;AACrC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,YAAA,CAAa,OAAA,GAAU,SAAA;AAAA,EACzB,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,EAAS;AACd,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,MAAA,EAAQ;AAIzB,IAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AAErC,IAAA,MAAM,aAAA,GAAgB,CAAC,CAAA,KAAa;AAClC,MAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,MAAA,IAAI,CAAC,EAAA,EAAI;AACT,MAAA,MAAM,SAAS,CAAA,CAAE,MAAA;AACjB,MAAA,IAAI,EAAE,kBAAkB,IAAA,CAAA,EAAO;AAC/B,MAAA,IAAI,EAAA,CAAG,QAAA,CAAS,MAAM,CAAA,EAAG;AACzB,MAAA,YAAA,CAAa,OAAA,EAAQ;AAAA,IACvB,CAAA;AAEA,IAAA,MAAM,aAAA,GAAgB,CAAC,CAAA,KAAqB;AAC1C,MAAA,IAAI,CAAA,CAAE,QAAQ,QAAA,EAAU;AACtB,QAAA,YAAA,CAAa,OAAA,EAAQ;AAAA,MACvB;AAAA,IACF,CAAA;AAEA,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,QAAA,CAAS,gBAAA,CAAiB,OAAO,aAAa,CAAA;AAAA,IAChD;AACA,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,QAAA,CAAS,gBAAA,CAAiB,WAAW,aAAa,CAAA;AAAA,IACpD;AAEA,IAAA,OAAO,MAAM;AACX,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,QAAA,CAAS,mBAAA,CAAoB,OAAO,aAAa,CAAA;AAAA,MACnD;AACA,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,QAAA,CAAS,mBAAA,CAAoB,WAAW,aAAa,CAAA;AAAA,MACvD;AAAA,IACF,CAAA;AAAA,EACF,GAAG,CAAC,GAAA,EAAK,SAAS,OAAA,EAAS,MAAA,EAAQ,KAAK,CAAC,CAAA;AAC3C","file":"index.js","sourcesContent":["import { useEffect, useRef } from \"react\";\r\nimport type { UseDismissOptions } from \"./types.js\";\r\n\r\nexport function useDismiss<T extends HTMLElement>(\r\n ref: React.RefObject<T | null>,\r\n onDismiss: () => void,\r\n options: UseDismissOptions = {},\r\n): void {\r\n const {\r\n enabled = true,\r\n outside = true,\r\n escape = true,\r\n event = \"pointerdown\",\r\n } = options;\r\n\r\n const onDismissRef = useRef(onDismiss);\r\n useEffect(() => {\r\n onDismissRef.current = onDismiss;\r\n }, [onDismiss]);\r\n\r\n useEffect(() => {\r\n if (!enabled) return;\r\n if (!outside && !escape) return;\r\n\r\n // SSR guard: document is unavailable on server. Effect only runs on client,\r\n // but guard keeps logic safe if environment is non-DOM.\r\n if (typeof document === \"undefined\") return;\r\n\r\n const handleOutside = (e: Event) => {\r\n const el = ref.current;\r\n if (!el) return;\r\n const target = e.target;\r\n if (!(target instanceof Node)) return;\r\n if (el.contains(target)) return;\r\n onDismissRef.current();\r\n };\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n onDismissRef.current();\r\n }\r\n };\r\n\r\n if (outside) {\r\n document.addEventListener(event, handleOutside);\r\n }\r\n if (escape) {\r\n document.addEventListener(\"keydown\", handleKeyDown);\r\n }\r\n\r\n return () => {\r\n if (outside) {\r\n document.removeEventListener(event, handleOutside);\r\n }\r\n if (escape) {\r\n document.removeEventListener(\"keydown\", handleKeyDown);\r\n }\r\n };\r\n }, [ref, enabled, outside, escape, event]);\r\n}\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-dismiss",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A tiny React hook for dismissing UI when users interact outside or press Escape.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"author": "",
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
20
|
-
"url": "https://github.com/parasraju/react-dismiss.git"
|
|
20
|
+
"url": "git+https://github.com/parasraju/react-dismiss.git"
|
|
21
21
|
},
|
|
22
22
|
"bugs": {
|
|
23
23
|
"url": "https://github.com/parasraju/react-dismiss/issues"
|