Skip to content

Commit

Permalink
feat: dom support dynamic target
Browse files Browse the repository at this point in the history
  • Loading branch information
brickspert committed Nov 5, 2021
1 parent 9e3f49b commit 107f69d
Show file tree
Hide file tree
Showing 30 changed files with 525 additions and 413 deletions.
52 changes: 25 additions & 27 deletions packages/hooks/src/useClickAway/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { useMemo } from 'react';
import useDeepCompareEffect from '../useDeepCompareEffect';
import useLatest from '../useLatest';
import { getTargetElement } from '../utils/dom2';
import type { BasicTarget } from '../utils/dom2';
import type { BasicTarget } from '../utils/domTarget';
import { getTargetElement } from '../utils/domTarget';
import useEffectWithTarget from '../utils/useEffectWithTarget';

export default function useClickAway<T extends Event = Event>(
onClickAway: (event: T) => void,
Expand All @@ -11,29 +10,28 @@ export default function useClickAway<T extends Event = Event>(
) {
const onClickAwayRef = useLatest(onClickAway);

const deps = useMemo(() => {
const targets = Array.isArray(target) ? target : [target];
return targets.map((item) => (typeof target === 'function' ? undefined : item));
}, [target]);
useEffectWithTarget(
() => {
const handler = (event: any) => {
const targets = Array.isArray(target) ? target : [target];
if (
targets.some((item) => {
const targetElement = getTargetElement(item);
return !targetElement || targetElement?.contains(event.target);
})
) {
return;
}
onClickAwayRef.current(event);
};

useDeepCompareEffect(() => {
const handler = (event: any) => {
const targets = Array.isArray(target) ? target : [target];
if (
targets.some((item) => {
const targetElement = getTargetElement(item);
return !targetElement || targetElement?.contains(event.target);
})
) {
return;
}
onClickAwayRef.current(event);
};
document.addEventListener(eventName, handler);

document.addEventListener(eventName, handler);

return () => {
document.removeEventListener(eventName, handler);
};
}, [deps, eventName]);
return () => {
document.removeEventListener(eventName, handler);
};
},
[eventName],
target,
);
}
9 changes: 1 addition & 8 deletions packages/hooks/src/useCreation/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { DependencyList } from 'react';
import { useRef } from 'react';
import depsAreSame from '../utils/depsAreSame';

export default function useCreation<T>(factory: () => T, deps: DependencyList) {
const { current } = useRef({
Expand All @@ -14,11 +15,3 @@ export default function useCreation<T>(factory: () => T, deps: DependencyList) {
}
return current.obj as T;
}

function depsAreSame(oldDeps: DependencyList, deps: DependencyList): boolean {
if (oldDeps === deps) return true;
for (let i = 0; i < oldDeps.length; i++) {
if (oldDeps[i] !== deps[i]) return false;
}
return true;
}
2 changes: 1 addition & 1 deletion packages/hooks/src/useDocumentVisibility/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { isBrowser } from '../utils/dom2';
import { useState } from 'react';
import useEventListener from '../useEventListener';
import isBrowser from '../utils/isBrowser';

type VisibilityState = 'hidden' | 'visible' | 'prerender' | undefined;

Expand Down
60 changes: 32 additions & 28 deletions packages/hooks/src/useDrag/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect } from 'react';
import useLatest from '../useLatest';
import type { BasicTarget } from '../utils/dom2';
import { getTargetElement } from '../utils/dom2';
import type { BasicTarget } from '../utils/domTarget';
import { getTargetElement } from '../utils/domTarget';
import useEffectWithTarget from '../utils/useEffectWithTarget';

export interface Options {
onDragStart?: (event: React.DragEvent) => void;
Expand All @@ -11,31 +11,35 @@ export interface Options {
const useDrag = <T>(data: T, target: BasicTarget, options: Options = {}) => {
const optionsRef = useLatest(options);

useEffect(() => {
const targetElement = getTargetElement(target);
if (!targetElement?.addEventListener) {
return;
}

const onDragStart = (event: React.DragEvent) => {
optionsRef.current.onDragStart?.(event);
event.dataTransfer.setData('custom', JSON.stringify(data));
};

const onDragEnd = (event: React.DragEvent) => {
optionsRef.current.onDragEnd?.(event);
};

targetElement.setAttribute('draggable', 'true');

targetElement.addEventListener('dragstart', onDragStart as any);
targetElement.addEventListener('dragend', onDragEnd as any);

return () => {
targetElement.removeEventListener('dragstart', onDragStart as any);
targetElement.removeEventListener('dragend', onDragEnd as any);
};
}, [typeof target === 'function' ? 'undefined' : target]);
useEffectWithTarget(
() => {
const targetElement = getTargetElement(target);
if (!targetElement?.addEventListener) {
return;
}

const onDragStart = (event: React.DragEvent) => {
optionsRef.current.onDragStart?.(event);
event.dataTransfer.setData('custom', JSON.stringify(data));
};

const onDragEnd = (event: React.DragEvent) => {
optionsRef.current.onDragEnd?.(event);
};

targetElement.setAttribute('draggable', 'true');

targetElement.addEventListener('dragstart', onDragStart as any);
targetElement.addEventListener('dragend', onDragEnd as any);

return () => {
targetElement.removeEventListener('dragstart', onDragStart as any);
targetElement.removeEventListener('dragend', onDragEnd as any);
};
},
[],
target,
);
};

export default useDrag;
156 changes: 82 additions & 74 deletions packages/hooks/src/useDrop/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useEffect } from 'react';
import useLatest from '../useLatest';
import type { BasicTarget } from '../utils/dom2';
import { getTargetElement } from '../utils/dom2';
import type { BasicTarget } from '../utils/domTarget';
import { getTargetElement } from '../utils/domTarget';
import useEffectWithTarget from '../utils/useEffectWithTarget';

export interface Options {
onFiles?: (files: File[], event?: React.DragEvent) => void;
Expand All @@ -18,83 +19,90 @@ export interface Options {
const useDrop = (target: BasicTarget, options: Options = {}) => {
const optionsRef = useLatest(options);

useEffect(() => {
const targetElement = getTargetElement(target);
if (!targetElement?.addEventListener) {
return;
}

const onData = (dataTransfer: DataTransfer, event: React.DragEvent | React.ClipboardEvent) => {
const uri = dataTransfer.getData('text/uri-list');
const dom = dataTransfer.getData('custom');

if (dom && optionsRef.current.onDom) {
let data = dom;
try {
data = JSON.parse(dom);
} catch (e) {
data = dom;
}
optionsRef.current.onDom(data, event as React.DragEvent);
useEffectWithTarget(
() => {
const targetElement = getTargetElement(target);
if (!targetElement?.addEventListener) {
return;
}

if (uri && optionsRef.current.onUri) {
optionsRef.current.onUri(uri, event as React.DragEvent);
return;
}
const onData = (
dataTransfer: DataTransfer,
event: React.DragEvent | React.ClipboardEvent,
) => {
const uri = dataTransfer.getData('text/uri-list');
const dom = dataTransfer.getData('custom');

if (dom && optionsRef.current.onDom) {
let data = dom;
try {
data = JSON.parse(dom);
} catch (e) {
data = dom;
}
optionsRef.current.onDom(data, event as React.DragEvent);
return;
}

if (dataTransfer.files && dataTransfer.files.length && optionsRef.current.onFiles) {
optionsRef.current.onFiles(Array.from(dataTransfer.files), event as React.DragEvent);
return;
}
if (uri && optionsRef.current.onUri) {
optionsRef.current.onUri(uri, event as React.DragEvent);
return;
}

if (dataTransfer.items && dataTransfer.items.length && optionsRef.current.onText) {
dataTransfer.items[0].getAsString((text) => {
optionsRef.current.onText!(text, event as React.ClipboardEvent);
});
}
};

const onDragEnter = (event: React.DragEvent) => {
event.preventDefault();
optionsRef.current.onDragEnter?.(event);
};

const onDragOver = (event: React.DragEvent) => {
event.preventDefault();
optionsRef.current.onDragOver?.(event);
};

const onDragLeave = (event: React.DragEvent) => {
optionsRef.current.onDragLeave?.(event);
};

const onDrop = (event: React.DragEvent) => {
event.preventDefault();
onData(event.dataTransfer, event);
optionsRef.current.onDrop?.(event);
};

const onPaste = (event: React.ClipboardEvent) => {
onData(event.clipboardData, event);
optionsRef.current.onPaste?.(event);
};

targetElement.addEventListener('dragenter', onDragEnter as any);
targetElement.addEventListener('dragover', onDragOver as any);
targetElement.addEventListener('dragleave', onDragLeave as any);
targetElement.addEventListener('drop', onDrop as any);
targetElement.addEventListener('paste', onPaste as any);

return () => {
targetElement.removeEventListener('dragenter', onDragEnter as any);
targetElement.removeEventListener('dragover', onDragOver as any);
targetElement.removeEventListener('dragleave', onDragLeave as any);
targetElement.removeEventListener('drop', onDrop as any);
targetElement.removeEventListener('paste', onPaste as any);
};
}, [typeof target === 'function' ? 'undefined' : target]);
if (dataTransfer.files && dataTransfer.files.length && optionsRef.current.onFiles) {
optionsRef.current.onFiles(Array.from(dataTransfer.files), event as React.DragEvent);
return;
}

if (dataTransfer.items && dataTransfer.items.length && optionsRef.current.onText) {
dataTransfer.items[0].getAsString((text) => {
optionsRef.current.onText!(text, event as React.ClipboardEvent);
});
}
};

const onDragEnter = (event: React.DragEvent) => {
event.preventDefault();
optionsRef.current.onDragEnter?.(event);
};

const onDragOver = (event: React.DragEvent) => {
event.preventDefault();
optionsRef.current.onDragOver?.(event);
};

const onDragLeave = (event: React.DragEvent) => {
optionsRef.current.onDragLeave?.(event);
};

const onDrop = (event: React.DragEvent) => {
event.preventDefault();
onData(event.dataTransfer, event);
optionsRef.current.onDrop?.(event);
};

const onPaste = (event: React.ClipboardEvent) => {
onData(event.clipboardData, event);
optionsRef.current.onPaste?.(event);
};

targetElement.addEventListener('dragenter', onDragEnter as any);
targetElement.addEventListener('dragover', onDragOver as any);
targetElement.addEventListener('dragleave', onDragLeave as any);
targetElement.addEventListener('drop', onDrop as any);
targetElement.addEventListener('paste', onPaste as any);

return () => {
targetElement.removeEventListener('dragenter', onDragEnter as any);
targetElement.removeEventListener('dragover', onDragOver as any);
targetElement.removeEventListener('dragleave', onDragLeave as any);
targetElement.removeEventListener('drop', onDrop as any);
targetElement.removeEventListener('paste', onPaste as any);
};
},
[],
target,
);
};

export default useDrop;
Loading

0 comments on commit 107f69d

Please sign in to comment.