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
dragMoveis an attachment, not a component: it makes the element it is written on draggable, and each element needs its own attachment. It writesposition,top,left,cursorandtouch-actioninline, so give the elementstyle:directives rather than a reactivestyleattribute, 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
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
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.
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.
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.
| Property | Description | Type | Default |
|---|---|---|---|
| allowInputDrag | Whether dragging is allowed when pressing on a native input or textarea. | boolean | false |
| allowMove | Function that decides whether a drag may start on pointer down; returning false prevents it. | (e: PointerEvent, element: HTMLElement) => boolean | - |
| constrainer | The 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) | - |
| customMove | Custom 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 | - |
| handler | The element that triggers dragging, or a function that returns it. Defaults to the dragged element itself. | HTMLElement | (() => HTMLElement | null) | - |
| onMouseDown | Called when a mouse or pen pointer is pressed. | (e: PointerEvent) => void | - |
| onMouseMove | Called when a mouse or pen pointer moves. | (e: PointerEvent) => void | - |
| onMouseUp | Called when a mouse or pen pointer is released. | (e: PointerEvent) => void | - |
| onTouchCancel | Called when the pointer is cancelled by the system (pointercancel). | (e: PointerEvent) => void | - |
| onTouchEnd | Called when a touch pointer is released. | (e: PointerEvent) => void | - |
| onTouchMove | Called when a touch pointer moves. | (e: PointerEvent) => void | - |
| onTouchStart | Called when a touch pointer is pressed. | (e: PointerEvent) => void | - |
| positionStrategy | Positioning strategy. relative keeps the element's original layout position. | 'absolute' | 'relative' | 'absolute' |