diff --git a/jest.config.js b/jest.config.js index 7ecebe2e..14b083b0 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,18 +1,18 @@ const config = { - transform: { - '^.+\\.js$': 'babel-jest', - '^.+\\.svelte$': [ - 'svelte-jester', - { - preprocess: true, - }, - ], - '^.+\\.ts$': 'ts-jest', - }, - moduleFileExtensions: ['js', 'ts', 'svelte'], - setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'], - testEnvironment: 'jsdom', - transformIgnorePatterns: ['/node_modules/(?!(.+\\.svelte$))', '\\.pnp\\.[^\\/]+$'], + transform: { + '^.+\\.js$': 'babel-jest', + '^.+\\.svelte$': [ + 'svelte-jester', + { + preprocess: true, + }, + ], + '^.+\\.ts$': 'ts-jest', + }, + moduleFileExtensions: ['js', 'ts', 'svelte'], + setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'], + testEnvironment: 'jsdom', + transformIgnorePatterns: ['/node_modules/(?!(.+\\.svelte$))', '\\.pnp\\.[^\\/]+$'], }; export default config; diff --git a/package.json b/package.json index 0ec5c9b2..abffe605 100644 --- a/package.json +++ b/package.json @@ -1,63 +1,63 @@ { - "name": "svelte-drag", - "version": "2.2.1", - "description": "Svelte port of react-draggable", - "main": "dist/index.cjs", - "module": "dist/index.js", - "type": "module", - "types": "dist/index.d.ts", - "typings": "dist/index.d.ts", - "files": [ - "dist/" - ], - "sideEffects": false, - "exports": { - "import": "./dist/index.js", - "default": "./dist/index.js", - "require": "./dist/index.cjs" - }, - "scripts": { - "test": "jest test", - "test:watch": "pnpm run test -- --watch", - "watch": "tsup --watch", - "prepublishOnly": "tsup", - "pub": "pnpm publish", - "format": "prettier --write --plugin-search-dir=. '**/*.{js,ts,tsx,json,svelte,html}'" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/PuruVJ/svelte-drag.git" - }, - "keywords": [ - "draggable", - "svelte", - "react-draggable", - "drag", - "svelte", - "small", - "tiny", - "performant" - ], - "author": "Puru Vijay", - "license": "MIT", - "bugs": { - "url": "https://github.com/PuruVJ/svelte-drag/issues" - }, - "homepage": "https://github.com/PuruVJ/svelte-drag#readme", - "devDependencies": { - "@babel/preset-env": "^7.16.4", - "@testing-library/jest-dom": "^5.15.1", - "@testing-library/svelte": "^3.0.3", - "babel-jest": "^27.3.1", - "jest": "^27.3.1", - "prettier": "^2.4.1", - "prettier-plugin-svelte": "^2.5.0", - "svelte": "^3.44.2", - "svelte-jester": "^2.1.5", - "svelte-preprocess": "^4.9.8", - "ts-jest": "^27.0.7", - "tsm": "^2.1.4", - "tsup": "^5.9.1", - "typescript": "^4.5.2" - } + "name": "svelte-drag", + "version": "2.2.1", + "description": "Svelte port of react-draggable", + "main": "dist/index.cjs", + "module": "dist/index.js", + "type": "module", + "types": "dist/index.d.ts", + "typings": "dist/index.d.ts", + "files": [ + "dist/" + ], + "sideEffects": false, + "exports": { + "import": "./dist/index.js", + "default": "./dist/index.js", + "require": "./dist/index.cjs" + }, + "scripts": { + "test": "jest test", + "test:watch": "pnpm run test -- --watch", + "watch": "tsup --watch", + "prepublishOnly": "tsup", + "pub": "pnpm publish", + "format": "prettier --write --plugin-search-dir=. '**/*.{js,ts,tsx,json,svelte,html}'" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/PuruVJ/svelte-drag.git" + }, + "keywords": [ + "draggable", + "svelte", + "react-draggable", + "drag", + "svelte", + "small", + "tiny", + "performant" + ], + "author": "Puru Vijay", + "license": "MIT", + "bugs": { + "url": "https://github.com/PuruVJ/svelte-drag/issues" + }, + "homepage": "https://github.com/PuruVJ/svelte-drag#readme", + "devDependencies": { + "@babel/preset-env": "^7.16.4", + "@testing-library/jest-dom": "^5.15.1", + "@testing-library/svelte": "^3.0.3", + "babel-jest": "^27.3.1", + "jest": "^27.3.1", + "prettier": "^2.4.1", + "prettier-plugin-svelte": "^2.5.0", + "svelte": "^3.44.2", + "svelte-jester": "^2.1.5", + "svelte-preprocess": "^4.9.8", + "ts-jest": "^27.0.7", + "tsm": "^2.1.4", + "tsup": "^5.9.1", + "typescript": "^4.5.2" + } } diff --git a/src/draggable.d.ts b/src/draggable.d.ts index 912a3046..1a23265a 100644 --- a/src/draggable.d.ts +++ b/src/draggable.d.ts @@ -1,7 +1,7 @@ export declare namespace svelte.JSX { - interface HTMLAttributes { - 'onsvelte-drag:start'?: (e: CustomEvent) => void; - 'onsvelte-drag:end'?: (e: CustomEvent) => void; - 'onsvelte-drag'?: (e: CustomEvent<{ offsetX: number; offsetY: number }>) => void; - } + interface HTMLAttributes { + 'onsvelte-drag:start'?: (e: CustomEvent) => void; + 'onsvelte-drag:end'?: (e: CustomEvent) => void; + 'onsvelte-drag'?: (e: CustomEvent<{ offsetX: number; offsetY: number }>) => void; + } } diff --git a/src/index.ts b/src/index.ts index 6838b0b6..72065056 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,17 +1,17 @@ import memoize from './memoize'; export type DragBoundsCoords = { - /** Number of pixels from left of the document */ - left: number; + /** Number of pixels from left of the document */ + left: number; - /** Number of pixels from top of the document */ - top: 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 right side of document */ + right: number; - /** Number of pixels from the bottom of the document */ - bottom: number; + /** Number of pixels from the bottom of the document */ + bottom: number; }; export type DragAxis = 'both' | 'x' | 'y' | 'none'; @@ -19,216 +19,216 @@ export type DragAxis = 'both' | 'x' | 'y' | 'none'; export type DragBounds = 'parent' | Partial | string; export 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`. - * - * @example - * ```svelte - * - *
- * Hello - *
- * ``` - * - * @example - * ```svelte - * - *
- * Hello - *
- * ``` - * - * @example - * ```svelte - * - *
- * Hello - *
- * ``` - */ - bounds?: DragBounds; - - /** - * 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' - * - * @example - * ```svelte - * - *
- * Text - *
- * ``` - */ - axis?: DragAxis; - - /** - * 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 - * - * @example - * ```svelte - * - *
- * Text - *
- * ``` - */ - gpuAcceleration?: boolean; - - /** - * Applies `user-select: none` on `` 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 - * - * @example - * ```svelte - * - *
- * Text - *
- * ``` - */ - applyUserSelectHack?: boolean; - - /** - * Disables dragging altogether. - * - * @default false - * - * @example - * ```svelte - * - *
- * Text - *
- * ``` - */ - 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 - * - * @example - * ```svelte - * - *
- * Text - *
- * ``` - */ - grid?: [number, 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 - * - * Examples: - * - * [Changing with inputs](https://svelte.dev/repl/e1e707358b37467ba272891715878a1d?version=3.44.1) - * - * [Changing with Sliders](https://svelte.dev/repl/6b437a1cdbfc4c748520a72330c6395b?version=3.44.1) - * - * [Draggable only through external state, not user input](https://svelte.dev/repl/0eae169f272e41ba9c07ef222ed2bf66?version=3.44.1) - * - * [Comes back to original position after drag end](https://svelte.dev/repl/83d3aa8c5e154b7baf1a9c417c217d2e?version=3.44.1) - * - * [Comes back to original position with transition](https://svelte.dev/repl/bc84ed4ca22f45acbc28de3e33199883?version=3.44.1) - */ - position?: { x: number; y: number }; - - /** - * CSS Selector of an element inside the parent node(on which `use:draggable` is applied). - * - * If it is provided, Trying to drag inside the `cancel` selector will prevent dragging. - * - * @default undefined - * - * @example - * - *
- * Text - *
This won't drag
- *
- * ``` - */ - cancel?: string; - - /** - * CSS Selector of an element inside the parent node(on which `use:draggable` is applied). - * - * 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 - * - * @example - * - *
- * This won't drag - *
This sure will drag!!
- *
- * ``` - */ - handle?: string; - - /** - * 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 parent element, **NOT** the handle - * - * @default 'svelte-draggable' - */ - defaultClass?: string; - - /** - * Class to apply on the parent element when it is dragging - * - * @default 'svelte-draggable-dragging' - */ - defaultClassDragging?: string; - - /** - * Class to apply on the parent element if it has been dragged at least once. - * - * @default 'svelte-draggable-dragged' - */ - defaultClassDragged?: string; - - /** + /** + * 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`. + * + * @example + * ```svelte + * + *
+ * Hello + *
+ * ``` + * + * @example + * ```svelte + * + *
+ * Hello + *
+ * ``` + * + * @example + * ```svelte + * + *
+ * Hello + *
+ * ``` + */ + bounds?: DragBounds; + + /** + * 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' + * + * @example + * ```svelte + * + *
+ * Text + *
+ * ``` + */ + axis?: DragAxis; + + /** + * 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 + * + * @example + * ```svelte + * + *
+ * Text + *
+ * ``` + */ + gpuAcceleration?: boolean; + + /** + * Applies `user-select: none` on `` 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 + * + * @example + * ```svelte + * + *
+ * Text + *
+ * ``` + */ + applyUserSelectHack?: boolean; + + /** + * Disables dragging altogether. + * + * @default false + * + * @example + * ```svelte + * + *
+ * Text + *
+ * ``` + */ + 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 + * + * @example + * ```svelte + * + *
+ * Text + *
+ * ``` + */ + grid?: [number, 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 + * + * Examples: + * + * [Changing with inputs](https://svelte.dev/repl/e1e707358b37467ba272891715878a1d?version=3.44.1) + * + * [Changing with Sliders](https://svelte.dev/repl/6b437a1cdbfc4c748520a72330c6395b?version=3.44.1) + * + * [Draggable only through external state, not user input](https://svelte.dev/repl/0eae169f272e41ba9c07ef222ed2bf66?version=3.44.1) + * + * [Comes back to original position after drag end](https://svelte.dev/repl/83d3aa8c5e154b7baf1a9c417c217d2e?version=3.44.1) + * + * [Comes back to original position with transition](https://svelte.dev/repl/bc84ed4ca22f45acbc28de3e33199883?version=3.44.1) + */ + position?: { x: number; y: number }; + + /** + * CSS Selector of an element inside the parent node(on which `use:draggable` is applied). + * + * If it is provided, Trying to drag inside the `cancel` selector will prevent dragging. + * + * @default undefined + * + * @example + * + *
+ * Text + *
This won't drag
+ *
+ * ``` + */ + cancel?: string; + + /** + * CSS Selector of an element inside the parent node(on which `use:draggable` is applied). + * + * 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 + * + * @example + * + *
+ * This won't drag + *
This sure will drag!!
+ *
+ * ``` + */ + handle?: string; + + /** + * 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 parent element, **NOT** the handle + * + * @default 'svelte-draggable' + */ + defaultClass?: string; + + /** + * Class to apply on the parent element when it is dragging + * + * @default 'svelte-draggable-dragging' + */ + defaultClassDragging?: string; + + /** + * Class to apply on the parent element if it has been dragged at least once. + * + * @default 'svelte-draggable-dragged' + */ + defaultClassDragged?: string; + + /** * Offsets your element to the position you specify in the very beginning. * `x` and `y` should be in pixels * @@ -241,356 +241,363 @@ export type DragOptions = { * * ``` */ - defaultPosition?: { x: number; y: number }; - - /** - * Fires when dragging start - */ - onDragStart?: (data: { offsetX: number; offsetY: number }) => void; - - /** - * Fires when dragging is going on - */ - onDrag?: (data: { offsetX: number; offsetY: number }) => void; - - /** - * Fires when dragging ends - */ - onDragEnd?: (data: { offsetX: number; offsetY: number }) => void; + defaultPosition?: { x: number; y: number }; + + /** + * Fires when dragging start + */ + onDragStart?: (data: { offsetX: number; offsetY: number }) => void; + + /** + * Fires when dragging is going on + */ + onDrag?: (data: { offsetX: number; offsetY: number }) => void; + + /** + * Fires when dragging ends + */ + onDragEnd?: (data: { offsetX: number; offsetY: number }) => void; }; const DEFAULT_CLASS = { - MAIN: 'svelte-draggable', - DRAGGING: 'svelte-draggable-dragging', - DRAGGED: 'svelte-draggable-dragged', + MAIN: 'svelte-draggable', + DRAGGING: 'svelte-draggable-dragging', + DRAGGED: 'svelte-draggable-dragged', }; export const draggable = (node: HTMLElement, options: DragOptions = {}) => { - let { - bounds, - axis = 'both', - gpuAcceleration = true, - applyUserSelectHack = true, - disabled = false, + let { + bounds, + axis = 'both', + gpuAcceleration = true, + applyUserSelectHack = true, + disabled = false, + + grid, - grid, + position, - position, + cancel, + handle, - cancel, - handle, + defaultClass = DEFAULT_CLASS.MAIN, + defaultClassDragging = DEFAULT_CLASS.DRAGGING, + defaultClassDragged = DEFAULT_CLASS.DRAGGED, - defaultClass = DEFAULT_CLASS.MAIN, - defaultClassDragging = DEFAULT_CLASS.DRAGGING, - defaultClassDragged = DEFAULT_CLASS.DRAGGED, + defaultPosition = { x: 0, y: 0 }, - defaultPosition = { x: 0, y: 0 }, + onDragStart, + onDrag, + onDragEnd, + } = options; - onDragStart, - onDrag, - onDragEnd, - } = options; + let active = false; - let active = false; + let [translateX, translateY] = [0, 0]; + let [initialX, initialY] = [0, 0]; - let [translateX, translateY] = [0, 0]; - let [initialX, initialY] = [0, 0]; - let [previousX, previousY] = [0, 0]; + // The offset of the client position relative to the node's top-left corner + let [clientToNodeOffsetX, clientToNodeOffsetY] = [0, 0]; - // The offset of the client position relative to the node's top-left corner - let [clientToNodeOffsetX, clientToNodeOffsetY] = [0, 0]; + let [xOffset, yOffset] = [defaultPosition.x, defaultPosition.y]; - let [xOffset, yOffset] = [defaultPosition.x, defaultPosition.y]; + setTranslate(xOffset, yOffset, node, gpuAcceleration); - setTranslate(xOffset, yOffset, node, gpuAcceleration); + let canMoveInX: boolean; + let canMoveInY: boolean; - let canMoveInX: boolean; - let canMoveInY: boolean; + let bodyOriginalUserSelectVal = ''; - let bodyOriginalUserSelectVal = ''; + let computedBounds: DragBoundsCoords; + let nodeRect: DOMRect; - let computedBounds: DragBoundsCoords; - let nodeRect: DOMRect; + let dragEl: HTMLElement | undefined; + let cancelEl: HTMLElement | undefined; - let dragEl: HTMLElement | undefined; - let cancelEl: HTMLElement | undefined; + let isControlled = !!position; - let isControlled = !!position; + function fireSvelteDragStartEvent(node: HTMLElement) { + const data = { offsetX: translateX, offsetY: translateY }; - function fireSvelteDragStartEvent(node: HTMLElement) { - const data = { offsetX: translateX, offsetY: translateY }; + node.dispatchEvent(new CustomEvent('svelte-drag:start', { detail: data })); + onDragStart?.(data); + } - node.dispatchEvent(new CustomEvent('svelte-drag:start', { detail: data })); - onDragStart?.(data); - } + function fireSvelteDragStopEvent(node: HTMLElement) { + const data = { offsetX: translateX, offsetY: translateY }; - function fireSvelteDragStopEvent(node: HTMLElement) { - const data = { offsetX: translateX, offsetY: translateY }; + node.dispatchEvent(new CustomEvent('svelte-drag:end', { detail: data })); + onDragEnd?.(data); + } - node.dispatchEvent(new CustomEvent('svelte-drag:end', { detail: data })); - onDragEnd?.(data); - } + function fireSvelteDragEvent(node: HTMLElement, translateX: number, translateY: number) { + const data = { offsetX: translateX, offsetY: translateY }; - function fireSvelteDragEvent(node: HTMLElement, translateX: number, translateY: number) { - const data = { offsetX: translateX, offsetY: translateY }; + node.dispatchEvent(new CustomEvent('svelte-drag', { detail: data })); + onDrag?.(data); + } - node.dispatchEvent(new CustomEvent('svelte-drag', { detail: data })); - onDrag?.(data); - } + const listen = addEventListener; - const listen = addEventListener; + listen('touchstart', dragStart, false); + listen('touchend', dragEnd, false); + listen('touchmove', drag, false); - listen('touchstart', dragStart, false); - listen('touchend', dragEnd, false); - listen('touchmove', drag, false); + listen('mousedown', dragStart, false); + listen('mouseup', dragEnd, false); + listen('mousemove', drag, false); - listen('mousedown', dragStart, false); - listen('mouseup', dragEnd, false); - listen('mousemove', drag, false); + // On mobile, touch can become extremely janky without it + node.style.touchAction = 'none'; - // On mobile, touch can become extremely janky without it - node.style.touchAction = 'none'; + function dragStart(e: TouchEvent | MouseEvent) { + if (disabled) return; - function dragStart(e: TouchEvent | MouseEvent) { - if (disabled) return; + node.classList.add(defaultClass); - node.classList.add(defaultClass); + dragEl = getDragEl(handle, node); + cancelEl = getCancelElement(cancel, node); - dragEl = getDragEl(handle, node); - cancelEl = getCancelElement(cancel, node); + canMoveInX = ['both', 'x'].includes(axis); + canMoveInY = ['both', 'y'].includes(axis); - canMoveInX = ['both', 'x'].includes(axis); - canMoveInY = ['both', 'y'].includes(axis); + // Compute bounds + if (typeof bounds !== 'undefined') computedBounds = computeBoundRect(bounds, node); - // Compute bounds - if (typeof bounds !== 'undefined') computedBounds = computeBoundRect(bounds, node); + // Compute current node's bounding client Rectangle + nodeRect = node.getBoundingClientRect(); - // Compute current node's bounding client Rectangle - nodeRect = node.getBoundingClientRect(); + if (isString(handle) && isString(cancel) && handle === cancel) + throw new Error("`handle` selector can't be same as `cancel` selector"); - if (isString(handle) && isString(cancel) && handle === cancel) - throw new Error("`handle` selector can't be same as `cancel` selector"); + if (cancelEl?.contains(dragEl)) + throw new Error( + "Element being dragged can't be a child of the element on which `cancel` is applied" + ); - if (cancelEl?.contains(dragEl)) - throw new Error( - "Element being dragged can't be a child of the element on which `cancel` is applied" - ); + if (dragEl.contains(e.target) && !cancelEl?.contains(e.target)) + active = true; - if (dragEl.contains(e.target) && !cancelEl?.contains(e.target)) - active = true; + if (!active) return; - if (!active) return; + if (applyUserSelectHack) { + // Apply user-select: none on body to prevent misbehavior + bodyOriginalUserSelectVal = document.body.style.userSelect; + document.body.style.userSelect = 'none'; + } - if (applyUserSelectHack) { - // Apply user-select: none on body to prevent misbehavior - bodyOriginalUserSelectVal = document.body.style.userSelect; - document.body.style.userSelect = 'none'; - } + // Dispatch custom event + fireSvelteDragStartEvent(node); - // Dispatch custom event - fireSvelteDragStartEvent(node); + const { clientX, clientY } = isTouchEvent(e) ? e.touches[0] : e; - const { clientX, clientY } = isTouchEvent(e) ? e.touches[0] : e; + if (canMoveInX) initialX = clientX - xOffset; + if (canMoveInY) initialY = clientY - yOffset; - if (canMoveInX) initialX = clientX - xOffset; - if (canMoveInY) initialY = clientY - yOffset; + // Only the bounds uses these properties at the moment, + // may open up in the future if others need it + if (computedBounds) { + clientToNodeOffsetX = clientX - nodeRect.left; + clientToNodeOffsetY = clientY - nodeRect.top; + } + } - // Only the bounds uses these properties at the moment, - // may open up in the future if others need it - if (computedBounds) { - clientToNodeOffsetX = clientX - nodeRect.left; - clientToNodeOffsetY = clientY - nodeRect.top; - } - } + function dragEnd(e: MouseEvent | TouchEvent) { + if (disabled) return; + if (!active) return; - function dragEnd(e: MouseEvent | TouchEvent) { - if (disabled) return; - if (!active) return; + // Apply class defaultClassDragged + node.classList.remove(defaultClassDragging); + node.classList.add(defaultClassDragged); - // Apply class defaultClassDragged - node.classList.remove(defaultClassDragging); - node.classList.add(defaultClassDragged); + if (applyUserSelectHack) document.body.style.userSelect = bodyOriginalUserSelectVal; - if (applyUserSelectHack) document.body.style.userSelect = bodyOriginalUserSelectVal; + fireSvelteDragStopEvent(node); - fireSvelteDragStopEvent(node); + if (canMoveInX) initialX = translateX; + if (canMoveInX) initialY = translateY; - if (canMoveInX) initialX = translateX; - if (canMoveInX) initialY = translateY; + active = false; + } - active = false; - } + function drag(e: TouchEvent | MouseEvent) { + if (!active) return; - function drag(e: TouchEvent | MouseEvent) { - if (!active) return; + // Apply class defaultClassDragging + node.classList.add(defaultClassDragging); - // Apply class defaultClassDragging - node.classList.add(defaultClassDragging); + e.preventDefault(); - e.preventDefault(); + nodeRect = node.getBoundingClientRect(); - nodeRect = node.getBoundingClientRect(); + const { clientX, clientY } = isTouchEvent(e) ? e.touches[0] : e; - const { clientX, clientY } = isTouchEvent(e) ? e.touches[0] : e; + // Get final values for clamping + let [finalX, finalY] = [clientX, clientY]; - // Get final values for clamping - let [finalX, finalY] = [clientX, clientY]; + // Calculate the current scale of the node + let inverseScale = node.offsetWidth / nodeRect.width; + if (isNaN(inverseScale)) inverseScale = 1; - if (computedBounds) { - // Client position is limited to this virtual boundary to prevent node going out of bounds - const virtualClientBounds: DragBoundsCoords = { - left: computedBounds.left + clientToNodeOffsetX, - top: computedBounds.top + clientToNodeOffsetY, - right: computedBounds.right + clientToNodeOffsetX - nodeRect.width, - bottom: computedBounds.bottom + clientToNodeOffsetY - nodeRect.height, - }; + if (computedBounds) { + // Client position is limited to this virtual boundary to prevent node going out of bounds + const virtualClientBounds: DragBoundsCoords = { + left: computedBounds.left + clientToNodeOffsetX, + top: computedBounds.top + clientToNodeOffsetY, + right: computedBounds.right + clientToNodeOffsetX - nodeRect.width, + bottom: computedBounds.bottom + clientToNodeOffsetY - nodeRect.height, + }; - finalX = Math.min(Math.max(finalX, virtualClientBounds.left), virtualClientBounds.right); - finalY = Math.min(Math.max(finalY, virtualClientBounds.top), virtualClientBounds.bottom); - } + finalX = Math.min(Math.max(finalX, virtualClientBounds.left), virtualClientBounds.right); + finalY = Math.min(Math.max(finalY, virtualClientBounds.top), virtualClientBounds.bottom); + } - if (Array.isArray(grid)) { - let [xSnap, ySnap] = grid; + if (Array.isArray(grid)) { + let [xSnap, ySnap] = grid; - if (isNaN(+xSnap) || xSnap < 0) - throw new Error('1st argument of `grid` must be a valid positive number'); + if (isNaN(+xSnap) || xSnap < 0) + throw new Error('1st argument of `grid` must be a valid positive number'); - if (isNaN(+ySnap) || ySnap < 0) - throw new Error('2nd argument of `grid` must be a valid positive number'); + if (isNaN(+ySnap) || ySnap < 0) + throw new Error('2nd argument of `grid` must be a valid positive number'); - let [deltaX, deltaY] = [finalX - previousX, finalY - previousY]; - [deltaX, deltaY] = snapToGrid([xSnap, ySnap], deltaX, deltaY); + let [deltaX, deltaY] = [finalX - initialX, finalY - initialY]; + [deltaX, deltaY] = snapToGrid( + [Math.floor(xSnap / inverseScale), Math.floor(ySnap / inverseScale)], + deltaX, + deltaY + ); - if (!deltaX && !deltaY) return; + if (!deltaX && !deltaY) return; - [finalX, finalY] = [previousX + deltaX, previousY + deltaY]; - } + [finalX, finalY] = [initialX + deltaX, initialY + deltaY]; + } - if (canMoveInX) translateX = finalX - initialX; - if (canMoveInY) translateY = finalY - initialY; + if (canMoveInX) translateX = (finalX - initialX) * inverseScale; + if (canMoveInY) translateY = (finalY - initialY) * inverseScale; - [xOffset, yOffset] = [translateX, translateY]; + [xOffset, yOffset] = [translateX, translateY]; - fireSvelteDragEvent(node, translateX, translateY); + fireSvelteDragEvent(node, translateX, translateY); - Promise.resolve().then(() => setTranslate(translateX, translateY, node, gpuAcceleration)); - } + Promise.resolve().then(() => setTranslate(translateX, translateY, node, gpuAcceleration)); + } - return { - destroy: () => { - const unlisten = removeEventListener; + return { + destroy: () => { + const unlisten = removeEventListener; - unlisten('touchstart', dragStart, false); - unlisten('touchend', dragEnd, false); - unlisten('touchmove', drag, false); + unlisten('touchstart', dragStart, false); + unlisten('touchend', dragEnd, false); + unlisten('touchmove', drag, false); - unlisten('mousedown', dragStart, false); - unlisten('mouseup', dragEnd, false); - unlisten('mousemove', drag, false); - }, - update: (options: DragOptions) => { - // Update all the values that need to be changed - axis = options.axis || 'both'; - disabled = options.disabled ?? false; - handle = options.handle; - bounds = options.bounds; - cancel = options.cancel; - applyUserSelectHack = options.applyUserSelectHack ?? true; - grid = options.grid; - gpuAcceleration = options.gpuAcceleration ?? true; + unlisten('mousedown', dragStart, false); + unlisten('mouseup', dragEnd, false); + unlisten('mousemove', drag, false); + }, + update: (options: DragOptions) => { + // Update all the values that need to be changed + axis = options.axis || 'both'; + disabled = options.disabled ?? false; + handle = options.handle; + bounds = options.bounds; + cancel = options.cancel; + applyUserSelectHack = options.applyUserSelectHack ?? true; + grid = options.grid; + gpuAcceleration = options.gpuAcceleration ?? true; - const dragged = node.classList.contains(defaultClassDragged); + const dragged = node.classList.contains(defaultClassDragged); - node.classList.remove(defaultClass, defaultClassDragged); + node.classList.remove(defaultClass, defaultClassDragged); - defaultClass = options.defaultClass ?? DEFAULT_CLASS.MAIN; - defaultClassDragging = options.defaultClassDragging ?? DEFAULT_CLASS.DRAGGING; - defaultClassDragged = options.defaultClassDragged ?? DEFAULT_CLASS.DRAGGED; + defaultClass = options.defaultClass ?? DEFAULT_CLASS.MAIN; + defaultClassDragging = options.defaultClassDragging ?? DEFAULT_CLASS.DRAGGING; + defaultClassDragged = options.defaultClassDragged ?? DEFAULT_CLASS.DRAGGED; - node.classList.add(defaultClass); + node.classList.add(defaultClass); - if (dragged) node.classList.add(defaultClassDragged); + if (dragged) node.classList.add(defaultClassDragged); - if (isControlled) { - xOffset = translateX = options.position?.x ?? translateX; - yOffset = translateY = options.position?.y ?? translateY; + if (isControlled) { + xOffset = translateX = options.position?.x ?? translateX; + yOffset = translateY = options.position?.y ?? translateY; - Promise.resolve().then(() => setTranslate(translateX, translateY, node, gpuAcceleration)); - } - }, - }; + Promise.resolve().then(() => setTranslate(translateX, translateY, node, gpuAcceleration)); + } + }, + }; }; function isTouchEvent(event: MouseEvent | TouchEvent): event is TouchEvent { - return Boolean((event as TouchEvent).touches && (event as TouchEvent).touches.length); + return Boolean((event as TouchEvent).touches && (event as TouchEvent).touches.length); } function isString(val: unknown): val is string { - return typeof val === 'string'; + return typeof val === 'string'; } const snapToGrid = memoize( - ([xSnap, ySnap]: [number, number], pendingX: number, pendingY: number): [number, number] => { - const x = Math.round(pendingX / xSnap) * xSnap; - const y = Math.round(pendingY / ySnap) * ySnap; - return [x, y]; - } + ([xSnap, ySnap]: [number, number], pendingX: number, pendingY: number): [number, number] => { + const x = Math.round(pendingX / xSnap) * xSnap; + const y = Math.round(pendingY / ySnap) * ySnap; + return [x, y]; + } ); function getDragEl(handle: string | undefined, node: HTMLElement) { - if (!handle) return node; + if (!handle) return node; - // Valid!! Let's check if this selector exists or not - const handleEl = node.querySelector(handle); - if (handleEl === null) - throw new Error( - 'Selector passed for `handle` option should be child of the element on which the action is applied' - ); + // Valid!! Let's check if this selector exists or not + const handleEl = node.querySelector(handle); + if (handleEl === null) + throw new Error( + 'Selector passed for `handle` option should be child of the element on which the action is applied' + ); - return handleEl!; + return handleEl!; } function getCancelElement(cancel: string | undefined, node: HTMLElement) { - if (!cancel) return; + if (!cancel) return; - const cancelEl = node.querySelector(cancel); + const cancelEl = node.querySelector(cancel); - if (cancelEl === null) - throw new Error( - 'Selector passed for `cancel` option should be child of the element on which the action is applied' - ); + if (cancelEl === null) + throw new Error( + 'Selector passed for `cancel` option should be child of the element on which the action is applied' + ); - return cancelEl; + return cancelEl; } function computeBoundRect(bounds: string | Partial, rootNode: HTMLElement) { - if (typeof bounds === 'object') { - // we have the left right etc - const [windowWidth, windowHeight] = [window.innerWidth, window.innerHeight]; + if (typeof bounds === 'object') { + // we have the left right etc + const [windowWidth, windowHeight] = [window.innerWidth, window.innerHeight]; - const { top = 0, left = 0, right = 0, bottom = 0 } = bounds; + const { top = 0, left = 0, right = 0, bottom = 0 } = bounds; - const computedRight = windowWidth - right; - const computedBottom = windowHeight - bottom; + const computedRight = windowWidth - right; + const computedBottom = windowHeight - bottom; - return { top, right: computedRight, bottom: computedBottom, left }; - } + return { top, right: computedRight, bottom: computedBottom, left }; + } - // It's a string - if (bounds === 'parent') return (rootNode.parentNode as HTMLElement).getBoundingClientRect(); + // It's a string + if (bounds === 'parent') return (rootNode.parentNode as HTMLElement).getBoundingClientRect(); - const node = document.querySelector(bounds); + const node = document.querySelector(bounds); - if (node === null) - throw new Error("The selector provided for bound doesn't exists in the document."); + if (node === null) + throw new Error("The selector provided for bound doesn't exists in the document."); - const computedBounds = node!.getBoundingClientRect(); + const computedBounds = node!.getBoundingClientRect(); - return computedBounds; + return computedBounds; } function setTranslate(xPos: number, yPos: number, el: HTMLElement, gpuAcceleration: boolean) { - el.style.transform = gpuAcceleration - ? `translate3d(${+xPos}px, ${+yPos}px, 0)` - : `translate(${+xPos}px, ${+yPos}px)`; + el.style.transform = gpuAcceleration + ? `translate3d(${+xPos}px, ${+yPos}px, 0)` + : `translate(${+xPos}px, ${+yPos}px)`; } diff --git a/src/memoize.d.ts b/src/memoize.d.ts index de407b1d..4f2dde6e 100644 --- a/src/memoize.d.ts +++ b/src/memoize.d.ts @@ -1,34 +1,34 @@ type Func = (...args: any[]) => any; export interface Cache { - create: CacheCreateFunc; + create: CacheCreateFunc; } interface CacheCreateFunc { - (): { - get(key: K): V; - set(key: K, value: V): void; - has(key: K): boolean; - }; + (): { + get(key: K): V; + set(key: K, value: V): void; + has(key: K): boolean; + }; } export type Serializer = (args: any[]) => string; export interface Options { - cache?: Cache>; - serializer?: Serializer; - strategy?: MemoizeFunc; + cache?: Cache>; + serializer?: Serializer; + strategy?: MemoizeFunc; } export interface MemoizeFunc { - (fn: F, options?: Options): F; + (fn: F, options?: Options): F; } interface Memoize extends MemoizeFunc { - strategies: { - variadic: MemoizeFunc; - monadic: MemoizeFunc; - }; + strategies: { + variadic: MemoizeFunc; + monadic: MemoizeFunc; + }; } declare const memoize: Memoize; diff --git a/src/memoize.js b/src/memoize.js index ec61e123..92121614 100644 --- a/src/memoize.js +++ b/src/memoize.js @@ -3,16 +3,16 @@ // function memoize(fn, options) { - var cache = options && options.cache ? options.cache : cacheDefault; + var cache = options && options.cache ? options.cache : cacheDefault; - var serializer = options && options.serializer ? options.serializer : serializerDefault; + var serializer = options && options.serializer ? options.serializer : serializerDefault; - var strategy = options && options.strategy ? options.strategy : strategyDefault; + var strategy = options && options.strategy ? options.strategy : strategyDefault; - return strategy(fn, { - cache: cache, - serializer: serializer, - }); + return strategy(fn, { + cache: cache, + serializer: serializer, + }); } // @@ -20,54 +20,54 @@ function memoize(fn, options) { // function isPrimitive(value) { - return value == null || typeof value === 'number' || typeof value === 'boolean'; // || typeof value === "string" 'unsafe' primitive for our needs + return value == null || typeof value === 'number' || typeof value === 'boolean'; // || typeof value === "string" 'unsafe' primitive for our needs } function monadic(fn, cache, serializer, arg) { - var cacheKey = isPrimitive(arg) ? arg : serializer(arg); + var cacheKey = isPrimitive(arg) ? arg : serializer(arg); - var computedValue = cache.get(cacheKey); - if (typeof computedValue === 'undefined') { - computedValue = fn.call(this, arg); - cache.set(cacheKey, computedValue); - } + var computedValue = cache.get(cacheKey); + if (typeof computedValue === 'undefined') { + computedValue = fn.call(this, arg); + cache.set(cacheKey, computedValue); + } - return computedValue; + return computedValue; } function variadic(fn, cache, serializer) { - var args = Array.prototype.slice.call(arguments, 3); - var cacheKey = serializer(args); + var args = Array.prototype.slice.call(arguments, 3); + var cacheKey = serializer(args); - var computedValue = cache.get(cacheKey); - if (typeof computedValue === 'undefined') { - computedValue = fn.apply(this, args); - cache.set(cacheKey, computedValue); - } + var computedValue = cache.get(cacheKey); + if (typeof computedValue === 'undefined') { + computedValue = fn.apply(this, args); + cache.set(cacheKey, computedValue); + } - return computedValue; + return computedValue; } function assemble(fn, context, strategy, cache, serialize) { - return strategy.bind(context, fn, cache, serialize); + return strategy.bind(context, fn, cache, serialize); } function strategyDefault(fn, options) { - var strategy = fn.length === 1 ? monadic : variadic; + var strategy = fn.length === 1 ? monadic : variadic; - return assemble(fn, this, strategy, options.cache.create(), options.serializer); + return assemble(fn, this, strategy, options.cache.create(), options.serializer); } function strategyVariadic(fn, options) { - var strategy = variadic; + var strategy = variadic; - return assemble(fn, this, strategy, options.cache.create(), options.serializer); + return assemble(fn, this, strategy, options.cache.create(), options.serializer); } function strategyMonadic(fn, options) { - var strategy = monadic; + var strategy = monadic; - return assemble(fn, this, strategy, options.cache.create(), options.serializer); + return assemble(fn, this, strategy, options.cache.create(), options.serializer); } // @@ -75,7 +75,7 @@ function strategyMonadic(fn, options) { // function serializerDefault() { - return JSON.stringify(arguments); + return JSON.stringify(arguments); } // @@ -83,25 +83,25 @@ function serializerDefault() { // function ObjectWithoutPrototypeCache() { - this.cache = Object.create(null); + this.cache = Object.create(null); } ObjectWithoutPrototypeCache.prototype.has = function (key) { - return key in this.cache; + return key in this.cache; }; ObjectWithoutPrototypeCache.prototype.get = function (key) { - return this.cache[key]; + return this.cache[key]; }; ObjectWithoutPrototypeCache.prototype.set = function (key, value) { - this.cache[key] = value; + this.cache[key] = value; }; var cacheDefault = { - create: function create() { - return new ObjectWithoutPrototypeCache(); - }, + create: function create() { + return new ObjectWithoutPrototypeCache(); + }, }; // @@ -110,6 +110,6 @@ var cacheDefault = { export default memoize; export const strategies = { - variadic: strategyVariadic, - monadic: strategyMonadic, + variadic: strategyVariadic, + monadic: strategyMonadic, }; diff --git a/svelte.config.js b/svelte.config.js index 7f040f17..80d59c82 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -1,7 +1,7 @@ import sveltePreprocess from 'svelte-preprocess'; const config = { - preprocess: sveltePreprocess(), + preprocess: sveltePreprocess(), }; export default config; diff --git a/tests/CancelDraggable.spec.ts b/tests/CancelDraggable.spec.ts index 516714a6..4d3ad896 100644 --- a/tests/CancelDraggable.spec.ts +++ b/tests/CancelDraggable.spec.ts @@ -6,79 +6,79 @@ import CancelDraggable from './components/CancelDraggable.svelte'; import { drag, touchDrag } from './testHelpers'; describe('CancelDraggable', () => { - it('renders a basic div', () => { - const { getByText } = render(CancelDraggable); + it('renders a basic div', () => { + const { getByText } = render(CancelDraggable); - const element = getByText('This will drag!'); + const element = getByText('This will drag!'); - expect(element).toBeInTheDocument(); - expect(element).not.toHaveClass('svelte-draggable'); - expect(element).not.toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + expect(element).toBeInTheDocument(); + expect(element).not.toHaveClass('svelte-draggable'); + expect(element).not.toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - const cancelElement = getByText('You shall not drag!!'); + const cancelElement = getByText('You shall not drag!!'); - expect(cancelElement).toBeInTheDocument(); - expect(cancelElement).toHaveClass('cancel'); - }); + expect(cancelElement).toBeInTheDocument(); + expect(cancelElement).toHaveClass('cancel'); + }); - it('should drag by the main element', async () => { - const { getByText } = render(CancelDraggable); + it('should drag by the main element', async () => { + const { getByText } = render(CancelDraggable); - const element = getByText('This will drag!'); + const element = getByText('This will drag!'); - expect(element).toBeInTheDocument(); + expect(element).toBeInTheDocument(); - await drag(element, 0, 0, 50, 50); + await drag(element, 0, 0, 50, 50); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)'); - }); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)'); + }); - it('should not drag by the cancel element', async () => { - const { getByText } = render(CancelDraggable); + it('should not drag by the cancel element', async () => { + const { getByText } = render(CancelDraggable); - const cancelElement = getByText('You shall not drag!!'); + const cancelElement = getByText('You shall not drag!!'); - expect(cancelElement).toBeInTheDocument(); + expect(cancelElement).toBeInTheDocument(); - await drag(cancelElement, 0, 0, 50, 50); + await drag(cancelElement, 0, 0, 50, 50); - const element = getByText('This will drag!'); + const element = getByText('This will drag!'); - expect(element).toHaveClass('svelte-draggable'); - expect(element).not.toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); + expect(element).toHaveClass('svelte-draggable'); + expect(element).not.toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); - it('should drag by the main element by touch', async () => { - const { getByText } = render(CancelDraggable); + it('should drag by the main element by touch', async () => { + const { getByText } = render(CancelDraggable); - const element = getByText('This will drag!'); + const element = getByText('This will drag!'); - expect(element).toBeInTheDocument(); + expect(element).toBeInTheDocument(); - await touchDrag(element, 0, 0, 50, 50); + await touchDrag(element, 0, 0, 50, 50); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)'); - }); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)'); + }); - it('should not drag by the cancel element by touch', async () => { - const { getByText } = render(CancelDraggable); + it('should not drag by the cancel element by touch', async () => { + const { getByText } = render(CancelDraggable); - const cancelElement = getByText('You shall not drag!!'); + const cancelElement = getByText('You shall not drag!!'); - expect(cancelElement).toBeInTheDocument(); + expect(cancelElement).toBeInTheDocument(); - await touchDrag(cancelElement, 0, 0, 50, 50); + await touchDrag(cancelElement, 0, 0, 50, 50); - const element = getByText('This will drag!'); + const element = getByText('This will drag!'); - expect(element).toHaveClass('svelte-draggable'); - expect(element).not.toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); + expect(element).toHaveClass('svelte-draggable'); + expect(element).not.toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); }); diff --git a/tests/Draggable.spec.ts b/tests/Draggable.spec.ts index 23a90f37..1c8a27ba 100644 --- a/tests/Draggable.spec.ts +++ b/tests/Draggable.spec.ts @@ -6,407 +6,430 @@ import Draggable from './components/Draggable.svelte'; import { drag, touchDrag } from './testHelpers'; describe('Draggable', () => { - describe('defaults', () => { - it('renders a basic div', () => { - const { getByText } = render(Draggable); + describe('defaults', () => { + it('renders a basic div', () => { + const { getByText } = render(Draggable); + + const element = getByText('Drag me!'); + + expect(element).toBeInTheDocument(); + expect(element).not.toHaveClass('svelte-draggable'); + expect(element).not.toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); + + it('should be dragged by the mouse', async () => { + const { getByText } = render(Draggable); + + const element = getByText('Drag me!'); + + expect(element).toBeInTheDocument(); + + await drag(element, 0, 0, 50, 50); + + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)'); + }); + + it('should be dragged by a touch event', async () => { + const { getByText } = render(Draggable); + + const element = getByText('Drag me!'); + + expect(element).toBeInTheDocument(); + + await touchDrag(element, 0, 0, 50, 50); - const element = getByText('Drag me!'); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)'); + }); + }); + + describe('disabled', () => { + it('should not drag when disabled', async () => { + const { getByText } = render(Draggable, { disabled: true }); + + const element = getByText('Drag me!'); - expect(element).toBeInTheDocument(); - expect(element).not.toHaveClass('svelte-draggable'); - expect(element).not.toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); + expect(element).toBeInTheDocument(); - it('should be dragged by the mouse', async () => { - const { getByText } = render(Draggable); + await drag(element, 0, 0, 50, 50); - const element = getByText('Drag me!'); + expect(element).not.toHaveClass('svelte-draggable'); + expect(element).not.toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); - expect(element).toBeInTheDocument(); + it('should not drag by touch when disabled', async () => { + const { getByText } = render(Draggable, { disabled: true }); - await drag(element, 0, 0, 50, 50); + const element = getByText('Drag me!'); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)'); - }); + expect(element).toBeInTheDocument(); - it('should be dragged by a touch event', async () => { - const { getByText } = render(Draggable); + await touchDrag(element, 0, 0, 50, 50); - const element = getByText('Drag me!'); + expect(element).not.toHaveClass('svelte-draggable'); + expect(element).not.toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); + }); - expect(element).toBeInTheDocument(); + describe('with coordinate bounds', () => { + beforeEach(() => { + const boundary = { + width: 10, + height: 10, + top: 0, + left: 0, + bottom: 10, + right: 10, + }; - await touchDrag(element, 0, 0, 50, 50); + Object.defineProperties(window.HTMLElement.prototype, { + offsetLeft: { + get() { + return boundary.left; + }, + }, + offsetTop: { + get() { + return boundary.top; + }, + }, + offsetHeight: { + get() { + return boundary.height; + }, + }, + offsetWidth: { + get() { + return boundary.width; + }, + }, + }); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)'); - }); - }); + HTMLElement.prototype.getBoundingClientRect = jest.fn(() => boundary as any); - describe('disabled', () => { - it('should not drag when disabled', async () => { - const { getByText } = render(Draggable, { disabled: true }); + Object.assign(window, { + innerWidth: 200, + innerHeight: 200, + }); + }); - const element = getByText('Drag me!'); + it('renders a basic div', () => { + const { getByText } = render(Draggable, { + bounds: { top: 0, bottom: 100, left: 0, right: 100 }, + }); - expect(element).toBeInTheDocument(); + const element = getByText('Drag me!'); - await drag(element, 0, 0, 50, 50); + expect(element).toBeInTheDocument(); + expect(element).not.toHaveClass('svelte-draggable'); + expect(element).not.toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); - expect(element).not.toHaveClass('svelte-draggable'); - expect(element).not.toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); + it('should move within bounds', async () => { + const { getByText } = render(Draggable, { + bounds: { top: 0, bottom: 100, left: 0, right: 100 }, + }); - it('should not drag by touch when disabled', async () => { - const { getByText } = render(Draggable, { disabled: true }); + const element = getByText('Drag me!'); - const element = getByText('Drag me!'); + expect(element).toBeInTheDocument(); - expect(element).toBeInTheDocument(); + await drag(element, 0, 0, 50, 50); - await touchDrag(element, 0, 0, 50, 50); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)'); + }); - expect(element).not.toHaveClass('svelte-draggable'); - expect(element).not.toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); - }); + it('should be dragged by a touch event', async () => { + const { getByText } = render(Draggable, { + bounds: { top: 0, bottom: 100, left: 0, right: 100 }, + }); - describe('with coordinate bounds', () => { - beforeEach(() => { - Element.prototype.getBoundingClientRect = jest.fn(() => { - return { - width: 10, - height: 10, - top: 0, - left: 0, - bottom: 10, - right: 10, - }; - }); + const element = getByText('Drag me!'); - Object.assign(window, { - innerWidth: 200, - innerHeight: 200, - }); - }); + expect(element).toBeInTheDocument(); - it('renders a basic div', () => { - const { getByText } = render(Draggable, { - bounds: { top: 0, bottom: 100, left: 0, right: 100 }, - }); + await touchDrag(element, 0, 0, 50, 50); - const element = getByText('Drag me!'); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)'); + }); - expect(element).toBeInTheDocument(); - expect(element).not.toHaveClass('svelte-draggable'); - expect(element).not.toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); + it('should not leave bounds from the bottom right', async () => { + const { getByText } = render(Draggable, { + bounds: { top: 0, bottom: 100, left: 0, right: 100 }, + }); - it('should move within bounds', async () => { - const { getByText } = render(Draggable, { - bounds: { top: 0, bottom: 100, left: 0, right: 100 }, - }); + const element = getByText('Drag me!'); - const element = getByText('Drag me!'); + expect(element).toBeInTheDocument(); - expect(element).toBeInTheDocument(); + await drag(element, 0, 0, 200, 200); - await drag(element, 0, 0, 50, 50); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(90px, 90px, 0)'); + }); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)'); - }); + it('should not leave bounds from the top left', async () => { + const { getByText } = render(Draggable, { + bounds: { top: 0, bottom: 100, left: 0, right: 100 }, + }); - it('should be dragged by a touch event', async () => { - const { getByText } = render(Draggable, { - bounds: { top: 0, bottom: 100, left: 0, right: 100 }, - }); + const element = getByText('Drag me!'); - const element = getByText('Drag me!'); + expect(element).toBeInTheDocument(); - expect(element).toBeInTheDocument(); + await drag(element, 0, 0, -100, -100); - await touchDrag(element, 0, 0, 50, 50); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)'); - }); + it('should not leave bounds from the bottom right when touched', async () => { + const { getByText } = render(Draggable, { + bounds: { top: 0, bottom: 100, left: 0, right: 100 }, + }); - it('should not leave bounds from the bottom right', async () => { - const { getByText } = render(Draggable, { - bounds: { top: 0, bottom: 100, left: 0, right: 100 }, - }); + const element = getByText('Drag me!'); - const element = getByText('Drag me!'); + expect(element).toBeInTheDocument(); - expect(element).toBeInTheDocument(); + await touchDrag(element, 0, 0, 200, 200); - await drag(element, 0, 0, 200, 200); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(90px, 90px, 0)'); + }); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(90px, 90px, 0)'); - }); + it('should not leave bounds from the top left when touched', async () => { + const { getByText } = render(Draggable, { + bounds: { top: 0, bottom: 100, left: 0, right: 100 }, + }); - it('should not leave bounds from the top left', async () => { - const { getByText } = render(Draggable, { - bounds: { top: 0, bottom: 100, left: 0, right: 100 }, - }); + const element = getByText('Drag me!'); - const element = getByText('Drag me!'); + expect(element).toBeInTheDocument(); - expect(element).toBeInTheDocument(); + await touchDrag(element, 0, 0, -100, -100); - await drag(element, 0, 0, -100, -100); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); + }); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); + describe('bound to the x axis', () => { + it('renders a basic div', () => { + const { getByText } = render(Draggable, { axis: 'x' }); - it('should not leave bounds from the bottom right when touched', async () => { - const { getByText } = render(Draggable, { - bounds: { top: 0, bottom: 100, left: 0, right: 100 }, - }); + const element = getByText('Drag me!'); - const element = getByText('Drag me!'); + expect(element).toBeInTheDocument(); + expect(element).not.toHaveClass('svelte-draggable'); + expect(element).not.toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); - expect(element).toBeInTheDocument(); + it('should only drag on the X axis', async () => { + const { getByText } = render(Draggable, { axis: 'x' }); - await touchDrag(element, 0, 0, 200, 200); + const element = getByText('Drag me!'); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(90px, 90px, 0)'); - }); + expect(element).toBeInTheDocument(); - it('should not leave bounds from the top left when touched', async () => { - const { getByText } = render(Draggable, { - bounds: { top: 0, bottom: 100, left: 0, right: 100 }, - }); + await drag(element, 0, 0, 50, 50); - const element = getByText('Drag me!'); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(50px, 0px, 0)'); + }); - expect(element).toBeInTheDocument(); + it('should only drag by touch on the X axis', async () => { + const { getByText } = render(Draggable, { axis: 'x' }); - await touchDrag(element, 0, 0, -100, -100); + const element = getByText('Drag me!'); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); - }); + expect(element).toBeInTheDocument(); - describe('bound to the x axis', () => { - it('renders a basic div', () => { - const { getByText } = render(Draggable, { axis: 'x' }); + await touchDrag(element, 0, 0, 50, 50); - const element = getByText('Drag me!'); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(50px, 0px, 0)'); + }); + }); - expect(element).toBeInTheDocument(); - expect(element).not.toHaveClass('svelte-draggable'); - expect(element).not.toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); + describe('bound to the y axis', () => { + it('renders a basic div', () => { + const { getByText } = render(Draggable, { axis: 'y' }); - it('should only drag on the X axis', async () => { - const { getByText } = render(Draggable, { axis: 'x' }); + const element = getByText('Drag me!'); - const element = getByText('Drag me!'); + expect(element).toBeInTheDocument(); + expect(element).not.toHaveClass('svelte-draggable'); + expect(element).not.toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); - expect(element).toBeInTheDocument(); + it('should only drag on the Y axis', async () => { + const { getByText } = render(Draggable, { axis: 'y' }); - await drag(element, 0, 0, 50, 50); + const element = getByText('Drag me!'); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(50px, 0px, 0)'); - }); + expect(element).toBeInTheDocument(); - it('should only drag by touch on the X axis', async () => { - const { getByText } = render(Draggable, { axis: 'x' }); + await drag(element, 0, 0, 50, 50); - const element = getByText('Drag me!'); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 50px, 0)'); + }); - expect(element).toBeInTheDocument(); + it('should only drag by touch on the Y axis', async () => { + const { getByText } = render(Draggable, { axis: 'y' }); - await touchDrag(element, 0, 0, 50, 50); + const element = getByText('Drag me!'); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(50px, 0px, 0)'); - }); - }); + expect(element).toBeInTheDocument(); - describe('bound to the y axis', () => { - it('renders a basic div', () => { - const { getByText } = render(Draggable, { axis: 'y' }); + await touchDrag(element, 0, 0, 50, 50); - const element = getByText('Drag me!'); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 50px, 0)'); + }); + }); - expect(element).toBeInTheDocument(); - expect(element).not.toHaveClass('svelte-draggable'); - expect(element).not.toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); + describe('bound to the none axis', () => { + it('renders a basic div', () => { + const { getByText } = render(Draggable, { axis: 'none' }); - it('should only drag on the Y axis', async () => { - const { getByText } = render(Draggable, { axis: 'y' }); + const element = getByText('Drag me!'); - const element = getByText('Drag me!'); + expect(element).toBeInTheDocument(); + expect(element).not.toHaveClass('svelte-draggable'); + expect(element).not.toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); - expect(element).toBeInTheDocument(); + it('should not drag', async () => { + const { getByText } = render(Draggable, { axis: 'none' }); - await drag(element, 0, 0, 50, 50); + const element = getByText('Drag me!'); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 50px, 0)'); - }); + expect(element).toBeInTheDocument(); - it('should only drag by touch on the Y axis', async () => { - const { getByText } = render(Draggable, { axis: 'y' }); + await drag(element, 0, 0, 50, 50); - const element = getByText('Drag me!'); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); - expect(element).toBeInTheDocument(); + it('should not drag by touch', async () => { + const { getByText } = render(Draggable, { axis: 'none' }); - await touchDrag(element, 0, 0, 50, 50); + const element = getByText('Drag me!'); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 50px, 0)'); - }); - }); + expect(element).toBeInTheDocument(); - describe('bound to the none axis', () => { - it('renders a basic div', () => { - const { getByText } = render(Draggable, { axis: 'none' }); + await touchDrag(element, 0, 0, 50, 50); - const element = getByText('Drag me!'); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); + }); - expect(element).toBeInTheDocument(); - expect(element).not.toHaveClass('svelte-draggable'); - expect(element).not.toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); + describe('grid', () => { + it('renders a basic div', () => { + const { getByText } = render(Draggable, { grid: [20, 20] }); - it('should not drag', async () => { - const { getByText } = render(Draggable, { axis: 'none' }); + const element = getByText('Drag me!'); - const element = getByText('Drag me!'); + expect(element).toBeInTheDocument(); + expect(element).not.toHaveClass('svelte-draggable'); + expect(element).not.toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); - expect(element).toBeInTheDocument(); + it('should restrict movement to the grid', async () => { + const { getByText } = render(Draggable, { grid: [20, 20] }); - await drag(element, 0, 0, 50, 50); + const element = getByText('Drag me!'); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); + expect(element).toBeInTheDocument(); - it('should not drag by touch', async () => { - const { getByText } = render(Draggable, { axis: 'none' }); + await drag(element, 0, 0, 51, 51); - const element = getByText('Drag me!'); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(60px, 60px, 0)'); + }); - expect(element).toBeInTheDocument(); + it('should restrict movement to an uneven grid', async () => { + const { getByText } = render(Draggable, { grid: [15, 20] }); - await touchDrag(element, 0, 0, 50, 50); + const element = getByText('Drag me!'); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); - }); + expect(element).toBeInTheDocument(); - describe('grid', () => { - it('renders a basic div', () => { - const { getByText } = render(Draggable, { grid: [20, 20] }); + await drag(element, 0, 0, 51, 51); - const element = getByText('Drag me!'); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(45px, 60px, 0)'); + }); + }); - expect(element).toBeInTheDocument(); - expect(element).not.toHaveClass('svelte-draggable'); - expect(element).not.toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); + describe('gpuAcceleration', () => { + it('should use translate3d when enabled', () => { + const { getByText } = render(Draggable, { gpuAcceleration: true }); - it('should restrict movement to the grid', async () => { - const { getByText } = render(Draggable, { grid: [20, 20] }); + const element = getByText('Drag me!'); - const element = getByText('Drag me!'); + expect(element).toBeInTheDocument(); + expect(element).not.toHaveClass('svelte-draggable'); + expect(element).not.toHaveClass('svelte-draggable-dragged'); + expect(element).not.toHaveStyle('transform: translate(0px, 0px)'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); - expect(element).toBeInTheDocument(); + it('should use translate when disabled', () => { + const { getByText } = render(Draggable, { gpuAcceleration: false }); - await drag(element, 0, 0, 51, 51); + const element = getByText('Drag me!'); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(60px, 60px, 0)'); - }); + expect(element).toBeInTheDocument(); + expect(element).not.toHaveClass('svelte-draggable'); + expect(element).not.toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate(0px, 0px)'); + expect(element).not.toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); - it('should restrict movement to an uneven grid', async () => { - const { getByText } = render(Draggable, { grid: [15, 20] }); + it('should drag when disabled', async () => { + const { getByText } = render(Draggable, { gpuAcceleration: false }); - const element = getByText('Drag me!'); + const element = getByText('Drag me!'); - expect(element).toBeInTheDocument(); + expect(element).toBeInTheDocument(); - await drag(element, 0, 0, 51, 51); + await drag(element, 0, 0, 50, 50); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(45px, 60px, 0)'); - }); - }); - - describe('gpuAcceleration', () => { - it('should use translate3d when enabled', () => { - const { getByText } = render(Draggable, { gpuAcceleration: true }); - - const element = getByText('Drag me!'); - - expect(element).toBeInTheDocument(); - expect(element).not.toHaveClass('svelte-draggable'); - expect(element).not.toHaveClass('svelte-draggable-dragged'); - expect(element).not.toHaveStyle('transform: translate(0px, 0px)'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); - - it('should use translate when disabled', () => { - const { getByText } = render(Draggable, { gpuAcceleration: false }); - - const element = getByText('Drag me!'); - - expect(element).toBeInTheDocument(); - expect(element).not.toHaveClass('svelte-draggable'); - expect(element).not.toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate(0px, 0px)'); - expect(element).not.toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); - - it('should drag when disabled', async () => { - const { getByText } = render(Draggable, { gpuAcceleration: false }); - - const element = getByText('Drag me!'); - - expect(element).toBeInTheDocument(); - - await drag(element, 0, 0, 50, 50); - - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate(50px, 50px)'); - }); - }); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate(50px, 50px)'); + }); + }); }); diff --git a/tests/HandleDraggable.spec.ts b/tests/HandleDraggable.spec.ts index f6d7c7ad..af84948b 100644 --- a/tests/HandleDraggable.spec.ts +++ b/tests/HandleDraggable.spec.ts @@ -6,79 +6,79 @@ import HandleDraggable from './components/HandleDraggable.svelte'; import { drag, touchDrag } from './testHelpers'; describe('CancelDraggable', () => { - it('renders a basic div', () => { - const { getByText } = render(HandleDraggable); + it('renders a basic div', () => { + const { getByText } = render(HandleDraggable); - const element = getByText('You shall not drag!!'); - const handleElement = getByText('This will drag!'); + const element = getByText('You shall not drag!!'); + const handleElement = getByText('This will drag!'); - expect(element).toBeInTheDocument(); - expect(handleElement).toBeInTheDocument(); + expect(element).toBeInTheDocument(); + expect(handleElement).toBeInTheDocument(); - expect(element).not.toHaveClass('svelte-draggable'); - expect(element).not.toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); + expect(element).not.toHaveClass('svelte-draggable'); + expect(element).not.toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); - it('should drag by the handle element', async () => { - const { getByText } = render(HandleDraggable); + it('should drag by the handle element', async () => { + const { getByText } = render(HandleDraggable); - const element = getByText('You shall not drag!!'); - const handleElement = getByText('This will drag!'); + const element = getByText('You shall not drag!!'); + const handleElement = getByText('This will drag!'); - expect(element).toBeInTheDocument(); - expect(handleElement).toBeInTheDocument(); + expect(element).toBeInTheDocument(); + expect(handleElement).toBeInTheDocument(); - await drag(handleElement, 0, 0, 50, 50); + await drag(handleElement, 0, 0, 50, 50); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)'); - }); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)'); + }); - it('should not drag by the main element', async () => { - const { getByText } = render(HandleDraggable); + it('should not drag by the main element', async () => { + const { getByText } = render(HandleDraggable); - const element = getByText('You shall not drag!!'); - const handleElement = getByText('This will drag!'); + const element = getByText('You shall not drag!!'); + const handleElement = getByText('This will drag!'); - expect(element).toBeInTheDocument(); - expect(handleElement).toBeInTheDocument(); + expect(element).toBeInTheDocument(); + expect(handleElement).toBeInTheDocument(); - await drag(element, 0, 0, 50, 50); + await drag(element, 0, 0, 50, 50); - expect(element).toHaveClass('svelte-draggable'); - expect(element).not.toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); + expect(element).toHaveClass('svelte-draggable'); + expect(element).not.toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); - it('should drag by the handle element by touch', async () => { - const { getByText } = render(HandleDraggable); + it('should drag by the handle element by touch', async () => { + const { getByText } = render(HandleDraggable); - const element = getByText('You shall not drag!!'); - const handleElement = getByText('This will drag!'); + const element = getByText('You shall not drag!!'); + const handleElement = getByText('This will drag!'); - expect(element).toBeInTheDocument(); - expect(handleElement).toBeInTheDocument(); + expect(element).toBeInTheDocument(); + expect(handleElement).toBeInTheDocument(); - await touchDrag(handleElement, 0, 0, 50, 50); + await touchDrag(handleElement, 0, 0, 50, 50); - expect(element).toHaveClass('svelte-draggable'); - expect(element).toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)'); - }); + expect(element).toHaveClass('svelte-draggable'); + expect(element).toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)'); + }); - it('should not drag by the main element by touch', async () => { - const { getByText } = render(HandleDraggable); + it('should not drag by the main element by touch', async () => { + const { getByText } = render(HandleDraggable); - const element = getByText('You shall not drag!!'); + const element = getByText('You shall not drag!!'); - expect(element).toBeInTheDocument(); + expect(element).toBeInTheDocument(); - await touchDrag(element, 0, 0, 50, 50); + await touchDrag(element, 0, 0, 50, 50); - expect(element).toHaveClass('svelte-draggable'); - expect(element).not.toHaveClass('svelte-draggable-dragged'); - expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); - }); + expect(element).toHaveClass('svelte-draggable'); + expect(element).not.toHaveClass('svelte-draggable-dragged'); + expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)'); + }); }); diff --git a/tests/components/CancelDraggable.svelte b/tests/components/CancelDraggable.svelte index d8afac71..beba3c5e 100644 --- a/tests/components/CancelDraggable.svelte +++ b/tests/components/CancelDraggable.svelte @@ -1,8 +1,8 @@
- This will drag! -
You shall not drag!!
+ This will drag! +
You shall not drag!!
diff --git a/tests/components/Draggable.svelte b/tests/components/Draggable.svelte index f92aebac..b72fb217 100644 --- a/tests/components/Draggable.svelte +++ b/tests/components/Draggable.svelte @@ -1,12 +1,12 @@
{body}
diff --git a/tests/components/HandleDraggable.svelte b/tests/components/HandleDraggable.svelte index 78cbe7dc..886f4595 100644 --- a/tests/components/HandleDraggable.svelte +++ b/tests/components/HandleDraggable.svelte @@ -1,8 +1,8 @@
- You shall not drag!! -
This will drag!
+ You shall not drag!! +
This will drag!
diff --git a/tests/testHelpers.ts b/tests/testHelpers.ts index bd4c1cb0..6d9e91b3 100644 --- a/tests/testHelpers.ts +++ b/tests/testHelpers.ts @@ -8,30 +8,30 @@ import { fireEvent } from '@testing-library/svelte'; * @param clientY the Y coordinate to drag the element to */ export async function drag(element: HTMLElement, startX = 0, startY = 0, endX = 0, endY = 0) { - await fireEvent.mouseEnter(element); - await fireEvent.mouseOver(element); - await fireEvent.mouseDown(element, { clientX: startX, clientY: startY }); - await fireEvent.mouseMove(element, { clientX: endX, clientY: endY }); - await fireEvent.mouseUp(element); + await fireEvent.mouseEnter(element); + await fireEvent.mouseOver(element); + await fireEvent.mouseDown(element, { clientX: startX, clientY: startY }); + await fireEvent.mouseMove(element, { clientX: endX, clientY: endY }); + await fireEvent.mouseUp(element); } function createTouchList(clientX: number, clientY: number) { - let touches: any = { - item: (index: number) => { - return { - clientX, - clientY, - }; - }, - length: 1, - 0: { clientX, clientY }, - }; + let touches: any = { + item: (index: number) => { + return { + clientX, + clientY, + }; + }, + length: 1, + 0: { clientX, clientY }, + }; - touches[Symbol.iterator] = function* () { - yield { clientX, clientY }; - }; + touches[Symbol.iterator] = function* () { + yield { clientX, clientY }; + }; - return touches; + return touches; } /** @@ -44,7 +44,7 @@ function createTouchList(clientX: number, clientY: number) { * @param endY the Y coordinate to drag the element to */ export async function touchDrag(element: HTMLElement, startX = 0, startY = 0, endX = 0, endY = 0) { - await fireEvent.touchStart(element, { touches: createTouchList(startX, startY) }); - await fireEvent.touchMove(element, { touches: createTouchList(endX, endY) }); - await fireEvent.touchEnd(element, { touches: createTouchList(endX, endY) }); + await fireEvent.touchStart(element, { touches: createTouchList(startX, startY) }); + await fireEvent.touchMove(element, { touches: createTouchList(endX, endY) }); + await fireEvent.touchEnd(element, { touches: createTouchList(endX, endY) }); } diff --git a/tsconfig.json b/tsconfig.json index 659194cf..22ebb823 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,13 +1,13 @@ { - "compilerOptions": { - "module": "ES2020", - "target": "ES2019", - "declaration": true, - "emitDeclarationOnly": true, - "declarationDir": "dist/", - "strict": true, - "esModuleInterop": true, - "moduleResolution": "Node" - }, - "files": ["./src/index.ts"] + "compilerOptions": { + "module": "ES2020", + "target": "ES2019", + "declaration": true, + "emitDeclarationOnly": true, + "declarationDir": "dist/", + "strict": true, + "esModuleInterop": true, + "moduleResolution": "Node" + }, + "files": ["./src/index.ts"] } diff --git a/tsup.config.ts b/tsup.config.ts index eb5cef6e..10ad6292 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -1,12 +1,12 @@ import { defineConfig } from 'tsup'; export default defineConfig({ - sourcemap: true, - clean: true, - dts: true, - format: ['esm', 'cjs'], - external: [], - entryPoints: ['src/index.ts'], - minify: false, - target: 'es2018', + sourcemap: true, + clean: true, + dts: true, + format: ['esm', 'cjs'], + external: [], + entryPoints: ['src/index.ts'], + minify: false, + target: 'es2018', });