Skip to content

Commit 6b5204d

Browse files
author
epriestley
committed
Add support for device swipe events
Summary: Ref T2700. Allow JS to listen for swipes on devices. There are a bunch of tricky cases here and I probably didn't get them all totally right, but this interaction broadly looks like this: - We implement gesture recognition for the mouse in device modes (narrow browser), and for touch events from an actual device. - The sigil `touchable` indicates that a node wants to react to touch events. - When the user touches a `touchable` node, we start listening for moves. They might be tapping/clicking (in which case we don't care), but they might also be gesturing. - Once the user moves their finger/pointer far enough away from the tap origin, we recognize it as a gesture. I hardcoded this at 20px; I wasn't able to find any "official" Apple value, but 20px seems like a common default. - At this point, we look at where their finger has moved. - If they moved it mostly up/down, we interpret the gesture as "scroll" and just stop listening. The device does its own thing. - However, if they moved it mostly left/right, we interpret it as a "swipe". We start killing the moves so the device doesn't scroll. - Once we've recognized that a gesture is underway, we send a "gesture.swipe.start" event and then "gesture.swipe.move" events for every move. - When the user ends the gesture, we send "gesture.swipe.end". - If the user cancels the gesture (currently, only by tapping with a second finger), we send "gesture.swipe.cancel". - Gesture events have raw position data and some convenience fields. Test Plan: Wrote UI example and used it from the Desktop, iPhone simulator, and a real iphone. - The code always seems to get "scroll" vs "swipe" correct (i.e., consistent with my intentions). - The threshold feels pretty good to me. - Tapping with a second finger cancels the action. Reviewers: chad, btrahan Reviewed By: chad CC: aran Maniphest Tasks: T2700 Differential Revision: https://secure.phabricator.com/D5308
1 parent 4c914a5 commit 6b5204d

File tree

7 files changed

+373
-92
lines changed

7 files changed

+373
-92
lines changed

scripts/celerity_mapper.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
'javelin-behavior-konami',
5252
'javelin-behavior-aphlict-dropdown',
5353
'javelin-behavior-history-install',
54+
'javelin-behavior-phabricator-gesture',
5455

5556
'javelin-behavior-phabricator-active-nav',
5657
'javelin-behavior-phabricator-nav',

src/__celerity_resource_map__.php

