usethishook 0.1.0 → 0.2.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,37 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ Bump `version` in `package.json` in the same change as the notes below, then run
9
+ **Actions → Publish npm** (manual). Create GitHub Release **vX.Y.Z** from the matching section.
10
+ Do not publish from a push or tag.
11
+
12
+ ## [0.2.0] - 2026-09-12
13
+
14
+ ### Added
15
+
16
+ - `useEventListener` — window / document / node / ref listener with a stable handler
17
+ - `useTimeout` — one-shot delay (`null` pauses)
18
+ - `useHover` — `{ ref, isHovered }`
19
+ - `useKeyPress` — key held down; ignores editable fields
20
+ - `usePreferredColorScheme` — OS `light` | `dark` via `matchMedia`
21
+ - Playground **Home** control in the sidebar
22
+ - Smooth-scroll to top when changing playground routes
23
+ - Radix Scroll Area on the playground sidebar hook list
24
+
25
+ ## [0.1.1] - 2026-09-12
26
+
27
+ ### Changed
28
+
29
+ - `repository` and `bugs` URLs use the camelCase GitHub repo `useThisHook`.
30
+ - Playground GitHub Pages build uses relative asset URLs so `/useThisHook/` loads scripts.
31
+
32
+ ## [0.1.0] - 2026-09-12
33
+
34
+ ### Added
35
+
36
+ - First public release of `usethishook` with state, browser, and app hooks.
37
+ - Playground documentation with live previews and API reference.
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # useThisHook
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/usethishook.svg)](https://www.npmjs.com/package/usethishook)
4
+ [![license](https://img.shields.io/npm/l/usethishook.svg)](LICENSE)
5
+ [![CI](https://github.com/senthilkumar979/useThisHook/actions/workflows/ci.yml/badge.svg)](https://github.com/senthilkumar979/useThisHook/actions/workflows/ci.yml)
6
+
3
7
  **useThisHook** is an open-source TypeScript library of named React hooks for everyday UI, forms, lists, overlays, and browser APIs. Import only what you need — the package is tree-shakeable.
4
8
 
5
9
  The npm package name is lowercase: [`usethishook`](https://www.npmjs.com/package/usethishook).
@@ -8,7 +12,7 @@ The npm package name is lowercase: [`usethishook`](https://www.npmjs.com/package
8
12
  npm install usethishook
9
13
  ```
10
14
 
11
- **Peer dependency:** React 18 or later (React 19 is supported).
15
+ **Peer dependencies:** React 18 or later, and **React DOM** 18 or later for confirm, prompt, overlay, and step-flow hooks (React 19 is supported).
12
16
 
13
17
  ## Why use it
14
18
 
@@ -19,17 +23,25 @@ npm install usethishook
19
23
 
20
24
  ## Documentation playground
21
25
 
26
+ - Hosted: [usethishook.mentorbridge.in](https://usethishook.mentorbridge.in) (Vercel)
27
+ - Mirror: [senthilkumar979.github.io/useThisHook](https://senthilkumar979.github.io/useThisHook/) (GitHub Pages)
28
+
22
29
  ```bash
23
30
  npm install
24
31
  npm run playground
25
32
  ```
26
33
 
27
- Open the URL Vite prints (usually `http://localhost:5173`).
34
+ Open the URL Vite prints (usually `http://localhost:5173`). Light mode is the default; use the sun/moon icon at the top right to switch theme.
28
35
 
29
36
  - Home (`#/`): what the library is, install, and the hook index
30
37
  - Hook pages (`#/useToggle`, `#/useConfirm`, …): description, live preview, API, example
31
38
 
32
- See [`playground/README.md`](playground/README.md). Build a static site with `npm run playground:build` (output in `playground/dist`).
39
+ See [`playground/README.md`](playground/README.md).
40
+
41
+ - `npm run playground:build` — static site with `base: /` (Vercel / custom domain)
42
+ - `npm run playground:build:pages` — same site with `base: /useThisHook/` for project GitHub Pages
43
+
44
+ Output is `playground/dist`. Hash routes (`#/…`) work on Pages without extra rewrite rules. In the GitHub repo, set **Settings → Pages → Source** to **GitHub Actions**.
33
45
 
34
46
  ## Usage
35
47
 
@@ -46,9 +58,7 @@ export const SearchBox = () => {
46
58
  <button type="button" onClick={toggle}>
47
59
  {isOpen ? 'Hide' : 'Show'} search
48
60
  </button>
49
- {isOpen && (
50
- <input value={query} onChange={(event) => setQuery(event.target.value)} />
51
- )}
61
+ {isOpen && <input value={query} onChange={(event) => setQuery(event.target.value)} />}
52
62
  <p>Searching for: {debouncedQuery}</p>
53
63
  </div>
54
64
  );
@@ -87,60 +97,73 @@ Playground paths are hash routes on the local docs app (for example `#/useToggle
87
97
 
88
98
  ### State
89
99
 
90
- | Hook | Purpose | Preview |
91
- | --- | --- | --- |
92
- | `useToggle` | Boolean with `toggle`, `setTrue`, `setFalse` | `#/useToggle` |
93
- | `useCounter` | Increment, decrement, reset | `#/useCounter` |
94
- | `useDisclosure` | Open / close / toggle for menus and dialogs | `#/useDisclosure` |
95
- | `useDebounce` | Debounce a rapidly changing value | `#/useDebounce` |
96
- | `usePrevious` | Previous render’s value | `#/usePrevious` |
97
- | `useInterval` | Declarative `setInterval` (`null` pauses) | `#/useInterval` |
98
- | `useCopyToClipboard` | Clipboard write + last copied text | `#/useCopyToClipboard` |
99
- | `useLocalStorage` | JSON state persisted in `localStorage` | `#/useLocalStorage` |
100
- | `useDocumentTitle` | Set `document.title` while mounted | `#/useDocumentTitle` |
100
+ | Hook | Purpose | Preview |
101
+ | -------------------- | -------------------------------------------- | ---------------------- |
102
+ | `useToggle` | Boolean with `toggle`, `setTrue`, `setFalse` | `#/useToggle` |
103
+ | `useCounter` | Increment, decrement, reset | `#/useCounter` |
104
+ | `useDisclosure` | Open / close / toggle for menus and dialogs | `#/useDisclosure` |
105
+ | `useDebounce` | Debounce a rapidly changing value | `#/useDebounce` |
106
+ | `usePrevious` | Previous render’s value | `#/usePrevious` |
107
+ | `useInterval` | Declarative `setInterval` (`null` pauses) | `#/useInterval` |
108
+ | `useCopyToClipboard` | Clipboard write + last copied text | `#/useCopyToClipboard` |
109
+ | `useLocalStorage` | JSON state persisted in `localStorage` | `#/useLocalStorage` |
110
+ | `useDocumentTitle` | Set `document.title` while mounted | `#/useDocumentTitle` |
101
111
 
102
112
  ### Browser
103
113
 
104
- | Hook | Purpose | Preview |
105
- | --- | --- | --- |
106
- | `useOnlineStatus` | `navigator.onLine` plus online/offline events | `#/useOnlineStatus` |
107
- | `useMediaQuery` | Subscribe to a CSS media query | `#/useMediaQuery` |
108
- | `useWindowSize` | Viewport width and height | `#/useWindowSize` |
109
- | `useOnClickOutside` | Handler when the user presses outside a ref | `#/useOnClickOutside` |
110
- | `useOverlay` | Promise-based custom overlay | `#/useOverlay` |
111
- | `useStepFlow` | Multi-step wizard that resolves when finished | `#/useStepFlow` |
112
- | `useAsyncSelect` | Native file picker as a Promise | `#/useAsyncSelect` |
114
+ | Hook | Purpose | Preview |
115
+ | ------------------- | --------------------------------------------- | --------------------- |
116
+ | `useOnlineStatus` | `navigator.onLine` plus online/offline events | `#/useOnlineStatus` |
117
+ | `useMediaQuery` | Subscribe to a CSS media query | `#/useMediaQuery` |
118
+ | `useWindowSize` | Viewport width and height | `#/useWindowSize` |
119
+ | `useOnClickOutside` | Handler when the user presses outside a ref | `#/useOnClickOutside` |
120
+ | `useOverlay` | Promise-based custom overlay | `#/useOverlay` |
121
+ | `useStepFlow` | Multi-step wizard that resolves when finished | `#/useStepFlow` |
122
+ | `useAsyncSelect` | Native file picker as a Promise | `#/useAsyncSelect` |
123
+ | `useEventListener` | DOM / window listener with a stable handler | `#/useEventListener` |
124
+ | `useTimeout` | One-shot timer (`null` pauses) | `#/useTimeout` |
125
+ | `useHover` | Pointer over a ref | `#/useHover` |
126
+ | `useKeyPress` | Key held down (ignores inputs) | `#/useKeyPress` |
127
+ | `usePreferredColorScheme` | OS `prefers-color-scheme` | `#/usePreferredColorScheme` |
113
128
 
114
129
  ### App
115
130
 
116
- | Hook | Purpose | Preview |
117
- | --- | --- | --- |
118
- | `useStableCallback` | Stable function identity, always-latest body | `#/useStableCallback` |
119
- | `useOnChange` | Callback when a value changes, not on mount | `#/useOnChange` |
120
- | `useResetState` | Local state that resets when a source key changes | `#/useResetState` |
121
- | `useAsyncAction` | Pending / error / data around one async action | `#/useAsyncAction` |
122
- | `useDebouncedCallback` | Debounce calling a function | `#/useDebouncedCallback` |
123
- | `useFields` | Small form object, optional Zod-shaped schema | `#/useFields` |
124
- | `useList` | Insert, update, remove, reorder by `id` | `#/useList` |
125
- | `useSelection` | Single or multi select ids | `#/useSelection` |
126
- | `useSearchState` | URL search params as React state | `#/useSearchState` |
127
- | `useConfirm` | Await a yes/no dialog | `#/useConfirm` |
128
- | `usePrompt` | Await a string from a dialog | `#/usePrompt` |
129
- | `useControllableState` | Controlled and uncontrolled in one setter | `#/useControllableState` |
130
- | `useUnsavedChanges` | Tab-close warning and in-app leave confirm | `#/useUnsavedChanges` |
131
- | `useElementSize` | Element size via `ResizeObserver` | `#/useElementSize` |
132
- | `useInView` | Element vs viewport via `IntersectionObserver` | `#/useInView` |
133
- | `usePagination` | Page, offset, next/prev with clamping | `#/usePagination` |
131
+ | Hook | Purpose | Preview |
132
+ | ---------------------- | ------------------------------------------------- | ------------------------ |
133
+ | `useStableCallback` | Stable function identity, always-latest body | `#/useStableCallback` |
134
+ | `useOnChange` | Callback when a value changes, not on mount | `#/useOnChange` |
135
+ | `useResetState` | Local state that resets when a source key changes | `#/useResetState` |
136
+ | `useAsyncAction` | Pending / error / data around one async action | `#/useAsyncAction` |
137
+ | `useDebouncedCallback` | Debounce calling a function | `#/useDebouncedCallback` |
138
+ | `useFields` | Small form object, optional Zod-shaped schema | `#/useFields` |
139
+ | `useList` | Insert, update, remove, reorder by `id` | `#/useList` |
140
+ | `useSelection` | Single or multi select ids | `#/useSelection` |
141
+ | `useSearchState` | URL search params as React state | `#/useSearchState` |
142
+ | `useConfirm` | Await a yes/no dialog | `#/useConfirm` |
143
+ | `usePrompt` | Await a string from a dialog | `#/usePrompt` |
144
+ | `useControllableState` | Controlled and uncontrolled in one setter | `#/useControllableState` |
145
+ | `useUnsavedChanges` | Tab-close warning and in-app leave confirm | `#/useUnsavedChanges` |
146
+ | `useElementSize` | Element size via `ResizeObserver` | `#/useElementSize` |
147
+ | `useInView` | Element vs viewport via `IntersectionObserver` | `#/useInView` |
148
+ | `usePagination` | Page, offset, next/prev with clamping | `#/usePagination` |
149
+
150
+ ## Server rendering
151
+
152
+ Hooks that read `window`, `document`, `navigator`, or observers are safe to _call_ on the server when they fall back (typical values: `false`, `{ width: 0, height: 0 }`, or an empty query). They subscribe after mount.
153
+
154
+ - `useMediaQuery`, `useWindowSize`, `useOnlineStatus`, `useLocalStorage`, `useSearchState`, `useDocumentTitle`, `useOnClickOutside`, `useElementSize`, `useInView`, `useUnsavedChanges`, `useEventListener`, `useTimeout`, `useHover`, `useKeyPress`, and `usePreferredColorScheme` must not assume a browser until after hydration.
155
+ - Confirm, prompt, overlay, and step-flow still need `render()` in the client tree.
134
156
 
135
157
  ## Scripts
136
158
 
137
- | Command | What it does |
138
- | --- | --- |
139
- | `npm test` | Vitest (jsdom) |
140
- | `npm run typecheck` | `tsc --noEmit` |
141
- | `npm run build` | ESM + CJS + types via tsup (`dist/`) |
142
- | `npm run playground` | Docs app |
143
- | `npm run playground:build` | Static playground |
159
+ | Command | What it does |
160
+ | -------------------------- | ------------------------------------ |
161
+ | `npm test` | Vitest (jsdom) |
162
+ | `npm run typecheck` | Library + playground `tsc --noEmit` |
163
+ | `npm run lint` | ESLint + Prettier check |
164
+ | `npm run build` | ESM + CJS + types via tsup (`dist/`) |
165
+ | `npm run playground` | Docs app |
166
+ | `npm run playground:build` | Static playground (`base: /`) |
144
167
 
145
168
  `prepublishOnly` runs typecheck, tests, and build.
146
169
 
@@ -155,14 +178,29 @@ npm run playground
155
178
 
156
179
  Guidelines for adding a hook: [`CONTRIBUTING.md`](CONTRIBUTING.md).
157
180
 
158
- ## Publish to npm
181
+ ## Publish
182
+
183
+ **npm** is the install path for everyone: `npm install usethishook`. Publishing to the public registry is **manual only** — it does not run on push.
184
+
185
+ 1. Bump `version` in `package.json` and add a [`CHANGELOG.md`](CHANGELOG.md) section in the same commit.
186
+ 2. Add a repo secret named `NPM_TOKEN` (npm access token with publish rights). Publish stays **Actions → Publish npm → Run workflow** — never on push.
187
+ 3. After publish, link the npm package on the GitHub repo if it is not already connected.
188
+
189
+ [`.github/workflows/npm-publish.yml`](.github/workflows/npm-publish.yml) runs `npm publish --access public`. `prepublishOnly` still typechecks, tests, and builds.
159
190
 
160
- 1. Push the public GitHub repository.
161
- 2. `npm login`
162
- 3. Confirm the name is free: `npm view usethishook`
163
- 4. `npm publish --access public`
191
+ **GitHub Packages** is a second registry and also does not publish on push. It needs a scoped name (`@owner/package`). The committed `package.json` stays `usethishook`. [`.github/workflows/github-packages.yml`](.github/workflows/github-packages.yml) renames it to `@senthilkumar979/usethishook` only for that job.
164
192
 
165
- Later releases: `npm version patch|minor|major`, then publish again.
193
+ **Actions Publish GitHub Package Run workflow**, or create a GitHub Release.
194
+
195
+ Install from GitHub Packages (optional; still requires a GitHub token for the registry):
196
+
197
+ ```bash
198
+ npm install @senthilkumar979/usethishook
199
+ ```
200
+
201
+ ```ini
202
+ @senthilkumar979:registry=https://npm.pkg.github.com
203
+ ```
166
204
 
167
205
  ## License
168
206
 
@@ -183,4 +221,4 @@ The full legal text is in [`LICENSE`](LICENSE).
183
221
  - Website: [senthilkumar.mentorbridge.in](https://senthilkumar.mentorbridge.in)
184
222
  - LinkedIn: [linkedin.com/in/senthilk979](https://www.linkedin.com/in/senthilk979)
185
223
 
186
- Issues and ideas: [GitHub issues](https://github.com/senthilkumar979/usethishook/issues).
224
+ Issues and ideas: [GitHub issues](https://github.com/senthilkumar979/useThisHook/issues).
package/dist/index.cjs CHANGED
@@ -143,6 +143,90 @@ var useOnClickOutside = (ref, handler) => {
143
143
  };
144
144
  }, [handler, ref]);
145
145
  };
146
+ function resolveTarget(target) {
147
+ if (target == null) return null;
148
+ if (typeof target === "object" && "current" in target) return target.current;
149
+ return target;
150
+ }
151
+ var useEventListener = (target, type, handler, options) => {
152
+ const handlerRef = react.useRef(handler);
153
+ handlerRef.current = handler;
154
+ const element = resolveTarget(target);
155
+ react.useEffect(() => {
156
+ if (typeof window === "undefined" || !element) return;
157
+ const listener = (event) => {
158
+ handlerRef.current(event);
159
+ };
160
+ element.addEventListener(type, listener, options);
161
+ return () => {
162
+ element.removeEventListener(type, listener, options);
163
+ };
164
+ }, [element, options, type]);
165
+ };
166
+ var useTimeout = (callback, delayMs) => {
167
+ const savedCallback = react.useRef(callback);
168
+ react.useEffect(() => {
169
+ savedCallback.current = callback;
170
+ }, [callback]);
171
+ react.useEffect(() => {
172
+ if (delayMs === null || typeof window === "undefined") return;
173
+ const timeoutId = window.setTimeout(() => {
174
+ savedCallback.current();
175
+ }, delayMs);
176
+ return () => {
177
+ window.clearTimeout(timeoutId);
178
+ };
179
+ }, [delayMs]);
180
+ };
181
+ var useHover = () => {
182
+ const ref = react.useRef(null);
183
+ const [isHovered, setIsHovered] = react.useState(false);
184
+ react.useEffect(() => {
185
+ const node = ref.current;
186
+ if (!node) return;
187
+ const handleEnter = () => setIsHovered(true);
188
+ const handleLeave = () => setIsHovered(false);
189
+ node.addEventListener("pointerenter", handleEnter);
190
+ node.addEventListener("pointerleave", handleLeave);
191
+ return () => {
192
+ node.removeEventListener("pointerenter", handleEnter);
193
+ node.removeEventListener("pointerleave", handleLeave);
194
+ };
195
+ }, []);
196
+ return { ref, isHovered };
197
+ };
198
+ function isEditableTarget(target) {
199
+ if (!(target instanceof HTMLElement)) return false;
200
+ const tag = target.tagName;
201
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
202
+ return target.isContentEditable;
203
+ }
204
+ var useKeyPress = (key) => {
205
+ const [isPressed, setIsPressed] = react.useState(false);
206
+ react.useEffect(() => {
207
+ if (typeof window === "undefined") return;
208
+ const handleDown = (event) => {
209
+ if (isEditableTarget(event.target)) return;
210
+ if (event.key === key) setIsPressed(true);
211
+ };
212
+ const handleUp = (event) => {
213
+ if (event.key === key) setIsPressed(false);
214
+ };
215
+ window.addEventListener("keydown", handleDown);
216
+ window.addEventListener("keyup", handleUp);
217
+ return () => {
218
+ window.removeEventListener("keydown", handleDown);
219
+ window.removeEventListener("keyup", handleUp);
220
+ };
221
+ }, [key]);
222
+ return isPressed;
223
+ };
224
+
225
+ // src/hooks/usePreferredColorScheme.ts
226
+ var usePreferredColorScheme = () => {
227
+ const isDark = useMediaQuery("(prefers-color-scheme: dark)");
228
+ return isDark ? "dark" : "light";
229
+ };
146
230
  var useCopyToClipboard = () => {
147
231
  const [copiedText, setCopiedText] = react.useState(null);
148
232
  const copy = react.useCallback(async (text) => {
@@ -233,9 +317,7 @@ var useStepFlow = (steps) => {
233
317
  const [currentStepIndex, setCurrentStepIndex] = react.useState(0);
234
318
  const [isActive, setIsActive] = react.useState(false);
235
319
  const resultsRef = react.useRef([]);
236
- const resolverRef = react.useRef(
237
- null
238
- );
320
+ const resolverRef = react.useRef(null);
239
321
  const next = react.useCallback(() => {
240
322
  setIsActive(true);
241
323
  setCurrentStepIndex(0);
@@ -522,9 +604,12 @@ var useSelection = ({ mode = "multiple", initial = [] } = {}) => {
522
604
  const selectOnly = react.useCallback((id) => {
523
605
  setSelected([id]);
524
606
  }, []);
525
- const selectAll = react.useCallback((ids) => {
526
- setSelected(mode === "single" ? ids.slice(0, 1) : [...new Set(ids)]);
527
- }, [mode]);
607
+ const selectAll = react.useCallback(
608
+ (ids) => {
609
+ setSelected(mode === "single" ? ids.slice(0, 1) : [...new Set(ids)]);
610
+ },
611
+ [mode]
612
+ );
528
613
  const clear = react.useCallback(() => {
529
614
  setSelected([]);
530
615
  }, []);
@@ -594,10 +679,19 @@ var panelStyle = {
594
679
  padding: "1.25rem",
595
680
  boxShadow: "0 24px 80px rgba(0,0,0,0.45)"
596
681
  };
597
- var DialogFrame = ({ title, children }) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: backdropStyle, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "dialog", "aria-modal": "true", "aria-labelledby": "usethishook-dialog-title", style: panelStyle, children: [
598
- /* @__PURE__ */ jsxRuntime.jsx("h2", { id: "usethishook-dialog-title", style: { margin: "0 0 0.75rem", fontSize: "1.05rem" }, children: title }),
599
- children
600
- ] }) });
682
+ var DialogFrame = ({ title, children }) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: backdropStyle, children: /* @__PURE__ */ jsxRuntime.jsxs(
683
+ "div",
684
+ {
685
+ role: "dialog",
686
+ "aria-modal": "true",
687
+ "aria-labelledby": "usethishook-dialog-title",
688
+ style: panelStyle,
689
+ children: [
690
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { id: "usethishook-dialog-title", style: { margin: "0 0 0.75rem", fontSize: "1.05rem" }, children: title }),
691
+ children
692
+ ]
693
+ }
694
+ ) });
601
695
  var buttonRowStyle = { display: "flex", gap: "0.5rem", marginTop: "1rem" };
602
696
  var dialogButtonRowStyle = buttonRowStyle;
603
697
  var primaryButtonStyle = {
@@ -633,7 +727,10 @@ var ConfirmDialog = ({
633
727
  "button",
634
728
  {
635
729
  type: "button",
636
- style: { ...primaryButtonStyle, background: danger ? "#e11d48" : primaryButtonStyle.background },
730
+ style: {
731
+ ...primaryButtonStyle,
732
+ background: danger ? "#e11d48" : primaryButtonStyle.background
733
+ },
637
734
  onClick: () => close(true),
638
735
  children: confirmLabel
639
736
  }
@@ -832,9 +929,12 @@ exports.useDebouncedCallback = useDebouncedCallback;
832
929
  exports.useDisclosure = useDisclosure;
833
930
  exports.useDocumentTitle = useDocumentTitle;
834
931
  exports.useElementSize = useElementSize;
932
+ exports.useEventListener = useEventListener;
835
933
  exports.useFields = useFields;
934
+ exports.useHover = useHover;
836
935
  exports.useInView = useInView;
837
936
  exports.useInterval = useInterval;
937
+ exports.useKeyPress = useKeyPress;
838
938
  exports.useList = useList;
839
939
  exports.useLocalStorage = useLocalStorage;
840
940
  exports.useMediaQuery = useMediaQuery;
@@ -843,6 +943,7 @@ exports.useOnClickOutside = useOnClickOutside;
843
943
  exports.useOnlineStatus = useOnlineStatus;
844
944
  exports.useOverlay = useOverlay;
845
945
  exports.usePagination = usePagination;
946
+ exports.usePreferredColorScheme = usePreferredColorScheme;
846
947
  exports.usePrevious = usePrevious;
847
948
  exports.usePrompt = usePrompt;
848
949
  exports.useResetState = useResetState;
@@ -850,6 +951,7 @@ exports.useSearchState = useSearchState;
850
951
  exports.useSelection = useSelection;
851
952
  exports.useStableCallback = useStableCallback;
852
953
  exports.useStepFlow = useStepFlow;
954
+ exports.useTimeout = useTimeout;
853
955
  exports.useToggle = useToggle;
854
956
  exports.useUnsavedChanges = useUnsavedChanges;
855
957
  exports.useWindowSize = useWindowSize;