@mantine/hooks 9.5.0 → 9.5.1-alpha.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.
|
@@ -21,6 +21,9 @@ function useFloatingWindow(options = {}) {
|
|
|
21
21
|
const isDraggingRef = (0, react.useRef)(false);
|
|
22
22
|
const initialized = (0, react.useRef)(false);
|
|
23
23
|
const enabledRef = useRefValue(options.enabled);
|
|
24
|
+
const onPositionChangeRef = useRefValue(options.onPositionChange);
|
|
25
|
+
const onDragStartRef = useRefValue(options.onDragStart);
|
|
26
|
+
const onDragEndRef = useRefValue(options.onDragEnd);
|
|
24
27
|
const setDragging = (0, react.useCallback)((value) => {
|
|
25
28
|
setIsDragging(value);
|
|
26
29
|
isDraggingRef.current = value;
|
|
@@ -74,7 +77,7 @@ function useFloatingWindow(options = {}) {
|
|
|
74
77
|
x: point.clientX - rect.left,
|
|
75
78
|
y: point.clientY - rect.top
|
|
76
79
|
};
|
|
77
|
-
|
|
80
|
+
onDragStartRef.current?.();
|
|
78
81
|
document.addEventListener("mousemove", onMove, { signal });
|
|
79
82
|
document.addEventListener("mouseup", onEnd, { signal });
|
|
80
83
|
document.addEventListener("touchmove", onMove, {
|
|
@@ -82,6 +85,7 @@ function useFloatingWindow(options = {}) {
|
|
|
82
85
|
passive: false
|
|
83
86
|
});
|
|
84
87
|
document.addEventListener("touchend", onEnd, { signal });
|
|
88
|
+
document.addEventListener("touchcancel", onEnd, { signal });
|
|
85
89
|
};
|
|
86
90
|
const onMove = (e) => {
|
|
87
91
|
if (!isDraggingRef.current) return;
|
|
@@ -111,7 +115,7 @@ function useFloatingWindow(options = {}) {
|
|
|
111
115
|
ref.current.style.left = `${x}px`;
|
|
112
116
|
ref.current.style.top = `${y}px`;
|
|
113
117
|
}
|
|
114
|
-
|
|
118
|
+
onPositionChangeRef.current?.({
|
|
115
119
|
x,
|
|
116
120
|
y
|
|
117
121
|
});
|
|
@@ -121,7 +125,7 @@ function useFloatingWindow(options = {}) {
|
|
|
121
125
|
setDragging(false);
|
|
122
126
|
document.body.style.userSelect = "";
|
|
123
127
|
document.body.style.webkitUserSelect = "";
|
|
124
|
-
|
|
128
|
+
onDragEndRef.current?.();
|
|
125
129
|
}
|
|
126
130
|
};
|
|
127
131
|
el.addEventListener("mousedown", onStart, { signal });
|
|
@@ -137,9 +141,6 @@ function useFloatingWindow(options = {}) {
|
|
|
137
141
|
options.constrainOffset,
|
|
138
142
|
options.dragHandleSelector,
|
|
139
143
|
options.axis,
|
|
140
|
-
options.onPositionChange,
|
|
141
|
-
options.onDragStart,
|
|
142
|
-
options.onDragEnd,
|
|
143
144
|
options.initialPosition?.top,
|
|
144
145
|
options.initialPosition?.left,
|
|
145
146
|
options.initialPosition?.right,
|
|
@@ -186,15 +187,11 @@ function useFloatingWindow(options = {}) {
|
|
|
186
187
|
};
|
|
187
188
|
el.style.left = `${x}px`;
|
|
188
189
|
el.style.top = `${y}px`;
|
|
189
|
-
|
|
190
|
+
onPositionChangeRef.current?.({
|
|
190
191
|
x,
|
|
191
192
|
y
|
|
192
193
|
});
|
|
193
|
-
}, [
|
|
194
|
-
options.constrainToViewport,
|
|
195
|
-
options.constrainOffset,
|
|
196
|
-
options.onPositionChange
|
|
197
|
-
]),
|
|
194
|
+
}, [options.constrainToViewport, options.constrainOffset]),
|
|
198
195
|
isDragging
|
|
199
196
|
};
|
|
200
197
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-floating-window.cjs","names":[],"sources":["../../src/use-floating-window/use-floating-window.ts"],"sourcesContent":["// Required to disable for webkit-user-select, although deprecated, it is still required for Safari support\nimport { RefCallback, useCallback, useEffect, useRef, useState } from 'react';\n\nfunction useRefValue<T>(value: T) {\n const ref = useRef(value);\n ref.current = value;\n return ref;\n}\n\ninterface FloatingWindowPositionConfig {\n top?: number;\n left?: number;\n right?: number;\n bottom?: number;\n}\n\ninterface FloatingWindowPosition {\n /** Element offset from the left side of the viewport */\n x: number;\n\n /** Element offset from the top side of the viewport */\n y: number;\n}\n\nexport interface UseFloatingWindowOptions {\n /** If `false`, the element can not be dragged. */\n enabled?: boolean;\n\n /** If `true`, the element can only move within the current viewport boundaries. */\n constrainToViewport?: boolean;\n\n /** The offset from the viewport edges when constraining the element. Requires `constrainToViewport: true`. */\n constrainOffset?: number;\n\n /** Selector of an element that should be used to drag floating window. If not specified, the entire root element is used as a drag target. */\n dragHandleSelector?: string;\n\n /** Selector of an element within `dragHandleSelector` that should be excluded from the drag event. */\n excludeDragHandleSelector?: string;\n\n /** If set, restricts movement to the specified axis */\n axis?: 'x' | 'y';\n\n /** Initial position. If not set, calculated from element styles. */\n initialPosition?: FloatingWindowPositionConfig;\n\n /** Called when the element position changes */\n onPositionChange?: (pos: FloatingWindowPosition) => void;\n\n /** Called when the drag starts */\n onDragStart?: () => void;\n\n /** Called when the drag stops */\n onDragEnd?: () => void;\n}\n\nexport type SetFloatingWindowPosition = (position: FloatingWindowPositionConfig) => void;\n\nexport interface UseFloatingWindowReturnValue<T extends HTMLElement> {\n /** Ref to the element that should be draggable */\n ref: RefCallback<T | null>;\n\n /** Function to set the position of the element */\n setPosition: SetFloatingWindowPosition;\n\n /** `true` if the element is currently being dragged */\n isDragging: boolean;\n}\n\nexport function useFloatingWindow<T extends HTMLElement>(\n options: UseFloatingWindowOptions = {}\n): UseFloatingWindowReturnValue<T> {\n const [element, setElement] = useState<T | null>(null);\n const ref = useRef<T>(null);\n const pos = useRef({ x: 0, y: 0 });\n const offset = useRef({ x: 0, y: 0 });\n const [isDragging, setIsDragging] = useState(false);\n const isDraggingRef = useRef(false);\n const initialized = useRef(false);\n const enabledRef = useRefValue(options.enabled);\n\n const setDragging = useCallback((value: boolean) => {\n setIsDragging(value);\n isDraggingRef.current = value;\n }, []);\n\n const assignRef = useCallback((node: T | null) => {\n if (node) {\n ref.current = node;\n setElement(node);\n } else {\n ref.current = null;\n setElement(null);\n }\n }, []);\n\n useEffect(() => {\n const el = ref.current;\n if (!initialized.current && el) {\n initialized.current = true;\n pos.current = calculateInitialPosition(el, options);\n el.style.left = `${pos.current.x}px`;\n el.style.top = `${pos.current.y}px`;\n el.style.right = 'unset';\n el.style.bottom = 'unset';\n }\n\n return () => {\n initialized.current = false;\n };\n }, [\n element,\n options.constrainOffset,\n options.initialPosition?.top,\n options.initialPosition?.left,\n options.initialPosition?.right,\n options.initialPosition?.bottom,\n options.constrainToViewport,\n ]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const controller = new AbortController();\n const signal = controller.signal;\n\n const onStart = (e: MouseEvent | TouchEvent) => {\n if (enabledRef.current === false) {\n return;\n }\n\n const point = 'touches' in e ? e.touches[0] : e;\n\n if ('button' in e && e.button !== 0) {\n return;\n }\n\n if (!getHandle(el, e.target, options)) {\n return;\n }\n\n setDragging(true);\n document.body.style.userSelect = 'none';\n document.body.style.webkitUserSelect = 'none';\n\n const rect = el.getBoundingClientRect();\n\n offset.current = {\n x: point.clientX - rect.left,\n y: point.clientY - rect.top,\n };\n\n options.onDragStart?.();\n\n document.addEventListener('mousemove', onMove, { signal });\n document.addEventListener('mouseup', onEnd, { signal });\n document.addEventListener('touchmove', onMove, { signal, passive: false });\n document.addEventListener('touchend', onEnd, { signal });\n };\n\n const onMove = (e: MouseEvent | TouchEvent) => {\n if (!isDraggingRef.current) {\n return;\n }\n\n const point = 'touches' in e ? e.touches[0] : e;\n e.preventDefault();\n\n let x = point.clientX - offset.current.x;\n let y = point.clientY - offset.current.y;\n\n const constrained = getConstrainedPosition(el, { x, y }, options);\n if (options.axis === 'x') {\n x = constrained.x;\n y = pos.current.y;\n } else if (options.axis === 'y') {\n x = pos.current.x;\n y = constrained.y;\n } else {\n x = constrained.x;\n y = constrained.y;\n }\n\n pos.current = { x, y };\n\n if (ref.current) {\n ref.current.style.left = `${x}px`;\n ref.current.style.top = `${y}px`;\n }\n\n options.onPositionChange?.({ x, y });\n };\n\n const onEnd = () => {\n if (isDraggingRef.current) {\n setDragging(false);\n document.body.style.userSelect = '';\n document.body.style.webkitUserSelect = '';\n options.onDragEnd?.();\n }\n };\n\n el.addEventListener('mousedown', onStart, { signal });\n el.addEventListener('touchstart', onStart, { signal, passive: false });\n\n return () => {\n controller.abort();\n };\n }, [\n options.constrainToViewport,\n options.constrainOffset,\n options.dragHandleSelector,\n options.axis,\n options.onPositionChange,\n options.onDragStart,\n options.onDragEnd,\n options.initialPosition?.top,\n options.initialPosition?.left,\n options.initialPosition?.right,\n options.initialPosition?.bottom,\n element,\n ]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const observer = new ResizeObserver(() => {\n // Re-clamp current position if element size changes\n const constrained = getConstrainedPosition(el, pos.current, options);\n pos.current = constrained;\n el.style.left = `${constrained.x}px`;\n el.style.top = `${constrained.y}px`;\n });\n\n observer.observe(el);\n\n return () => {\n observer.disconnect();\n };\n }, [options.constrainToViewport, options.constrainOffset]);\n\n const setPosition = useCallback(\n (position: FloatingWindowPositionConfig) => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const offset = options.constrainOffset ?? 0;\n const rect = el.getBoundingClientRect();\n\n let x: number | undefined;\n let y: number | undefined;\n\n if (position.left != null) {\n x = position.left;\n } else if (position.right != null) {\n x = window.innerWidth - rect.width - position.right;\n }\n\n if (position.top != null) {\n y = position.top;\n } else if (position.bottom != null) {\n y = window.innerHeight - rect.height - position.bottom;\n }\n\n x = x ?? pos.current.x;\n y = y ?? pos.current.y;\n\n if (options.constrainToViewport) {\n const clamped = clampToViewport(x, y, el, offset);\n x = clamped.x;\n y = clamped.y;\n }\n\n pos.current = { x, y };\n el.style.left = `${x}px`;\n el.style.top = `${y}px`;\n options.onPositionChange?.({ x, y });\n },\n [options.constrainToViewport, options.constrainOffset, options.onPositionChange]\n );\n\n return {\n ref: assignRef,\n setPosition,\n isDragging,\n };\n}\n\n// -------------------------------------------------------\n// Helper functions\n// -------------------------------------------------------\n\nfunction px(v: string) {\n return v.endsWith('px') ? parseFloat(v) : 0;\n}\n\nfunction calculateInitialPosition(\n el: HTMLElement,\n options: UseFloatingWindowOptions\n): { x: number; y: number } {\n const rect = el.getBoundingClientRect();\n const offset = options.constrainOffset ?? 0;\n const winW = window.innerWidth;\n const winH = window.innerHeight;\n const style = window.getComputedStyle(el);\n const top = options.initialPosition?.top;\n const left = options.initialPosition?.left;\n const right = options.initialPosition?.right;\n const bottom = options.initialPosition?.bottom;\n\n let x = offset;\n let y = offset;\n\n if (left != null) {\n x = left;\n } else if (right != null) {\n x = winW - rect.width - right;\n } else {\n x = px(style.left) || winW - rect.width - px(style.right) || offset;\n }\n\n if (top != null) {\n y = top;\n } else if (bottom != null) {\n y = winH - rect.height - bottom;\n } else {\n y = px(style.top) || winH - rect.height - px(style.bottom) || offset;\n }\n\n return options.constrainToViewport\n ? clampToViewport(x, y, el, options.constrainOffset)\n : { x, y };\n}\n\nfunction getConstrainedPosition(\n el: HTMLElement,\n pos: FloatingWindowPosition,\n options: UseFloatingWindowOptions\n) {\n if (!options.constrainToViewport || !el) {\n return pos;\n }\n\n const rect = el.getBoundingClientRect();\n const offset = options.constrainOffset ?? 0;\n const maxX = window.innerWidth - rect.width - offset;\n const maxY = window.innerHeight - rect.height - offset;\n\n return {\n x: Math.min(Math.max(offset, pos.x), maxX),\n y: Math.min(Math.max(offset, pos.y), maxY),\n };\n}\n\nfunction matchesExcludeSelector(target: Node, excludeSelector?: string): boolean {\n if (!excludeSelector) {\n return false;\n }\n if (!(target instanceof Element)) {\n return false;\n }\n\n return Boolean(target.closest(excludeSelector));\n}\n\nfunction getHandle(\n el: HTMLElement,\n target: EventTarget | null,\n options: UseFloatingWindowOptions\n): boolean {\n if (!(target instanceof Node)) {\n return false;\n }\n\n // If no drag handle selector, allow dragging from entire element\n if (!options.dragHandleSelector) {\n return !matchesExcludeSelector(target, options.excludeDragHandleSelector);\n }\n\n const handles = Array.from(el.querySelectorAll(options.dragHandleSelector));\n return handles.some(\n (handle) =>\n handle.contains(target) && !matchesExcludeSelector(target, options.excludeDragHandleSelector)\n );\n}\n\nfunction clampToViewport(\n x: number,\n y: number,\n el: HTMLElement,\n offset: number = 0\n): { x: number; y: number } {\n const rect = el.getBoundingClientRect();\n const maxX = window.innerWidth - rect.width - offset;\n const maxY = window.innerHeight - rect.height - offset;\n\n return {\n x: Math.min(Math.max(offset, x), maxX),\n y: Math.min(Math.max(offset, y), maxY),\n };\n}\n\nexport namespace useFloatingWindow {\n export type Options = UseFloatingWindowOptions;\n export type Position = FloatingWindowPosition;\n export type SetPosition = SetFloatingWindowPosition;\n export type ReturnValue<T extends HTMLElement> = UseFloatingWindowReturnValue<T>;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-floating-window.cjs","names":[],"sources":["../../src/use-floating-window/use-floating-window.ts"],"sourcesContent":["// Required to disable for webkit-user-select, although deprecated, it is still required for Safari support\nimport { RefCallback, useCallback, useEffect, useRef, useState } from 'react';\n\nfunction useRefValue<T>(value: T) {\n const ref = useRef(value);\n ref.current = value;\n return ref;\n}\n\ninterface FloatingWindowPositionConfig {\n top?: number;\n left?: number;\n right?: number;\n bottom?: number;\n}\n\ninterface FloatingWindowPosition {\n /** Element offset from the left side of the viewport */\n x: number;\n\n /** Element offset from the top side of the viewport */\n y: number;\n}\n\nexport interface UseFloatingWindowOptions {\n /** If `false`, the element can not be dragged. */\n enabled?: boolean;\n\n /** If `true`, the element can only move within the current viewport boundaries. */\n constrainToViewport?: boolean;\n\n /** The offset from the viewport edges when constraining the element. Requires `constrainToViewport: true`. */\n constrainOffset?: number;\n\n /** Selector of an element that should be used to drag floating window. If not specified, the entire root element is used as a drag target. */\n dragHandleSelector?: string;\n\n /** Selector of an element within `dragHandleSelector` that should be excluded from the drag event. */\n excludeDragHandleSelector?: string;\n\n /** If set, restricts movement to the specified axis */\n axis?: 'x' | 'y';\n\n /** Initial position. If not set, calculated from element styles. */\n initialPosition?: FloatingWindowPositionConfig;\n\n /** Called when the element position changes */\n onPositionChange?: (pos: FloatingWindowPosition) => void;\n\n /** Called when the drag starts */\n onDragStart?: () => void;\n\n /** Called when the drag stops */\n onDragEnd?: () => void;\n}\n\nexport type SetFloatingWindowPosition = (position: FloatingWindowPositionConfig) => void;\n\nexport interface UseFloatingWindowReturnValue<T extends HTMLElement> {\n /** Ref to the element that should be draggable */\n ref: RefCallback<T | null>;\n\n /** Function to set the position of the element */\n setPosition: SetFloatingWindowPosition;\n\n /** `true` if the element is currently being dragged */\n isDragging: boolean;\n}\n\nexport function useFloatingWindow<T extends HTMLElement>(\n options: UseFloatingWindowOptions = {}\n): UseFloatingWindowReturnValue<T> {\n const [element, setElement] = useState<T | null>(null);\n const ref = useRef<T>(null);\n const pos = useRef({ x: 0, y: 0 });\n const offset = useRef({ x: 0, y: 0 });\n const [isDragging, setIsDragging] = useState(false);\n const isDraggingRef = useRef(false);\n const initialized = useRef(false);\n const enabledRef = useRefValue(options.enabled);\n const onPositionChangeRef = useRefValue(options.onPositionChange);\n const onDragStartRef = useRefValue(options.onDragStart);\n const onDragEndRef = useRefValue(options.onDragEnd);\n\n const setDragging = useCallback((value: boolean) => {\n setIsDragging(value);\n isDraggingRef.current = value;\n }, []);\n\n const assignRef = useCallback((node: T | null) => {\n if (node) {\n ref.current = node;\n setElement(node);\n } else {\n ref.current = null;\n setElement(null);\n }\n }, []);\n\n useEffect(() => {\n const el = ref.current;\n if (!initialized.current && el) {\n initialized.current = true;\n pos.current = calculateInitialPosition(el, options);\n el.style.left = `${pos.current.x}px`;\n el.style.top = `${pos.current.y}px`;\n el.style.right = 'unset';\n el.style.bottom = 'unset';\n }\n\n return () => {\n initialized.current = false;\n };\n }, [\n element,\n options.constrainOffset,\n options.initialPosition?.top,\n options.initialPosition?.left,\n options.initialPosition?.right,\n options.initialPosition?.bottom,\n options.constrainToViewport,\n ]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const controller = new AbortController();\n const signal = controller.signal;\n\n const onStart = (e: MouseEvent | TouchEvent) => {\n if (enabledRef.current === false) {\n return;\n }\n\n const point = 'touches' in e ? e.touches[0] : e;\n\n if ('button' in e && e.button !== 0) {\n return;\n }\n\n if (!getHandle(el, e.target, options)) {\n return;\n }\n\n setDragging(true);\n document.body.style.userSelect = 'none';\n document.body.style.webkitUserSelect = 'none';\n\n const rect = el.getBoundingClientRect();\n\n offset.current = {\n x: point.clientX - rect.left,\n y: point.clientY - rect.top,\n };\n\n onDragStartRef.current?.();\n\n document.addEventListener('mousemove', onMove, { signal });\n document.addEventListener('mouseup', onEnd, { signal });\n document.addEventListener('touchmove', onMove, { signal, passive: false });\n document.addEventListener('touchend', onEnd, { signal });\n document.addEventListener('touchcancel', onEnd, { signal });\n };\n\n const onMove = (e: MouseEvent | TouchEvent) => {\n if (!isDraggingRef.current) {\n return;\n }\n\n const point = 'touches' in e ? e.touches[0] : e;\n e.preventDefault();\n\n let x = point.clientX - offset.current.x;\n let y = point.clientY - offset.current.y;\n\n const constrained = getConstrainedPosition(el, { x, y }, options);\n if (options.axis === 'x') {\n x = constrained.x;\n y = pos.current.y;\n } else if (options.axis === 'y') {\n x = pos.current.x;\n y = constrained.y;\n } else {\n x = constrained.x;\n y = constrained.y;\n }\n\n pos.current = { x, y };\n\n if (ref.current) {\n ref.current.style.left = `${x}px`;\n ref.current.style.top = `${y}px`;\n }\n\n onPositionChangeRef.current?.({ x, y });\n };\n\n const onEnd = () => {\n if (isDraggingRef.current) {\n setDragging(false);\n document.body.style.userSelect = '';\n document.body.style.webkitUserSelect = '';\n onDragEndRef.current?.();\n }\n };\n\n el.addEventListener('mousedown', onStart, { signal });\n el.addEventListener('touchstart', onStart, { signal, passive: false });\n\n return () => {\n controller.abort();\n };\n }, [\n options.constrainToViewport,\n options.constrainOffset,\n options.dragHandleSelector,\n options.axis,\n options.initialPosition?.top,\n options.initialPosition?.left,\n options.initialPosition?.right,\n options.initialPosition?.bottom,\n element,\n ]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const observer = new ResizeObserver(() => {\n // Re-clamp current position if element size changes\n const constrained = getConstrainedPosition(el, pos.current, options);\n pos.current = constrained;\n el.style.left = `${constrained.x}px`;\n el.style.top = `${constrained.y}px`;\n });\n\n observer.observe(el);\n\n return () => {\n observer.disconnect();\n };\n }, [options.constrainToViewport, options.constrainOffset]);\n\n const setPosition = useCallback(\n (position: FloatingWindowPositionConfig) => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const offset = options.constrainOffset ?? 0;\n const rect = el.getBoundingClientRect();\n\n let x: number | undefined;\n let y: number | undefined;\n\n if (position.left != null) {\n x = position.left;\n } else if (position.right != null) {\n x = window.innerWidth - rect.width - position.right;\n }\n\n if (position.top != null) {\n y = position.top;\n } else if (position.bottom != null) {\n y = window.innerHeight - rect.height - position.bottom;\n }\n\n x = x ?? pos.current.x;\n y = y ?? pos.current.y;\n\n if (options.constrainToViewport) {\n const clamped = clampToViewport(x, y, el, offset);\n x = clamped.x;\n y = clamped.y;\n }\n\n pos.current = { x, y };\n el.style.left = `${x}px`;\n el.style.top = `${y}px`;\n onPositionChangeRef.current?.({ x, y });\n },\n [options.constrainToViewport, options.constrainOffset]\n );\n\n return {\n ref: assignRef,\n setPosition,\n isDragging,\n };\n}\n\n// -------------------------------------------------------\n// Helper functions\n// -------------------------------------------------------\n\nfunction px(v: string) {\n return v.endsWith('px') ? parseFloat(v) : 0;\n}\n\nfunction calculateInitialPosition(\n el: HTMLElement,\n options: UseFloatingWindowOptions\n): { x: number; y: number } {\n const rect = el.getBoundingClientRect();\n const offset = options.constrainOffset ?? 0;\n const winW = window.innerWidth;\n const winH = window.innerHeight;\n const style = window.getComputedStyle(el);\n const top = options.initialPosition?.top;\n const left = options.initialPosition?.left;\n const right = options.initialPosition?.right;\n const bottom = options.initialPosition?.bottom;\n\n let x = offset;\n let y = offset;\n\n if (left != null) {\n x = left;\n } else if (right != null) {\n x = winW - rect.width - right;\n } else {\n x = px(style.left) || winW - rect.width - px(style.right) || offset;\n }\n\n if (top != null) {\n y = top;\n } else if (bottom != null) {\n y = winH - rect.height - bottom;\n } else {\n y = px(style.top) || winH - rect.height - px(style.bottom) || offset;\n }\n\n return options.constrainToViewport\n ? clampToViewport(x, y, el, options.constrainOffset)\n : { x, y };\n}\n\nfunction getConstrainedPosition(\n el: HTMLElement,\n pos: FloatingWindowPosition,\n options: UseFloatingWindowOptions\n) {\n if (!options.constrainToViewport || !el) {\n return pos;\n }\n\n const rect = el.getBoundingClientRect();\n const offset = options.constrainOffset ?? 0;\n const maxX = window.innerWidth - rect.width - offset;\n const maxY = window.innerHeight - rect.height - offset;\n\n return {\n x: Math.min(Math.max(offset, pos.x), maxX),\n y: Math.min(Math.max(offset, pos.y), maxY),\n };\n}\n\nfunction matchesExcludeSelector(target: Node, excludeSelector?: string): boolean {\n if (!excludeSelector) {\n return false;\n }\n if (!(target instanceof Element)) {\n return false;\n }\n\n return Boolean(target.closest(excludeSelector));\n}\n\nfunction getHandle(\n el: HTMLElement,\n target: EventTarget | null,\n options: UseFloatingWindowOptions\n): boolean {\n if (!(target instanceof Node)) {\n return false;\n }\n\n // If no drag handle selector, allow dragging from entire element\n if (!options.dragHandleSelector) {\n return !matchesExcludeSelector(target, options.excludeDragHandleSelector);\n }\n\n const handles = Array.from(el.querySelectorAll(options.dragHandleSelector));\n return handles.some(\n (handle) =>\n handle.contains(target) && !matchesExcludeSelector(target, options.excludeDragHandleSelector)\n );\n}\n\nfunction clampToViewport(\n x: number,\n y: number,\n el: HTMLElement,\n offset: number = 0\n): { x: number; y: number } {\n const rect = el.getBoundingClientRect();\n const maxX = window.innerWidth - rect.width - offset;\n const maxY = window.innerHeight - rect.height - offset;\n\n return {\n x: Math.min(Math.max(offset, x), maxX),\n y: Math.min(Math.max(offset, y), maxY),\n };\n}\n\nexport namespace useFloatingWindow {\n export type Options = UseFloatingWindowOptions;\n export type Position = FloatingWindowPosition;\n export type SetPosition = SetFloatingWindowPosition;\n export type ReturnValue<T extends HTMLElement> = UseFloatingWindowReturnValue<T>;\n}\n"],"mappings":"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"}
|
|
@@ -21,6 +21,9 @@ function useFloatingWindow(options = {}) {
|
|
|
21
21
|
const isDraggingRef = useRef(false);
|
|
22
22
|
const initialized = useRef(false);
|
|
23
23
|
const enabledRef = useRefValue(options.enabled);
|
|
24
|
+
const onPositionChangeRef = useRefValue(options.onPositionChange);
|
|
25
|
+
const onDragStartRef = useRefValue(options.onDragStart);
|
|
26
|
+
const onDragEndRef = useRefValue(options.onDragEnd);
|
|
24
27
|
const setDragging = useCallback((value) => {
|
|
25
28
|
setIsDragging(value);
|
|
26
29
|
isDraggingRef.current = value;
|
|
@@ -74,7 +77,7 @@ function useFloatingWindow(options = {}) {
|
|
|
74
77
|
x: point.clientX - rect.left,
|
|
75
78
|
y: point.clientY - rect.top
|
|
76
79
|
};
|
|
77
|
-
|
|
80
|
+
onDragStartRef.current?.();
|
|
78
81
|
document.addEventListener("mousemove", onMove, { signal });
|
|
79
82
|
document.addEventListener("mouseup", onEnd, { signal });
|
|
80
83
|
document.addEventListener("touchmove", onMove, {
|
|
@@ -82,6 +85,7 @@ function useFloatingWindow(options = {}) {
|
|
|
82
85
|
passive: false
|
|
83
86
|
});
|
|
84
87
|
document.addEventListener("touchend", onEnd, { signal });
|
|
88
|
+
document.addEventListener("touchcancel", onEnd, { signal });
|
|
85
89
|
};
|
|
86
90
|
const onMove = (e) => {
|
|
87
91
|
if (!isDraggingRef.current) return;
|
|
@@ -111,7 +115,7 @@ function useFloatingWindow(options = {}) {
|
|
|
111
115
|
ref.current.style.left = `${x}px`;
|
|
112
116
|
ref.current.style.top = `${y}px`;
|
|
113
117
|
}
|
|
114
|
-
|
|
118
|
+
onPositionChangeRef.current?.({
|
|
115
119
|
x,
|
|
116
120
|
y
|
|
117
121
|
});
|
|
@@ -121,7 +125,7 @@ function useFloatingWindow(options = {}) {
|
|
|
121
125
|
setDragging(false);
|
|
122
126
|
document.body.style.userSelect = "";
|
|
123
127
|
document.body.style.webkitUserSelect = "";
|
|
124
|
-
|
|
128
|
+
onDragEndRef.current?.();
|
|
125
129
|
}
|
|
126
130
|
};
|
|
127
131
|
el.addEventListener("mousedown", onStart, { signal });
|
|
@@ -137,9 +141,6 @@ function useFloatingWindow(options = {}) {
|
|
|
137
141
|
options.constrainOffset,
|
|
138
142
|
options.dragHandleSelector,
|
|
139
143
|
options.axis,
|
|
140
|
-
options.onPositionChange,
|
|
141
|
-
options.onDragStart,
|
|
142
|
-
options.onDragEnd,
|
|
143
144
|
options.initialPosition?.top,
|
|
144
145
|
options.initialPosition?.left,
|
|
145
146
|
options.initialPosition?.right,
|
|
@@ -186,15 +187,11 @@ function useFloatingWindow(options = {}) {
|
|
|
186
187
|
};
|
|
187
188
|
el.style.left = `${x}px`;
|
|
188
189
|
el.style.top = `${y}px`;
|
|
189
|
-
|
|
190
|
+
onPositionChangeRef.current?.({
|
|
190
191
|
x,
|
|
191
192
|
y
|
|
192
193
|
});
|
|
193
|
-
}, [
|
|
194
|
-
options.constrainToViewport,
|
|
195
|
-
options.constrainOffset,
|
|
196
|
-
options.onPositionChange
|
|
197
|
-
]),
|
|
194
|
+
}, [options.constrainToViewport, options.constrainOffset]),
|
|
198
195
|
isDragging
|
|
199
196
|
};
|
|
200
197
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-floating-window.mjs","names":[],"sources":["../../src/use-floating-window/use-floating-window.ts"],"sourcesContent":["// Required to disable for webkit-user-select, although deprecated, it is still required for Safari support\nimport { RefCallback, useCallback, useEffect, useRef, useState } from 'react';\n\nfunction useRefValue<T>(value: T) {\n const ref = useRef(value);\n ref.current = value;\n return ref;\n}\n\ninterface FloatingWindowPositionConfig {\n top?: number;\n left?: number;\n right?: number;\n bottom?: number;\n}\n\ninterface FloatingWindowPosition {\n /** Element offset from the left side of the viewport */\n x: number;\n\n /** Element offset from the top side of the viewport */\n y: number;\n}\n\nexport interface UseFloatingWindowOptions {\n /** If `false`, the element can not be dragged. */\n enabled?: boolean;\n\n /** If `true`, the element can only move within the current viewport boundaries. */\n constrainToViewport?: boolean;\n\n /** The offset from the viewport edges when constraining the element. Requires `constrainToViewport: true`. */\n constrainOffset?: number;\n\n /** Selector of an element that should be used to drag floating window. If not specified, the entire root element is used as a drag target. */\n dragHandleSelector?: string;\n\n /** Selector of an element within `dragHandleSelector` that should be excluded from the drag event. */\n excludeDragHandleSelector?: string;\n\n /** If set, restricts movement to the specified axis */\n axis?: 'x' | 'y';\n\n /** Initial position. If not set, calculated from element styles. */\n initialPosition?: FloatingWindowPositionConfig;\n\n /** Called when the element position changes */\n onPositionChange?: (pos: FloatingWindowPosition) => void;\n\n /** Called when the drag starts */\n onDragStart?: () => void;\n\n /** Called when the drag stops */\n onDragEnd?: () => void;\n}\n\nexport type SetFloatingWindowPosition = (position: FloatingWindowPositionConfig) => void;\n\nexport interface UseFloatingWindowReturnValue<T extends HTMLElement> {\n /** Ref to the element that should be draggable */\n ref: RefCallback<T | null>;\n\n /** Function to set the position of the element */\n setPosition: SetFloatingWindowPosition;\n\n /** `true` if the element is currently being dragged */\n isDragging: boolean;\n}\n\nexport function useFloatingWindow<T extends HTMLElement>(\n options: UseFloatingWindowOptions = {}\n): UseFloatingWindowReturnValue<T> {\n const [element, setElement] = useState<T | null>(null);\n const ref = useRef<T>(null);\n const pos = useRef({ x: 0, y: 0 });\n const offset = useRef({ x: 0, y: 0 });\n const [isDragging, setIsDragging] = useState(false);\n const isDraggingRef = useRef(false);\n const initialized = useRef(false);\n const enabledRef = useRefValue(options.enabled);\n\n const setDragging = useCallback((value: boolean) => {\n setIsDragging(value);\n isDraggingRef.current = value;\n }, []);\n\n const assignRef = useCallback((node: T | null) => {\n if (node) {\n ref.current = node;\n setElement(node);\n } else {\n ref.current = null;\n setElement(null);\n }\n }, []);\n\n useEffect(() => {\n const el = ref.current;\n if (!initialized.current && el) {\n initialized.current = true;\n pos.current = calculateInitialPosition(el, options);\n el.style.left = `${pos.current.x}px`;\n el.style.top = `${pos.current.y}px`;\n el.style.right = 'unset';\n el.style.bottom = 'unset';\n }\n\n return () => {\n initialized.current = false;\n };\n }, [\n element,\n options.constrainOffset,\n options.initialPosition?.top,\n options.initialPosition?.left,\n options.initialPosition?.right,\n options.initialPosition?.bottom,\n options.constrainToViewport,\n ]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const controller = new AbortController();\n const signal = controller.signal;\n\n const onStart = (e: MouseEvent | TouchEvent) => {\n if (enabledRef.current === false) {\n return;\n }\n\n const point = 'touches' in e ? e.touches[0] : e;\n\n if ('button' in e && e.button !== 0) {\n return;\n }\n\n if (!getHandle(el, e.target, options)) {\n return;\n }\n\n setDragging(true);\n document.body.style.userSelect = 'none';\n document.body.style.webkitUserSelect = 'none';\n\n const rect = el.getBoundingClientRect();\n\n offset.current = {\n x: point.clientX - rect.left,\n y: point.clientY - rect.top,\n };\n\n options.onDragStart?.();\n\n document.addEventListener('mousemove', onMove, { signal });\n document.addEventListener('mouseup', onEnd, { signal });\n document.addEventListener('touchmove', onMove, { signal, passive: false });\n document.addEventListener('touchend', onEnd, { signal });\n };\n\n const onMove = (e: MouseEvent | TouchEvent) => {\n if (!isDraggingRef.current) {\n return;\n }\n\n const point = 'touches' in e ? e.touches[0] : e;\n e.preventDefault();\n\n let x = point.clientX - offset.current.x;\n let y = point.clientY - offset.current.y;\n\n const constrained = getConstrainedPosition(el, { x, y }, options);\n if (options.axis === 'x') {\n x = constrained.x;\n y = pos.current.y;\n } else if (options.axis === 'y') {\n x = pos.current.x;\n y = constrained.y;\n } else {\n x = constrained.x;\n y = constrained.y;\n }\n\n pos.current = { x, y };\n\n if (ref.current) {\n ref.current.style.left = `${x}px`;\n ref.current.style.top = `${y}px`;\n }\n\n options.onPositionChange?.({ x, y });\n };\n\n const onEnd = () => {\n if (isDraggingRef.current) {\n setDragging(false);\n document.body.style.userSelect = '';\n document.body.style.webkitUserSelect = '';\n options.onDragEnd?.();\n }\n };\n\n el.addEventListener('mousedown', onStart, { signal });\n el.addEventListener('touchstart', onStart, { signal, passive: false });\n\n return () => {\n controller.abort();\n };\n }, [\n options.constrainToViewport,\n options.constrainOffset,\n options.dragHandleSelector,\n options.axis,\n options.onPositionChange,\n options.onDragStart,\n options.onDragEnd,\n options.initialPosition?.top,\n options.initialPosition?.left,\n options.initialPosition?.right,\n options.initialPosition?.bottom,\n element,\n ]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const observer = new ResizeObserver(() => {\n // Re-clamp current position if element size changes\n const constrained = getConstrainedPosition(el, pos.current, options);\n pos.current = constrained;\n el.style.left = `${constrained.x}px`;\n el.style.top = `${constrained.y}px`;\n });\n\n observer.observe(el);\n\n return () => {\n observer.disconnect();\n };\n }, [options.constrainToViewport, options.constrainOffset]);\n\n const setPosition = useCallback(\n (position: FloatingWindowPositionConfig) => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const offset = options.constrainOffset ?? 0;\n const rect = el.getBoundingClientRect();\n\n let x: number | undefined;\n let y: number | undefined;\n\n if (position.left != null) {\n x = position.left;\n } else if (position.right != null) {\n x = window.innerWidth - rect.width - position.right;\n }\n\n if (position.top != null) {\n y = position.top;\n } else if (position.bottom != null) {\n y = window.innerHeight - rect.height - position.bottom;\n }\n\n x = x ?? pos.current.x;\n y = y ?? pos.current.y;\n\n if (options.constrainToViewport) {\n const clamped = clampToViewport(x, y, el, offset);\n x = clamped.x;\n y = clamped.y;\n }\n\n pos.current = { x, y };\n el.style.left = `${x}px`;\n el.style.top = `${y}px`;\n options.onPositionChange?.({ x, y });\n },\n [options.constrainToViewport, options.constrainOffset, options.onPositionChange]\n );\n\n return {\n ref: assignRef,\n setPosition,\n isDragging,\n };\n}\n\n// -------------------------------------------------------\n// Helper functions\n// -------------------------------------------------------\n\nfunction px(v: string) {\n return v.endsWith('px') ? parseFloat(v) : 0;\n}\n\nfunction calculateInitialPosition(\n el: HTMLElement,\n options: UseFloatingWindowOptions\n): { x: number; y: number } {\n const rect = el.getBoundingClientRect();\n const offset = options.constrainOffset ?? 0;\n const winW = window.innerWidth;\n const winH = window.innerHeight;\n const style = window.getComputedStyle(el);\n const top = options.initialPosition?.top;\n const left = options.initialPosition?.left;\n const right = options.initialPosition?.right;\n const bottom = options.initialPosition?.bottom;\n\n let x = offset;\n let y = offset;\n\n if (left != null) {\n x = left;\n } else if (right != null) {\n x = winW - rect.width - right;\n } else {\n x = px(style.left) || winW - rect.width - px(style.right) || offset;\n }\n\n if (top != null) {\n y = top;\n } else if (bottom != null) {\n y = winH - rect.height - bottom;\n } else {\n y = px(style.top) || winH - rect.height - px(style.bottom) || offset;\n }\n\n return options.constrainToViewport\n ? clampToViewport(x, y, el, options.constrainOffset)\n : { x, y };\n}\n\nfunction getConstrainedPosition(\n el: HTMLElement,\n pos: FloatingWindowPosition,\n options: UseFloatingWindowOptions\n) {\n if (!options.constrainToViewport || !el) {\n return pos;\n }\n\n const rect = el.getBoundingClientRect();\n const offset = options.constrainOffset ?? 0;\n const maxX = window.innerWidth - rect.width - offset;\n const maxY = window.innerHeight - rect.height - offset;\n\n return {\n x: Math.min(Math.max(offset, pos.x), maxX),\n y: Math.min(Math.max(offset, pos.y), maxY),\n };\n}\n\nfunction matchesExcludeSelector(target: Node, excludeSelector?: string): boolean {\n if (!excludeSelector) {\n return false;\n }\n if (!(target instanceof Element)) {\n return false;\n }\n\n return Boolean(target.closest(excludeSelector));\n}\n\nfunction getHandle(\n el: HTMLElement,\n target: EventTarget | null,\n options: UseFloatingWindowOptions\n): boolean {\n if (!(target instanceof Node)) {\n return false;\n }\n\n // If no drag handle selector, allow dragging from entire element\n if (!options.dragHandleSelector) {\n return !matchesExcludeSelector(target, options.excludeDragHandleSelector);\n }\n\n const handles = Array.from(el.querySelectorAll(options.dragHandleSelector));\n return handles.some(\n (handle) =>\n handle.contains(target) && !matchesExcludeSelector(target, options.excludeDragHandleSelector)\n );\n}\n\nfunction clampToViewport(\n x: number,\n y: number,\n el: HTMLElement,\n offset: number = 0\n): { x: number; y: number } {\n const rect = el.getBoundingClientRect();\n const maxX = window.innerWidth - rect.width - offset;\n const maxY = window.innerHeight - rect.height - offset;\n\n return {\n x: Math.min(Math.max(offset, x), maxX),\n y: Math.min(Math.max(offset, y), maxY),\n };\n}\n\nexport namespace useFloatingWindow {\n export type Options = UseFloatingWindowOptions;\n export type Position = FloatingWindowPosition;\n export type SetPosition = SetFloatingWindowPosition;\n export type ReturnValue<T extends HTMLElement> = UseFloatingWindowReturnValue<T>;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-floating-window.mjs","names":[],"sources":["../../src/use-floating-window/use-floating-window.ts"],"sourcesContent":["// Required to disable for webkit-user-select, although deprecated, it is still required for Safari support\nimport { RefCallback, useCallback, useEffect, useRef, useState } from 'react';\n\nfunction useRefValue<T>(value: T) {\n const ref = useRef(value);\n ref.current = value;\n return ref;\n}\n\ninterface FloatingWindowPositionConfig {\n top?: number;\n left?: number;\n right?: number;\n bottom?: number;\n}\n\ninterface FloatingWindowPosition {\n /** Element offset from the left side of the viewport */\n x: number;\n\n /** Element offset from the top side of the viewport */\n y: number;\n}\n\nexport interface UseFloatingWindowOptions {\n /** If `false`, the element can not be dragged. */\n enabled?: boolean;\n\n /** If `true`, the element can only move within the current viewport boundaries. */\n constrainToViewport?: boolean;\n\n /** The offset from the viewport edges when constraining the element. Requires `constrainToViewport: true`. */\n constrainOffset?: number;\n\n /** Selector of an element that should be used to drag floating window. If not specified, the entire root element is used as a drag target. */\n dragHandleSelector?: string;\n\n /** Selector of an element within `dragHandleSelector` that should be excluded from the drag event. */\n excludeDragHandleSelector?: string;\n\n /** If set, restricts movement to the specified axis */\n axis?: 'x' | 'y';\n\n /** Initial position. If not set, calculated from element styles. */\n initialPosition?: FloatingWindowPositionConfig;\n\n /** Called when the element position changes */\n onPositionChange?: (pos: FloatingWindowPosition) => void;\n\n /** Called when the drag starts */\n onDragStart?: () => void;\n\n /** Called when the drag stops */\n onDragEnd?: () => void;\n}\n\nexport type SetFloatingWindowPosition = (position: FloatingWindowPositionConfig) => void;\n\nexport interface UseFloatingWindowReturnValue<T extends HTMLElement> {\n /** Ref to the element that should be draggable */\n ref: RefCallback<T | null>;\n\n /** Function to set the position of the element */\n setPosition: SetFloatingWindowPosition;\n\n /** `true` if the element is currently being dragged */\n isDragging: boolean;\n}\n\nexport function useFloatingWindow<T extends HTMLElement>(\n options: UseFloatingWindowOptions = {}\n): UseFloatingWindowReturnValue<T> {\n const [element, setElement] = useState<T | null>(null);\n const ref = useRef<T>(null);\n const pos = useRef({ x: 0, y: 0 });\n const offset = useRef({ x: 0, y: 0 });\n const [isDragging, setIsDragging] = useState(false);\n const isDraggingRef = useRef(false);\n const initialized = useRef(false);\n const enabledRef = useRefValue(options.enabled);\n const onPositionChangeRef = useRefValue(options.onPositionChange);\n const onDragStartRef = useRefValue(options.onDragStart);\n const onDragEndRef = useRefValue(options.onDragEnd);\n\n const setDragging = useCallback((value: boolean) => {\n setIsDragging(value);\n isDraggingRef.current = value;\n }, []);\n\n const assignRef = useCallback((node: T | null) => {\n if (node) {\n ref.current = node;\n setElement(node);\n } else {\n ref.current = null;\n setElement(null);\n }\n }, []);\n\n useEffect(() => {\n const el = ref.current;\n if (!initialized.current && el) {\n initialized.current = true;\n pos.current = calculateInitialPosition(el, options);\n el.style.left = `${pos.current.x}px`;\n el.style.top = `${pos.current.y}px`;\n el.style.right = 'unset';\n el.style.bottom = 'unset';\n }\n\n return () => {\n initialized.current = false;\n };\n }, [\n element,\n options.constrainOffset,\n options.initialPosition?.top,\n options.initialPosition?.left,\n options.initialPosition?.right,\n options.initialPosition?.bottom,\n options.constrainToViewport,\n ]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const controller = new AbortController();\n const signal = controller.signal;\n\n const onStart = (e: MouseEvent | TouchEvent) => {\n if (enabledRef.current === false) {\n return;\n }\n\n const point = 'touches' in e ? e.touches[0] : e;\n\n if ('button' in e && e.button !== 0) {\n return;\n }\n\n if (!getHandle(el, e.target, options)) {\n return;\n }\n\n setDragging(true);\n document.body.style.userSelect = 'none';\n document.body.style.webkitUserSelect = 'none';\n\n const rect = el.getBoundingClientRect();\n\n offset.current = {\n x: point.clientX - rect.left,\n y: point.clientY - rect.top,\n };\n\n onDragStartRef.current?.();\n\n document.addEventListener('mousemove', onMove, { signal });\n document.addEventListener('mouseup', onEnd, { signal });\n document.addEventListener('touchmove', onMove, { signal, passive: false });\n document.addEventListener('touchend', onEnd, { signal });\n document.addEventListener('touchcancel', onEnd, { signal });\n };\n\n const onMove = (e: MouseEvent | TouchEvent) => {\n if (!isDraggingRef.current) {\n return;\n }\n\n const point = 'touches' in e ? e.touches[0] : e;\n e.preventDefault();\n\n let x = point.clientX - offset.current.x;\n let y = point.clientY - offset.current.y;\n\n const constrained = getConstrainedPosition(el, { x, y }, options);\n if (options.axis === 'x') {\n x = constrained.x;\n y = pos.current.y;\n } else if (options.axis === 'y') {\n x = pos.current.x;\n y = constrained.y;\n } else {\n x = constrained.x;\n y = constrained.y;\n }\n\n pos.current = { x, y };\n\n if (ref.current) {\n ref.current.style.left = `${x}px`;\n ref.current.style.top = `${y}px`;\n }\n\n onPositionChangeRef.current?.({ x, y });\n };\n\n const onEnd = () => {\n if (isDraggingRef.current) {\n setDragging(false);\n document.body.style.userSelect = '';\n document.body.style.webkitUserSelect = '';\n onDragEndRef.current?.();\n }\n };\n\n el.addEventListener('mousedown', onStart, { signal });\n el.addEventListener('touchstart', onStart, { signal, passive: false });\n\n return () => {\n controller.abort();\n };\n }, [\n options.constrainToViewport,\n options.constrainOffset,\n options.dragHandleSelector,\n options.axis,\n options.initialPosition?.top,\n options.initialPosition?.left,\n options.initialPosition?.right,\n options.initialPosition?.bottom,\n element,\n ]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const observer = new ResizeObserver(() => {\n // Re-clamp current position if element size changes\n const constrained = getConstrainedPosition(el, pos.current, options);\n pos.current = constrained;\n el.style.left = `${constrained.x}px`;\n el.style.top = `${constrained.y}px`;\n });\n\n observer.observe(el);\n\n return () => {\n observer.disconnect();\n };\n }, [options.constrainToViewport, options.constrainOffset]);\n\n const setPosition = useCallback(\n (position: FloatingWindowPositionConfig) => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const offset = options.constrainOffset ?? 0;\n const rect = el.getBoundingClientRect();\n\n let x: number | undefined;\n let y: number | undefined;\n\n if (position.left != null) {\n x = position.left;\n } else if (position.right != null) {\n x = window.innerWidth - rect.width - position.right;\n }\n\n if (position.top != null) {\n y = position.top;\n } else if (position.bottom != null) {\n y = window.innerHeight - rect.height - position.bottom;\n }\n\n x = x ?? pos.current.x;\n y = y ?? pos.current.y;\n\n if (options.constrainToViewport) {\n const clamped = clampToViewport(x, y, el, offset);\n x = clamped.x;\n y = clamped.y;\n }\n\n pos.current = { x, y };\n el.style.left = `${x}px`;\n el.style.top = `${y}px`;\n onPositionChangeRef.current?.({ x, y });\n },\n [options.constrainToViewport, options.constrainOffset]\n );\n\n return {\n ref: assignRef,\n setPosition,\n isDragging,\n };\n}\n\n// -------------------------------------------------------\n// Helper functions\n// -------------------------------------------------------\n\nfunction px(v: string) {\n return v.endsWith('px') ? parseFloat(v) : 0;\n}\n\nfunction calculateInitialPosition(\n el: HTMLElement,\n options: UseFloatingWindowOptions\n): { x: number; y: number } {\n const rect = el.getBoundingClientRect();\n const offset = options.constrainOffset ?? 0;\n const winW = window.innerWidth;\n const winH = window.innerHeight;\n const style = window.getComputedStyle(el);\n const top = options.initialPosition?.top;\n const left = options.initialPosition?.left;\n const right = options.initialPosition?.right;\n const bottom = options.initialPosition?.bottom;\n\n let x = offset;\n let y = offset;\n\n if (left != null) {\n x = left;\n } else if (right != null) {\n x = winW - rect.width - right;\n } else {\n x = px(style.left) || winW - rect.width - px(style.right) || offset;\n }\n\n if (top != null) {\n y = top;\n } else if (bottom != null) {\n y = winH - rect.height - bottom;\n } else {\n y = px(style.top) || winH - rect.height - px(style.bottom) || offset;\n }\n\n return options.constrainToViewport\n ? clampToViewport(x, y, el, options.constrainOffset)\n : { x, y };\n}\n\nfunction getConstrainedPosition(\n el: HTMLElement,\n pos: FloatingWindowPosition,\n options: UseFloatingWindowOptions\n) {\n if (!options.constrainToViewport || !el) {\n return pos;\n }\n\n const rect = el.getBoundingClientRect();\n const offset = options.constrainOffset ?? 0;\n const maxX = window.innerWidth - rect.width - offset;\n const maxY = window.innerHeight - rect.height - offset;\n\n return {\n x: Math.min(Math.max(offset, pos.x), maxX),\n y: Math.min(Math.max(offset, pos.y), maxY),\n };\n}\n\nfunction matchesExcludeSelector(target: Node, excludeSelector?: string): boolean {\n if (!excludeSelector) {\n return false;\n }\n if (!(target instanceof Element)) {\n return false;\n }\n\n return Boolean(target.closest(excludeSelector));\n}\n\nfunction getHandle(\n el: HTMLElement,\n target: EventTarget | null,\n options: UseFloatingWindowOptions\n): boolean {\n if (!(target instanceof Node)) {\n return false;\n }\n\n // If no drag handle selector, allow dragging from entire element\n if (!options.dragHandleSelector) {\n return !matchesExcludeSelector(target, options.excludeDragHandleSelector);\n }\n\n const handles = Array.from(el.querySelectorAll(options.dragHandleSelector));\n return handles.some(\n (handle) =>\n handle.contains(target) && !matchesExcludeSelector(target, options.excludeDragHandleSelector)\n );\n}\n\nfunction clampToViewport(\n x: number,\n y: number,\n el: HTMLElement,\n offset: number = 0\n): { x: number; y: number } {\n const rect = el.getBoundingClientRect();\n const maxX = window.innerWidth - rect.width - offset;\n const maxY = window.innerHeight - rect.height - offset;\n\n return {\n x: Math.min(Math.max(offset, x), maxX),\n y: Math.min(Math.max(offset, y), maxY),\n };\n}\n\nexport namespace useFloatingWindow {\n export type Options = UseFloatingWindowOptions;\n export type Position = FloatingWindowPosition;\n export type SetPosition = SetFloatingWindowPosition;\n export type ReturnValue<T extends HTMLElement> = UseFloatingWindowReturnValue<T>;\n}\n"],"mappings":"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"}
|