@tiny-codes/react-easy 1.4.20 → 1.4.21

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 CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## 1.4.21
6
+
7
+ 2025-10-22
8
+
9
+ ### Features
10
+
11
+ #### `useMovable`
12
+
13
+ - ✨ Improve position clamping on window resize, and make sure the movable element always stays within the viewport.
14
+
5
15
  ## 1.4.20
6
16
 
7
17
  2025-10-17
@@ -27,6 +27,7 @@ var useMovable = function useMovable(props) {
27
27
  _useState2 = _slicedToArray(_useState, 2),
28
28
  position = _useState2[0],
29
29
  setPosition = _useState2[1];
30
+ var positionRef = useRefValue(position);
30
31
  var draggingRef = useRef(false);
31
32
  var dragOffsetRef = useRef({
32
33
  x: 0,
@@ -65,24 +66,43 @@ var useMovable = function useMovable(props) {
65
66
  e.preventDefault();
66
67
  });
67
68
 
68
- // Adjust position when window size changes
69
+ // 调整:在窗口 resize 时自动收敛位置,防止超出可视区域
69
70
  useEffect(function () {
70
- if (!position) return;
71
- var maxLeft = Math.max(0, window.innerWidth - sizeRef.current.w);
72
- var maxTop = Math.max(0, window.innerHeight - sizeRef.current.h);
73
- var clampedLeft = Math.min(Math.max(0, position.left), maxLeft);
74
- var clampedTop = Math.min(Math.max(0, position.top), maxTop);
75
- if (clampedLeft !== position.left || clampedTop !== position.top) {
76
- var pos = {
77
- left: clampedLeft,
78
- top: clampedTop
79
- };
80
- setPosition(pos);
81
- if (storageKeyRef.current) {
82
- savePositionRef.current(pos);
71
+ var clampToViewport = function clampToViewport() {
72
+ var _containerRef$current3;
73
+ var pos = positionRef.current;
74
+ if (!pos) return;
75
+
76
+ // Refresh the container size before each convergence to ensure accurate boundaries.
77
+ var rect = (_containerRef$current3 = containerRef.current) === null || _containerRef$current3 === void 0 ? void 0 : _containerRef$current3.getBoundingClientRect();
78
+ if (rect) {
79
+ sizeRef.current = {
80
+ w: rect.width,
81
+ h: rect.height
82
+ };
83
83
  }
84
- }
85
- }, [position]);
84
+ var maxLeft = Math.max(0, window.innerWidth - sizeRef.current.w);
85
+ var maxTop = Math.max(0, window.innerHeight - sizeRef.current.h);
86
+ var clampedLeft = Math.min(Math.max(0, pos.left), maxLeft);
87
+ var clampedTop = Math.min(Math.max(0, pos.top), maxTop);
88
+ if (clampedLeft !== pos.left || clampedTop !== pos.top) {
89
+ var next = {
90
+ left: clampedLeft,
91
+ top: clampedTop
92
+ };
93
+ setPosition(next);
94
+ if (storageKeyRef.current) {
95
+ savePositionRef.current(next);
96
+ }
97
+ }
98
+ };
99
+ window.addEventListener('resize', clampToViewport);
100
+ // Calibrate immediately after the first mount/position change.
101
+ clampToViewport();
102
+ return function () {
103
+ window.removeEventListener('resize', clampToViewport);
104
+ };
105
+ }, [containerRef]);
86
106
 
87
107
  // Update position during dragging; restrict within the visible area.
88
108
  useEffect(function () {
@@ -1 +1 @@
1
- {"version":3,"names":["useEffect","useRef","useState","useLocalStorage","useRefFunction","useRefValue","useMovable","props","enabled","containerRef","ignoreSelectors","storageKey","storageKeyRef","_useLocalStorage","_useLocalStorage2","_slicedToArray","savedPosition","savePosition","savePositionRef","_useState","undefined","_useState2","position","setPosition","draggingRef","dragOffsetRef","x","y","sizeRef","w","h","handlePointerDown","e","_position$left","_position$top","target","closest","join","current","rect","getBoundingClientRect","width","height","currentLeft","left","currentTop","top","clientX","clientY","_containerRef$current","_containerRef$current2","setPointerCapture","call","pointerId","_unused","preventDefault","maxLeft","Math","max","window","innerWidth","maxTop","innerHeight","clampedLeft","min","clampedTop","pos","onMove","newLeft","newTop","onUp","addEventListener","removeEventListener","onPointerDown"],"sources":["../../src/hooks/useMovable.ts"],"sourcesContent":["import { type RefObject, useEffect, useRef, useState } from 'react';\nimport { useLocalStorage } from 'react-use';\nimport useRefFunction from './useRefFunction';\nimport useRefValue from './useRefValue';\n\nexport interface UseMovableProps {\n /**\n * - **EN:** Whether dragging is enabled, default is `true`\n * - **CN:** 是否启用拖动,默认`true`\n */\n enabled?: boolean;\n /**\n * - **EN:** The ref of the container element\n * - **CN:** 容器元素的ref\n */\n containerRef: RefObject<HTMLElement>;\n /**\n * - **EN:** Selectors of elements that should not trigger dragging, e.g., interactive controls\n * - **CN:** 不应触发拖动的元素的选择器,例如交互控件\n */\n ignoreSelectors?: string[];\n /**\n * - **EN:** Key for storing position in localStorage; if not provided, position won't be saved\n * - **CN:** 用于在 localStorage 中存储位置的键;如果未提供,则不会保存位置\n */\n storageKey?: string;\n}\n\n/**\n * - **EN:** Hook to make an element movable by dragging, with position persistence using localStorage\n * - **CN:** 通过拖动使元素可移动的钩子,并使用 localStorage 持久化位置\n */\nconst useMovable = (props: UseMovableProps) => {\n const { enabled, containerRef, ignoreSelectors, storageKey } = props;\n\n const storageKeyRef = useRefValue(storageKey);\n const [savedPosition, savePosition] = useLocalStorage<MovePosition>(storageKey ?? '');\n const savePositionRef = useRefValue(savePosition);\n const [position, setPosition] = useState<MovePosition | undefined>(savedPosition ?? undefined);\n const draggingRef = useRef(false);\n const dragOffsetRef = useRef({ x: 0, y: 0 });\n const sizeRef = useRef({ w: 0, h: 0 });\n\n // Drag start (exclude interactive controls)\n const handlePointerDown = useRefFunction((e: React.PointerEvent<HTMLDivElement>) => {\n const target = e.target as HTMLElement;\n // Set the selector for elements that do not trigger dragging\n if (ignoreSelectors && target.closest(ignoreSelectors.join(','))) return;\n\n if (!containerRef.current) return;\n const rect = containerRef.current.getBoundingClientRect();\n sizeRef.current = { w: rect.width, h: rect.height };\n const currentLeft = position?.left ?? rect.left;\n const currentTop = position?.top ?? rect.top;\n\n dragOffsetRef.current = { x: e.clientX - currentLeft, y: e.clientY - currentTop };\n draggingRef.current = true;\n try {\n containerRef.current.setPointerCapture?.(e.pointerId);\n } catch {\n // do nothing\n }\n e.preventDefault();\n });\n\n // Adjust position when window size changes\n useEffect(() => {\n if (!position) return;\n const maxLeft = Math.max(0, window.innerWidth - sizeRef.current.w);\n const maxTop = Math.max(0, window.innerHeight - sizeRef.current.h);\n const clampedLeft = Math.min(Math.max(0, position.left), maxLeft);\n const clampedTop = Math.min(Math.max(0, position.top), maxTop);\n if (clampedLeft !== position.left || clampedTop !== position.top) {\n const pos = { left: clampedLeft, top: clampedTop };\n setPosition(pos);\n if (storageKeyRef.current) {\n savePositionRef.current(pos);\n }\n }\n }, [position]);\n\n // Update position during dragging; restrict within the visible area.\n useEffect(() => {\n const onMove = (e: PointerEvent) => {\n if (!draggingRef.current) return;\n const newLeft = e.clientX - dragOffsetRef.current.x;\n const newTop = e.clientY - dragOffsetRef.current.y;\n const maxLeft = Math.max(0, window.innerWidth - sizeRef.current.w);\n const maxTop = Math.max(0, window.innerHeight - sizeRef.current.h);\n const clampedLeft = Math.min(Math.max(0, newLeft), maxLeft);\n const clampedTop = Math.min(Math.max(0, newTop), maxTop);\n const pos = { left: clampedLeft, top: clampedTop };\n setPosition(pos);\n if (storageKeyRef.current) {\n savePositionRef.current(pos);\n }\n };\n const onUp = () => {\n if (draggingRef.current) draggingRef.current = false;\n };\n if (enabled) {\n window.addEventListener('pointermove', onMove);\n window.addEventListener('pointerup', onUp);\n window.addEventListener('pointercancel', onUp);\n }\n return () => {\n window.removeEventListener('pointermove', onMove);\n window.removeEventListener('pointerup', onUp);\n window.removeEventListener('pointercancel', onUp);\n };\n }, [enabled]);\n\n return {\n onPointerDown: handlePointerDown,\n position,\n };\n};\n\nexport interface MovePosition {\n left: number;\n top: number;\n}\n\nexport default useMovable;\n"],"mappings":"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"}
1
+ {"version":3,"names":["useEffect","useRef","useState","useLocalStorage","useRefFunction","useRefValue","useMovable","props","enabled","containerRef","ignoreSelectors","storageKey","storageKeyRef","_useLocalStorage","_useLocalStorage2","_slicedToArray","savedPosition","savePosition","savePositionRef","_useState","undefined","_useState2","position","setPosition","positionRef","draggingRef","dragOffsetRef","x","y","sizeRef","w","h","handlePointerDown","e","_position$left","_position$top","target","closest","join","current","rect","getBoundingClientRect","width","height","currentLeft","left","currentTop","top","clientX","clientY","_containerRef$current","_containerRef$current2","setPointerCapture","call","pointerId","_unused","preventDefault","clampToViewport","_containerRef$current3","pos","maxLeft","Math","max","window","innerWidth","maxTop","innerHeight","clampedLeft","min","clampedTop","next","addEventListener","removeEventListener","onMove","newLeft","newTop","onUp","onPointerDown"],"sources":["../../src/hooks/useMovable.ts"],"sourcesContent":["import { type RefObject, useEffect, useRef, useState } from 'react';\nimport { useLocalStorage } from 'react-use';\nimport useRefFunction from './useRefFunction';\nimport useRefValue from './useRefValue';\n\nexport interface UseMovableProps {\n /**\n * - **EN:** Whether dragging is enabled, default is `true`\n * - **CN:** 是否启用拖动,默认`true`\n */\n enabled?: boolean;\n /**\n * - **EN:** The ref of the container element\n * - **CN:** 容器元素的ref\n */\n containerRef: RefObject<HTMLElement>;\n /**\n * - **EN:** Selectors of elements that should not trigger dragging, e.g., interactive controls\n * - **CN:** 不应触发拖动的元素的选择器,例如交互控件\n */\n ignoreSelectors?: string[];\n /**\n * - **EN:** Key for storing position in localStorage; if not provided, position won't be saved\n * - **CN:** 用于在 localStorage 中存储位置的键;如果未提供,则不会保存位置\n */\n storageKey?: string;\n}\n\n/**\n * - **EN:** Hook to make an element movable by dragging, with position persistence using localStorage\n * - **CN:** 通过拖动使元素可移动的钩子,并使用 localStorage 持久化位置\n */\nconst useMovable = (props: UseMovableProps) => {\n const { enabled, containerRef, ignoreSelectors, storageKey } = props;\n\n const storageKeyRef = useRefValue(storageKey);\n const [savedPosition, savePosition] = useLocalStorage<MovePosition>(storageKey ?? '');\n const savePositionRef = useRefValue(savePosition);\n const [position, setPosition] = useState<MovePosition | undefined>(savedPosition ?? undefined);\n const positionRef = useRefValue(position);\n const draggingRef = useRef(false);\n const dragOffsetRef = useRef({ x: 0, y: 0 });\n const sizeRef = useRef({ w: 0, h: 0 });\n\n // Drag start (exclude interactive controls)\n const handlePointerDown = useRefFunction((e: React.PointerEvent<HTMLDivElement>) => {\n const target = e.target as HTMLElement;\n // Set the selector for elements that do not trigger dragging\n if (ignoreSelectors && target.closest(ignoreSelectors.join(','))) return;\n\n if (!containerRef.current) return;\n const rect = containerRef.current.getBoundingClientRect();\n sizeRef.current = { w: rect.width, h: rect.height };\n const currentLeft = position?.left ?? rect.left;\n const currentTop = position?.top ?? rect.top;\n\n dragOffsetRef.current = { x: e.clientX - currentLeft, y: e.clientY - currentTop };\n draggingRef.current = true;\n try {\n containerRef.current.setPointerCapture?.(e.pointerId);\n } catch {\n // do nothing\n }\n e.preventDefault();\n });\n\n // 调整:在窗口 resize 时自动收敛位置,防止超出可视区域\n useEffect(() => {\n const clampToViewport = () => {\n const pos = positionRef.current;\n if (!pos) return;\n\n // Refresh the container size before each convergence to ensure accurate boundaries.\n const rect = containerRef.current?.getBoundingClientRect();\n if (rect) {\n sizeRef.current = { w: rect.width, h: rect.height };\n }\n\n const maxLeft = Math.max(0, window.innerWidth - sizeRef.current.w);\n const maxTop = Math.max(0, window.innerHeight - sizeRef.current.h);\n const clampedLeft = Math.min(Math.max(0, pos.left), maxLeft);\n const clampedTop = Math.min(Math.max(0, pos.top), maxTop);\n\n if (clampedLeft !== pos.left || clampedTop !== pos.top) {\n const next = { left: clampedLeft, top: clampedTop };\n setPosition(next);\n if (storageKeyRef.current) {\n savePositionRef.current(next);\n }\n }\n };\n\n window.addEventListener('resize', clampToViewport);\n // Calibrate immediately after the first mount/position change.\n clampToViewport();\n\n return () => {\n window.removeEventListener('resize', clampToViewport);\n };\n }, [containerRef]);\n\n // Update position during dragging; restrict within the visible area.\n useEffect(() => {\n const onMove = (e: PointerEvent) => {\n if (!draggingRef.current) return;\n const newLeft = e.clientX - dragOffsetRef.current.x;\n const newTop = e.clientY - dragOffsetRef.current.y;\n const maxLeft = Math.max(0, window.innerWidth - sizeRef.current.w);\n const maxTop = Math.max(0, window.innerHeight - sizeRef.current.h);\n const clampedLeft = Math.min(Math.max(0, newLeft), maxLeft);\n const clampedTop = Math.min(Math.max(0, newTop), maxTop);\n const pos = { left: clampedLeft, top: clampedTop };\n setPosition(pos);\n if (storageKeyRef.current) {\n savePositionRef.current(pos);\n }\n };\n const onUp = () => {\n if (draggingRef.current) draggingRef.current = false;\n };\n if (enabled) {\n window.addEventListener('pointermove', onMove);\n window.addEventListener('pointerup', onUp);\n window.addEventListener('pointercancel', onUp);\n }\n return () => {\n window.removeEventListener('pointermove', onMove);\n window.removeEventListener('pointerup', onUp);\n window.removeEventListener('pointercancel', onUp);\n };\n }, [enabled]);\n\n return {\n onPointerDown: handlePointerDown,\n position,\n };\n};\n\nexport interface MovePosition {\n left: number;\n top: number;\n}\n\nexport default useMovable;\n"],"mappings":"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"}
@@ -42,6 +42,7 @@ var useMovable = (props) => {
42
42
  const [savedPosition, savePosition] = (0, import_react_use.useLocalStorage)(storageKey ?? "");
43
43
  const savePositionRef = (0, import_useRefValue.default)(savePosition);
44
44
  const [position, setPosition] = (0, import_react.useState)(savedPosition ?? void 0);
45
+ const positionRef = (0, import_useRefValue.default)(position);
45
46
  const draggingRef = (0, import_react.useRef)(false);
46
47
  const dragOffsetRef = (0, import_react.useRef)({ x: 0, y: 0 });
47
48
  const sizeRef = (0, import_react.useRef)({ w: 0, h: 0 });
@@ -65,20 +66,33 @@ var useMovable = (props) => {
65
66
  e.preventDefault();
66
67
  });
67
68
  (0, import_react.useEffect)(() => {
68
- if (!position)
69
- return;
70
- const maxLeft = Math.max(0, window.innerWidth - sizeRef.current.w);
71
- const maxTop = Math.max(0, window.innerHeight - sizeRef.current.h);
72
- const clampedLeft = Math.min(Math.max(0, position.left), maxLeft);
73
- const clampedTop = Math.min(Math.max(0, position.top), maxTop);
74
- if (clampedLeft !== position.left || clampedTop !== position.top) {
75
- const pos = { left: clampedLeft, top: clampedTop };
76
- setPosition(pos);
77
- if (storageKeyRef.current) {
78
- savePositionRef.current(pos);
69
+ const clampToViewport = () => {
70
+ var _a;
71
+ const pos = positionRef.current;
72
+ if (!pos)
73
+ return;
74
+ const rect = (_a = containerRef.current) == null ? void 0 : _a.getBoundingClientRect();
75
+ if (rect) {
76
+ sizeRef.current = { w: rect.width, h: rect.height };
79
77
  }
80
- }
81
- }, [position]);
78
+ const maxLeft = Math.max(0, window.innerWidth - sizeRef.current.w);
79
+ const maxTop = Math.max(0, window.innerHeight - sizeRef.current.h);
80
+ const clampedLeft = Math.min(Math.max(0, pos.left), maxLeft);
81
+ const clampedTop = Math.min(Math.max(0, pos.top), maxTop);
82
+ if (clampedLeft !== pos.left || clampedTop !== pos.top) {
83
+ const next = { left: clampedLeft, top: clampedTop };
84
+ setPosition(next);
85
+ if (storageKeyRef.current) {
86
+ savePositionRef.current(next);
87
+ }
88
+ }
89
+ };
90
+ window.addEventListener("resize", clampToViewport);
91
+ clampToViewport();
92
+ return () => {
93
+ window.removeEventListener("resize", clampToViewport);
94
+ };
95
+ }, [containerRef]);
82
96
  (0, import_react.useEffect)(() => {
83
97
  const onMove = (e) => {
84
98
  if (!draggingRef.current)
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/hooks/useMovable.ts"],
4
- "sourcesContent": ["import { type RefObject, useEffect, useRef, useState } from 'react';\nimport { useLocalStorage } from 'react-use';\nimport useRefFunction from './useRefFunction';\nimport useRefValue from './useRefValue';\n\nexport interface UseMovableProps {\n /**\n * - **EN:** Whether dragging is enabled, default is `true`\n * - **CN:** 是否启用拖动,默认`true`\n */\n enabled?: boolean;\n /**\n * - **EN:** The ref of the container element\n * - **CN:** 容器元素的ref\n */\n containerRef: RefObject<HTMLElement>;\n /**\n * - **EN:** Selectors of elements that should not trigger dragging, e.g., interactive controls\n * - **CN:** 不应触发拖动的元素的选择器,例如交互控件\n */\n ignoreSelectors?: string[];\n /**\n * - **EN:** Key for storing position in localStorage; if not provided, position won't be saved\n * - **CN:** 用于在 localStorage 中存储位置的键;如果未提供,则不会保存位置\n */\n storageKey?: string;\n}\n\n/**\n * - **EN:** Hook to make an element movable by dragging, with position persistence using localStorage\n * - **CN:** 通过拖动使元素可移动的钩子,并使用 localStorage 持久化位置\n */\nconst useMovable = (props: UseMovableProps) => {\n const { enabled, containerRef, ignoreSelectors, storageKey } = props;\n\n const storageKeyRef = useRefValue(storageKey);\n const [savedPosition, savePosition] = useLocalStorage<MovePosition>(storageKey ?? '');\n const savePositionRef = useRefValue(savePosition);\n const [position, setPosition] = useState<MovePosition | undefined>(savedPosition ?? undefined);\n const draggingRef = useRef(false);\n const dragOffsetRef = useRef({ x: 0, y: 0 });\n const sizeRef = useRef({ w: 0, h: 0 });\n\n // Drag start (exclude interactive controls)\n const handlePointerDown = useRefFunction((e: React.PointerEvent<HTMLDivElement>) => {\n const target = e.target as HTMLElement;\n // Set the selector for elements that do not trigger dragging\n if (ignoreSelectors && target.closest(ignoreSelectors.join(','))) return;\n\n if (!containerRef.current) return;\n const rect = containerRef.current.getBoundingClientRect();\n sizeRef.current = { w: rect.width, h: rect.height };\n const currentLeft = position?.left ?? rect.left;\n const currentTop = position?.top ?? rect.top;\n\n dragOffsetRef.current = { x: e.clientX - currentLeft, y: e.clientY - currentTop };\n draggingRef.current = true;\n try {\n containerRef.current.setPointerCapture?.(e.pointerId);\n } catch {\n // do nothing\n }\n e.preventDefault();\n });\n\n // Adjust position when window size changes\n useEffect(() => {\n if (!position) return;\n const maxLeft = Math.max(0, window.innerWidth - sizeRef.current.w);\n const maxTop = Math.max(0, window.innerHeight - sizeRef.current.h);\n const clampedLeft = Math.min(Math.max(0, position.left), maxLeft);\n const clampedTop = Math.min(Math.max(0, position.top), maxTop);\n if (clampedLeft !== position.left || clampedTop !== position.top) {\n const pos = { left: clampedLeft, top: clampedTop };\n setPosition(pos);\n if (storageKeyRef.current) {\n savePositionRef.current(pos);\n }\n }\n }, [position]);\n\n // Update position during dragging; restrict within the visible area.\n useEffect(() => {\n const onMove = (e: PointerEvent) => {\n if (!draggingRef.current) return;\n const newLeft = e.clientX - dragOffsetRef.current.x;\n const newTop = e.clientY - dragOffsetRef.current.y;\n const maxLeft = Math.max(0, window.innerWidth - sizeRef.current.w);\n const maxTop = Math.max(0, window.innerHeight - sizeRef.current.h);\n const clampedLeft = Math.min(Math.max(0, newLeft), maxLeft);\n const clampedTop = Math.min(Math.max(0, newTop), maxTop);\n const pos = { left: clampedLeft, top: clampedTop };\n setPosition(pos);\n if (storageKeyRef.current) {\n savePositionRef.current(pos);\n }\n };\n const onUp = () => {\n if (draggingRef.current) draggingRef.current = false;\n };\n if (enabled) {\n window.addEventListener('pointermove', onMove);\n window.addEventListener('pointerup', onUp);\n window.addEventListener('pointercancel', onUp);\n }\n return () => {\n window.removeEventListener('pointermove', onMove);\n window.removeEventListener('pointerup', onUp);\n window.removeEventListener('pointercancel', onUp);\n };\n }, [enabled]);\n\n return {\n onPointerDown: handlePointerDown,\n position,\n };\n};\n\nexport interface MovePosition {\n left: number;\n top: number;\n}\n\nexport default useMovable;\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { type RefObject, useEffect, useRef, useState } from 'react';\nimport { useLocalStorage } from 'react-use';\nimport useRefFunction from './useRefFunction';\nimport useRefValue from './useRefValue';\n\nexport interface UseMovableProps {\n /**\n * - **EN:** Whether dragging is enabled, default is `true`\n * - **CN:** 是否启用拖动,默认`true`\n */\n enabled?: boolean;\n /**\n * - **EN:** The ref of the container element\n * - **CN:** 容器元素的ref\n */\n containerRef: RefObject<HTMLElement>;\n /**\n * - **EN:** Selectors of elements that should not trigger dragging, e.g., interactive controls\n * - **CN:** 不应触发拖动的元素的选择器,例如交互控件\n */\n ignoreSelectors?: string[];\n /**\n * - **EN:** Key for storing position in localStorage; if not provided, position won't be saved\n * - **CN:** 用于在 localStorage 中存储位置的键;如果未提供,则不会保存位置\n */\n storageKey?: string;\n}\n\n/**\n * - **EN:** Hook to make an element movable by dragging, with position persistence using localStorage\n * - **CN:** 通过拖动使元素可移动的钩子,并使用 localStorage 持久化位置\n */\nconst useMovable = (props: UseMovableProps) => {\n const { enabled, containerRef, ignoreSelectors, storageKey } = props;\n\n const storageKeyRef = useRefValue(storageKey);\n const [savedPosition, savePosition] = useLocalStorage<MovePosition>(storageKey ?? '');\n const savePositionRef = useRefValue(savePosition);\n const [position, setPosition] = useState<MovePosition | undefined>(savedPosition ?? undefined);\n const positionRef = useRefValue(position);\n const draggingRef = useRef(false);\n const dragOffsetRef = useRef({ x: 0, y: 0 });\n const sizeRef = useRef({ w: 0, h: 0 });\n\n // Drag start (exclude interactive controls)\n const handlePointerDown = useRefFunction((e: React.PointerEvent<HTMLDivElement>) => {\n const target = e.target as HTMLElement;\n // Set the selector for elements that do not trigger dragging\n if (ignoreSelectors && target.closest(ignoreSelectors.join(','))) return;\n\n if (!containerRef.current) return;\n const rect = containerRef.current.getBoundingClientRect();\n sizeRef.current = { w: rect.width, h: rect.height };\n const currentLeft = position?.left ?? rect.left;\n const currentTop = position?.top ?? rect.top;\n\n dragOffsetRef.current = { x: e.clientX - currentLeft, y: e.clientY - currentTop };\n draggingRef.current = true;\n try {\n containerRef.current.setPointerCapture?.(e.pointerId);\n } catch {\n // do nothing\n }\n e.preventDefault();\n });\n\n // 调整:在窗口 resize 时自动收敛位置,防止超出可视区域\n useEffect(() => {\n const clampToViewport = () => {\n const pos = positionRef.current;\n if (!pos) return;\n\n // Refresh the container size before each convergence to ensure accurate boundaries.\n const rect = containerRef.current?.getBoundingClientRect();\n if (rect) {\n sizeRef.current = { w: rect.width, h: rect.height };\n }\n\n const maxLeft = Math.max(0, window.innerWidth - sizeRef.current.w);\n const maxTop = Math.max(0, window.innerHeight - sizeRef.current.h);\n const clampedLeft = Math.min(Math.max(0, pos.left), maxLeft);\n const clampedTop = Math.min(Math.max(0, pos.top), maxTop);\n\n if (clampedLeft !== pos.left || clampedTop !== pos.top) {\n const next = { left: clampedLeft, top: clampedTop };\n setPosition(next);\n if (storageKeyRef.current) {\n savePositionRef.current(next);\n }\n }\n };\n\n window.addEventListener('resize', clampToViewport);\n // Calibrate immediately after the first mount/position change.\n clampToViewport();\n\n return () => {\n window.removeEventListener('resize', clampToViewport);\n };\n }, [containerRef]);\n\n // Update position during dragging; restrict within the visible area.\n useEffect(() => {\n const onMove = (e: PointerEvent) => {\n if (!draggingRef.current) return;\n const newLeft = e.clientX - dragOffsetRef.current.x;\n const newTop = e.clientY - dragOffsetRef.current.y;\n const maxLeft = Math.max(0, window.innerWidth - sizeRef.current.w);\n const maxTop = Math.max(0, window.innerHeight - sizeRef.current.h);\n const clampedLeft = Math.min(Math.max(0, newLeft), maxLeft);\n const clampedTop = Math.min(Math.max(0, newTop), maxTop);\n const pos = { left: clampedLeft, top: clampedTop };\n setPosition(pos);\n if (storageKeyRef.current) {\n savePositionRef.current(pos);\n }\n };\n const onUp = () => {\n if (draggingRef.current) draggingRef.current = false;\n };\n if (enabled) {\n window.addEventListener('pointermove', onMove);\n window.addEventListener('pointerup', onUp);\n window.addEventListener('pointercancel', onUp);\n }\n return () => {\n window.removeEventListener('pointermove', onMove);\n window.removeEventListener('pointerup', onUp);\n window.removeEventListener('pointercancel', onUp);\n };\n }, [enabled]);\n\n return {\n onPointerDown: handlePointerDown,\n position,\n };\n};\n\nexport interface MovePosition {\n left: number;\n top: number;\n}\n\nexport default useMovable;\n"],
5
+ "mappings": "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",
6
6
  "names": ["useRefValue", "useRefFunction"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiny-codes/react-easy",
3
- "version": "1.4.20",
3
+ "version": "1.4.21",
4
4
  "description": "Simplify React and AntDesign development with practical components and hooks",
5
5
  "keywords": [
6
6
  "react",