From 290fd6dc66c245809e8944841aa6c5e70812ef51 Mon Sep 17 00:00:00 2001 From: Taye Adeyemi Date: Thu, 8 Mar 2018 16:44:00 +0100 Subject: [PATCH 1/6] reflow: add a method to re-apply modifiers, etc const interactable = interact(target); const drag = { name: drag, axis: 'x' }; const resize = { name: resize, edges: { left: true, bottom: true }; interactable.reflow(drag); interactable.reflow(resize); --- src/index.js | 4 +++ src/reflow.js | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 src/reflow.js diff --git a/src/index.js b/src/index.js index dc6765a36..de394cf96 100644 --- a/src/index.js +++ b/src/index.js @@ -32,6 +32,7 @@ import restrictEdges from './modifiers/restrictEdges'; import restrictSize from './modifiers/restrictSize'; import autoScroll from './autoScroll'; +import reflow from './reflow'; export function init (window) { scopeInit(window); @@ -74,5 +75,8 @@ export function init (window) { // autoScroll interact.use(autoScroll); + // reflow + interact.use(reflow); + return interact; } diff --git a/src/reflow.js b/src/reflow.js new file mode 100644 index 000000000..b234602c6 --- /dev/null +++ b/src/reflow.js @@ -0,0 +1,83 @@ +import Interactable from './Interactable'; +import { + arr, + is, + extend, + rect as rectUtils, + pointer as pointerUtils, +} from './utils'; + +export function init (scope) { + const { actions, Interaction } = scope; + + // add action reflow event types + for (const actionName of actions.names) { + Interactable.eventTypes.push(`${actionName}reflow`); + } + + // remove completed reflow interactions + Interaction.signals.on('stop', ({ interaction }) => { + if (interaction.pointerType === 'reflow') { + arr.remove(scope.interactions, interaction); + } + }); + + Interactable.prototype.reflow = function (action) { + return reflow(this, action, scope); + }; +} + +function reflow (interactable, action, scope) { + let elements = is.string(interactable.target) + ? arr.from(interactable._context.querySelectorAll(interactable.target)) + : [interactable.target]; + + // ignore elements that are currently being interacted with + elements = elements.filter( + element => !arr.find( + scope.interactions, + i => i.target === interactable && i.element === element && i.interacting())); + + for (const element of elements) { + const interaction = scope.Interaction.new({ pointerType: 'reflow' }); + + const rect = interactable.getRect(element); + + if (!rect) { break; } + + const xywh = rectUtils.tlbrToXywh(rect); + const coords = { + page: xywh, + client: xywh, + }; + const event = extend(pointerUtils.coordsToEvent(coords), coords); + const signalArg = { + interaction, + event, + pointer: event, + eventTarget: element, + phase: 'reflow', + }; + + interaction.target = interactable; + interaction.element = element; + interaction.prepared = extend({}, action); + interaction.prevEvent = event; + interaction.updatePointer(event, event, element, true); + + interaction._doPhase(signalArg); + + signalArg.phase = 'start'; + interaction._interacting = interaction._doPhase(signalArg); + + if (interaction._interacting) { + interaction.move(signalArg); + interaction.end(event); + } + else { + interaction.stop(); + } + } +} + +export default { init }; From 228f64d78807c11aaf91be1bcdaa278c125f8756 Mon Sep 17 00:00:00 2001 From: Taye Adeyemi Date: Thu, 8 Mar 2018 18:24:26 +0100 Subject: [PATCH 2/6] reflow: don't exceed autoStart interaction limits --- src/reflow.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/reflow.js b/src/reflow.js index b234602c6..52ad09fdd 100644 --- a/src/reflow.js +++ b/src/reflow.js @@ -1,4 +1,6 @@ import Interactable from './Interactable'; +import interactions from './interactions'; +import autoStart from './autoStart/base'; import { arr, is, @@ -34,12 +36,10 @@ function reflow (interactable, action, scope) { // ignore elements that are currently being interacted with elements = elements.filter( - element => !arr.find( - scope.interactions, - i => i.target === interactable && i.element === element && i.interacting())); + element => autoStart.withinInteractionLimit(interactable, element, action, scope)); for (const element of elements) { - const interaction = scope.Interaction.new({ pointerType: 'reflow' }); + const interaction = interactions.newInteraction({ pointerType: 'reflow' }, scope); const rect = interactable.getRect(element); From e9b06d12d54d3da1dea5af8b65316be9022fab0a Mon Sep 17 00:00:00 2001 From: Taye Adeyemi Date: Thu, 8 Mar 2018 18:25:17 +0100 Subject: [PATCH 3/6] reflow: add jsdoc comments --- src/reflow.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/reflow.js b/src/reflow.js index 52ad09fdd..3a4bcd57a 100644 --- a/src/reflow.js +++ b/src/reflow.js @@ -1,3 +1,4 @@ +/** @lends Interactable */ import Interactable from './Interactable'; import interactions from './interactions'; import autoStart from './autoStart/base'; @@ -24,6 +25,21 @@ export function init (scope) { } }); + /** + * ```js + * const interactable = interact(target); + * const drag = { name: drag, axis: 'x' }; + * const resize = { name: resize, edges: { left: true, bottom: true }; + * + * interactable.reflow(drag); + * interactable.reflow(resize); + * ``` + * + * Start an action sequence to re-apply modifiers, check drops, etc. + * + * @param { Object } action The action to begin + * @param { string } action.name The name of the action + */ Interactable.prototype.reflow = function (action) { return reflow(this, action, scope); }; From 29703ce89d2135af2d9e4e76af0f60f08c276438 Mon Sep 17 00:00:00 2001 From: Taye Adeyemi Date: Thu, 8 Mar 2018 18:56:51 +0100 Subject: [PATCH 4/6] reflow: use scope.Interactable --- src/reflow.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/reflow.js b/src/reflow.js index 3a4bcd57a..27476d65d 100644 --- a/src/reflow.js +++ b/src/reflow.js @@ -1,5 +1,3 @@ -/** @lends Interactable */ -import Interactable from './Interactable'; import interactions from './interactions'; import autoStart from './autoStart/base'; import { @@ -11,7 +9,12 @@ import { } from './utils'; export function init (scope) { - const { actions, Interaction } = scope; + const { + actions, + Interaction, + /** @lends Interactable */ + Interactable, + } = scope; // add action reflow event types for (const actionName of actions.names) { From a30b586ceccdb6357b5b407a3b4acffd8eeaf41c Mon Sep 17 00:00:00 2001 From: Taye Adeyemi Date: Sat, 10 Mar 2018 02:21:31 +0100 Subject: [PATCH 5/6] reflow: use autoStart from scope --- src/autoStart/base.js | 1 + src/reflow.js | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/autoStart/base.js b/src/autoStart/base.js index f3f3d1b66..d90486ae4 100644 --- a/src/autoStart/base.js +++ b/src/autoStart/base.js @@ -96,6 +96,7 @@ function init (scope) { scope.autoStart = { // Allow this many interactions to happen simultaneously maxInteractions: Infinity, + withinInteractionLimit, signals: new Signals(), }; } diff --git a/src/reflow.js b/src/reflow.js index 27476d65d..312f09cd5 100644 --- a/src/reflow.js +++ b/src/reflow.js @@ -1,5 +1,4 @@ import interactions from './interactions'; -import autoStart from './autoStart/base'; import { arr, is, @@ -18,7 +17,7 @@ export function init (scope) { // add action reflow event types for (const actionName of actions.names) { - Interactable.eventTypes.push(`${actionName}reflow`); + actions.eventTypes.push(`${actionName}reflow`); } // remove completed reflow interactions @@ -53,9 +52,11 @@ function reflow (interactable, action, scope) { ? arr.from(interactable._context.querySelectorAll(interactable.target)) : [interactable.target]; - // ignore elements that are currently being interacted with - elements = elements.filter( - element => autoStart.withinInteractionLimit(interactable, element, action, scope)); + // follow autoStart max interaction settings + if (scope.autoStart) { + elements = elements.filter( + element => scope.autoStart.withinInteractionLimit(interactable, element, action, scope)); + } for (const element of elements) { const interaction = interactions.newInteraction({ pointerType: 'reflow' }, scope); From e453ad3ed31220fb040e0d629e1593330809a227 Mon Sep 17 00:00:00 2001 From: Taye Adeyemi Date: Sat, 10 Mar 2018 02:38:51 +0100 Subject: [PATCH 6/6] reflow: add test --- tests/index.js | 2 ++ tests/reflow.js | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 tests/reflow.js diff --git a/tests/index.js b/tests/index.js index dc82058da..278dd196e 100644 --- a/tests/index.js +++ b/tests/index.js @@ -43,6 +43,8 @@ import './actions/drag'; // autoScroll //import './autoScroll'; +import './reflow'; + import './interact'; //const index = import '../src/index'; diff --git a/tests/reflow.js b/tests/reflow.js new file mode 100644 index 000000000..c43382598 --- /dev/null +++ b/tests/reflow.js @@ -0,0 +1,52 @@ +import test from './test'; +import * as helpers from './helpers'; +import reflow from '../src/reflow'; +import win from '../src/utils/window'; + +test('reflow', t => { + const scope = helpers.mockScope({ + autoStart: {}, + }); + + Object.assign(scope.actions, { test: {}, names: ['test'] }); + + reflow.init(scope); + + t.ok( + scope.Interactable.prototype.reflow instanceof Function, + 'reflow method is added to Interactable.prototype' + ); + + const fired = []; + const interactable = helpers.newInteractable(scope, win.window); + const rect = Object.freeze({ top: 100, left: 200, bottom: 300, right: 400 }); + interactable.fire = iEvent => fired.push(iEvent); + interactable.target = {}; + interactable.options.test = {}; + interactable.rectChecker(() => rect); + + scope.autoStart.withinInteractionLimit = () => false; + t.equal(fired.length, 0, 'follows scope.autoStart.withinInteractionLimit'); + + scope.autoStart.withinInteractionLimit = () => true; + interactable.reflow({ name: 'test' }); + + const phases = ['reflow', 'start', 'move', 'end']; + + for (const [index, phase] of Object.entries(phases)) { + t.equal(fired[index].type, `test${phase}`, `event #${index} is ${phase}`); + } + + const interaction = fired[0].interaction; + + t.deepEqual( + interaction.startCoords.page, + { + x: rect.left, + y: rect.top, + }, + 'uses element top left for event coords' + ); + + t.end(); +});