{"_id":"@ahmnouira/hooks","_rev":"2-85082e71830730ca794977e87a3699fd","name":"@ahmnouira/hooks","dist-tags":{"latest":"1.1.0"},"versions":{"1.0.0":{"name":"@ahmnouira/hooks","version":"1.0.0","keywords":["react","hooks","typescript","ahmnouira"],"author":{"url":"https://github.com/ahmnouira","name":"Ahmed Nouira","email":"ahmnouira@gmail.com"},"license":"MIT","_id":"@ahmnouira/hooks@1.0.0","maintainers":[{"name":"ahmnouira","email":"ahmnouira@gmail.com"}],"homepage":"https://github.com/ahmnouira/ahmnouira-hooks#readme","bugs":{"url":"https://github.com/ahmnouira/ahmnouira-hooks/issues"},"dist":{"shasum":"af89f2ab7fa855d8d8f9906e55c88b4310ca9366","tarball":"https://registry.npmjs.org/@ahmnouira/hooks/-/hooks-1.0.0.tgz","fileCount":47,"integrity":"sha512-szY9byFq7cSISJBtpHA1EWb0bTqZI71SXESmkaetsRpK9qALT+ctw25XdQFUv6G9YRkrrjvAsNYlPJmgQK72Wg==","signatures":[{"sig":"MEQCIF/tWEtOMFSzLlX6+O+8xaRiXMQXv478Xio67Sowjb2sAiBcR4tRuRIrZjQTdeXsy/KTPZtCfv/TBpvpooAg5AZ7IA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":26311},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"c1e2e5269e5cc86b6b96b6f36083430653da33d9","scripts":{"test":"vitest run","build":"tsc","prepare":"rimraf dist && tsc","changelog":"auto-changelog -p","test:watch":"vitest"},"_npmUser":{"name":"ahmnouira","email":"ahmnouira@gmail.com"},"repository":{"url":"git+https://github.com/ahmnouira/ahmnouira-hooks.git","type":"git"},"_npmVersion":"11.19.0","description":"A collection of TypeScript React hooks for UI state, data loading, dialogs, and common interactions.","directories":{},"_nodeVersion":"24.13.0","_hasShrinkwrap":false,"devDependencies":{"jsdom":"^30.0.1","react":"^19.2.8","rimraf":"^6.1.3","vitest":"^5.0.0","react-dom":"^19.2.8","typescript":"^6.0.3","@types/react":"^19.2.18","auto-changelog":"^2.5.1","@types/react-dom":"^19.2.7","@testing-library/dom":"^10.4.1","@testing-library/react":"^16.3.3"},"peerDependencies":{"react":">=18"},"_npmOperationalInternal":{"tmp":"tmp/hooks_1.0.0_1788505671455_0.6757331832237758","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@ahmnouira/hooks","version":"1.1.0","description":"A collection of TypeScript React hooks for UI state, data loading, dialogs, and common interactions.","type":"module","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"repository":{"type":"git","url":"git+https://github.com/ahmnouira/ahmnouira-hooks.git"},"homepage":"https://github.com/ahmnouira/ahmnouira-hooks#readme","bugs":{"url":"https://github.com/ahmnouira/ahmnouira-hooks/issues"},"scripts":{"build":"tsc","test":"vitest run","test:watch":"vitest","changelog":"auto-changelog -p","prepare":"rimraf dist && tsc"},"keywords":["react","hooks","typescript","ahmnouira"],"author":{"name":"Ahmed Nouira","email":"ahmnouira@gmail.com","url":"https://github.com/ahmnouira"},"license":"MIT","peerDependencies":{"react":">=18"},"devDependencies":{"@testing-library/dom":"^10.4.1","@testing-library/react":"^16.3.3","@types/react":"^19.2.18","@types/react-dom":"^19.2.7","auto-changelog":"^2.5.1","jsdom":"^30.0.1","react":"^19.2.8","react-dom":"^19.2.8","rimraf":"^6.1.3","typescript":"^6.0.3","vitest":"^5.0.0"},"gitHead":"3b314996eeff1c4397f72faf8b1bde45f53de56e","_id":"@ahmnouira/hooks@1.1.0","_nodeVersion":"24.13.0","_npmVersion":"11.19.0","dist":{"integrity":"sha512-MKKvJy3LAPMlQWmt6E1YPE7rvvbc0cNGOsXAF5QlRx3Egmu60J3kudyHa5bRcLiYlUwcCR5FyDM0AoO6E3TQpQ==","shasum":"0769c36947db07b8d5adc0a50fd6bcb0d44b4378","tarball":"https://registry.npmjs.org/@ahmnouira/hooks/-/hooks-1.1.0.tgz","fileCount":47,"unpackedSize":26396,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGi82nSzcRgDoS8i6nGb79YbbS1uHPzr2W3q5vEqJT8iAiAh50auUo2mMhi01d7hQbwDWG+tLfix/TV/YZo96P6ufg=="}]},"_npmUser":{"name":"ahmnouira","email":"ahmnouira@gmail.com"},"directories":{},"maintainers":[{"name":"ahmnouira","email":"ahmnouira@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/hooks_1.1.0_1788510247593_0.8496339787635874"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-04T07:07:51.262Z","modified":"2026-09-04T08:24:07.853Z","1.0.0":"2026-09-04T07:07:51.630Z","1.1.0":"2026-09-04T08:24:07.714Z"},"bugs":{"url":"https://github.com/ahmnouira/ahmnouira-hooks/issues"},"author":{"name":"Ahmed Nouira","email":"ahmnouira@gmail.com","url":"https://github.com/ahmnouira"},"license":"MIT","homepage":"https://github.com/ahmnouira/ahmnouira-hooks#readme","keywords":["react","hooks","typescript","ahmnouira"],"repository":{"type":"git","url":"git+https://github.com/ahmnouira/ahmnouira-hooks.git"},"description":"A collection of TypeScript React hooks for UI state, data loading, dialogs, and common interactions.","maintainers":[{"name":"ahmnouira","email":"ahmnouira@gmail.com"}],"readme":"# @ahmnouira/hooks\n\nThe React hooks you keep writing again and again.\n\nA lightweight collection of reusable React hooks for everyday UI state — dialogs, menus, inputs, visibility, loading, and more.\n\n## Install\n\n```bash\nnpm install @ahmnouira/hooks\n```\n\nPeer dependency: **React 18+**.\n\n```ts\nimport {\n  useShow,\n  useMenu,\n  useData,\n  useDialogEvent,\n} from \"@ahmnouira/hooks\";\n```\n\n## Hooks\n\n### `useToggle`\n\nBoolean flag with `toggle` and `setValue`. Starts as `false`.\n\n```ts\nconst { value, toggle, setValue } = useToggle();\n```\n\n### `useShow` / `useShowIn`\n\nShow/hide helpers with `show`, `hide`, and `toggle`.\n\n- `useShow` starts hidden (`_show === false`)\n- `useShowIn` starts visible (`_show === true`)\n\n```ts\nconst { _show, show, hide, toggle } = useShow();\n```\n\n### `useLoading` / `useLoadingStart`\n\nLoading flags.\n\n- `useLoading` starts idle (`false`), with `startLoading` / `endLoading`\n- `useLoadingStart` starts busy (`true`), with `endLoading`\n\n```ts\nconst { loading, startLoading, endLoading } = useLoading();\n```\n\n### `useValue`\n\nGeneric value holder. Defaults to `string` if you omit the type argument.\n\n```ts\nconst { value, set } = useValue<number>({ defaultValue: 0 });\n```\n\n### `useViewMode`\n\nToggles between `\"list\"` and `\"circle\"`.\n\n```ts\nconst { viewMode, setViewMode, viewModeProps } = useViewMode();\n```\n\n`viewModeProps` is `{ viewMode, change }` for passing into a view-mode control.\n\n### `useInput`\n\nControlled input helpers. Pass `value` to sync from a parent.\n\n```ts\nconst { props, value, changeValue, vLower } = useInput({ value: query });\n\n<input {...props} />\n```\n\n### `useEditing`\n\nInline edit flow: start, type, save or cancel. Cancel restores the value from when editing began.\n\n```ts\nconst { editing, v, change, start, save, cancel } = useEditing({\n  value: title,\n  onSave: persistTitle,\n});\n```\n\n### `useHover`\n\nBoolean hover state for `onMouseEnter` / `onMouseLeave`.\n\n```ts\nconst { hovered, handleMouseEnter, handleMouseLeave } = useHover();\n\n<div onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} />\n```\n\n### `useHoverIndex`\n\nTracks which item in a list is hovered. Leave resets the index to `-1`.\n\n```ts\nconst { hoverIndex, handleMouseEnter, handleMouseLeave } = useHoverIndex();\n\nitems.map((item, i) => (\n  <li\n    key={item.id}\n    onMouseEnter={() => handleMouseEnter(i)}\n    onMouseLeave={handleMouseLeave}\n  />\n));\n```\n\n### `useClickOutside`\n\nCalls `onClose` on `mousedown` outside the returned `ref`. Set `allow` to `false` to disable the listener.\n\n```ts\nconst { ref } = useClickOutside({\n  onClose: hide,\n  allow: open,\n});\n\n<div ref={ref}>…</div>\n```\n\n### `useEscape`\n\nRuns `onEscape` when `Escape` is pressed, only while `open` is `true`.\n\n```ts\nuseEscape({ open: dialog, onEscape: close });\n```\n\n### `useMenu`\n\nDropdown/menu state: visibility, toggle click handler, and click-outside close.\n\n```ts\nconst { ref, show, click, hide, showMenu } = useMenu();\n\n<button onClick={click}>Open</button>\n{show && <div ref={ref}>Menu</div>}\n```\n\n### `useDialog`\n\nDialog open state plus an optional typed `item` (for edit/delete confirmations).\n\n```ts\nconst { dialog, item, openDialog, closeDialog, toggleDialog, resetDialog } =\n  useDialog<User>();\n\ntoggleDialog(user); // open with a selected item\nresetDialog(); // close and clear item\n```\n\n### `useDialogEvent`\n\n`useDialog` plus Escape-to-close.\n\n```ts\nconst { dialog, item, open, close } = useDialogEvent<User>();\n\nopen(user);\nclose();\n```\n\n`open` toggles the dialog (and can set `item`). `close` resets both.\n\n### `useData` / `useDataLoading`\n\n`useDataLoading` is the `{ data, loading, error }` store.\n\n`useData` loads on mount from `f()`, or seeds `testData` without fetching. `url` is optional metadata for callers.\n\n```ts\nconst { data, loading, error, refetch } = useData<Post[]>({\n  f: () => fetch(\"/api/posts\").then((r) => r.json()),\n});\n```\n\n### `useProgress`\n\nPercent progress (0–100) with helpers for common steps and simulated ramps. Intervals are cleared on unmount.\n\n```ts\nconst { progress, pTo90, finishProgress, resetProgress } = useProgress();\n```\n\n| Helper | Effect |\n| --- | --- |\n| `p20` `p25` `p33` `p50` | Jump to that percent |\n| `pTo20` | Animate toward 20 |\n| `pTo90` / `pTo90_02` / `pTo90_01` | Animate toward 90 (step 2 / 0.2 / 0.1) |\n| `finishProgress` | Jump to 100 |\n| `resetProgress` | Back to 0 |\n\n### `useShake`\n\nField-level shake flags (for invalid form fields). Each trigger clears after 500ms.\n\n```ts\nconst { shakeFields, triggerShake, removeShake, resetShake } = useShake();\n\ntriggerShake(\"email\");\nshakeFields.has(\"email\");\n```\n\n## Development\n\n```bash\nnpm install\nnpm run build\n```\n\nPublished files live in `dist/` (TypeScript declarations included).\n\n## License\n\nMIT © [Ahmed Nouira](https://github.com/ahmnouira)\n","readmeFilename":"README.md"}