Merge pull request #55 from webstudio-is/30-remove-bouncing-back-animation-when-dropping-a-new-instance

Fixes bouncing back animation from native thumbnail on drop
This commit is contained in:
Oleg Isonen authored and GitHub committed 2022-06-01 18:58:03 +01:00
commit 6b4faeb5c5
7 files changed
+113 -63

No files matched your search

@@ -1,4 +1 @@
export { SidebarLeft } from "./sidebar-left";
export { CustomDragLayer } from "./custom-drag-layer";
import type { CustomDragLayerProps } from "./custom-drag-layer";
export type OnDrag = CustomDragLayerProps["onDrag"];
@@ -0,0 +1,50 @@
import { type Instance } from "@webstudio-is/sdk";
import { forwardRef, type ElementRef, type ComponentProps } from "react";
import { primitives } from "~/shared/component";
import { Flex, Text, styled } from "~/shared/design-system";
const Thumb = styled(Flex, {
px: 5,
width: 75,
height: 75,
border: "1px solid $slate6",
userSelect: "none",
color: "$hiContrast",
cursor: "grab",
"&:hover": {
background: "$slate3",
},
variants: {
state: {
dragging: {
background: "$slate3",
},
},
},
});
type ComponentThumbProps = {
component: Instance["component"];
} & ComponentProps<typeof Thumb>;
export const ComponentThumb = forwardRef<
ElementRef<typeof Thumb>,
ComponentThumbProps
>(({ component, ...rest }, ref) => {
const { Icon, label } = primitives[component];
return (
<Thumb
direction="column"
align="center"
justify="center"
gap="3"
ref={ref}
{...rest}
>
<Icon width={30} height={30} />
<Text size="1">{label}</Text>
</Thumb>
);
});
ComponentThumb.displayName = "ComponentThumb";
@@ -1,30 +1,23 @@
import { useEffect, useRef, type MouseEventHandler, useCallback } from "react";
import { useDrag } from "react-dnd";
import { getEmptyImage } from "react-dnd-html5-backend";
import { type Instance, type Publish } from "@webstudio-is/sdk";
import { Flex, Text } from "~/shared/design-system";
import { Flex } from "~/shared/design-system";
import { PlusIcon } from "~/shared/icons";
import { primitives, type DragData } from "~/shared/component";
import { createInstance } from "~/shared/tree-utils";
import { CustomDragLayer } from "../custom-drag-layer";
import type { TabName } from "../types";
import type { TabName } from "../../types";
import { CustomDragLayer } from "./custom-drag-layer";
import { ComponentThumb } from "./component-thumb";
type ComponentProps = {
Icon: typeof PlusIcon;
type UseDraggableProps = {
component: Instance["component"];
onDragChange: (isDragging: boolean) => void;
onClick: MouseEventHandler<HTMLDivElement>;
label: string;
};
const Component = ({
Icon,
component,
label,
onDragChange,
onClick,
}: ComponentProps) => {
const useDraggable = ({ component, onDragChange }: UseDraggableProps) => {
const lastIsDragging = useRef<boolean | void>();
const [{ isDragging }, dragRef] = useDrag(
const [{ isDragging }, dragRef, preview] = useDrag(
() => ({
type: component,
collect: (monitor) => ({
@@ -39,35 +32,30 @@ const Component = ({
lastIsDragging.current = isDragging;
}, [isDragging, onDragChange]);
useEffect(() => {
preview(getEmptyImage(), { captureDraggingState: true });
}, [preview]);
return dragRef;
};
type DraggableThumbProps = {
onClick: MouseEventHandler<HTMLDivElement>;
} & UseDraggableProps;
const DraggableThumb = ({
component,
onDragChange,
onClick,
}: DraggableThumbProps) => {
const dragRef = useDraggable({ component, onDragChange });
return (
<Flex
direction="column"
align="center"
justify="center"
gap="3"
css={{
px: 5,
width: 75,
height: 75,
border: "1px solid $slate6",
userSelect: "none",
color: "$hiContrast",
cursor: "grab",
"&:hover": {
background: "$slate3",
},
}}
ref={dragRef}
onClick={onClick}
>
<Icon width={30} height={30} />
<Text size="1">{label}</Text>
</Flex>
<ComponentThumb component={component} ref={dragRef} onClick={onClick} />
);
};
type ComponentsProps = {
onDragChange: ComponentProps["onDragChange"];
type TabContentProps = {
onDragChange: UseDraggableProps["onDragChange"];
onSetActiveTab: (tabName: TabName) => void;
publish: Publish;
};
@@ -76,7 +64,7 @@ export const TabContent = ({
onDragChange,
publish,
onSetActiveTab,
}: ComponentsProps) => {
}: TabContentProps) => {
const components = (
Object.keys(primitives) as Array<Instance["component"]>
).filter((component) => primitives[component].isInlineOnly === false);
@@ -94,11 +82,9 @@ export const TabContent = ({
return (
<Flex gap="1" wrap="wrap" css={{ padding: "$1" }}>
{components.map((component: Instance["component"]) => (
<Component
<DraggableThumb
key={component}
Icon={primitives[component].Icon}
component={component}
label={primitives[component].label}
onClick={() => {
onSetActiveTab("none");
publish<"insertInstance", { instance: Instance }>({
@@ -3,6 +3,7 @@ import { createPortal } from "react-dom";
import { useDragLayer, type XYCoord } from "react-dnd";
import { type Instance } from "@webstudio-is/sdk";
import { useCanvasRect, useZoom } from "~/designer/shared/nano-values";
import { ComponentThumb } from "./component-thumb";
const layerStyles = {
position: "absolute",
@@ -22,24 +23,19 @@ export type CustomDragLayerProps = {
};
export const CustomDragLayer = ({ onDrag }: CustomDragLayerProps) => {
const {
itemType: component,
isDragging,
initialOffset,
clientOffset,
} = useDragLayer((monitor) => ({
itemType: monitor.getItemType() as Instance["component"],
initialOffset: monitor.getInitialSourceClientOffset(),
isDragging: monitor.isDragging(),
clientOffset: monitor.getClientOffset(),
}));
const { component, isDragging, clientOffset, sourceClientOffset } =
useDragLayer((monitor) => ({
component: monitor.getItemType() as Instance["component"],
isDragging: monitor.isDragging(),
clientOffset: monitor.getClientOffset(),
sourceClientOffset: monitor.getSourceClientOffset(),
}));
const [canvasRect] = useCanvasRect();
const [zoom] = useZoom();
useEffect(() => {
if (
clientOffset === null ||
initialOffset === null ||
component === null ||
canvasRect === undefined
) {
@@ -56,9 +52,20 @@ export const CustomDragLayer = ({ onDrag }: CustomDragLayerProps) => {
currentOffset,
component,
});
}, [clientOffset, initialOffset, component, onDrag, zoom, canvasRect]);
}, [clientOffset, component, onDrag, zoom, canvasRect]);
if (isDragging === false) return null;
if (isDragging === false || sourceClientOffset === null) return null;
return createPortal(<div style={layerStyles} />, document.body);
return createPortal(
<div style={layerStyles}>
<ComponentThumb
component={component}
style={{
transform: `translate3d(${sourceClientOffset.x}px, ${sourceClientOffset.y}px, 0)`,
}}
state="dragging"
/>
</div>,
document.body
);
};
@@ -0,0 +1 @@
export * from "./components";
+1
View File
@@ -6,3 +6,4 @@ export * from "./undo";
export * from "./redo";
export * from "./devices";
export * from "./tablet";
export * from "./types";
+9 -1
View File
@@ -1,6 +1,14 @@
import type { SVGAttributes } from "react";
import type {
SVGAttributes,
RefAttributes,
ForwardRefExoticComponent,
} from "react";
export interface IconProps extends SVGAttributes<SVGElement> {
children?: never;
color?: string;
}
export type IconComponent = ForwardRefExoticComponent<
IconProps & RefAttributes<SVGSVGElement>
>;