clauderic / dnd-kit

The modern, lightweight, performant, accessible and extensible drag & drop toolkit for React.
http://dndkit.com
MIT License
12.74k stars 633 forks source link

Resize - is it possible? #1127

Open ghost opened 1 year ago

ghost commented 1 year ago

Hello

Looking to implement a resize functionality using dnd-kit. I already have implemented drag & drop. Is it possilble at all? I would like not to use another library just for resize.

Thanks!

ninode97 commented 1 year ago

Is it possible? (yes)

Is it easy ? (Depends on your knowledge about the library and the requirement of your project)

Simplest approach would be:

Code snippets I have used:

Component.tsx

const { buttonStyles } = useContext(ExampleCtx);
    const leftHandleCtx = useContext(LeftHandleCtx);
    const rightHandleCtx = useContext(RightHandleCtx);

    leftHandleCtx.useButtonState(buttonStyles);
    rightHandleCtx.useButtonState(buttonStyles);

    useEffect(() => {
      const resizeLeftElement = document.getElementById("resize-left");
      const resizeRightElement = document.getElementById("resize-right");

      const handleMouseDownRight = (event: MouseEvent) => {
        event.stopPropagation();
        rightHandleCtx.setResizing(true, event);
      };

      const handleMouseDownLeft = (event: MouseEvent) => {
        event.stopPropagation();
        leftHandleCtx.setResizing(true, event);
      };

      resizeRightElement?.addEventListener("mousedown", handleMouseDownRight);
      resizeLeftElement?.addEventListener("mousedown", handleMouseDownLeft);

      return () => {
        resizeRightElement?.removeEventListener(
          "mousedown",
          handleMouseDownRight
        );
        resizeLeftElement?.removeEventListener(
          "mousedown",
          handleMouseDownLeft
        );
      };
    }, []);

Mobx Store

class LeftHandleStore {
  initialMousePos = 0;
  initialWidth = 0;
  initialMarginLeft = 0;

  constructor() {
    makeAutoObservable(this);

    autorun(() => {
      if (this.isResizing) {
        window.addEventListener("mousemove", this.handleMouseMove);
        window.addEventListener("mouseup", this.handleMouseUp);
      } else {
        window.removeEventListener("mousemove", this.handleMouseMove);
        window.removeEventListener("mouseup", this.handleMouseUp);
      }
    });
  }

  @observable buttonStyles: any;

  @action useButtonState = (buttonStyles: any) => {
    this.buttonStyles = buttonStyles;
  };

  @observable isResizing = false;

  @action setStyle = (key: ButtonStyleKey, val: number) => {
    this.buttonStyles[key] = val;
  };

  @action setResizing = (val: boolean, event?: MouseEvent) => {
    this.isResizing = val;

    if (event) {
      this.initialMousePos = event.clientX;
      this.initialWidth = this.buttonStyles.width;
      this.initialMarginLeft = this.buttonStyles.marginLeft;
    }
  };

  @action handleMouseUp = () => {
    this.setResizing(false);
  };

  @action handleMouseMove = (event: MouseEvent) => {
    if (!this.isResizing) return;

    // Calculate the new width and marginLeft based on mouse movement
    const deltaX = event.clientX - this.initialMousePos;
    const newWidth = this.initialWidth - deltaX;
    const newMarginLeft = this.initialMarginLeft + deltaX;

    // Update the width and marginLeft in the state
    this.setStyle("width", newWidth);
    this.setStyle("marginLeft", newMarginLeft);
  };
}
export const LeftHandleCtx = createContext(new LeftHandleStore());

Hopefully above, gave you any ideas how you could implement resize functionality within your project.

ghost commented 1 year ago

Hello I'm having an hard time understanding your example. Do you have one without contexts and store? Thanks in advance!

nicostombros commented 1 year ago

Hey @diegonogaretti is this still an issue?

ghost commented 1 year ago

Hey @diegonogaretti is this still an issue?

Hi! Yes it's still an issue. It would be awesome if dnd-kit could offer this feature too!

pixelass commented 10 months ago

I needed something similar.

  1. elements need to be draggable
  2. elements need to be resizable in width
  3. elements need to work in scaled context (i.e. transform: scale(0.5) on parent)
  4. elements need to stay in bound when resizing or dragging
  5. elements should remain in bound if dimension of parent changes
  6. element resize needs the same a11y support as dnd-kit (keyboard sensor)

