Skip to content

Commit

Permalink
feat: forward the ref to the DropZone component
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisvxd committed Jan 2, 2025
1 parent 6ffb87c commit 676aa1c
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 24 deletions.
23 changes: 23 additions & 0 deletions apps/docs/pages/docs/api-reference/components/drop-zone.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const config = {
| [`collisionAxis`](#collisionAxis) | `collisionAxis: "x"` | String | - |
| [`disallow`](#disallow) | `disallow: ["HeadingBlock"]` | Array | - |
| [`minEmptyHeight`](#minEmptyHeight) | `minEmptyHeight: 256` | Number | - |
| [`ref`](#ref) | `ref: ref` | Ref | - |
| [`style`](#style) | `style: {display: "flex"}` | CSSProperties | - |

## Required props
Expand Down Expand Up @@ -175,6 +176,28 @@ const config = {
};
```

### `ref`

A [React ref](https://react.dev/learn/manipulating-the-dom-with-refs), assigned to the root node of the DropZone.

```tsx copy {9}
const config = {
components: {
Example: {
render: () => {
const ref = useRef();

return (
<div>
<DropZone zone="my-content" ref={ref} />
</div>
);
},
},
},
};
```

### `style`

Provide a style attribute to the DropZone. The default DropZone styles will still be applied.
Expand Down
52 changes: 29 additions & 23 deletions packages/core/components/DropZone/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
CSSProperties,
forwardRef,
useCallback,
useContext,
useEffect,
Expand All @@ -26,6 +27,7 @@ import { previewContext } from "../DragDropContext";
import { Draggable, UniqueIdentifier } from "@dnd-kit/abstract";
import { useDroppableSafe } from "../../lib/dnd-kit/safe";
import { useMinEmptyHeight } from "./use-min-empty-height";
import { assignRefs } from "../../lib/assign-refs";

const getClassName = getClassNameFactory("DropZone", styles);

Expand All @@ -44,16 +46,18 @@ export type DropZoneDndData = {
isDroppableTarget: boolean;
};

function DropZoneEdit({
zone,
allow,
disallow,
style,
className,
minEmptyHeight: userMinEmptyHeight = 128,
dragRef,
collisionAxis,
}: DropZoneProps) {
const DropZoneEdit = forwardRef<HTMLDivElement, DropZoneProps>(function (
{
zone,
allow,
disallow,
style,
className,
minEmptyHeight: userMinEmptyHeight = 128,
collisionAxis,
},
userRef
) {
const appContext = useAppContext();
const ctx = useContext(dropZoneContext);

Expand Down Expand Up @@ -288,11 +292,7 @@ function DropZoneEdit({
isAnimating,
})}${className ? ` ${className}` : ""}`}
ref={(node) => {
ref.current = node;

dropRef(node);

if (dragRef) dragRef(node);
assignRefs<HTMLDivElement>([ref, dropRef, userRef], node);
}}
data-testid={`dropzone:${zoneCompound}`}
data-puck-dropzone={zoneCompound}
Expand Down Expand Up @@ -399,9 +399,12 @@ function DropZoneEdit({
})}
</div>
);
}
});

function DropZoneRender({ className, style, zone }: DropZoneProps) {
const DropZoneRender = forwardRef<HTMLDivElement, DropZoneProps>(function (
{ className, style, zone },
ref
) {
const ctx = useContext(dropZoneContext);

const { data, areaId = "root", config } = ctx || {};
Expand All @@ -419,7 +422,7 @@ function DropZoneRender({ className, style, zone }: DropZoneProps) {
}

return (
<div className={className} style={style}>
<div className={className} style={style} ref={ref}>
{content.map((item) => {
const Component = config.components[item.type];

Expand Down Expand Up @@ -447,22 +450,25 @@ function DropZoneRender({ className, style, zone }: DropZoneProps) {
})}
</div>
);
}
});

export function DropZone(props: DropZoneProps) {
export const DropZone = forwardRef<HTMLDivElement, DropZoneProps>(function (
props: DropZoneProps,
ref
) {
const ctx = useContext(dropZoneContext);

if (ctx?.mode === "edit") {
return (
<>
<DropZoneEdit {...props} />
<DropZoneEdit {...props} ref={ref} />
</>
);
}

return (
<>
<DropZoneRender {...props} />
<DropZoneRender {...props} ref={ref} />
</>
);
}
});
1 change: 0 additions & 1 deletion packages/core/components/DropZone/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@ export type DropZoneProps = {
style?: CSSProperties;
minEmptyHeight?: number;
className?: string;
dragRef?: ((element: Element | null) => void) | null;
collisionAxis?: DragAxis;
};
24 changes: 24 additions & 0 deletions packages/core/lib/assign-refs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export type Ref<ElementType = HTMLElement> =
| React.RefObject<ElementType | null>
| React.ForwardedRef<ElementType | null>
| ((element: ElementType | null) => void);

export function assignRef<ElementType = HTMLElement>(
ref: Ref<ElementType>,
node: ElementType | null
) {
if (typeof ref === "function") {
ref(node);
} else if (ref && typeof ref === "object" && "current" in ref) {
ref.current = node;
}
}

export function assignRefs<ElementType = HTMLElement>(
refs: Ref<ElementType>[],
node: ElementType | null
) {
refs.forEach((ref) => {
assignRef<ElementType>(ref, node);
});
}

0 comments on commit 676aa1c

Please sign in to comment.