Plus · DragMove

DragMove

Set elements to change their position by dragging

When to use

It is used to set the element that can be dragged to change its position. It supports limiting the drag range and customizing the elements that trigger dragging.

Demos

How to introduce

<script lang="ts">
    import { dragMove } from '@fibrae/semi-ui-svelte';
</script>

Basic usage

In Svelte, DragMove is the dragMove attachment. Write {@attach dragMove(options)} on an element and that element can be dragged to change its position.

Notice

  • DragMove uses absolute positioning by default. Set positionStrategy="relative" when the element should keep its original layout position, such as a centered Modal
  • dragMove is an attachment, not a component: it makes the element it is written on draggable, and each element needs its own attachment. It writes position, top, left, cursor and touch-action inline, so give the element style: directives rather than a reactive style attribute, which would overwrite them. The options are read when the attachment runs; passing a new options object runs it again and ends a drag in progress
Drag me
Code
<script lang="ts">
    import { dragMove } from '@fibrae/semi-ui-svelte';
</script>

<!-- dragMove positions the element absolutely, so the wrapper keeps room for it in the preview -->
<div style="height: 120px">
    <div {@attach dragMove()} style:align-items="center" style:background-color="var(--semi-color-primary)" style:border-radius="10px" style:color="var(--semi-white)" style:display="flex" style:font-weight="500" style:height="80px" style:justify-content="center" style:position="absolute" style:width="80px">
        Drag me
    </div>
</div>

Limit drag range

Pass in constrainer, either 'parent' or a function that returns the element limiting the draggable range.

Note: The elements returned by the constrainer need to be positioned relative

Constrainer
Drag me
Code
<script lang="ts">
    import { dragMove } from '@fibrae/semi-ui-svelte';

    let container = $state<HTMLDivElement>();
</script>

<div bind:this={container} style="background-color: var(--semi-grey-2); width: 300px; height: 300px; padding: 5px; position: relative; color: var(--semi-white); font-weight: 500">
    <span style="color: var(--semi-color-text-0)">Constrainer</span>
    <div
        {@attach dragMove({ constrainer: () => container ?? null })}
        style:align-items="center"
        style:background-color="var(--semi-color-primary)"
        style:border-radius="10px"
        style:display="flex"
        style:height="80px"
        style:justify-content="center"
        style:left="80px"
        style:position="absolute"
        style:top="80px"
        style:width="80px"
    >
        Drag me
    </div>
</div>

Customize elements that trigger dragging

Passing in handler, this function returns the element that triggered the drag. If not set, you can click anywhere to drag; if set, only the part of the element returned by the handler can be dragged.

Constrainer
Code
<script lang="ts">
    import { IconTransparentStroked } from '@fibrae/semi-icons-svelte';
    import { dragMove } from '@fibrae/semi-ui-svelte';

    let container = $state<HTMLDivElement>();
    let handlerEl = $state<HTMLDivElement>();
</script>

<div bind:this={container} style="background-color: var(--semi-grey-2); width: 300px; height: 300px; padding: 5px; position: relative; color: var(--semi-white); font-weight: 500">
    <span style="color: var(--semi-color-text-0)">Constrainer</span>
    <div
        {@attach dragMove({ constrainer: () => container ?? null, handler: () => handlerEl ?? null })}
        style:align-items="center"
        style:background-color="var(--semi-color-primary)"
        style:border-radius="10px"
        style:display="flex"
        style:height="80px"
        style:justify-content="center"
        style:left="50px"
        style:position="absolute"
        style:top="50px"
        style:width="80px"
    >
        <div bind:this={handlerEl} style="width: fit-content; height: fit-content">
            <IconTransparentStroked size="large" />
        </div>
    </div>
</div>

Customize position processing after dragging

You can customize the position processing after dragging through customMove. After this parameter is set, the DragMove component will only return the calculated position through the parameters without setting it. The user can set the new position as needed.

蓝色色块点击可改变宽度,改变前后蓝色色块均不会超出范围限制

