From 4ad6b66e8f25bf2864c301d95b15330494b7bae4 Mon Sep 17 00:00:00 2001 From: Alexey Iskhakov Date: Wed, 20 Sep 2023 00:18:46 +0000 Subject: [PATCH 1/6] fix: #61 - allow three different ways of dealing with the problem --- .../options/touchAction/+option.mdx | 21 +++++++++ package.json | 1 + packages/core/src/index.ts | 45 ++++++++++++++++++- 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 docs/src/documentation/options/touchAction/+option.mdx diff --git a/docs/src/documentation/options/touchAction/+option.mdx b/docs/src/documentation/options/touchAction/+option.mdx new file mode 100644 index 00000000..8dd0f54d --- /dev/null +++ b/docs/src/documentation/options/touchAction/+option.mdx @@ -0,0 +1,21 @@ +--- +title: touchAction +type: 'Property.TouchAction | ((el: HTMLElement, setTouchAction: (action: Property.TouchAction) => void) => void)' +defaultValue: 'none' +--- + +import Code from '$components/options/OptionsCode.astro'; +import Example from '$components/options/OptionsExample.astro'; +import Examples from '$components/options/OptionsExamples.svelte'; + +export const shortDescription = + 'Choose when and how to set "touch-action" style for mobile devices'; + +

{shortDescription}