I was able to add all features without any issues. Sadly trhere is no really good library out there that works well in this context so I decided to implement it myself.

Currently I'm handling all aspects myself but I guess it could be refactored to make use of dnd-kit utilities.

If this is still nedded by anyone, I can try to extract the custom Draggable component into a codesandbox. Adding height resize could be added pretty easily, but I needed width only The component itself is rather simple, but my version is slightly complex since I need to handle it in up/down-scaled views

https://github.com/clauderic/dnd-kit/assets/1148334/f1477131-8457-4b7c-88cd-df3e4d7ddafe

CodyBontecou commented 10 months ago

Hey @pixelass that sounds like you built a solution many would find value from. Could you share the source for this component?

pixelass commented 10 months ago

@CodyBontecou Sure, I need to extract it first though. Right now it's still entangled with business logic. But I'll post a sandbox when I cleaned my room 😉

ghost commented 9 months ago

Thanks @pixelass, it would be awesome! Hope this would be included in a future dnd-kit's release aswell. Waiting for you, thanks again!

atrauzzi commented 9 months ago

Likewise, would love to see!

iakovmarkov commented 9 months ago

@pixelass hi! I am going to build a similar thing for a work project - a draggable element that needs to be also resizeable in width. I use dnd-kit for drag-and-drop implementation and wanted to use it for resize as well. Have you had a chance to remove the business logic from your code? It doesn't have to be working and compiling, I'm more interested in general approach.

pixelass commented 9 months ago

Pretty mauch hardcoded into a custom component using joy-ui (MUI successor)

I'm not sure if or how much this helps.

We have 2 custom hooks for keyboard and mouse/touch/pointer handling. We also added a scale factor since we needed this in a sclaed down/up context. It takes care of unscaling the interacive UI elements.

But to KISS, We jsut added a new handle and added the resize there. Most of the logic e.g. bounding box is handled in our hooks (which are also custom to this component) I think it should be rather easy to extract this into a full resize hook, but , you know... time...

function Draggable({
    children,
    id,
    x,
    y,
    width,
    scaleFactor,
}: {
    children?: ReactNode;
    id: string;
    x: number;
    y: number;
    width: number;
    scaleFactor: number;
    modal: ReactElement;
}) {
    const reference = useRef<HTMLDivElement | null>(null);
    const { attributes, listeners, setNodeRef, transform, isDragging } = useDraggable({
        id,
    });
    const resizeActive = useRef(false);

    const handleMouseDown = useResizeHandlers(id, { x, width, scaleFactor, ref: reference });
    const handleKeyDown = useKeyboardHandler(id, {
        x,
        width,
        scaleFactor,
        ref: reference,
        activeRef: resizeActive,
    });

    function handleBlur(event: ReactFocusEvent) {
        resizeActive.current = false;
        (event.currentTarget as HTMLButtonElement)?.setAttribute("aria-pressed", "false");
    }

    const style: CSSProperties & {
        "--boxShadow": string;
    } = {
        transform: transform
            ? CSS.Translate.toString({
                    scaleX: 1,
                    scaleY: 1,
                    x: transform.x / scaleFactor,
                    y: transform.y / scaleFactor,
              })
            : undefined,
        top: y,
        left: x,
        width,
        "--boxShadow": `0 0 0 ${2 / scaleFactor}px ${getCssVariable(`palette-neutral-500`)}`,
    };

    return (
        <Box
            ref={element => {
                setNodeRef(element);
                reference.current = element;
            }}
            style={style}
            sx={{
                position: "absolute",
                display: "flex",
                touchAction: "none",
                ".MuiIconButton-root": {
                    opacity: 0,
                },
                "&:focus-within, &:hover": {
                    boxShadow: "var(--boxShadow)",
                    ".MuiIconButton-root": {
                        opacity: 1,
                    },
                },
            }}
        >
            {/* ... the drag handle and other things that are not content */}
            <Box sx={{ position: "relative", flex: 1, textAlign: "center", width: "100%" }}>
                {children}
                <StyledResizeHandle
                    variant="plain"
                    color="neutral"
                    size="sm"
                    style={{
                        transform: `scaleX(${1 / scaleFactor}) translateX(50%)`,
                    }}
                    onPointerDown={handleMouseDown}
                    onKeyDown={handleKeyDown}
                    onBlur={handleBlur}
                />
            </Box>
        </Box>
    );
}
CodyBontecou commented 8 months ago

ChatGPT to the rescue! I was able to adjust this to be resizable like so:

dndkit-drag-resize

import React, { forwardRef, useCallback, useEffect, useState } from 'react'
import classNames from 'classnames'
import type { DraggableSyntheticListeners } from '@dnd-kit/core'
import type { Transform } from '@dnd-kit/utilities'

import { Handle } from '../Item/components/Handle'

import {
  draggable,
  draggableHorizontal,
  draggableVertical,
} from './draggable-svg'
import styles from './Draggable.module.scss'

export enum Axis {
  All,
  Vertical,
  Horizontal,
}

interface Props {
  axis?: Axis
  dragOverlay?: boolean
  dragging?: boolean
  handle?: boolean
  label?: string
  listeners?: DraggableSyntheticListeners
  style?: React.CSSProperties
  buttonStyle?: React.CSSProperties
  transform?: Transform | null
  resizable?: boolean
}

export const Draggable = forwardRef<HTMLButtonElement, Props>(
  function Draggable(
    {
      axis,
      dragOverlay,
      dragging,
      handle,
      label,
      listeners,
      transform,
      style,
      buttonStyle,
      resizable,
      ...props
    },
    ref
  ) {
    const [size, setSize] = useState({ width: 200, height: 200 })
    const [isResizing, setIsResizing] = useState(false)
    const [startSize, setStartSize] = useState({ width: 0, height: 0 })
    const [startPos, setStartPos] = useState({ x: 0, y: 0 })

    const handleMouseDown = e => {
      e.preventDefault()
      setIsResizing(true)
      setStartSize(size)
      setStartPos({ x: e.clientX, y: e.clientY })
    }

    const handleMouseMove = useCallback(
      e => {
        if (!isResizing) return

        const newWidth = startSize.width + e.clientX - startPos.x
        const newHeight = startSize.height + e.clientY - startPos.y

        setSize({ width: newWidth, height: newHeight })
      },
      [isResizing, startSize, startPos]
    )

    const handleMouseUp = () => {
      setIsResizing(false)
    }

    useEffect(() => {
      if (isResizing) {
        window.addEventListener('mousemove', handleMouseMove)
        window.addEventListener('mouseup', handleMouseUp)
      } else {
        window.removeEventListener('mousemove', handleMouseMove)
        window.removeEventListener('mouseup', handleMouseUp)
      }

      return () => {
        window.removeEventListener('mousemove', handleMouseMove)
        window.removeEventListener('mouseup', handleMouseUp)
      }
    }, [isResizing, handleMouseMove])

    return (
      <div
        className={classNames(
          styles.Draggable,
          dragOverlay && styles.dragOverlay,
          dragging && styles.dragging,
          handle && styles.handle
        )}
        style={
          {
            ...style,
            width: `${size.width}px`,
            height: `${size.height}px`,
            '--translate-x': `${transform?.x ?? 0}px`,
            '--translate-y': `${transform?.y ?? 0}px`,
          } as React.CSSProperties
        }
      >
        <button
          {...props}
          aria-label="Draggable"
          data-cypress="draggable-item"
          {...(handle ? {} : listeners)}
          tabIndex={handle ? -1 : undefined}
          ref={ref}
          style={{
            ...buttonStyle,
            width: `${size.width}px`,
            height: `${size.height}px`,
          }}
        >
          {axis === Axis.Vertical
            ? draggableVertical
            : axis === Axis.Horizontal
            ? draggableHorizontal
            : draggable}
          {handle ? <Handle {...(handle ? listeners : {})} /> : null}
        </button>
        {resizable && (
          <div
            className="resize-handle"
            onMouseDown={handleMouseDown}
            style={{
              position: 'absolute',
              bottom: 0,
              right: 0,
              width: '10px',
              height: '10px',
              backgroundColor: 'grey',
              cursor: 'nwse-resize',
            }}
          ></div>
        )}
      </div>
    )
  }
)
pixelass commented 8 months ago

looks like you're missing touch and keyboard listeners, but nice start. :)

ghost commented 8 months ago

Pretty mauch hardcoded into a custom component using joy-ui (MUI successor)

I'm not sure if or how much this helps.

We have 2 custom hooks for keyboard and mouse/touch/pointer handling. We also added a scale factor since we needed this in a sclaed down/up context. It takes care of unscaling the interacive UI elements.

But to KISS, We jsut added a new handle and added the resize there. Most of the logic e.g. bounding box is handled in our hooks (which are also custom to this component) I think it should be rather easy to extract this into a full resize hook, but , you know... time...

