diff --git a/packages/core/components/DropZone/index.tsx b/packages/core/components/DropZone/index.tsx index 8a0c160f9..0b93cc935 100644 --- a/packages/core/components/DropZone/index.tsx +++ b/packages/core/components/DropZone/index.tsx @@ -46,429 +46,429 @@ export type DropZoneDndData = { isDroppableTarget: boolean; }; -const DropZoneEdit = forwardRef(function ( - { - zone, - allow, - disallow, - style, - className, - minEmptyHeight: userMinEmptyHeight = 128, - collisionAxis, - }, - userRef -) { - const appContext = useAppContext(); - const ctx = useContext(dropZoneContext); - - const { - // These all need setting via context - data, - config, - areaId, - draggedItem, - registerZoneArea, - depth, - registerLocalZone, - unregisterLocalZone, - deepestZone = rootDroppableId, - deepestArea, - nextDeepestArea, - path = [], - activeZones, - } = ctx!; - - const { itemSelector } = appContext.state.ui; - - let zoneCompound = rootDroppableId; - - useEffect(() => { - if (areaId && registerZoneArea) { - registerZoneArea(areaId); - } - }, [areaId]); - - // Register and unregister zone on mount - useEffect(() => { - if (ctx?.registerZone) { - ctx?.registerZone(zoneCompound); - } +const DropZoneEdit = forwardRef( + function DropZoneEdit( + { + zone, + allow, + disallow, + style, + className, + minEmptyHeight: userMinEmptyHeight = 128, + collisionAxis, + }, + userRef + ) { + const appContext = useAppContext(); + const ctx = useContext(dropZoneContext); + + const { + // These all need setting via context + data, + config, + areaId, + draggedItem, + registerZoneArea, + depth, + registerLocalZone, + unregisterLocalZone, + deepestZone = rootDroppableId, + deepestArea, + nextDeepestArea, + path = [], + activeZones, + } = ctx!; + + const { itemSelector } = appContext.state.ui; + + let zoneCompound = rootDroppableId; + + useEffect(() => { + if (areaId && registerZoneArea) { + registerZoneArea(areaId); + } + }, [areaId]); - return () => { - if (ctx?.unregisterZone) { - ctx?.unregisterZone(zoneCompound); + // Register and unregister zone on mount + useEffect(() => { + if (ctx?.registerZone) { + ctx?.registerZone(zoneCompound); } - }; - }, []); - if (areaId) { - if (zone !== rootDroppableId) { - zoneCompound = `${areaId}:${zone}`; - } - } + return () => { + if (ctx?.unregisterZone) { + ctx?.unregisterZone(zoneCompound); + } + }; + }, []); - const content = useMemo(() => { - if (areaId && zone !== rootDroppableId) { - return setupZone(data, zoneCompound).zones[zoneCompound]; + if (areaId) { + if (zone !== rootDroppableId) { + zoneCompound = `${areaId}:${zone}`; + } } - return data.content || []; - }, [data, zoneCompound]); + const content = useMemo(() => { + if (areaId && zone !== rootDroppableId) { + return setupZone(data, zoneCompound).zones[zoneCompound]; + } - const ref = useRef(null); + return data.content || []; + }, [data, zoneCompound]); - const acceptsTarget = useCallback( - (target: Draggable | undefined | null) => { - if (!target) { - return true; - } + const ref = useRef(null); - const data = target.data as ComponentDndData; + const acceptsTarget = useCallback( + (target: Draggable | undefined | null) => { + if (!target) { + return true; + } - const { componentType } = data; + const data = target.data as ComponentDndData; - if (disallow) { - const defaultedAllow = allow || []; + const { componentType } = data; - // remove any explicitly allowed items from disallow - const filteredDisallow = (disallow || []).filter( - (item) => defaultedAllow.indexOf(item) === -1 - ); + if (disallow) { + const defaultedAllow = allow || []; - if (filteredDisallow.indexOf(componentType) !== -1) { - return false; - } - } else if (allow) { - if (allow.indexOf(componentType) === -1) { - return false; - } - } + // remove any explicitly allowed items from disallow + const filteredDisallow = (disallow || []).filter( + (item) => defaultedAllow.indexOf(item) === -1 + ); - return true; - }, - [allow, disallow] - ); + if (filteredDisallow.indexOf(componentType) !== -1) { + return false; + } + } else if (allow) { + if (allow.indexOf(componentType) === -1) { + return false; + } + } - useEffect(() => { - if (registerLocalZone) { - registerLocalZone(zoneCompound, acceptsTarget(draggedItem)); - } + return true; + }, + [allow, disallow] + ); - return () => { - if (unregisterLocalZone) { - unregisterLocalZone(zoneCompound); + useEffect(() => { + if (registerLocalZone) { + registerLocalZone(zoneCompound, acceptsTarget(draggedItem)); } - }; - }, [draggedItem, zoneCompound]); - const isRootZone = - zoneCompound === rootDroppableId || - zone === rootDroppableId || - areaId === "root"; + return () => { + if (unregisterLocalZone) { + unregisterLocalZone(zoneCompound); + } + }; + }, [draggedItem, zoneCompound]); - const hoveringOverArea = nextDeepestArea - ? nextDeepestArea === areaId - : isRootZone; + const isRootZone = + zoneCompound === rootDroppableId || + zone === rootDroppableId || + areaId === "root"; - const userIsDragging = !!draggedItem; + const hoveringOverArea = nextDeepestArea + ? nextDeepestArea === areaId + : isRootZone; - let isEnabled = true; + const userIsDragging = !!draggedItem; - if (draggedItem) { - isEnabled = deepestZone === zoneCompound; - } + let isEnabled = true; - if (isEnabled) { - isEnabled = acceptsTarget(draggedItem); - } + if (draggedItem) { + isEnabled = deepestZone === zoneCompound; + } - const preview = useContext(previewContext); + if (isEnabled) { + isEnabled = acceptsTarget(draggedItem); + } - const previewInZone = preview?.zone === zoneCompound; + const preview = useContext(previewContext); - const contentWithPreview = useMemo(() => { - let contentWithPreview = preview - ? content.filter((item) => item.props.id !== preview.props.id) - : content; + const previewInZone = preview?.zone === zoneCompound; - if (previewInZone) { - contentWithPreview = content.filter( - (item) => item.props.id !== preview.props.id - ); + const contentWithPreview = useMemo(() => { + let contentWithPreview = preview + ? content.filter((item) => item.props.id !== preview.props.id) + : content; - if (preview.type === "insert") { - contentWithPreview = insert(contentWithPreview, preview.index, { - type: "preview", - props: { id: preview.props.id }, - }); - } else { - contentWithPreview = insert(contentWithPreview, preview.index, { - type: preview.componentType, - props: preview.props, - }); - } - } + if (previewInZone) { + contentWithPreview = content.filter( + (item) => item.props.id !== preview.props.id + ); - return contentWithPreview; - }, [preview, content]); - - const isDropEnabled = - isEnabled && - (previewInZone - ? contentWithPreview.length === 1 - : contentWithPreview.length === 0); - - const droppableConfig: UseDroppableInput = { - id: zoneCompound, - collisionPriority: isEnabled ? depth : 0, - disabled: !isDropEnabled, - collisionDetector: pointerIntersection, - type: "dropzone", - data: { - areaId, - depth, - isDroppableTarget: acceptsTarget(draggedItem), - path, - }, - }; - - const { ref: dropRef } = useDroppableSafe(droppableConfig); - - const selectedItem = itemSelector ? getItem(itemSelector, data) : null; - const isAreaSelected = selectedItem && areaId === selectedItem.props.id; - - const [dragAxis, setDragAxis] = useState( - collisionAxis || DEFAULT_DRAG_AXIS - ); - - const calculateDragAxis = useCallback(() => { - if (ref.current) { - const computedStyle = window.getComputedStyle(ref.current); - - if (computedStyle.display === "grid") { - setDragAxis(GRID_DRAG_AXIS); - } else if ( - computedStyle.display === "flex" && - computedStyle.flexDirection === "row" - ) { - setDragAxis(FLEX_ROW_DRAG_AXIS); - } else { - setDragAxis(DEFAULT_DRAG_AXIS); + if (preview.type === "insert") { + contentWithPreview = insert(contentWithPreview, preview.index, { + type: "preview", + props: { id: preview.props.id }, + }); + } else { + contentWithPreview = insert(contentWithPreview, preview.index, { + type: preview.componentType, + props: preview.props, + }); + } } - } - }, [ref.current]); - useEffect(calculateDragAxis, [appContext.status, collisionAxis]); - - useEffect(() => { - const onViewportChange = () => { - calculateDragAxis(); + return contentWithPreview; + }, [preview, content]); + + const isDropEnabled = + isEnabled && + (previewInZone + ? contentWithPreview.length === 1 + : contentWithPreview.length === 0); + + const droppableConfig: UseDroppableInput = { + id: zoneCompound, + collisionPriority: isEnabled ? depth : 0, + disabled: !isDropEnabled, + collisionDetector: pointerIntersection, + type: "dropzone", + data: { + areaId, + depth, + isDroppableTarget: acceptsTarget(draggedItem), + path, + }, }; - window.addEventListener("viewportchange", onViewportChange); + const { ref: dropRef } = useDroppableSafe(droppableConfig); - return () => { - window.removeEventListener("viewportchange", onViewportChange); - }; - }, []); - - const [minEmptyHeight, isAnimating] = useMinEmptyHeight({ - draggedItem, - zoneCompound, - userMinEmptyHeight, - ref, - }); - - return ( -
0, - isActive: activeZones?.[zoneCompound], - isAnimating, - })}${className ? ` ${className}` : ""}`} - ref={(node) => { - assignRefs([ref, dropRef, userRef], node); - }} - data-testid={`dropzone:${zoneCompound}`} - data-puck-dropzone={zoneCompound} - data-puck-dnd={zoneCompound} - style={ - { - ...style, - "--min-empty-height": `${minEmptyHeight}px`, - } as CSSProperties - } - > - {isRootZone && DEBUG && ( -
-

{deepestZone || rootDroppableId}

-

{deepestArea || "No area"}

-
- )} - - {contentWithPreview.map((item, i) => { - const componentId = item.props.id; - - const puckProps: PuckContext = { - renderDropZone: DropZone, - isEditing: true, - dragRef: null, - }; - - const defaultedProps = { - ...config.components[item.type]?.defaultProps, - ...item.props, - puck: puckProps, - editMode: true, // DEPRECATED - }; - - const isSelected = selectedItem?.props.id === componentId || false; - - let Render = config.components[item.type] - ? config.components[item.type].render - : () => ( -
- No configuration for {item.type} -
- ); + const selectedItem = itemSelector ? getItem(itemSelector, data) : null; + const isAreaSelected = selectedItem && areaId === selectedItem.props.id; - const componentConfig: ComponentConfig | undefined = - config.components[item.type]; + const [dragAxis, setDragAxis] = useState( + collisionAxis || DEFAULT_DRAG_AXIS + ); + + const calculateDragAxis = useCallback(() => { + if (ref.current) { + const computedStyle = window.getComputedStyle(ref.current); + + if (computedStyle.display === "grid") { + setDragAxis(GRID_DRAG_AXIS); + } else if ( + computedStyle.display === "flex" && + computedStyle.flexDirection === "row" + ) { + setDragAxis(FLEX_ROW_DRAG_AXIS); + } else { + setDragAxis(DEFAULT_DRAG_AXIS); + } + } + }, [ref.current]); - let componentType = item.type as string; + useEffect(calculateDragAxis, [appContext.status, collisionAxis]); - let label = componentConfig?.["label"] ?? item.type.toString(); + useEffect(() => { + const onViewportChange = () => { + calculateDragAxis(); + }; - if (item.type === "preview") { - componentType = preview!.componentType; + window.addEventListener("viewportchange", onViewportChange); - label = - config.components[componentType]?.label ?? preview!.componentType; + return () => { + window.removeEventListener("viewportchange", onViewportChange); + }; + }, []); - function Preview() { - return ; - } + const [minEmptyHeight, isAnimating] = useMinEmptyHeight({ + draggedItem, + zoneCompound, + userMinEmptyHeight, + ref, + }); - Render = Preview; + return ( +
0, + isActive: activeZones?.[zoneCompound], + isAnimating, + })}${className ? ` ${className}` : ""}`} + ref={(node) => { + assignRefs([ref, dropRef, userRef], node); + }} + data-testid={`dropzone:${zoneCompound}`} + data-puck-dropzone={zoneCompound} + data-puck-dnd={zoneCompound} + style={ + { + ...style, + "--min-empty-height": `${minEmptyHeight}px`, + } as CSSProperties } + > + {isRootZone && DEBUG && ( +
+

{deepestZone || rootDroppableId}

+

{deepestArea || "No area"}

+
+ )} + + {contentWithPreview.map((item, i) => { + const componentId = item.props.id; + + const puckProps: PuckContext = { + renderDropZone: DropZone, + isEditing: true, + dragRef: null, + }; + + const defaultedProps = { + ...config.components[item.type]?.defaultProps, + ...item.props, + puck: puckProps, + editMode: true, // DEPRECATED + }; + + const isSelected = selectedItem?.props.id === componentId || false; + + let Render = config.components[item.type] + ? config.components[item.type].render + : () => ( +
+ No configuration for {item.type} +
+ ); + + const componentConfig: ComponentConfig | undefined = + config.components[item.type]; + + let componentType = item.type as string; + + let label = componentConfig?.["label"] ?? item.type.toString(); + + if (item.type === "preview") { + componentType = preview!.componentType; + + label = + config.components[componentType]?.label ?? preview!.componentType; + + function Preview() { + return ; + } + + Render = Preview; + } - return ( - - 0 - } - isSelected={isSelected} - label={label} - isEnabled={isEnabled} - autoDragAxis={dragAxis} - userDragAxis={collisionAxis} - inDroppableZone={acceptsTarget(draggedItem)} + return ( + - {(dragRef) => - componentConfig?.inline ? ( - - ) : ( -
- -
- ) - } -
-
- ); - })} -
- ); -}); + 0 + } + isSelected={isSelected} + label={label} + isEnabled={isEnabled} + autoDragAxis={dragAxis} + userDragAxis={collisionAxis} + inDroppableZone={acceptsTarget(draggedItem)} + > + {(dragRef) => + componentConfig?.inline ? ( + + ) : ( +
+ +
+ ) + } +
+ + ); + })} +
+ ); + } +); -const DropZoneRender = forwardRef(function ( - { className, style, zone }, - ref -) { - const ctx = useContext(dropZoneContext); +const DropZoneRender = forwardRef( + function DropZoneRender({ className, style, zone }, ref) { + const ctx = useContext(dropZoneContext); - const { data, areaId = "root", config } = ctx || {}; + const { data, areaId = "root", config } = ctx || {}; - let zoneCompound = rootDroppableId; - let content = data?.content || []; + let zoneCompound = rootDroppableId; + let content = data?.content || []; - if (!data || !config) { - return null; - } + if (!data || !config) { + return null; + } - if (areaId && zone && zone !== rootDroppableId) { - zoneCompound = `${areaId}:${zone}`; - content = setupZone(data, zoneCompound).zones[zoneCompound]; - } + if (areaId && zone && zone !== rootDroppableId) { + zoneCompound = `${areaId}:${zone}`; + content = setupZone(data, zoneCompound).zones[zoneCompound]; + } - return ( -
- {content.map((item) => { - const Component = config.components[item.type]; + return ( +
+ {content.map((item) => { + const Component = config.components[item.type]; + + if (Component) { + return ( + + + + ); + } - if (Component) { - return ( - - - - ); - } + return null; + })} +
+ ); + } +); - return null; - })} -
- ); -}); +export const DropZone = forwardRef( + function DropZone(props: DropZoneProps, ref) { + const ctx = useContext(dropZoneContext); -export const DropZone = forwardRef(function ( - props: DropZoneProps, - ref -) { - const ctx = useContext(dropZoneContext); + if (ctx?.mode === "edit") { + return ( + <> + + + ); + } - if (ctx?.mode === "edit") { return ( <> - + ); } - - return ( - <> - - - ); -}); +);