+ +By default, the "touch-action" style is set to `none` upon initalizing the draggable on an element. + +This option allows to change the default to some other value in a number of ways: +1. Simply set to another value for any draggable elements that use this draggable instance. +2. Set to different values based on amount of pixels the element was dragged. +3. Set a callback to assign the property to a different value for a specific element based on any arbitrary logic. diff --git a/package.json b/package.json index 99eaaab2..b7489199 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", "brotli-size": "^4.0.0", + "csstype": "^3.1.2", "fast-glob": "^3.2.12", "jsdom": "^21.1.0", "nx": "15.7.2", diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 59a1aaae..3149865a 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,3 +1,5 @@ +import type { Property } from 'csstype'; + export type DragBoundsCoords = { /** Number of pixels from left of the document */ left: number; @@ -182,6 +184,14 @@ export type DragOptions = { */ handle?: string | HTMLElement | HTMLElement[]; + /** + * Choose when and how to set touch action for mobile devices + */ + touchAction?: + | Property.TouchAction + | Partial> + | ((el: HTMLElement, setTouchAction: (action: Property.TouchAction) => void) => void); + /** * 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 @@ -258,6 +268,8 @@ export const draggable = (node: HTMLElement, options: DragOptions = {}) => { cancel, handle, + touchAction = 'none', + defaultClass = DEFAULT_CLASS.MAIN, defaultClassDragging = DEFAULT_CLASS.DRAGGING, defaultClassDragged = DEFAULT_CLASS.DRAGGED, @@ -361,8 +373,32 @@ export const draggable = (node: HTMLElement, options: DragOptions = {}) => { listen('pointerup', dragEnd, false); listen('pointermove', drag, false); + let setTouchAction: undefined | ((threshold: number) => void) = undefined; + // On mobile, touch can become extremely janky without it - setStyle(node, 'touch-action', 'none'); + if (typeof touchAction === 'function') { + touchAction(node, (action) => setStyle(node, 'touch-action', action)) + } else if (typeof touchAction === 'string') { + setStyle(node, 'touch-action', touchAction); + } else { + const thresholds = Object.keys(touchAction).map(threshold => Number(threshold)).sort(); + + setTouchAction = (threshold: number) => { + const crossedThreshold = thresholds.find(value => threshold >= value); + + if ( + typeof touchAction !== 'object' || + typeof crossedThreshold !== 'number' || + (typeof touchAction === 'object' && !(crossedThreshold in touchAction)) + ) return; + + const touchActionValue = touchAction[crossedThreshold]; + + if (typeof touchActionValue !== 'string') return; + + setStyle(node, 'touch-action', touchActionValue); + }; + } const calculateInverseScale = () => { // Calculate the current scale of the node @@ -507,6 +543,13 @@ export const draggable = (node: HTMLElement, options: DragOptions = {}) => { xOffset = translateX; yOffset = translateY; + if (setTouchAction) { + const initialVector = (initialX * initialX + initialY * initialY); + const finalVector = (finalX * finalX + finalY * finalY); + + setTouchAction(finalVector - initialVector); + } + fireSvelteDragEvent(); setTranslate(); From 2b2cfaceae4b281a44befc7dbc14370b5d6372f6 Mon Sep 17 00:00:00 2001 From: Alexey Iskhakov Date: Wed, 20 Sep 2023 00:24:49 +0000 Subject: [PATCH 2/6] chore: update pnpm lock file --- pnpm-lock.yaml | 65 +++++++++++++------------------------------------- 1 file changed, 17 insertions(+), 48 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 63a39d11..40709e2e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: brotli-size: specifier: ^4.0.0 version: 4.0.0 + csstype: + specifier: ^3.1.2 + version: 3.1.2 fast-glob: specifier: ^3.2.12 version: 3.2.12 @@ -139,7 +142,7 @@ importers: version: 5.0.0 astro: specifier: ^2.0.14 - version: 2.0.14(sass@1.58.3) + version: 2.0.14(sass@1.58.3)(terser@5.16.4) astrojs-service-worker: specifier: ^0.0.9 version: 0.0.9(astro@2.0.14) @@ -196,7 +199,7 @@ importers: version: 0.15.3 vite: specifier: ^4.1.3 - version: 4.1.3(sass@1.58.3) + version: 4.1.3(sass@1.58.3)(terser@5.16.4) packages/core: devDependencies: @@ -418,7 +421,7 @@ packages: astro: ^2.0.2 dependencies: '@astrojs/prism': 2.0.0 - astro: 2.0.14(sass@1.58.3) + astro: 2.0.14(sass@1.58.3)(terser@5.16.4) github-slugger: 1.5.0 import-meta-resolve: 2.2.1 rehype-raw: 6.1.1 @@ -490,7 +493,7 @@ packages: svelte: ^3.54.0 dependencies: '@sveltejs/vite-plugin-svelte': 2.0.2(svelte@3.55.1)(vite@4.1.3) - astro: 2.0.14(sass@1.58.3) + astro: 2.0.14(sass@1.58.3)(terser@5.16.4) svelte: 3.55.1 svelte2tsx: 0.5.23(svelte@3.55.1)(typescript@4.9.5) transitivePeerDependencies: @@ -2910,7 +2913,7 @@ packages: magic-string: 0.27.0 svelte: 3.55.1 svelte-hmr: 0.15.1(svelte@3.55.1) - vite: 4.1.3(sass@1.58.3) + vite: 4.1.3(terser@5.16.4) vitefu: 0.2.4(vite@4.1.3) transitivePeerDependencies: - supports-color @@ -3139,7 +3142,7 @@ packages: dependencies: '@types/prop-types': 15.7.5 '@types/scheduler': 0.16.2 - csstype: 3.1.1 + csstype: 3.1.2 dev: true /@types/resolve@1.17.1: @@ -3571,7 +3574,7 @@ packages: resolution: {integrity: sha512-8Yo1nQrl2iB5zj+VUAHrvGIck8bZ4WAmXQgJOeF9FuAZGQD5qK6DtSZaRbpBXsFfZYHA6tE8pjs3btHhLbC1zQ==} dev: false - /astro@2.0.14(sass@1.58.3): + /astro@2.0.14(sass@1.58.3)(terser@5.16.4): resolution: {integrity: sha512-BiXnHyK3rj5Uz45V5p9jRi0xtJc/zxhCxnXYAekHHF1bVvvoa3aXMwl0GZ3Bc0mxP6vPLmbRcjNKdqfyZn1B3Q==} engines: {node: '>=16.12.0', npm: '>=6.14.0'} hasBin: true @@ -3624,7 +3627,7 @@ packages: typescript: 4.9.5 unist-util-visit: 4.1.2 vfile: 5.3.7 - vite: 4.1.3(sass@1.58.3) + vite: 4.1.3(sass@1.58.3)(terser@5.16.4) vitefu: 0.2.4(vite@4.1.3) yargs-parser: 21.1.1 zod: 3.20.6 @@ -3643,7 +3646,7 @@ packages: peerDependencies: astro: '>=1.0.0' dependencies: - astro: 2.0.14(sass@1.58.3) + astro: 2.0.14(sass@1.58.3)(terser@5.16.4) workbox-build: 6.5.4 transitivePeerDependencies: - '@types/babel__core' @@ -4261,8 +4264,8 @@ packages: /csstype@2.6.21: resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==} - /csstype@3.1.1: - resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} + /csstype@3.1.2: + resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} /csv-generate@3.4.3: resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==} @@ -8506,7 +8509,7 @@ packages: /solid-js@1.6.11: resolution: {integrity: sha512-JquQQHPArGq+i2PLURxJ99Pcz2/1docpbycSio/cKSA0SeI3z5zRjy0TNcH4NRYvbOLrcini+iovXwnexKabyw==} dependencies: - csstype: 3.1.1 + csstype: 3.1.2 /solid-refresh@0.4.3(solid-js@1.6.11): resolution: {integrity: sha512-7+4/gYsVi0BlM4PzT1PU1TB5nW3Hv8FWuB+Kw/ofWui7KQkWBf+dVZOrReQYHEmLCzytHUa2JysUXgzVALJmSw==} @@ -8833,7 +8836,7 @@ packages: resolution: {integrity: sha512-wTGLVlhWJPauViy56AdtJop0fTaEJMqnCadjrlhcIe76R+KrnW+QvHBx69Xots8GaXgds8y+yBAr0cV7YNIeGw==} dependencies: clsx: 1.2.1 - csstype: 3.1.1 + csstype: 3.1.2 dev: false /svelte-copy@1.3.0: @@ -9735,40 +9738,6 @@ packages: fsevents: 2.3.2 dev: true - /vite@4.1.3(sass@1.58.3): - resolution: {integrity: sha512-0Zqo4/Fr/swSOBmbl+HAAhOjrqNwju+yTtoe4hQX9UsARdcuc9njyOdr6xU0DDnV7YP0RT6mgTTOiRtZgxfCxA==} - engines: {node: ^14.18.0 || >=16.0.0} - hasBin: true - peerDependencies: - '@types/node': '>= 14' - less: '*' - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - dependencies: - esbuild: 0.16.17 - postcss: 8.4.21 - resolve: 1.22.1 - rollup: 3.17.2 - sass: 1.58.3 - optionalDependencies: - fsevents: 2.3.2 - dev: true - /vite@4.1.3(sass@1.58.3)(terser@5.16.4): resolution: {integrity: sha512-0Zqo4/Fr/swSOBmbl+HAAhOjrqNwju+yTtoe4hQX9UsARdcuc9njyOdr6xU0DDnV7YP0RT6mgTTOiRtZgxfCxA==} engines: {node: ^14.18.0 || >=16.0.0} @@ -9846,7 +9815,7 @@ packages: vite: optional: true dependencies: - vite: 4.1.3(sass@1.58.3) + vite: 4.1.3(terser@5.16.4) dev: true /vitest@0.28.5(jsdom@21.1.0)(terser@5.16.4): From 97d417cd15f27efe6a709e5a66610404c504a9b3 Mon Sep 17 00:00:00 2001 From: Alexey Iskhakov Date: Wed, 20 Sep 2023 00:43:57 +0000 Subject: [PATCH 3/6] fix: pick last touch action threshold instead of the first --- packages/core/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 3149865a..443f9b00 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -384,7 +384,7 @@ export const draggable = (node: HTMLElement, options: DragOptions = {}) => { const thresholds = Object.keys(touchAction).map(threshold => Number(threshold)).sort(); setTouchAction = (threshold: number) => { - const crossedThreshold = thresholds.find(value => threshold >= value); + const crossedThreshold = thresholds.findLast(value => threshold >= value); if ( typeof touchAction !== 'object' || From 9b54e46f7763f16c6767be3bebb7a23d74dc67d0 Mon Sep 17 00:00:00 2001 From: Alexey Iskhakov Date: Wed, 20 Sep 2023 00:47:08 +0000 Subject: [PATCH 4/6] fix: invert threshold sort instead of taking the last item --- packages/core/src/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 443f9b00..cd7ac892 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -381,10 +381,12 @@ export const draggable = (node: HTMLElement, options: DragOptions = {}) => { } else if (typeof touchAction === 'string') { setStyle(node, 'touch-action', touchAction); } else { - const thresholds = Object.keys(touchAction).map(threshold => Number(threshold)).sort(); + const thresholds = Object.keys(touchAction) + .map(threshold => Number(threshold)) + .sort((x, y) => y - x); setTouchAction = (threshold: number) => { - const crossedThreshold = thresholds.findLast(value => threshold >= value); + const crossedThreshold = thresholds.find(value => threshold >= value); if ( typeof touchAction !== 'object' || From 9ff1d71e67b96026fb6413fd2aae1f98e56b1078 Mon Sep 17 00:00:00 2001 From: Alexey Iskhakov Date: Wed, 20 Sep 2023 01:45:50 +0000 Subject: [PATCH 5/6] fix: remove complicated touch-action options, instead properly trigger drag-end on mobile --- .../options/touchAction/+option.mdx | 7 +-- packages/core/src/index.ts | 43 ++----------------- 2 files changed, 5 insertions(+), 45 deletions(-) diff --git a/docs/src/documentation/options/touchAction/+option.mdx b/docs/src/documentation/options/touchAction/+option.mdx index 8dd0f54d..4d0f8bf5 100644 --- a/docs/src/documentation/options/touchAction/+option.mdx +++ b/docs/src/documentation/options/touchAction/+option.mdx @@ -1,6 +1,6 @@ --- title: touchAction -type: 'Property.TouchAction | ((el: HTMLElement, setTouchAction: (action: Property.TouchAction) => void) => void)' +type: 'string' defaultValue: 'none' --- @@ -14,8 +14,3 @@ export const shortDescription =