Lines changed: 124 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,7 @@
805805
),
806806
'conpherence-widget-pane-css' =>
807807
array(
808-
'uri' => '/res/6e5755bb/rsrc/css/application/conpherence/widget-pane.css',
808+
'uri' => '/res/e67ad581/rsrc/css/application/conpherence/widget-pane.css',
809809
'type' => 'css',
810810
'requires' =>
811811
array(
@@ -1167,28 +1167,30 @@
11671167
),
11681168
'javelin-behavior-conpherence-menu' =>
11691169
array(
1170-
'uri' => '/res/0ad6ab54/rsrc/js/application/conpherence/behavior-menu.js',
1170+
'uri' => '/res/cb1a5cf0/rsrc/js/application/conpherence/behavior-menu.js',
11711171
'type' => 'js',
11721172
'requires' =>
11731173
array(
11741174
0 => 'javelin-behavior',
11751175
1 => 'javelin-dom',
1176-
2 => 'javelin-workflow',
1177-
3 => 'javelin-util',
1178-
4 => 'javelin-stratcom',
1179-
5 => 'javelin-uri',
1176+
2 => 'javelin-request',
1177+
3 => 'javelin-stratcom',
1178+
4 => 'javelin-uri',
1179+
5 => 'javelin-util',
1180+
6 => 'javelin-workflow',
11801181
),
11811182
'disk' => '/rsrc/js/application/conpherence/behavior-menu.js',
11821183
),
11831184
'javelin-behavior-conpherence-pontificate' =>
11841185
array(
1185-
'uri' => '/res/06214a06/rsrc/js/application/conpherence/behavior-pontificate.js',
1186+
'uri' => '/res/15263692/rsrc/js/application/conpherence/behavior-pontificate.js',
11861187
'type' => 'js',
11871188
'requires' =>
11881189
array(
11891190
0 => 'javelin-behavior',
11901191
1 => 'javelin-dom',
1191-
2 => 'javelin-request',
1192+
2 => 'javelin-util',
1193+
3 => 'javelin-workflow',
11921194
),
11931195
'disk' => '/rsrc/js/application/conpherence/behavior-pontificate.js',
11941196
),
@@ -1746,6 +1748,34 @@
17461748
),
17471749
'disk' => '/rsrc/js/application/core/behavior-file-tree.js',
17481750
),
1751+
'javelin-behavior-phabricator-gesture' =>
1752+
array(
1753+
'uri' => '/res/1222d486/rsrc/js/application/core/behavior-gesture.js',
1754+
'type' => 'js',
1755+
'requires' =>
1756+
array(
1757+
0 => 'javelin-behavior',
1758+
1 => 'javelin-behavior-device',
1759+
2 => 'javelin-stratcom',
1760+
3 => 'javelin-vector',
1761+
4 => 'javelin-dom',
1762+
5 => 'javelin-magical-init',
1763+
),
1764+
'disk' => '/rsrc/js/application/core/behavior-gesture.js',
1765+
),
1766+
'javelin-behavior-phabricator-gesture-example' =>
1767+
array(
1768+
'uri' => '/res/da636e19/rsrc/js/application/uiexample/gesture-example.js',
1769+
'type' => 'js',
1770+
'requires' =>
1771+
array(
1772+
0 => 'javelin-stratcom',
1773+
1 => 'javelin-behavior',
1774+
2 => 'javelin-vector',
1775+
3 => 'javelin-dom',
1776+
),
1777+
'disk' => '/rsrc/js/application/uiexample/gesture-example.js',
1778+
),
17491779
'javelin-behavior-phabricator-keyboard-pager' =>
17501780
array(
17511781
'uri' => '/res/56d64eff/rsrc/js/application/core/behavior-keyboard-pager.js',
@@ -2664,7 +2694,7 @@
26642694
),
26652695
'phabricator-core-css' =>
26662696
array(
2667-
'uri' => '/res/b34e5c75/rsrc/css/core/core.css',
2697+
'uri' => '/res/1e7afaa9/rsrc/css/core/core.css',
26682698
'type' => 'css',
26692699
'requires' =>
26702700
array(
@@ -3055,7 +3085,7 @@
30553085
),
30563086
'phabricator-standard-page-view' =>
30573087
array(
3058-
'uri' => '/res/252faaf4/rsrc/css/application/base/standard-page-view.css',
3088+
'uri' => '/res/70fa2da4/rsrc/css/application/base/standard-page-view.css',
30593089
'type' => 'css',
30603090
'requires' =>
30613091
array(
@@ -3500,7 +3530,7 @@
35003530
), array(
35013531
'packages' =>
35023532
array(
3503-
'8e2735e2' =>
3533+
'fdd3bb5f' =>
35043534
array(
35053535
'name' => 'core.pkg.css',
35063536
'symbols' =>
@@ -3543,10 +3573,10 @@
35433573
35 => 'phabricator-object-item-list-view-css',
35443574
36 => 'global-drag-and-drop-css',
35453575
),
3546-
'uri' => '/res/pkg/8e2735e2/core.pkg.css',
3576+
'uri' => '/res/pkg/fdd3bb5f/core.pkg.css',
35473577
'type' => 'css',
35483578
),
3549-
'0ed3458f' =>
3579+
'9abfe389' =>
35503580
array(
35513581
'name' => 'core.pkg.js',
35523582
'symbols' =>
@@ -3578,15 +3608,16 @@
35783608
24 => 'javelin-behavior-konami',
35793609
25 => 'javelin-behavior-aphlict-dropdown',
35803610
26 => 'javelin-behavior-history-install',
3581-
27 => 'javelin-behavior-phabricator-active-nav',
3582-
28 => 'javelin-behavior-phabricator-nav',
3583-
29 => 'javelin-behavior-phabricator-remarkup-assist',
3584-
30 => 'phabricator-textareautils',
3585-
31 => 'phabricator-file-upload',
3586-
32 => 'javelin-behavior-global-drag-and-drop',
3587-
33 => 'javelin-behavior-phabricator-reveal-content',
3611+
27 => 'javelin-behavior-phabricator-gesture',
3612+
28 => 'javelin-behavior-phabricator-active-nav',
3613+
29 => 'javelin-behavior-phabricator-nav',
3614+
30 => 'javelin-behavior-phabricator-remarkup-assist',
3615+
31 => 'phabricator-textareautils',
3616+
32 => 'phabricator-file-upload',
3617+
33 => 'javelin-behavior-global-drag-and-drop',
3618+
34 => 'javelin-behavior-phabricator-reveal-content',
35883619
),
3589-
'uri' => '/res/pkg/0ed3458f/core.pkg.js',
3620+
'uri' => '/res/pkg/9abfe389/core.pkg.js',
35903621
'type' => 'js',
35913622
),
35923623
'dca4a03d' =>
@@ -3733,17 +3764,17 @@
37333764
'reverse' =>
37343765
array(
37353766
'aphront-attached-file-view-css' => 'eb35a026',
3736-
'aphront-crumbs-view-css' => '8e2735e2',
3737-
'aphront-dialog-view-css' => '8e2735e2',
3738-
'aphront-error-view-css' => '8e2735e2',
3739-
'aphront-form-view-css' => '8e2735e2',
3740-
'aphront-list-filter-view-css' => '8e2735e2',
3741-
'aphront-pager-view-css' => '8e2735e2',
3742-
'aphront-panel-view-css' => '8e2735e2',
3743-
'aphront-table-view-css' => '8e2735e2',
3744-
'aphront-tokenizer-control-css' => '8e2735e2',
3745-
'aphront-tooltip-css' => '8e2735e2',
3746-
'aphront-typeahead-control-css' => '8e2735e2',
3767+
'aphront-crumbs-view-css' => 'fdd3bb5f',
3768+
'aphront-dialog-view-css' => 'fdd3bb5f',
3769+
'aphront-error-view-css' => 'fdd3bb5f',
3770+
'aphront-form-view-css' => 'fdd3bb5f',
3771+
'aphront-list-filter-view-css' => 'fdd3bb5f',
3772+
'aphront-pager-view-css' => 'fdd3bb5f',
3773+
'aphront-panel-view-css' => 'fdd3bb5f',
3774+
'aphront-table-view-css' => 'fdd3bb5f',
3775+
'aphront-tokenizer-control-css' => 'fdd3bb5f',
3776+
'aphront-tooltip-css' => 'fdd3bb5f',
3777+
'aphront-typeahead-control-css' => 'fdd3bb5f',
37473778
'differential-changeset-view-css' => '8aaacd1b',
37483779
'differential-core-view-css' => '8aaacd1b',
37493780
'differential-inline-comment-editor' => '322728f3',
@@ -3757,19 +3788,19 @@
37573788
'differential-table-of-contents-css' => '8aaacd1b',
37583789
'diffusion-commit-view-css' => 'c8ce2d88',
37593790
'diffusion-icons-css' => 'c8ce2d88',
3760-
'global-drag-and-drop-css' => '8e2735e2',
3791+
'global-drag-and-drop-css' => 'fdd3bb5f',
37613792
'inline-comment-summary-css' => '8aaacd1b',
3762-
'javelin-aphlict' => '0ed3458f',
3793+
'javelin-aphlict' => '9abfe389',
37633794
'javelin-behavior' => 'cd1d650a',
3764-
'javelin-behavior-aphlict-dropdown' => '0ed3458f',
3765-
'javelin-behavior-aphlict-listen' => '0ed3458f',
3766-
'javelin-behavior-aphront-basic-tokenizer' => '0ed3458f',
3795+
'javelin-behavior-aphlict-dropdown' => '9abfe389',
3796+
'javelin-behavior-aphlict-listen' => '9abfe389',
3797+
'javelin-behavior-aphront-basic-tokenizer' => '9abfe389',
37673798
'javelin-behavior-aphront-drag-and-drop' => '322728f3',
37683799
'javelin-behavior-aphront-drag-and-drop-textarea' => '322728f3',
3769-
'javelin-behavior-aphront-form-disable-on-submit' => '0ed3458f',
3800+
'javelin-behavior-aphront-form-disable-on-submit' => '9abfe389',
37703801
'javelin-behavior-audit-preview' => 'f96657b8',
37713802
'javelin-behavior-dark-console' => 'dca4a03d',
3772-
'javelin-behavior-device' => '0ed3458f',
3803+
'javelin-behavior-device' => '9abfe389',
37733804
'javelin-behavior-differential-accept-with-errors' => '322728f3',
37743805
'javelin-behavior-differential-add-reviewers-and-ccs' => '322728f3',
37753806
'javelin-behavior-differential-comment-jump' => '322728f3',
@@ -3785,31 +3816,32 @@
37853816
'javelin-behavior-diffusion-commit-graph' => 'f96657b8',
37863817
'javelin-behavior-diffusion-pull-lastmodified' => 'f96657b8',
37873818
'javelin-behavior-error-log' => 'dca4a03d',
3788-
'javelin-behavior-global-drag-and-drop' => '0ed3458f',
3789-
'javelin-behavior-history-install' => '0ed3458f',
3790-
'javelin-behavior-konami' => '0ed3458f',
3791-
'javelin-behavior-lightbox-attachments' => '0ed3458f',
3819+
'javelin-behavior-global-drag-and-drop' => '9abfe389',
3820+
'javelin-behavior-history-install' => '9abfe389',
3821+
'javelin-behavior-konami' => '9abfe389',
3822+
'javelin-behavior-lightbox-attachments' => '9abfe389',
37923823
'javelin-behavior-load-blame' => '322728f3',
37933824
'javelin-behavior-maniphest-batch-selector' => '7707de41',
37943825
'javelin-behavior-maniphest-subpriority-editor' => '7707de41',
37953826
'javelin-behavior-maniphest-transaction-controls' => '7707de41',
37963827
'javelin-behavior-maniphest-transaction-expand' => '7707de41',
37973828
'javelin-behavior-maniphest-transaction-preview' => '7707de41',
3798-
'javelin-behavior-phabricator-active-nav' => '0ed3458f',
3799-
'javelin-behavior-phabricator-autofocus' => '0ed3458f',
3800-
'javelin-behavior-phabricator-keyboard-shortcuts' => '0ed3458f',
3801-
'javelin-behavior-phabricator-nav' => '0ed3458f',
3829+
'javelin-behavior-phabricator-active-nav' => '9abfe389',
3830+
'javelin-behavior-phabricator-autofocus' => '9abfe389',
3831+
'javelin-behavior-phabricator-gesture' => '9abfe389',
3832+
'javelin-behavior-phabricator-keyboard-shortcuts' => '9abfe389',
3833+
'javelin-behavior-phabricator-nav' => '9abfe389',
38023834
'javelin-behavior-phabricator-object-selector' => '322728f3',
3803-
'javelin-behavior-phabricator-oncopy' => '0ed3458f',
3804-
'javelin-behavior-phabricator-remarkup-assist' => '0ed3458f',
3805-
'javelin-behavior-phabricator-reveal-content' => '0ed3458f',
3806-
'javelin-behavior-phabricator-search-typeahead' => '0ed3458f',
3807-
'javelin-behavior-phabricator-tooltips' => '0ed3458f',
3808-
'javelin-behavior-phabricator-watch-anchor' => '0ed3458f',
3809-
'javelin-behavior-refresh-csrf' => '0ed3458f',
3835+
'javelin-behavior-phabricator-oncopy' => '9abfe389',
3836+
'javelin-behavior-phabricator-remarkup-assist' => '9abfe389',
3837+
'javelin-behavior-phabricator-reveal-content' => '9abfe389',
3838+
'javelin-behavior-phabricator-search-typeahead' => '9abfe389',
3839+
'javelin-behavior-phabricator-tooltips' => '9abfe389',
3840+
'javelin-behavior-phabricator-watch-anchor' => '9abfe389',
3841+
'javelin-behavior-refresh-csrf' => '9abfe389',
38103842
'javelin-behavior-repository-crossreference' => '322728f3',
3811-
'javelin-behavior-toggle-class' => '0ed3458f',
3812-
'javelin-behavior-workflow' => '0ed3458f',
3843+
'javelin-behavior-toggle-class' => '9abfe389',
3844+
'javelin-behavior-workflow' => '9abfe389',
38133845
'javelin-dom' => 'cd1d650a',
38143846
'javelin-event' => 'cd1d650a',
38153847
'javelin-install' => 'cd1d650a',
@@ -3828,48 +3860,48 @@
38283860
'javelin-util' => 'cd1d650a',
38293861
'javelin-vector' => 'cd1d650a',
38303862
'javelin-workflow' => 'cd1d650a',
3831-
'lightbox-attachment-css' => '8e2735e2',
3863+
'lightbox-attachment-css' => 'fdd3bb5f',
38323864
'maniphest-task-summary-css' => 'eb35a026',
38333865
'maniphest-transaction-detail-css' => 'eb35a026',
3834-
'phabricator-busy' => '0ed3458f',
3866+
'phabricator-busy' => '9abfe389',
38353867
'phabricator-content-source-view-css' => '8aaacd1b',
3836-
'phabricator-core-buttons-css' => '8e2735e2',
3837-
'phabricator-core-css' => '8e2735e2',
3838-
'phabricator-crumbs-view-css' => '8e2735e2',
3839-
'phabricator-directory-css' => '8e2735e2',
3868+
'phabricator-core-buttons-css' => 'fdd3bb5f',
3869+
'phabricator-core-css' => 'fdd3bb5f',
3870+
'phabricator-crumbs-view-css' => 'fdd3bb5f',
3871+
'phabricator-directory-css' => 'fdd3bb5f',
38403872
'phabricator-drag-and-drop-file-upload' => '322728f3',
3841-
'phabricator-dropdown-menu' => '0ed3458f',
3842-
'phabricator-file-upload' => '0ed3458f',
3843-
'phabricator-filetree-view-css' => '8e2735e2',
3844-
'phabricator-flag-css' => '8e2735e2',
3845-
'phabricator-form-view-css' => '8e2735e2',
3846-
'phabricator-header-view-css' => '8e2735e2',
3847-
'phabricator-jump-nav' => '8e2735e2',
3848-
'phabricator-keyboard-shortcut' => '0ed3458f',
3849-
'phabricator-keyboard-shortcut-manager' => '0ed3458f',
3850-
'phabricator-main-menu-view' => '8e2735e2',
3851-
'phabricator-menu-item' => '0ed3458f',
3852-
'phabricator-nav-view-css' => '8e2735e2',
3853-
'phabricator-notification' => '0ed3458f',
3854-
'phabricator-notification-css' => '8e2735e2',
3855-
'phabricator-notification-menu-css' => '8e2735e2',
3856-
'phabricator-object-item-list-view-css' => '8e2735e2',
3873+
'phabricator-dropdown-menu' => '9abfe389',
3874+
'phabricator-file-upload' => '9abfe389',
3875+
'phabricator-filetree-view-css' => 'fdd3bb5f',
3876+
'phabricator-flag-css' => 'fdd3bb5f',
3877+
'phabricator-form-view-css' => 'fdd3bb5f',
3878+
'phabricator-header-view-css' => 'fdd3bb5f',
3879+
'phabricator-jump-nav' => 'fdd3bb5f',
3880+
'phabricator-keyboard-shortcut' => '9abfe389',
3881+
'phabricator-keyboard-shortcut-manager' => '9abfe389',
3882+
'phabricator-main-menu-view' => 'fdd3bb5f',
3883+
'phabricator-menu-item' => '9abfe389',
3884+
'phabricator-nav-view-css' => 'fdd3bb5f',
3885+
'phabricator-notification' => '9abfe389',
3886+
'phabricator-notification-css' => 'fdd3bb5f',
3887+
'phabricator-notification-menu-css' => 'fdd3bb5f',
3888+
'phabricator-object-item-list-view-css' => 'fdd3bb5f',
38573889
'phabricator-object-selector-css' => '8aaacd1b',
3858-
'phabricator-paste-file-upload' => '0ed3458f',
3859-
'phabricator-prefab' => '0ed3458f',
3890+
'phabricator-paste-file-upload' => '9abfe389',
3891+
'phabricator-prefab' => '9abfe389',
38603892
'phabricator-project-tag-css' => 'eb35a026',
3861-
'phabricator-remarkup-css' => '8e2735e2',
3893+
'phabricator-remarkup-css' => 'fdd3bb5f',
38623894
'phabricator-shaped-request' => '322728f3',
3863-
'phabricator-side-menu-view-css' => '8e2735e2',
3864-
'phabricator-standard-page-view' => '8e2735e2',
3865-
'phabricator-textareautils' => '0ed3458f',
3866-
'phabricator-tooltip' => '0ed3458f',
3867-
'phabricator-transaction-view-css' => '8e2735e2',
3868-
'phabricator-zindex-css' => '8e2735e2',
3869-
'sprite-apps-large-css' => '8e2735e2',
3870-
'sprite-gradient-css' => '8e2735e2',
3871-
'sprite-icon-css' => '8e2735e2',
3872-
'sprite-menu-css' => '8e2735e2',
3873-
'syntax-highlighting-css' => '8e2735e2',
3895+
'phabricator-side-menu-view-css' => 'fdd3bb5f',
3896+
'phabricator-standard-page-view' => 'fdd3bb5f',
3897+
'phabricator-textareautils' => '9abfe389',
3898+
'phabricator-tooltip' => '9abfe389',
3899+
'phabricator-transaction-view-css' => 'fdd3bb5f',
3900+
'phabricator-zindex-css' => 'fdd3bb5f',
3901+
'sprite-apps-large-css' => 'fdd3bb5f',
3902+
'sprite-gradient-css' => 'fdd3bb5f',
3903+
'sprite-icon-css' => 'fdd3bb5f',
3904+
'sprite-menu-css' => 'fdd3bb5f',
3905+
'syntax-highlighting-css' => 'fdd3bb5f',
38743906
),
38753907
));

