goldenChat base source add
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* 마우스·터치·펜 입력을 통합한 드래그/포인터 좌표 유틸
|
||||
*/
|
||||
|
||||
export interface ClientXY {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
type ReactPointerLike =
|
||||
| React.MouseEvent
|
||||
| React.TouchEvent
|
||||
| React.PointerEvent;
|
||||
|
||||
/** React 합성 이벤트에서 client 좌표 추출 */
|
||||
export function clientXYFromReact(e: ReactPointerLike): ClientXY {
|
||||
if ('touches' in e && e.touches.length > 0) {
|
||||
return { x: e.touches[0].clientX, y: e.touches[0].clientY };
|
||||
}
|
||||
if ('changedTouches' in e && e.changedTouches.length > 0) {
|
||||
return { x: e.changedTouches[0].clientX, y: e.changedTouches[0].clientY };
|
||||
}
|
||||
const pe = e as React.MouseEvent | React.PointerEvent;
|
||||
return { x: pe.clientX, y: pe.clientY };
|
||||
}
|
||||
|
||||
/** window/document 네이티브 이벤트에서 client 좌표 추출 */
|
||||
export function clientXYFromNative(e: Event): ClientXY | null {
|
||||
if (e instanceof TouchEvent) {
|
||||
const t = e.touches[0] ?? e.changedTouches[0];
|
||||
if (!t) return null;
|
||||
return { x: t.clientX, y: t.clientY };
|
||||
}
|
||||
if (e instanceof MouseEvent || e instanceof PointerEvent) {
|
||||
return { x: e.clientX, y: e.clientY };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export interface BindWindowDragOptions {
|
||||
/** 터치 드래그 중 페이지 스크롤 방지 (기본 true) */
|
||||
preventTouchScroll?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 드래그 시작 후 window 에 move/end 리스너 등록.
|
||||
* pointer + mouse + touch 모두 수신 (구형 브라우저·iOS 대응).
|
||||
*/
|
||||
export function bindWindowDrag(
|
||||
onMove: (xy: ClientXY, ev: Event) => void,
|
||||
onEnd: () => void,
|
||||
options: BindWindowDragOptions = {},
|
||||
): () => void {
|
||||
const { preventTouchScroll = true } = options;
|
||||
|
||||
const move = (ev: Event) => {
|
||||
const xy = clientXYFromNative(ev);
|
||||
if (!xy) return;
|
||||
if (preventTouchScroll && ev.cancelable && (ev instanceof TouchEvent || ev.type === 'touchmove')) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
onMove(xy, ev);
|
||||
};
|
||||
|
||||
const end = () => onEnd();
|
||||
|
||||
window.addEventListener('pointermove', move);
|
||||
window.addEventListener('pointerup', end);
|
||||
window.addEventListener('pointercancel', end);
|
||||
window.addEventListener('mousemove', move);
|
||||
window.addEventListener('mouseup', end);
|
||||
window.addEventListener('touchmove', move, { passive: !preventTouchScroll });
|
||||
window.addEventListener('touchend', end);
|
||||
window.addEventListener('touchcancel', end);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('pointermove', move);
|
||||
window.removeEventListener('pointerup', end);
|
||||
window.removeEventListener('pointercancel', end);
|
||||
window.removeEventListener('mousemove', move);
|
||||
window.removeEventListener('mouseup', end);
|
||||
window.removeEventListener('touchmove', move);
|
||||
window.removeEventListener('touchend', end);
|
||||
window.removeEventListener('touchcancel', end);
|
||||
};
|
||||
}
|
||||
|
||||
/** 드래그 핸들(타이틀바 등)에 붙일 공통 스타일 */
|
||||
export const DRAG_HANDLE_TOUCH_STYLE: React.CSSProperties = {
|
||||
touchAction: 'none',
|
||||
};
|
||||
|
||||
/** primary 버튼(마우스 왼쪽 / 터치)만 허용 */
|
||||
export function isPrimaryPointerButton(e: { button: number; pointerType: string }): boolean {
|
||||
return e.button === 0;
|
||||
}
|
||||
Reference in New Issue
Block a user