@neodrag/svelte
Version:
Svelte Action to add dragging to your apps 😉
285 lines (282 loc) • 11.1 kB
TypeScript
type DragBoundsCoords = {
/** Number of pixels from left of the document */
left: number;
/** Number of pixels from top of the document */
top: number;
/** Number of pixels from the right side of document */
right: number;
/** Number of pixels from the bottom of the document */
bottom: number;
};
type DragAxis = 'both' | 'x' | 'y' | 'none';
type DragBounds = HTMLElement | Partial<DragBoundsCoords> | 'parent' | 'body' | (string & Record<never, never>);
type DragEventData = {
/** How much element moved from its original position horizontally */
offsetX: number;
/** How much element moved from its original position vertically */
offsetY: number;
/** The node on which the draggable is applied */
rootNode: HTMLElement;
/** The element being dragged */
currentNode: HTMLElement;
/** The pointer event that triggered the drag */
event: PointerEvent;
};
type DragOptions = {
/**
* Optionally limit the drag area
*
* Accepts `parent` as prefixed value, and limits it to its parent.
*
* Or, you can specify any selector and it will be bound to that.
*
* **Note**: We don't check whether the selector is bigger than the node element.
* You yourself will have to make sure of that, or it may lead to strange behavior
*
* Or, finally, you can pass an object of type `{ top: number; right: number; bottom: number; left: number }`.
* These mimic the css `top`, `right`, `bottom` and `left`, in the sense that `bottom` starts from the bottom of the window, and `right` from right of window.
* If any of these properties are unspecified, they are assumed to be `0`.
*/
bounds?: DragBounds;
/**
* When to recalculate the dimensions of the `bounds` element.
*
* By default, bounds are recomputed only on dragStart. Use this options to change that behavior.
*
* @default '{ dragStart: true, drag: false, dragEnd: false }'
*/
recomputeBounds?: {
dragStart?: boolean;
drag?: boolean;
dragEnd?: boolean;
};
/**
* Axis on which the element can be dragged on. Valid values: `both`, `x`, `y`, `none`.
*
* - `both` - Element can move in any direction
* - `x` - Only horizontal movement possible
* - `y` - Only vertical movement possible
* - `none` - No movement at all
*
* @default 'both'
*/
axis?: DragAxis;
/**
* If false, uses the new translate property instead of transform: translate(); to move the element around.
*
* At present this is true by default, but will be changed to false in a future major version.
*
* @default false
* @deprecated Use `transform` option instead for transform: translate() or any other custom transform. Will be removed in v3.
*/
legacyTranslate?: boolean;
/**
* If true, uses `translate3d` instead of `translate` to move the element around, and the hardware acceleration kicks in.
*
* `true` by default, but can be set to `false` if [blurry text issue](https://developpaper.com/question/why-does-the-use-of-css3-translate3d-result-in-blurred-display/) occur
*
* @default true
* @deprecated Use `transform` option instead with translate(x, y, 1px). 1px forces some browsers to use GPU acceleration. Will be removed in v3
*/
gpuAcceleration?: boolean;
/**
* Custom transform function. If provided, this function will be used to apply the DOM transformations to the root node to move it.
* Existing transform logic, including `gpuAcceleration` and `legacyTranslate`, will be ignored.
*
* You can return a string to apply to a `transform` property, or not return anything and apply your transformations using `rootNode.style.transform = VALUE`
*
* @default undefined
*/
transform?: ({ offsetX, offsetY, rootNode, }: {
offsetX: number;
offsetY: number;
rootNode: HTMLElement;
}) => string | undefined | void;
/**
* Applies `user-select: none` on `<body />` element when dragging,
* to prevent the irritating effect where dragging doesn't happen and the text is selected.
* Applied when dragging starts and removed when it stops.
*
* Can be disabled using this option
*
* @default true
*/
applyUserSelectHack?: boolean;
/**
* Ignores touch events with more than 1 touch.
* This helps when you have multiple elements on a canvas where you want to implement
* pinch-to-zoom behaviour.
*
* @default false
*
*/
ignoreMultitouch?: boolean;
/**
* Disables dragging altogether.
*
* @default false
*/
disabled?: boolean;
/**
* Applies a grid on the page to which the element snaps to when dragging, rather than the default continuous grid.
*
* `Note`: If you're programmatically creating the grid, do not set it to [0, 0] ever, that will stop drag at all. Set it to `undefined`.
*
* @default undefined
*/
grid?: [number, number];
/**
* Threshold for dragging to start. If the user moves the mouse/finger less than this distance, the dragging won't start.
*
* @default { delay: 0, distance: 3 }
*/
threshold?: {
/**
* Threshold in milliseconds for a pointer movement to be considered a drag
*
* @default 0
*/
delay?: number;
/**
* Threshold in pixels for movement to be considered a drag
*
* @default 3
*/
distance?: number;
};
/**
* Control the position manually with your own state
*
* By default, the element will be draggable by mouse/finger, and all options will work as default while dragging.
*
* But changing the `position` option will also move the draggable around. These parameters are reactive,
* so using Svelte's reactive variables as values for position will work like a charm.
*
*
* Note: If you set `disabled: true`, you'll still be able to move the draggable through state variables. Only the user interactions won't work
*
*/
position?: {
x: number;
y: number;
};
/**
* CSS Selector of an element or multiple elements inside the parent node(on which `use:draggable` is applied).
*
* Can be an element or elements too. If it is provided, Trying to drag inside the `cancel` element(s) will prevent dragging.
*
* @default undefined
*/
cancel?: string | HTMLElement | HTMLElement[];
/**
* CSS Selector of an element or multiple elements inside the parent node(on which `use:draggable` is applied). Can be an element or elements too.
*
* If it is provided, Only clicking and dragging on this element will allow the parent to drag, anywhere else on the parent won't work.
*
* @default undefined
*/
handle?: string | HTMLElement | HTMLElement[];
/**
* Class to apply on the element on which `use:draggable` is applied.
* Note that if `handle` is provided, it will still apply class on the element to which this action is applied, **NOT** the handle
*
*/
defaultClass?: string;
/**
* Class to apply on the element when it is dragging
*
* @default 'neodrag-dragging'
*/
defaultClassDragging?: string;
/**
* Class to apply on the element if it has been dragged at least once.
*
* @default 'neodrag-dragged'
*/
defaultClassDragged?: string;
/**
* Offsets your element to the position you specify in the very beginning.
* `x` and `y` should be in pixels
*
*/
defaultPosition?: {
x: number;
y: number;
};
/**
* Fires when dragging start
*/
onDragStart?: (data: DragEventData) => void;
/**
* Fires when dragging is going on
*/
onDrag?: (data: DragEventData) => void;
/**
* Fires when dragging ends
*/
onDragEnd?: (data: DragEventData) => void;
};
/**
* Actions can return an object containing the two properties defined in this interface. Both are optional.
* - update: An action can have a parameter. This method will be called whenever that parameter changes,
* immediately after Svelte has applied updates to the markup. `ActionReturn` and `ActionReturn<undefined>` both
* mean that the action accepts no parameters.
* - destroy: Method that is called after the element is unmounted
*
* Additionally, you can specify which additional attributes and events the action enables on the applied element.
* This applies to TypeScript typings only and has no effect at runtime.
*
* Example usage:
* ```ts
* interface Attributes {
* newprop?: string;
* 'on:event': (e: CustomEvent<boolean>) => void;
* }
*
* export function myAction(node: HTMLElement, parameter: Parameter): ActionReturn<Parameter, Attributes> {
* // ...
* return {
* update: (updatedParameter) => {...},
* destroy: () => {...}
* };
* }
* ```
*
* Docs: https://svelte.dev/docs/svelte-action
*/
interface ActionReturn<Parameter = undefined, Attributes extends Record<string, any> = Record<never, any>> {
update?: (parameter: Parameter) => void;
destroy?: () => void;
/**
* ### DO NOT USE THIS
* This exists solely for type-checking and has no effect at runtime.
* Set this through the `Attributes` generic instead.
*/
$$_attributes?: Attributes;
}
/**
* Actions are functions that are called when an element is created.
* You can use this interface to type such actions.
* The following example defines an action that only works on `<div>` elements
* and optionally accepts a parameter which it has a default value for:
* ```ts
* export const myAction: Action<HTMLDivElement, { someProperty: boolean } | undefined> = (node, param = { someProperty: true }) => {
* // ...
* }
* ```
* `Action<HTMLDivElement>` and `Action<HTMLDivElement, undefined>` both signal that the action accepts no parameters.
*
* You can return an object with methods `update` and `destroy` from the function and type which additional attributes and events it has.
* See interface `ActionReturn` for more details.
*
* Docs: https://svelte.dev/docs/svelte-action
*/
interface Action<Element = HTMLElement, Parameter = undefined, Attributes extends Record<string, any> = Record<never, any>> {
<Node extends Element>(...args: undefined extends Parameter ? [node: Node, parameter?: Parameter] : [node: Node, parameter: Parameter]): void | ActionReturn<Parameter, Attributes>;
}
declare const draggable: Action<HTMLElement, DragOptions | undefined, {
"on:neodrag:start": (e: CustomEvent<DragEventData>) => void;
"on:neodrag": (e: CustomEvent<DragEventData>) => void;
"on:neodrag:end": (e: CustomEvent<DragEventData>) => void;
}>;
export { type Action, type ActionReturn, type DragAxis, type DragBounds, type DragBoundsCoords, type DragEventData, type DragOptions, draggable };