@nucleux/hooks 0.5.0 → 0.7.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/dist/index.cjs +2 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
var react = require('react');
|
|
5
5
|
|
|
6
6
|
// src/use-auto-scroll.ts
|
|
7
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect;
|
|
7
8
|
function useAutoScroll({
|
|
8
9
|
dependency,
|
|
9
10
|
threshold = 48
|
|
@@ -22,7 +23,7 @@ function useAutoScroll({
|
|
|
22
23
|
const distance = el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
23
24
|
setPinned(distance <= threshold);
|
|
24
25
|
}, [threshold]);
|
|
25
|
-
|
|
26
|
+
useIsomorphicLayoutEffect(() => {
|
|
26
27
|
if (pinned) scrollToBottom("auto");
|
|
27
28
|
}, [dependency]);
|
|
28
29
|
react.useEffect(() => {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/use-auto-scroll.ts","../src/use-copy-to-clipboard.ts"],"names":["
|
|
1
|
+
{"version":3,"sources":["../src/use-auto-scroll.ts","../src/use-copy-to-clipboard.ts"],"names":["useLayoutEffect","useEffect","useRef","useState","useCallback"],"mappings":";;;;;AAGA,IAAM,yBAAA,GAA4B,OAAO,MAAA,KAAW,WAAA,GAAcA,qBAAA,GAAkBC,eAAA;AAc7E,SAAS,aAAA,CAAsD;AAAA,EACpE,UAAA;AAAA,EACA,SAAA,GAAY;AACd,CAAA,GAA0B,EAAC,EAAG;AAC5B,EAAA,MAAM,GAAA,GAAMC,aAAU,IAAI,CAAA;AAC1B,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAIC,eAAS,IAAI,CAAA;AAEzC,EAAA,MAAM,cAAA,GAAiBC,iBAAA,CAAY,CAAC,QAAA,GAA2B,QAAA,KAAa;AAC1E,IAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,IAAA,IAAI,CAAC,EAAA,EAAI;AACT,IAAA,EAAA,CAAG,SAAS,EAAE,GAAA,EAAK,EAAA,CAAG,YAAA,EAAc,UAAU,CAAA;AAC9C,IAAA,SAAA,CAAU,IAAI,CAAA;AAAA,EAChB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,YAAA,GAAeA,kBAAY,MAAM;AACrC,IAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,IAAA,IAAI,CAAC,EAAA,EAAI;AACT,IAAA,MAAM,QAAA,GAAW,EAAA,CAAG,YAAA,GAAe,EAAA,CAAG,YAAY,EAAA,CAAG,YAAA;AACrD,IAAA,SAAA,CAAU,YAAY,SAAS,CAAA;AAAA,EACjC,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAA,yBAAA,CAA0B,MAAM;AAC9B,IAAA,IAAI,MAAA,iBAAuB,MAAM,CAAA;AAAA,EAEnC,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAAH,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,IAAA,IAAI,CAAC,EAAA,EAAI;AACT,IAAA,EAAA,CAAG,iBAAiB,QAAA,EAAU,YAAA,EAAc,EAAE,OAAA,EAAS,MAAM,CAAA;AAC7D,IAAA,OAAO,MAAM,EAAA,CAAG,mBAAA,CAAoB,QAAA,EAAU,YAAY,CAAA;AAAA,EAC5D,CAAA,EAAG,CAAC,YAAY,CAAC,CAAA;AAEjB,EAAA,OAAO,EAAE,GAAA,EAAK,MAAA,EAAQ,cAAA,EAAe;AACvC;ACxCO,SAAS,mBAAmB,EAAE,OAAA,GAAU,GAAA,EAAK,GAA+B,EAAC,EAAG;AACrF,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAIE,eAAS,KAAK,CAAA;AAC1C,EAAA,MAAM,KAAA,GAAQD,aAA6C,IAAI,CAAA;AAE/D,EAAA,MAAM,IAAA,GAAOE,iBAAAA;AAAA,IACX,OAAO,KAAA,KAAkB;AACvB,MAAA,IAAI,OAAO,SAAA,KAAc,WAAA,IAAe,CAAC,SAAA,CAAU,WAAW,OAAO,KAAA;AACrE,MAAA,IAAI;AACF,QAAA,MAAM,SAAA,CAAU,SAAA,CAAU,SAAA,CAAU,KAAK,CAAA;AACzC,QAAA,SAAA,CAAU,IAAI,CAAA;AACd,QAAA,IAAI,KAAA,CAAM,OAAA,EAAS,YAAA,CAAa,KAAA,CAAM,OAAO,CAAA;AAC7C,QAAA,KAAA,CAAM,UAAU,UAAA,CAAW,MAAM,SAAA,CAAU,KAAK,GAAG,OAAO,CAAA;AAC1D,QAAA,OAAO,IAAA;AAAA,MACT,CAAA,CAAA,MAAQ;AACN,QAAA,SAAA,CAAU,KAAK,CAAA;AACf,QAAA,OAAO,KAAA;AAAA,MACT;AAAA,IACF,CAAA;AAAA,IACA,CAAC,OAAO;AAAA,GACV;AAEA,EAAA,OAAO,EAAE,QAAQ,IAAA,EAAK;AACxB","file":"index.cjs","sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useRef, useState } from \"react\";\n\n// useLayoutEffect warns when rendered on the server; fall back to useEffect there.\nconst useIsomorphicLayoutEffect = typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\nexport interface UseAutoScrollOptions {\n /** Re-evaluate scrolling whenever this value changes (e.g. message count). */\n dependency?: unknown;\n /** Distance (px) from the bottom still considered \"pinned\". */\n threshold?: number;\n}\n\n/**\n * Keep a scroll container pinned to the bottom as new content streams in —\n * unless the user has scrolled up, in which case auto-scroll pauses until they\n * return to the bottom. Ideal for chat/agent transcripts.\n */\nexport function useAutoScroll<T extends HTMLElement = HTMLDivElement>({\n dependency,\n threshold = 48,\n}: UseAutoScrollOptions = {}) {\n const ref = useRef<T>(null);\n const [pinned, setPinned] = useState(true);\n\n const scrollToBottom = useCallback((behavior: ScrollBehavior = \"smooth\") => {\n const el = ref.current;\n if (!el) return;\n el.scrollTo({ top: el.scrollHeight, behavior });\n setPinned(true);\n }, []);\n\n const handleScroll = useCallback(() => {\n const el = ref.current;\n if (!el) return;\n const distance = el.scrollHeight - el.scrollTop - el.clientHeight;\n setPinned(distance <= threshold);\n }, [threshold]);\n\n useIsomorphicLayoutEffect(() => {\n if (pinned) scrollToBottom(\"auto\");\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [dependency]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n el.addEventListener(\"scroll\", handleScroll, { passive: true });\n return () => el.removeEventListener(\"scroll\", handleScroll);\n }, [handleScroll]);\n\n return { ref, pinned, scrollToBottom };\n}\n","import { useCallback, useRef, useState } from \"react\";\n\nexport interface UseCopyToClipboardOptions {\n /** How long (ms) the `copied` flag stays true after a successful copy. */\n timeout?: number;\n}\n\n/**\n * Copy text to the clipboard and expose a transient `copied` flag, handy for\n * \"copy\" buttons on code blocks and messages.\n */\nexport function useCopyToClipboard({ timeout = 2000 }: UseCopyToClipboardOptions = {}) {\n const [copied, setCopied] = useState(false);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const copy = useCallback(\n async (value: string) => {\n if (typeof navigator === \"undefined\" || !navigator.clipboard) return false;\n try {\n await navigator.clipboard.writeText(value);\n setCopied(true);\n if (timer.current) clearTimeout(timer.current);\n timer.current = setTimeout(() => setCopied(false), timeout);\n return true;\n } catch {\n setCopied(false);\n return false;\n }\n },\n [timeout],\n );\n\n return { copied, copy };\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useRef, useState, useCallback,
|
|
2
|
+
import { useRef, useState, useCallback, useEffect, useLayoutEffect } from 'react';
|
|
3
3
|
|
|
4
4
|
// src/use-auto-scroll.ts
|
|
5
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
|
|
5
6
|
function useAutoScroll({
|
|
6
7
|
dependency,
|
|
7
8
|
threshold = 48
|
|
@@ -20,7 +21,7 @@ function useAutoScroll({
|
|
|
20
21
|
const distance = el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
21
22
|
setPinned(distance <= threshold);
|
|
22
23
|
}, [threshold]);
|
|
23
|
-
|
|
24
|
+
useIsomorphicLayoutEffect(() => {
|
|
24
25
|
if (pinned) scrollToBottom("auto");
|
|
25
26
|
}, [dependency]);
|
|
26
27
|
useEffect(() => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/use-auto-scroll.ts","../src/use-copy-to-clipboard.ts"],"names":["useState","useRef","useCallback"],"mappings":";;;
|
|
1
|
+
{"version":3,"sources":["../src/use-auto-scroll.ts","../src/use-copy-to-clipboard.ts"],"names":["useState","useRef","useCallback"],"mappings":";;;AAGA,IAAM,yBAAA,GAA4B,OAAO,MAAA,KAAW,WAAA,GAAc,eAAA,GAAkB,SAAA;AAc7E,SAAS,aAAA,CAAsD;AAAA,EACpE,UAAA;AAAA,EACA,SAAA,GAAY;AACd,CAAA,GAA0B,EAAC,EAAG;AAC5B,EAAA,MAAM,GAAA,GAAM,OAAU,IAAI,CAAA;AAC1B,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAAS,IAAI,CAAA;AAEzC,EAAA,MAAM,cAAA,GAAiB,WAAA,CAAY,CAAC,QAAA,GAA2B,QAAA,KAAa;AAC1E,IAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,IAAA,IAAI,CAAC,EAAA,EAAI;AACT,IAAA,EAAA,CAAG,SAAS,EAAE,GAAA,EAAK,EAAA,CAAG,YAAA,EAAc,UAAU,CAAA;AAC9C,IAAA,SAAA,CAAU,IAAI,CAAA;AAAA,EAChB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,YAAA,GAAe,YAAY,MAAM;AACrC,IAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,IAAA,IAAI,CAAC,EAAA,EAAI;AACT,IAAA,MAAM,QAAA,GAAW,EAAA,CAAG,YAAA,GAAe,EAAA,CAAG,YAAY,EAAA,CAAG,YAAA;AACrD,IAAA,SAAA,CAAU,YAAY,SAAS,CAAA;AAAA,EACjC,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAA,yBAAA,CAA0B,MAAM;AAC9B,IAAA,IAAI,MAAA,iBAAuB,MAAM,CAAA;AAAA,EAEnC,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,IAAA,IAAI,CAAC,EAAA,EAAI;AACT,IAAA,EAAA,CAAG,iBAAiB,QAAA,EAAU,YAAA,EAAc,EAAE,OAAA,EAAS,MAAM,CAAA;AAC7D,IAAA,OAAO,MAAM,EAAA,CAAG,mBAAA,CAAoB,QAAA,EAAU,YAAY,CAAA;AAAA,EAC5D,CAAA,EAAG,CAAC,YAAY,CAAC,CAAA;AAEjB,EAAA,OAAO,EAAE,GAAA,EAAK,MAAA,EAAQ,cAAA,EAAe;AACvC;ACxCO,SAAS,mBAAmB,EAAE,OAAA,GAAU,GAAA,EAAK,GAA+B,EAAC,EAAG;AACrF,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAIA,SAAS,KAAK,CAAA;AAC1C,EAAA,MAAM,KAAA,GAAQC,OAA6C,IAAI,CAAA;AAE/D,EAAA,MAAM,IAAA,GAAOC,WAAAA;AAAA,IACX,OAAO,KAAA,KAAkB;AACvB,MAAA,IAAI,OAAO,SAAA,KAAc,WAAA,IAAe,CAAC,SAAA,CAAU,WAAW,OAAO,KAAA;AACrE,MAAA,IAAI;AACF,QAAA,MAAM,SAAA,CAAU,SAAA,CAAU,SAAA,CAAU,KAAK,CAAA;AACzC,QAAA,SAAA,CAAU,IAAI,CAAA;AACd,QAAA,IAAI,KAAA,CAAM,OAAA,EAAS,YAAA,CAAa,KAAA,CAAM,OAAO,CAAA;AAC7C,QAAA,KAAA,CAAM,UAAU,UAAA,CAAW,MAAM,SAAA,CAAU,KAAK,GAAG,OAAO,CAAA;AAC1D,QAAA,OAAO,IAAA;AAAA,MACT,CAAA,CAAA,MAAQ;AACN,QAAA,SAAA,CAAU,KAAK,CAAA;AACf,QAAA,OAAO,KAAA;AAAA,MACT;AAAA,IACF,CAAA;AAAA,IACA,CAAC,OAAO;AAAA,GACV;AAEA,EAAA,OAAO,EAAE,QAAQ,IAAA,EAAK;AACxB","file":"index.js","sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useRef, useState } from \"react\";\n\n// useLayoutEffect warns when rendered on the server; fall back to useEffect there.\nconst useIsomorphicLayoutEffect = typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\nexport interface UseAutoScrollOptions {\n /** Re-evaluate scrolling whenever this value changes (e.g. message count). */\n dependency?: unknown;\n /** Distance (px) from the bottom still considered \"pinned\". */\n threshold?: number;\n}\n\n/**\n * Keep a scroll container pinned to the bottom as new content streams in —\n * unless the user has scrolled up, in which case auto-scroll pauses until they\n * return to the bottom. Ideal for chat/agent transcripts.\n */\nexport function useAutoScroll<T extends HTMLElement = HTMLDivElement>({\n dependency,\n threshold = 48,\n}: UseAutoScrollOptions = {}) {\n const ref = useRef<T>(null);\n const [pinned, setPinned] = useState(true);\n\n const scrollToBottom = useCallback((behavior: ScrollBehavior = \"smooth\") => {\n const el = ref.current;\n if (!el) return;\n el.scrollTo({ top: el.scrollHeight, behavior });\n setPinned(true);\n }, []);\n\n const handleScroll = useCallback(() => {\n const el = ref.current;\n if (!el) return;\n const distance = el.scrollHeight - el.scrollTop - el.clientHeight;\n setPinned(distance <= threshold);\n }, [threshold]);\n\n useIsomorphicLayoutEffect(() => {\n if (pinned) scrollToBottom(\"auto\");\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [dependency]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n el.addEventListener(\"scroll\", handleScroll, { passive: true });\n return () => el.removeEventListener(\"scroll\", handleScroll);\n }, [handleScroll]);\n\n return { ref, pinned, scrollToBottom };\n}\n","import { useCallback, useRef, useState } from \"react\";\n\nexport interface UseCopyToClipboardOptions {\n /** How long (ms) the `copied` flag stays true after a successful copy. */\n timeout?: number;\n}\n\n/**\n * Copy text to the clipboard and expose a transient `copied` flag, handy for\n * \"copy\" buttons on code blocks and messages.\n */\nexport function useCopyToClipboard({ timeout = 2000 }: UseCopyToClipboardOptions = {}) {\n const [copied, setCopied] = useState(false);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const copy = useCallback(\n async (value: string) => {\n if (typeof navigator === \"undefined\" || !navigator.clipboard) return false;\n try {\n await navigator.clipboard.writeText(value);\n setCopied(true);\n if (timer.current) clearTimeout(timer.current);\n timer.current = setTimeout(() => setCopied(false), timeout);\n return true;\n } catch {\n setCopied(false);\n return false;\n }\n },\n [timeout],\n );\n\n return { copied, copy };\n}\n"]}
|