function Draggable({
  children,
  id,
  x,
  y,
  width,
  scaleFactor,
}: {
  children?: ReactNode;
  id: string;
  x: number;
  y: number;
  width: number;
  scaleFactor: number;
  modal: ReactElement;
}) {
  const reference = useRef<HTMLDivElement | null>(null);
  const { attributes, listeners, setNodeRef, transform, isDragging } = useDraggable({
      id,
  });
  const resizeActive = useRef(false);

  const handleMouseDown = useResizeHandlers(id, { x, width, scaleFactor, ref: reference });
  const handleKeyDown = useKeyboardHandler(id, {
      x,
      width,
      scaleFactor,
      ref: reference,
      activeRef: resizeActive,
  });

  function handleBlur(event: ReactFocusEvent) {
      resizeActive.current = false;
      (event.currentTarget as HTMLButtonElement)?.setAttribute("aria-pressed", "false");
  }

  const style: CSSProperties & {
      "--boxShadow": string;
  } = {
      transform: transform
          ? CSS.Translate.toString({
                  scaleX: 1,
                  scaleY: 1,
                  x: transform.x / scaleFactor,
                  y: transform.y / scaleFactor,
            })
          : undefined,
      top: y,
      left: x,
      width,
      "--boxShadow": `0 0 0 ${2 / scaleFactor}px ${getCssVariable(`palette-neutral-500`)}`,
  };

  return (
      <Box
          ref={element => {
              setNodeRef(element);
              reference.current = element;
          }}
          style={style}
          sx={{
              position: "absolute",
              display: "flex",
              touchAction: "none",
              ".MuiIconButton-root": {
                  opacity: 0,
              },
              "&:focus-within, &:hover": {
                  boxShadow: "var(--boxShadow)",
                  ".MuiIconButton-root": {
                      opacity: 1,
                  },
              },
          }}
      >
          {/* ... the drag handle and other things that are not content */}
          <Box sx={{ position: "relative", flex: 1, textAlign: "center", width: "100%" }}>
              {children}
              <StyledResizeHandle
                  variant="plain"
                  color="neutral"
                  size="sm"
                  style={{
                      transform: `scaleX(${1 / scaleFactor}) translateX(50%)`,
                  }}
                  onPointerDown={handleMouseDown}
                  onKeyDown={handleKeyDown}
                  onBlur={handleBlur}
              />
          </Box>
      </Box>
  );
}

Hi @pixelass Could you please share also the useResizeHandlers code? I'm using MUI too btw :) Thanks again for your help!

pixelass commented 8 months ago

@diegonogaretti pretty much hardcoded using Jotai for global state. (BTW we use Joy UI)

This should be made into a reusable component as it currently only allows a single instance. THis was a really lazy implementation which works well for our use-case but does not scale at all.

import { useAtom } from "jotai";
import type {
    MouseEvent as ReactMouseEvent,
    MutableRefObject,
    TouchEvent as ReactTouchEvent,
} from "react";
import { useCallback } from "react";

import { textElementsAtom } from "@/ions/atoms";

export function useResizeHandlers(
    id: string,
    {
        scaleFactor,
        x,
        width,
        ref,
    }: {
        scaleFactor: number;
        x: number;
        width: number;
        ref: MutableRefObject<HTMLDivElement | null>;
    }
) {
    const [, setTextElements] = useAtom(textElementsAtom);

    return useCallback(
        (event: ReactMouseEvent | ReactTouchEvent) => {
            const startX = Object.hasOwn(event, "touches")
                ? (event as ReactTouchEvent).touches[0].pageX
                : (event as ReactMouseEvent).pageX;

            function handleTouchMove(event: TouchEvent) {
                // Calculate the difference
                const { pageX } = event.touches[0];
                const deltaX = pageX - startX;
                const parentWidth = ref.current?.parentElement?.offsetWidth ?? width;
                const maxWidth = parentWidth - x;

                // Calculate the new size
                const newWidth = Math.min(maxWidth, width + deltaX / scaleFactor);

                // Update the size state
                setTextElements(previousState =>
                    previousState.map(textElement =>
                        textElement.id === id ? { ...textElement, width: newWidth } : textElement
                    )
                );
            }

            function handleMouseMove(event: MouseEvent) {
                // Calculate the difference
                const { pageX } = event;
                const deltaX = pageX - startX;
                const parentWidth = ref.current?.parentElement?.offsetWidth ?? width;
                const maxWidth = parentWidth - x;

                // Calculate the new size
                const newWidth = Math.min(maxWidth, width + deltaX / scaleFactor);

                // Update the size state
                setTextElements(previousState =>
                    previousState.map(textElement =>
                        textElement.id === id ? { ...textElement, width: newWidth } : textElement
                    )
                );
            }

            function handleMouseUp() {
                // Remove the event listeners when the mouse is released
                document.removeEventListener("touchmove", handleTouchMove);
                document.removeEventListener("touchend", handleMouseUp);
                document.removeEventListener("mousemove", handleMouseMove);
                document.removeEventListener("mouseup", handleMouseUp);
            }

            // Add the event listeners to the document
            document.addEventListener("mousemove", handleMouseMove, { passive: true });
            document.addEventListener("touchmove", handleTouchMove, { passive: true });
            document.addEventListener("mouseup", handleMouseUp);
            document.addEventListener("touchend", handleMouseUp);
        },
        [id, ref, scaleFactor, setTextElements, width, x]
    );
}
pixelass commented 8 months ago

