Files
AFFiNE-Mirror/packages/frontend/component/src/ui/dnd/drop-indicator.tsx
2024-07-26 04:35:32 +00:00

123 lines
3.0 KiB
TypeScript

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';
import { assignInlineVars } from '@vanilla-extract/dynamic';
import clsx from 'clsx';
import { type ReactElement } from 'react';
import * as styles from './drop-indicator.css';
export type DropIndicatorProps = {
instruction?: Instruction | null;
edge?: Edge | null;
};
function getTreeElement({
instruction,
isBlocked,
}: {
instruction: Exclude<Instruction, { type: 'instruction-blocked' }>;
isBlocked: boolean;
}): ReactElement | null {
const style = {
[styles.horizontalIndent]: `${instruction.currentLevel * instruction.indentPerLevel}px`,
[styles.indicatorColor]: !isBlocked
? cssVar('--affine-primary-color')
: cssVar('--affine-warning-color'),
};
if (instruction.type === 'reorder-above') {
return (
<div
className={clsx(styles.treeLine, styles.lineAboveStyles)}
style={assignInlineVars(style)}
/>
);
}
if (instruction.type === 'reorder-below') {
return (
<div
className={clsx(styles.treeLine, styles.lineBelowStyles)}
style={assignInlineVars(style)}
/>
);
}
if (instruction.type === 'make-child') {
return (
<div
className={clsx(styles.outlineStyles)}
style={assignInlineVars(style)}
/>
);
}
if (instruction.type === 'reparent') {
style[styles.horizontalIndent] = `${
instruction.desiredLevel * instruction.indentPerLevel
}px`;
return (
<div
className={clsx(styles.treeLine, styles.lineBelowStyles)}
style={assignInlineVars(style)}
/>
);
}
return null;
}
type Orientation = 'horizontal' | 'vertical';
const edgeToOrientationMap: Record<Edge, Orientation> = {
top: 'horizontal',
bottom: 'horizontal',
left: 'vertical',
right: 'vertical',
};
const orientationStyles: Record<Orientation, string> = {
horizontal: styles.horizontal,
vertical: styles.vertical,
};
const edgeStyles: Record<Edge, string> = {
top: styles.top,
bottom: styles.bottom,
left: styles.left,
right: styles.right,
};
function getEdgeElement(edge: Edge, gap: number = 0) {
const lineOffset = `calc(-0.5 * (${gap}px + 2px))`;
const orientation = edgeToOrientationMap[edge];
return (
<div
className={clsx([
styles.edgeLine,
orientationStyles[orientation],
edgeStyles[edge],
])}
style={assignInlineVars({ [styles.localLineOffset]: lineOffset })}
/>
);
}
export function DropIndicator({ instruction, edge }: DropIndicatorProps) {
if (edge) {
return getEdgeElement(edge, 0);
}
if (instruction) {
if (instruction.type === 'instruction-blocked') {
return getTreeElement({
instruction: instruction.desired,
isBlocked: true,
});
}
return getTreeElement({ instruction, isBlocked: false });
}
return;
}