{shortDescription}

By default, the "touch-action" style is set to `none` upon initalizing the draggable on an element. - -This option allows to change the default to some other value in a number of ways: -1. Simply set to another value for any draggable elements that use this draggable instance. -2. Set to different values based on amount of pixels the element was dragged. -3. Set a callback to assign the property to a different value for a specific element based on any arbitrary logic. diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index cd7ac892..e3535a8c 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -185,12 +185,9 @@ export type DragOptions = { handle?: string | HTMLElement | HTMLElement[]; /** - * Choose when and how to set touch action for mobile devices + * Choose to set the initial touch-action of an element to a different value than 'none' */ - touchAction?: - | Property.TouchAction - | Partial> - | ((el: HTMLElement, setTouchAction: (action: Property.TouchAction) => void) => void); + touchAction?: Property.TouchAction; /** * Class to apply on the element on which `use:draggable` is applied. @@ -371,36 +368,11 @@ export const draggable = (node: HTMLElement, options: DragOptions = {}) => { listen('pointerdown', dragStart, false); listen('pointerup', dragEnd, false); + listen('touchend', dragEnd, false); listen('pointermove', drag, false); - let setTouchAction: undefined | ((threshold: number) => void) = undefined; - // On mobile, touch can become extremely janky without it - if (typeof touchAction === 'function') { - touchAction(node, (action) => setStyle(node, 'touch-action', action)) - } else if (typeof touchAction === 'string') { - setStyle(node, 'touch-action', touchAction); - } else { - const thresholds = Object.keys(touchAction) - .map(threshold => Number(threshold)) - .sort((x, y) => y - x); - - setTouchAction = (threshold: number) => { - const crossedThreshold = thresholds.find(value => threshold >= value); - - if ( - typeof touchAction !== 'object' || - typeof crossedThreshold !== 'number' || - (typeof touchAction === 'object' && !(crossedThreshold in touchAction)) - ) return; - - const touchActionValue = touchAction[crossedThreshold]; - - if (typeof touchActionValue !== 'string') return; - - setStyle(node, 'touch-action', touchActionValue); - }; - } + setStyle(node, 'touch-action', touchAction); const calculateInverseScale = () => { // Calculate the current scale of the node @@ -545,13 +517,6 @@ export const draggable = (node: HTMLElement, options: DragOptions = {}) => { xOffset = translateX; yOffset = translateY; - if (setTouchAction) { - const initialVector = (initialX * initialX + initialY * initialY); - const finalVector = (finalX * finalX + finalY * finalY); - - setTouchAction(finalVector - initialVector); - } - fireSvelteDragEvent(); setTranslate(); From 3c6c6f4b6c8dd9d59b21d0b04db98401f72d8ecd Mon Sep 17 00:00:00 2001 From: Alexey Iskhakov Date: Wed, 20 Sep 2023 02:16:24 +0000 Subject: [PATCH 6/6] docs: update touchAction description --- docs/src/documentation/options/touchAction/+option.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/documentation/options/touchAction/+option.mdx b/docs/src/documentation/options/touchAction/+option.mdx index 4d0f8bf5..75f5f14f 100644 --- a/docs/src/documentation/options/touchAction/+option.mdx +++ b/docs/src/documentation/options/touchAction/+option.mdx @@ -9,7 +9,7 @@ import Example from '$components/options/OptionsExample.astro'; import Examples from '$components/options/OptionsExamples.svelte'; export const shortDescription = - 'Choose when and how to set "touch-action" style for mobile devices'; + 'Choose to set the initial touch-action of an element to a different value than "none"';

{shortDescription}