The same counts for the keyboardhandlers: Very hardcoded.

It aims to mimic dnd-kit's sensor behavior

import { useAtom } from "jotai";
import type { KeyboardEvent as ReactKeyboardEvent, MutableRefObject } from "react";
import { useCallback } from "react";

import { textElementsAtom } from "@/ions/atoms";

export function useKeyboardHandler(
    id: string,
    {
        scaleFactor,
        x,
        width,
        ref,
        activeRef,
    }: {
        x: number;
        width: number;
        scaleFactor: number;
        activeRef: MutableRefObject<boolean>;
        ref: MutableRefObject<HTMLDivElement | null>;
    }
) {
    const [, setTextElements] = useAtom(textElementsAtom);

    return useCallback(
        (event: ReactKeyboardEvent<HTMLButtonElement>) => {
            if (["Space", "Enter"].includes(event.code)) {
                activeRef.current = !activeRef.current;

                (event.currentTarget as HTMLButtonElement)?.setAttribute(
                    "aria-pressed",
                    activeRef.current ? "true" : "false"
                );
            } else if (["ArrowLeft", "ArrowRight"].includes(event.code) && activeRef.current) {
                const parentWidth = ref.current?.parentElement?.offsetWidth ?? width;
                const maxWidth = parentWidth - x;

                switch (event.code) {
                    case "ArrowLeft": {
                        setTextElements(previousState =>
                            previousState.map(textElement =>
                                textElement.id === id
                                    ? {
                                            ...textElement,
                                            width: Math.max(
                                                0,
                                                textElement.width! - 10 / scaleFactor
                                            ),
                                      }
                                    : textElement
                            )
                        );
                        break;
                    }

                    case "ArrowRight": {
                        setTextElements(previousState =>
                            previousState.map(textElement =>
                                textElement.id === id
                                    ? {
                                            ...textElement,
                                            width: Math.min(
                                                maxWidth,
                                                textElement.width! + 10 / scaleFactor
                                            ),
                                      }
                                    : textElement
                            )
                        );
                        break;
                    }

                    default: {
                        break;
                    }
                }
            }
        },
        [activeRef, id, ref, scaleFactor, setTextElements, width, x]
    );
}
ghost commented 8 months ago

Thanks a lot @pixelass!

iakovmarkov commented 8 months ago

Awesome code samples, thanks a lot for a good starting point for me @pixelass!

KuzonFyre commented 6 months ago

@pixelass @CodyBontecou I am wondering if it would be better to use the built in CSS resize property. Then their is the resizeObserver property which you could use to identify when to change the size of the element vs when to drag the element.

ChatGPT to the rescue! I was able to adjust this to be resizable like so:

dndkit-drag-resize dndkit-drag-resize

import React, { forwardRef, useCallback, useEffect, useState } from 'react'
import classNames from 'classnames'
import type { DraggableSyntheticListeners } from '@dnd-kit/core'
import type { Transform } from '@dnd-kit/utilities'

import { Handle } from '../Item/components/Handle'

import {
  draggable,
  draggableHorizontal,
  draggableVertical,
} from './draggable-svg'
import styles from './Draggable.module.scss'

export enum Axis {
  All,
  Vertical,
  Horizontal,
}

