feat(core): init organize (#7456)

This commit is contained in:
EYHN
2024-07-26 04:35:31 +00:00
parent b26b0c3a22
commit 54da85ec62
140 changed files with 6257 additions and 2804 deletions

View File

@@ -174,7 +174,10 @@ NestedDropTarget.args = {
};
export const DynamicDragPreview = () => {
type DataType = DNDData<Record<string, never>, { type: 'big' | 'small' }>;
type DataType = DNDData<
Record<string, never>,
{ type: 'big' | 'small' | 'tips' }
>;
const { dragRef, dragging, draggingPosition, dropTarget, CustomDragPreview } =
useDraggable<DataType>(() => ({}), []);
const { dropTargetRef: bigDropTargetRef } = useDropTarget<DataType>(
@@ -189,6 +192,16 @@ export const DynamicDragPreview = () => {
}),
[]
);
const {
dropTargetRef: tipsDropTargetRef,
draggedOver: tipsDraggedOver,
draggedOverPosition: tipsDraggedOverPosition,
} = useDropTarget<DataType>(
() => ({
data: { type: 'tips' },
}),
[]
);
return (
<div
style={{
@@ -240,6 +253,30 @@ export const DynamicDragPreview = () => {
>
Small
</div>
<div
ref={tipsDropTargetRef}
style={{
position: 'relative',
width: '100%',
border: '1px solid green',
height: '100px',
fontSize: '50px',
}}
>
Tips
{tipsDraggedOver && (
<div
style={{
position: 'absolute',
top: 0,
left: 0,
transform: `translate(${tipsDraggedOverPosition.relativeX}px, ${tipsDraggedOverPosition.relativeY}px)`,
}}
>
tips
</div>
)}
</div>
<CustomDragPreview position="pointer-outside">
<div
style={{

View File

@@ -43,10 +43,16 @@ export interface DraggableOptions<D extends DNDData = DNDData> {
}>;
canDrag?: DraggableGet<boolean>;
disableDragPreview?: boolean;
dragPreviewPosition?: DraggableDragPreviewPosition;
}
export type DraggableDragPreviewPosition =
| 'pointer-outside'
| 'pointer-center'
| 'native';
export type DraggableCustomDragPreviewProps = React.PropsWithChildren<{
position?: 'pointer-outside' | 'pointer-center' | 'native';
position?: DraggableDragPreviewPosition;
}>;
export const useDraggable = <D extends DNDData = DNDData>(
@@ -172,9 +178,11 @@ export const useDraggable = <D extends DNDData = DNDData>(
disableNativeDragPreview({ nativeSetDragImage });
return;
}
let previewPosition: DraggableDragPreviewPosition =
options.dragPreviewPosition ?? 'native';
if (enableCustomDragPreview.current) {
let previewPosition: DraggableCustomDragPreviewProps['position'] =
'native';
setCustomNativeDragPreview({
getOffset: (...args) => {
if (previewPosition === 'pointer-center') {
@@ -199,7 +207,7 @@ export const useDraggable = <D extends DNDData = DNDData>(
children,
position,
}: DraggableCustomDragPreviewProps) => {
previewPosition = position;
previewPosition = position || previewPosition;
return ReactDOM.createPortal(children, container);
}
);
@@ -208,6 +216,28 @@ export const useDraggable = <D extends DNDData = DNDData>(
},
nativeSetDragImage,
});
} else if (previewPosition !== 'native') {
setCustomNativeDragPreview({
getOffset: (...args) => {
if (previewPosition === 'pointer-center') {
return centerUnderPointer(...args);
} else if (previewPosition === 'pointer-outside') {
return pointerOutsideOfPreview({
x: '8px',
y: '4px',
})(...args);
} else {
return preserveOffsetOnSource({
element: source.element,
input: location.current.input,
})(...args);
}
},
render({ container }) {
container.append(source.element.cloneNode(true));
},
nativeSetDragImage,
});
}
},
});

View File

@@ -100,7 +100,7 @@ export const horizontal = style({
right: 0,
'::before': {
// Horizontal indicators have the terminal on the left
left: `calc(-${terminalSize})`,
left: `calc(-1 * ${terminalSize})`,
},
});

View File

@@ -1,5 +1,3 @@
/** @jsx jsx */
import type { Edge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
import type { Instruction } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
import { cssVar } from '@toeverything/theme';

View File

@@ -20,6 +20,9 @@ type DropTargetGetFeedback<D extends DNDData> = Parameters<
source: {
data: D['draggable'];
};
} & {
treeInstruction: Instruction | null;
closestEdge: Edge | null;
};
type DropTargetGet<T, D extends DNDData> =
@@ -27,17 +30,60 @@ type DropTargetGet<T, D extends DNDData> =
| ((data: DropTargetGetFeedback<D>) => T);
function dropTargetGet<T, D extends DNDData>(
get: T
get: T,
options: DropTargetOptions<D>
): T extends undefined
? undefined
: T extends DropTargetGet<infer I, D>
? (args: DropTargetGetFeedback<D>) => I
? (
args: Omit<DropTargetGetFeedback<D>, 'treeInstruction' | 'closestEdge'>
) => I
: never {
if (get === undefined) {
return undefined as any;
}
return ((args: DropTargetGetFeedback<D>) =>
typeof get === 'function' ? (get as any)(args) : get) as any;
return ((
args: Omit<DropTargetGetFeedback<D>, 'treeInstruction' | 'closestEdge'>
) => {
if (typeof get === 'function') {
return (get as any)({
...args,
get treeInstruction() {
return options.treeInstruction
? extractInstruction(
attachInstruction(
{},
{
input: args.input,
element: args.element,
currentLevel: options.treeInstruction.currentLevel,
indentPerLevel: options.treeInstruction.indentPerLevel,
mode: options.treeInstruction.mode,
block: options.treeInstruction.block,
}
)
)
: null;
},
get closestEdge() {
return options.closestEdge
? extractClosestEdge(
attachClosestEdge(
{},
{
input: args.input,
element: args.element,
allowedEdges: options.closestEdge.allowedEdges,
}
)
)
: null;
},
});
} else {
return get;
}
}) as any;
}
export type DropTargetDropEvent<D extends DNDData> = Parameters<
@@ -52,6 +98,8 @@ export type DropTargetDragEvent<D extends DNDData> = Parameters<
source: { data: D['draggable'] };
};
export type DropTargetTreeInstruction = Instruction;
export interface DropTargetOptions<D extends DNDData = DNDData> {
data?: DropTargetGet<D['dropTarget'], D>;
canDrop?: DropTargetGet<boolean, D>;
@@ -80,8 +128,26 @@ export const useDropTarget = <D extends DNDData = DNDData>(
null
);
const [closestEdge, setClosestEdge] = useState<Edge | null>(null);
const [dropEffect, setDropEffect] = useState<'copy' | 'link' | 'move' | null>(
null
);
const [draggedOverDraggable, setDraggedOverDraggable] = useState<{
data: D['draggable'];
} | null>(null);
const [draggedOverPosition, setDraggedOverPosition] = useState<{
/**
* relative position to the drop target element top-left corner
*/
relativeX: number;
relativeY: number;
clientX: number;
clientY: number;
}>({ relativeX: 0, relativeY: 0, clientX: 0, clientY: 0 });
const enableDraggedOver = useRef(false);
const enableDraggedOverDraggable = useRef(false);
const enableDraggedOverPosition = useRef(false);
const enableDropEffect = useRef(false);
// eslint-disable-next-line react-hooks/exhaustive-deps
const options = useMemo(getOptions, deps);
@@ -92,19 +158,36 @@ export const useDropTarget = <D extends DNDData = DNDData>(
}
return dropTargetForElements({
element: dropTargetRef.current,
canDrop: dropTargetGet(options.canDrop),
getDropEffect: dropTargetGet(options.dropEffect),
getIsSticky: dropTargetGet(options.isSticky),
canDrop: dropTargetGet(options.canDrop, options),
getDropEffect: dropTargetGet(options.dropEffect, options),
getIsSticky: dropTargetGet(options.isSticky, options),
onDrop: args => {
if (enableDraggedOver.current) {
setDraggedOver(false);
}
if (enableDraggedOverDraggable.current) {
setDraggedOverDraggable(null);
}
if (enableDraggedOverPosition.current) {
setDraggedOverPosition({
relativeX: 0,
relativeY: 0,
clientX: 0,
clientY: 0,
});
}
if (options.treeInstruction) {
setTreeInstruction(null);
if (dropTargetRef.current) {
delete dropTargetRef.current.dataset['treeInstruction'];
}
}
if (options.closestEdge) {
setClosestEdge(null);
}
if (enableDropEffect.current) {
setDropEffect(null);
}
if (dropTargetRef.current) {
delete dropTargetRef.current.dataset['draggedOver'];
}
@@ -120,7 +203,7 @@ export const useDropTarget = <D extends DNDData = DNDData>(
}
},
getData: args => {
const originData = dropTargetGet(options.data ?? {})(args);
const originData = dropTargetGet(options.data ?? {}, options)(args);
const { input, element } = args;
const withInstruction = options.treeInstruction
? attachInstruction(originData, {
@@ -141,43 +224,116 @@ export const useDropTarget = <D extends DNDData = DNDData>(
: withInstruction;
return withClosestEdge;
},
onDragEnter: () => {
if (enableDraggedOver.current) {
setDraggedOver(true);
}
if (dropTargetRef.current) {
dropTargetRef.current.dataset['draggedOver'] = 'true';
}
},
onDrag: args => {
let instruction = null;
let closestEdge = null;
if (options.treeInstruction) {
instruction = extractInstruction(args.self.data);
setTreeInstruction(instruction);
if (
args.location.current.dropTargets[0]?.element ===
dropTargetRef.current
) {
if (enableDraggedOverDraggable.current) {
setDraggedOverDraggable({ data: args.source.data });
}
let instruction = null;
let closestEdge = null;
if (options.treeInstruction) {
instruction = extractInstruction(args.self.data);
setTreeInstruction(instruction);
if (dropTargetRef.current) {
dropTargetRef.current.dataset['treeInstruction'] =
instruction?.type;
}
}
if (options.closestEdge) {
closestEdge = extractClosestEdge(args.self.data);
setClosestEdge(closestEdge);
}
if (enableDropEffect.current) {
setDropEffect(args.self.dropEffect);
}
if (enableDraggedOverPosition.current) {
const rect = args.self.element.getBoundingClientRect();
const { clientX, clientY } = args.location.current.input;
setDraggedOverPosition({
relativeX: clientX - rect.x,
relativeY: clientY - rect.y,
clientX: clientX,
clientY: clientY,
});
}
options.onDrag?.({
...args,
treeInstruction: instruction,
closestEdge,
} as DropTargetDropEvent<D>);
}
if (options.closestEdge) {
closestEdge = extractClosestEdge(args.self.data);
setClosestEdge(closestEdge);
}
options.onDrag?.({
...args,
treeInstruction: instruction,
closestEdge,
} as DropTargetDropEvent<D>);
},
onDragLeave: () => {
if (enableDraggedOver.current) {
setDraggedOver(false);
}
if (options.treeInstruction) {
setTreeInstruction(null);
}
if (options.closestEdge) {
setClosestEdge(null);
}
if (dropTargetRef.current) {
delete dropTargetRef.current.dataset['draggedOver'];
onDropTargetChange: args => {
if (
args.location.current.dropTargets[0]?.element ===
dropTargetRef.current
) {
if (enableDraggedOver.current) {
setDraggedOver(true);
}
if (options.treeInstruction) {
const instruction = extractInstruction(args.self.data);
setTreeInstruction(instruction);
if (dropTargetRef.current) {
dropTargetRef.current.dataset['treeInstruction'] =
instruction?.type;
}
}
if (options.closestEdge) {
const closestEdge = extractClosestEdge(args.self.data);
setClosestEdge(closestEdge);
}
if (enableDropEffect.current) {
setDropEffect(args.self.dropEffect);
}
if (enableDraggedOverDraggable.current) {
setDraggedOverDraggable({ data: args.source.data });
}
if (enableDraggedOverPosition.current) {
const rect = args.self.element.getBoundingClientRect();
setDraggedOverPosition({
relativeX: args.location.current.input.clientX - rect.x,
relativeY: args.location.current.input.clientY - rect.y,
clientX: args.location.current.input.clientX,
clientY: args.location.current.input.clientY,
});
}
if (dropTargetRef.current) {
dropTargetRef.current.dataset['draggedOver'] = 'true';
}
} else {
if (enableDraggedOver.current) {
setDraggedOver(false);
}
if (enableDraggedOverDraggable.current) {
setDraggedOverDraggable(null);
}
if (options.treeInstruction) {
setTreeInstruction(null);
if (dropTargetRef.current) {
delete dropTargetRef.current.dataset['treeInstruction'];
}
}
if (enableDropEffect.current) {
setDropEffect(args.self.dropEffect);
}
if (enableDraggedOverPosition.current) {
setDraggedOverPosition({
relativeX: 0,
relativeY: 0,
clientX: 0,
clientY: 0,
});
}
if (options.closestEdge) {
setClosestEdge(null);
}
if (dropTargetRef.current) {
delete dropTargetRef.current.dataset['draggedOver'];
}
}
},
});
@@ -189,6 +345,18 @@ export const useDropTarget = <D extends DNDData = DNDData>(
enableDraggedOver.current = true;
return draggedOver;
},
get draggedOverDraggable() {
enableDraggedOverDraggable.current = true;
return draggedOverDraggable;
},
get draggedOverPosition() {
enableDraggedOverPosition.current = true;
return draggedOverPosition;
},
get dropEffect() {
enableDropEffect.current = true;
return dropEffect;
},
treeInstruction,
closestEdge,
};