src/__phutil_library_map__.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,7 @@
960960
'PhabricatorFormExample' => 'applications/uiexample/examples/PhabricatorFormExample.php',
961961
'PhabricatorGarbageCollectorConfigOptions' => 'applications/config/option/PhabricatorGarbageCollectorConfigOptions.php',
962962
'PhabricatorGarbageCollectorDaemon' => 'infrastructure/daemon/PhabricatorGarbageCollectorDaemon.php',
963+
'PhabricatorGestureExample' => 'applications/uiexample/examples/PhabricatorGestureExample.php',
963964
'PhabricatorGitGraphStream' => 'applications/repository/daemon/PhabricatorGitGraphStream.php',
964965
'PhabricatorGitHubConfigOptions' => 'applications/config/option/PhabricatorGitHubConfigOptions.php',
965966
'PhabricatorGlobalLock' => 'infrastructure/util/PhabricatorGlobalLock.php',
@@ -2481,6 +2482,7 @@
24812482
'PhabricatorFormExample' => 'PhabricatorUIExample',
24822483
'PhabricatorGarbageCollectorConfigOptions' => 'PhabricatorApplicationConfigOptions',
24832484
'PhabricatorGarbageCollectorDaemon' => 'PhabricatorDaemon',
2485+
'PhabricatorGestureExample' => 'PhabricatorUIExample',
24842486
'PhabricatorGitHubConfigOptions' => 'PhabricatorApplicationConfigOptions',
24852487
'PhabricatorGlobalLock' => 'PhutilLock',
24862488
'PhabricatorGlobalUploadTargetView' => 'AphrontView',
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
final class PhabricatorGestureExample extends PhabricatorUIExample {
4+
5+
public function getName() {
6+
return 'Gestures';
7+
}
8+
9+
public function getDescription() {
10+
return hsprintf(
11+
'Use <tt>touchable</tt> to listen for gesture events. Note that you '.
12+
'must be in device mode for this to work (you can narrow your browser '.
13+
'window if you are on a desktop).');
14+
}
15+
16+
public function renderExample() {
17+
18+
$id = celerity_generate_unique_node_id();
19+
20+
Javelin::initBehavior(
21+
'phabricator-gesture-example',
22+
array(
23+
'rootID' => $id,
24+
));
25+
26+
return javelin_tag(
27+
'div',
28+
array(
29+
'sigil' => 'touchable',
30+
'id' => $id,
31+
'style' => 'width: 320px; height: 240px; margin: auto;',
32+
),
33+
'');
34+
}
35+
}

0 commit comments

Comments
 (0)