Skip to content

Commit

Permalink
Reorderings with Long Press
Browse files Browse the repository at this point in the history
Swapping to a Long Press to drag and reorders files signals intent. Without this intent, the reordering feature can hijack scrolling on touch-devices, and users can become unintentionally trapped reordering a file. The long press restores user-intent and dramatically improves the UX on touch-devices.

Resolves pqina#865
  • Loading branch information
jakejackson1 committed Oct 16, 2023
1 parent 97bcbca commit 191ef51
Show file tree
Hide file tree
Showing 11 changed files with 107 additions and 47 deletions.
2 changes: 1 addition & 1 deletion dist/filepond.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*!
* FilePond 4.30.4
* FilePond 4.30.5
* Licensed under MIT, https://opensource.org/licenses/MIT/
* Please visit https://pqina.nl/filepond/ for details.
*/
Expand Down
48 changes: 34 additions & 14 deletions dist/filepond.esm.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*!
* FilePond 4.30.4
* FilePond 4.30.5
* Licensed under MIT, https://opensource.org/licenses/MIT/
* Please visit https://pqina.nl/filepond/ for details.
*/
Expand Down Expand Up @@ -1831,6 +1831,7 @@ const defaultOptions = {
allowRemove: [true, Type.BOOLEAN], // Allow user to remove a file
allowProcess: [true, Type.BOOLEAN], // Allows user to process a file, when set to false, this removes the file upload button
allowReorder: [false, Type.BOOLEAN], // Allow reordering of files
reorderLongPressInterval: [100, Type.INT], // The longpress interval to wait before reordering the file
allowDirectoriesOnly: [false, Type.BOOLEAN], // Allow only selecting directories with browse (no support for filtering dnd at this point)

// Try store file if `server` not set
Expand Down Expand Up @@ -5966,6 +5967,24 @@ const createDragHelper = items => {
};
};

const onLongPress = (element, callback, interval) => {
let timer;

element.addEventListener('pointerdown', e => {
timer = setTimeout(() => {
timer = null;
callback(e);
}, interval);
});

function cancel() {
clearTimeout(timer);
}

element.addEventListener('pointerup', cancel);
element.addEventListener('pointermove', cancel);
};