Constrainer
Code
<script lang="ts">
    import { dragMove } from '@fibrae/semi-ui-svelte';

    let container = $state<HTMLDivElement>();
    let startPoint: { x: number; y: number } | null = null;

    const customMove = (element: HTMLElement, top: number, left: number) => {
        if (container && left + 100 > container.offsetWidth) {
            element.style.right = `${container.offsetWidth - left - element.offsetWidth}px`;
            element.style.left = 'auto';
        } else {
            element.style.left = left + 'px';
        }
        element.style.top = top + 'px';
    };

    const toggleWidth = (element: HTMLElement) => {
        element.style.width = element.style.width === '60px' ? '100px' : '60px';
    };

    const onmousedown = (e: MouseEvent) => {
        startPoint = { x: e.clientX, y: e.clientY };
    };

    const onmouseup = (e: MouseEvent) => {
        if (startPoint && Math.abs(e.clientX - startPoint.x) < 5 && Math.abs(e.clientY - startPoint.y) < 5) {
            toggleWidth(e.currentTarget as HTMLElement);
        }
        startPoint = null;
    };

    const onkeydown = (e: KeyboardEvent) => {
        if (e.key === 'Enter') {
            toggleWidth(e.currentTarget as HTMLElement);
        }
    };
</script>

<span>蓝色色块点击可改变宽度,改变前后蓝色色块均不会超出范围限制 </span>
<br />
<br />
<div bind:this={container} style="background-color: var(--semi-grey-2); width: 300px; height: 300px; position: relative; padding: 10px; color: var(--semi-white); font-weight: 500">
    <span style="color: var(--semi-color-text-0)">Constrainer</span>
    <button
        {@attach dragMove({ constrainer: () => container ?? null, customMove })}
        style:align-items="center"
        style:background-color="var(--semi-color-primary)"
        style:border="none"
        style:border-radius="10px"
        style:box-sizing="content-box"
        style:color="inherit"
        style:display="flex"
        style:font="inherit"
        style:height="50px"
        style:justify-content="center"
        style:left="50px"
        style:padding="5px"
        style:position="absolute"
        style:top="50px"
        style:width="60px"
        type="button"
        {onkeydown}
        {onmousedown}
        {onmouseup}
    >
        Drag me
    </button>
</div>

API

dragMove(options?: DragMoveOptions) returns an Attachment<HTMLElement>. DragMoveOptions is exported from @fibrae/semi-ui-svelte.

PropertyDescriptionTypeDefault
allowInputDragWhether dragging is allowed when pressing on a native input or textarea.booleanfalse
allowMoveFunction that decides whether a drag may start on pointer down; returning false prevents it.(e: PointerEvent, element: HTMLElement) => boolean-
constrainerThe element that limits the draggable range: 'parent' for the parent element, or a function that returns it. It needs to be positioned relatively.'parent' | (() => HTMLElement | null)-
customMoveCustom position handling after dragging. When set, only the calculated position is passed back and the element's top and left are no longer written.(element: HTMLElement, top: number, left: number) => void-
handlerThe element that triggers dragging, or a function that returns it. Defaults to the dragged element itself.HTMLElement | (() => HTMLElement | null)-
onMouseDownCalled when a mouse or pen pointer is pressed.(e: PointerEvent) => void-
onMouseMoveCalled when a mouse or pen pointer moves.(e: PointerEvent) => void-
onMouseUpCalled when a mouse or pen pointer is released.(e: PointerEvent) => void-
onTouchCancelCalled when the pointer is cancelled by the system (pointercancel).(e: PointerEvent) => void-
onTouchEndCalled when a touch pointer is released.(e: PointerEvent) => void-
onTouchMoveCalled when a touch pointer moves.(e: PointerEvent) => void-
onTouchStartCalled when a touch pointer is pressed.(e: PointerEvent) => void-
positionStrategyPositioning strategy. relative keeps the element's original layout position.'absolute' | 'relative''absolute'