interface Props {
  axis?: Axis
  dragOverlay?: boolean
  dragging?: boolean
  handle?: boolean
  label?: string
  listeners?: DraggableSyntheticListeners
  style?: React.CSSProperties
  buttonStyle?: React.CSSProperties
  transform?: Transform | null
  resizable?: boolean
}

export const Draggable = forwardRef<HTMLButtonElement, Props>(
  function Draggable(
    {
      axis,
      dragOverlay,
      dragging,
      handle,
      label,
      listeners,
      transform,
      style,
      buttonStyle,
      resizable,
      ...props
    },
    ref
  ) {
    const [size, setSize] = useState({ width: 200, height: 200 })
    const [isResizing, setIsResizing] = useState(false)
    const [startSize, setStartSize] = useState({ width: 0, height: 0 })
    const [startPos, setStartPos] = useState({ x: 0, y: 0 })

    const handleMouseDown = e => {
      e.preventDefault()
      setIsResizing(true)
      setStartSize(size)
      setStartPos({ x: e.clientX, y: e.clientY })
    }

    const handleMouseMove = useCallback(
      e => {
        if (!isResizing) return

        const newWidth = startSize.width + e.clientX - startPos.x
        const newHeight = startSize.height + e.clientY - startPos.y

        setSize({ width: newWidth, height: newHeight })
      },
      [isResizing, startSize, startPos]
    )

    const handleMouseUp = () => {
      setIsResizing(false)
    }

    useEffect(() => {
      if (isResizing) {
        window.addEventListener('mousemove', handleMouseMove)
        window.addEventListener('mouseup', handleMouseUp)
      } else {
        window.removeEventListener('mousemove', handleMouseMove)
        window.removeEventListener('mouseup', handleMouseUp)
      }

      return () => {
        window.removeEventListener('mousemove', handleMouseMove)
        window.removeEventListener('mouseup', handleMouseUp)
      }
    }, [isResizing, handleMouseMove])

    return (
      <div
        className={classNames(
          styles.Draggable,
          dragOverlay && styles.dragOverlay,
          dragging && styles.dragging,
          handle && styles.handle
        )}
        style={
          {
            ...style,
            width: `${size.width}px`,
            height: `${size.height}px`,
            '--translate-x': `${transform?.x ?? 0}px`,
            '--translate-y': `${transform?.y ?? 0}px`,
          } as React.CSSProperties
        }
      >
        <button
          {...props}
          aria-label="Draggable"
          data-cypress="draggable-item"
          {...(handle ? {} : listeners)}
          tabIndex={handle ? -1 : undefined}
          ref={ref}
          style={{
            ...buttonStyle,
            width: `${size.width}px`,
            height: `${size.height}px`,
          }}
        >
          {axis === Axis.Vertical
            ? draggableVertical
            : axis === Axis.Horizontal
            ? draggableHorizontal
            : draggable}
          {handle ? <Handle {...(handle ? listeners : {})} /> : null}
        </button>
        {resizable && (
          <div
            className="resize-handle"
            onMouseDown={handleMouseDown}
            style={{
              position: 'absolute',
              bottom: 0,
              right: 0,
              width: '10px',
              height: '10px',
              backgroundColor: 'grey',
              cursor: 'nwse-resize',
            }}
          ></div>
        )}
      </div>
    )
  }
)

The resize handle is absolute which means it moves away from the actual element. @CodyBontecou did you find a better way to render the resize component in the bottom corner?

pixelass commented 6 months ago

@KuzonFyre My implementation was a quick MVP which only requires resizing on the x axis. Please don't see my code as a reusable or recommended approach. My main focus was an accessible interface to solve ONE problem.

awesomezao commented 5 months ago

20240430175003_rec_ I use re-resizable and dnd-kit to get the effect you want

MasterRedStorm commented 4 months ago

So should the recommended approach be to use re-resizable?

priyath commented 3 months ago

20240430175003_rec_ I use re-resizable and dnd-kit to get the effect you want

This looks neat. Any chance you can share some code on how to get this to work? Having some trouble figuring out ref binding and which component to wrap with which. thanks!

pixelass commented 3 months ago

So should the recommended approach be to use re-resizable?

I chose to not use it because of its missing a11y support (no-keyboard interaction). Besides that it it seems to be a solid solution.

RikidWai commented 1 month ago

Simplest approach would be:

@awesomezao Mind sharing your code please?

MeteorsLiu commented 1 month ago

I use re-resizable and dnd-kit to get the effect you want

Can you share the code? I try re-resizeable, it doesn't work for me.