const ITEM_TRANSLATE_SPRING = {
type: 'spring',
stiffness: 0.75,
Expand Down Expand Up @@ -5993,6 +6012,8 @@ const StateMap = {
DID_REVERT_ITEM_PROCESSING: 'idle',
};

const prevent = e => e.preventDefault();

/**
* Creates the file view
*/
Expand Down Expand Up @@ -6026,6 +6047,7 @@ const create$7 = ({ root, props }) => {
if (!e.isPrimary) return;

let removedActivateListener = false;
const rootViewElement = root.element.closest('.filepond--root');

const origin = {
x: e.pageX,
Expand Down Expand Up @@ -6083,15 +6105,23 @@ const create$7 = ({ root, props }) => {

// start listening to clicks again
if (removedActivateListener) {
setTimeout(() => root.element.addEventListener('click', root.ref.handleClick), 0);
setTimeout(() => {
root.element.addEventListener('click', root.ref.handleClick);
rootViewElement.removeEventListener('touchmove', prevent);
rootViewElement.removeEventListener('gesturestart', prevent);
}, 0);
}
};

// prevent scrolling and zooming on mobile devices while dragging/dropping
rootViewElement.addEventListener('touchmove', prevent, { passive: false });
rootViewElement.addEventListener('gesturestart', prevent);

document.addEventListener('pointermove', drag);
document.addEventListener('pointerup', drop);
};

root.element.addEventListener('pointerdown', grab);
onLongPress(root.element, grab, root.query('GET_REORDER_LONG_PRESS_INTERVAL'));
};

const route$1 = createRoute({
Expand Down Expand Up @@ -7961,7 +7991,7 @@ const debounce = (func, interval = 16, immidiateOnly = true) => {

const MAX_FILES_LIMIT = 1000000;

const prevent = e => e.preventDefault();
const prevent$1 = e => e.preventDefault();

const create$e = ({ root, props }) => {
// Add id
Expand Down Expand Up @@ -8029,14 +8059,6 @@ const create$e = ({ root, props }) => {
root.ref.previousAspectRatio = null;
root.ref.updateHistory = [];

// prevent scrolling and zooming on iOS (only if supports pointer events, for then we can enable reorder)
const canHover = window.matchMedia('(pointer: fine) and (hover: hover)').matches;
const hasPointerEvents = 'PointerEvent' in window;
if (root.query('GET_ALLOW_REORDER') && hasPointerEvents && !canHover) {
root.element.addEventListener('touchmove', prevent, { passive: false });
root.element.addEventListener('gesturestart', prevent);
}

// add credits
const credits = root.query('GET_CREDITS');
const hasCredits = credits.length === 2;
Expand Down Expand Up @@ -8593,8 +8615,6 @@ const root = createView({
if (root.ref.hopper) {
root.ref.hopper.destroy();
}
root.element.removeEventListener('touchmove', prevent);
root.element.removeEventListener('gesturestart', prevent);
},
mixins: {
styles: ['height'],
Expand Down
4 changes: 2 additions & 2 deletions dist/filepond.esm.min.js

Large diffs are not rendered by default.

48 changes: 34 additions & 14 deletions dist/filepond.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*!
* FilePond 4.30.4
* FilePond 4.30.5
* Licensed under MIT, https://opensource.org/licenses/MIT/
* Please visit https://pqina.nl/filepond/ for details.
*/
Expand Down Expand Up @@ -3757,6 +3757,7 @@
allowRemove: [true, Type.BOOLEAN], // Allow user to remove a file
allowProcess: [true, Type.BOOLEAN], // Allows user to process a file, when set to false, this removes the file upload button
allowReorder: [false, Type.BOOLEAN], // Allow reordering of files
reorderLongPressInterval: [100, Type.INT], // The longpress interval to wait before reordering the file
allowDirectoriesOnly: [false, Type.BOOLEAN], // Allow only selecting directories with browse (no support for filtering dnd at this point)

// Try store file if `server` not set
Expand Down Expand Up @@ -8506,6 +8507,24 @@
};
};

var onLongPress = function onLongPress(element, callback, interval) {
var timer;

element.addEventListener('pointerdown', function(e) {
timer = setTimeout(function() {
timer = null;
callback(e);
}, interval);
});

function cancel() {
clearTimeout(timer);
}

element.addEventListener('pointerup', cancel);
element.addEventListener('pointermove', cancel);
};

var ITEM_TRANSLATE_SPRING = {
type: 'spring',
stiffness: 0.75,
Expand Down Expand Up @@ -8533,6 +8552,10 @@
DID_REVERT_ITEM_PROCESSING: 'idle',
};

var prevent = function prevent(e) {
return e.preventDefault();
};

/**
* Creates the file view
*/
Expand Down Expand Up @@ -8573,6 +8596,7 @@
if (!e.isPrimary) return;

var removedActivateListener = false;
var rootViewElement = root.element.closest('.filepond--root');

var origin = {
x: e.pageX,
Expand Down Expand Up @@ -8632,16 +8656,22 @@
// start listening to clicks again
if (removedActivateListener) {
setTimeout(function() {
return root.element.addEventListener('click', root.ref.handleClick);
root.element.addEventListener('click', root.ref.handleClick);
rootViewElement.removeEventListener('touchmove', prevent);
rootViewElement.removeEventListener('gesturestart', prevent);
}, 0);
}
};

// prevent scrolling and zooming on mobile devices while dragging/dropping
rootViewElement.addEventListener('touchmove', prevent, { passive: false });
rootViewElement.addEventListener('gesturestart', prevent);

document.addEventListener('pointermove', drag);
document.addEventListener('pointerup', drop);
};

root.element.addEventListener('pointerdown', grab);
onLongPress(root.element, grab, root.query('GET_REORDER_LONG_PRESS_INTERVAL'));
};

var route$1 = createRoute({
Expand Down Expand Up @@ -10744,7 +10774,7 @@

var MAX_FILES_LIMIT = 1000000;

var prevent = function prevent(e) {
var prevent$1 = function prevent(e) {
return e.preventDefault();
};

Expand Down Expand Up @@ -10828,14 +10858,6 @@
root.ref.previousAspectRatio = null;
root.ref.updateHistory = [];

// prevent scrolling and zooming on iOS (only if supports pointer events, for then we can enable reorder)
var canHover = window.matchMedia('(pointer: fine) and (hover: hover)').matches;
var hasPointerEvents = 'PointerEvent' in window;
if (root.query('GET_ALLOW_REORDER') && hasPointerEvents && !canHover) {
root.element.addEventListener('touchmove', prevent, { passive: false });
root.element.addEventListener('gesturestart', prevent);
}

// add credits
var credits = root.query('GET_CREDITS');
var hasCredits = credits.length === 2;
Expand Down Expand Up @@ -11451,8 +11473,6 @@
if (root.ref.hopper) {
root.ref.hopper.destroy();
}
root.element.removeEventListener('touchmove', prevent);
root.element.removeEventListener('gesturestart', prevent);
},
mixins: {
styles: ['height'],
Expand Down
2 changes: 1 addition & 1 deletion dist/filepond.min.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions dist/filepond.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "filepond",
"version": "4.30.4",
"version": "4.30.5",
"description": "FilePond, Where files go to stretch their bits.",
"license": "MIT",
"author": {
Expand Down
1 change: 1 addition & 0 deletions src/js/app/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export const defaultOptions = {
allowRemove: [true, Type.BOOLEAN], // Allow user to remove a file
allowProcess: [true, Type.BOOLEAN], // Allows user to process a file, when set to false, this removes the file upload button
allowReorder: [false, Type.BOOLEAN], // Allow reordering of files
reorderLongPressInterval: [100, Type.INT], // The longpress interval to wait before reordering the file
allowDirectoriesOnly: [false, Type.BOOLEAN], // Allow only selecting directories with browse (no support for filtering dnd at this point)

// Try store file if `server` not set
Expand Down
16 changes: 14 additions & 2 deletions src/js/app/view/item.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createView, createRoute } from '../frame/index';
import { fileWrapper } from './fileWrapper';
import { panel } from './panel';
import { createDragHelper } from '../utils/createDragHelper';
import { onLongPress } from '../../utils/onLongPress'

const ITEM_TRANSLATE_SPRING = {
type: 'spring',
Expand Down Expand Up @@ -31,6 +32,8 @@ const StateMap = {
DID_REVERT_ITEM_PROCESSING: 'idle'
};

const prevent = e => e.preventDefault();

/**
* Creates the file view
*/
Expand Down Expand Up @@ -70,6 +73,7 @@ const create = ({ root, props }) => {
if (!e.isPrimary) return;

let removedActivateListener = false;
const rootViewElement = root.element.closest('.filepond--root');

const origin = {
x: e.pageX,
Expand Down Expand Up @@ -128,15 +132,23 @@ const create = ({ root, props }) => {

// start listening to clicks again
if (removedActivateListener) {
setTimeout(() => root.element.addEventListener('click', root.ref.handleClick), 0);
setTimeout(() => {
root.element.addEventListener('click', root.ref.handleClick)
rootViewElement.removeEventListener('touchmove', prevent);
rootViewElement.removeEventListener('gesturestart', prevent);
}, 0);
}
};

// prevent scrolling and zooming on mobile devices while dragging/dropping
rootViewElement.addEventListener('touchmove', prevent, { passive: false });
rootViewElement.addEventListener('gesturestart', prevent);

document.addEventListener('pointermove', drag);
document.addEventListener('pointerup', drop);
}

root.element.addEventListener('pointerdown', grab);
onLongPress(root.element, grab, root.query('GET_REORDER_LONG_PRESS_INTERVAL'))
};

const route = createRoute({
Expand Down
10 changes: 0 additions & 10 deletions src/js/app/view/root.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,6 @@ const create = ({ root, props }) => {
root.ref.previousAspectRatio = null;
root.ref.updateHistory = [];

// prevent scrolling and zooming on iOS (only if supports pointer events, for then we can enable reorder)
const canHover = window.matchMedia('(pointer: fine) and (hover: hover)').matches;
const hasPointerEvents = 'PointerEvent' in window;
if (root.query('GET_ALLOW_REORDER') && hasPointerEvents && !canHover) {
root.element.addEventListener('touchmove', prevent, { passive: false });
root.element.addEventListener('gesturestart', prevent);
}

// add credits
const credits = root.query('GET_CREDITS');
const hasCredits = credits.length === 2;
Expand Down Expand Up @@ -654,8 +646,6 @@ export const root = createView({
if (root.ref.hopper) {
root.ref.hopper.destroy();
}
root.element.removeEventListener('touchmove', prevent);
root.element.removeEventListener('gesturestart', prevent);
},
mixins: {
styles: ['height'],
Expand Down
17 changes: 17 additions & 0 deletions src/js/utils/onLongPress.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const onLongPress = (element, callback, interval) => {
let timer;

element.addEventListener('pointerdown', (e) => {
timer = setTimeout(() => {
timer = null;
callback(e);
}, interval);
});

function cancel() {
clearTimeout(timer);
}

element.addEventListener('pointerup', cancel);
element.addEventListener('pointermove', cancel);
};

0 comments on commit 191ef51

Please sign in to comment.