From 8bdc7133521d2480bdc77a716d9f1eb8cbe979c7 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 18 Apr 2020 11:22:55 -0700 Subject: [PATCH] Make the "Keyboard Shortcuts" dialog in Differential less hideous Summary: Ref T13515. Adding "\" ("Open in External Editor") made this slighlty worse, but it was already pretty bad. Long ago the keys had a special style on them, but this got changed and dropped somewhere around D16568 -- although at the time, I think they still had a grey background (see T11654). Some later change removed this background. Put the background back and separate the keystrokes into groups. Test Plan: {F7370615} Maniphest Tasks: T13515 Differential Revision: https://secure.phabricator.com/D21141 --- resources/celerity/map.php | 156 +++++++++--------- ...bricatorHelpKeyboardShortcutController.php | 83 +++++++++- src/view/phui/PHUIIconView.php | 4 + src/view/phui/PHUIListView.php | 16 +- src/view/phui/PHUITabGroupView.php | 53 +++++- src/view/phui/PHUITabView.php | 15 ++ .../application/base/standard-page-view.css | 22 ++- webroot/rsrc/css/phui/phui-list.css | 44 ++++- .../js/application/diff/DiffChangesetList.js | 31 ++-- .../behavior-show-older-transactions.js | 1 + webroot/rsrc/js/core/KeyboardShortcut.js | 1 + .../rsrc/js/core/KeyboardShortcutManager.js | 6 +- webroot/rsrc/js/core/behavior-file-tree.js | 1 + .../js/core/behavior-keyboard-shortcuts.js | 2 + .../behavior-phabricator-remarkup-assist.js | 1 + .../core/darkconsole/behavior-dark-console.js | 1 + 16 files changed, 327 insertions(+), 110 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 33d3bc03cc..75328646b4 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,10 +9,10 @@ return array( 'names' => array( 'conpherence.pkg.css' => '3c8a0668', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '86f155f9', - 'core.pkg.js' => '705aec2c', + 'core.pkg.css' => 'a4a2417c', + 'core.pkg.js' => '4355a8d3', 'differential.pkg.css' => '607c84be', - 'differential.pkg.js' => '99e2cb01', + 'differential.pkg.js' => 'ececaeef', 'diffusion.pkg.css' => '42c75c37', 'diffusion.pkg.js' => 'a98c0bf7', 'maniphest.pkg.css' => '35995d6d', @@ -40,7 +40,7 @@ return array( 'rsrc/css/application/base/main-menu-view.css' => 'bcec20f0', 'rsrc/css/application/base/notification-menu.css' => '4df1ee30', 'rsrc/css/application/base/phui-theme.css' => '35883b37', - 'rsrc/css/application/base/standard-page-view.css' => '8a295cb9', + 'rsrc/css/application/base/standard-page-view.css' => 'ed076e5a', 'rsrc/css/application/chatlog/chatlog.css' => 'abdc76ee', 'rsrc/css/application/conduit/conduit-api.css' => 'ce2cfc41', 'rsrc/css/application/config/config-options.css' => '16c920ae', @@ -165,7 +165,7 @@ return array( 'rsrc/css/phui/phui-invisible-character-view.css' => 'c694c4a4', 'rsrc/css/phui/phui-left-right.css' => '68513c34', 'rsrc/css/phui/phui-lightbox.css' => '4ebf22da', - 'rsrc/css/phui/phui-list.css' => 'b05144dd', + 'rsrc/css/phui/phui-list.css' => '2f253c22', 'rsrc/css/phui/phui-object-box.css' => 'b8d7eea0', 'rsrc/css/phui/phui-pager.css' => 'd022c7ad', 'rsrc/css/phui/phui-pinboard-view.css' => '1f08f5d8', @@ -378,7 +378,7 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '1e413dc9', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => '0116d3e8', 'rsrc/js/application/diff/DiffChangeset.js' => '5a4e4a3b', - 'rsrc/js/application/diff/DiffChangesetList.js' => '4769cfe7', + 'rsrc/js/application/diff/DiffChangesetList.js' => 'f813ef26', 'rsrc/js/application/diff/DiffInline.js' => '16e97ebc', 'rsrc/js/application/diff/behavior-preview-link.js' => 'f51e9c17', 'rsrc/js/application/differential/behavior-diff-radios.js' => '925fe8cd', @@ -435,7 +435,7 @@ return array( 'rsrc/js/application/transactions/behavior-comment-actions.js' => '4dffaeb2', 'rsrc/js/application/transactions/behavior-reorder-configs.js' => '4842f137', 'rsrc/js/application/transactions/behavior-reorder-fields.js' => '0ad8d31f', - 'rsrc/js/application/transactions/behavior-show-older-transactions.js' => '600f440c', + 'rsrc/js/application/transactions/behavior-show-older-transactions.js' => '8b5c7d65', 'rsrc/js/application/transactions/behavior-transaction-comment-form.js' => '2bdadf1a', 'rsrc/js/application/transactions/behavior-transaction-list.js' => '9cec214e', 'rsrc/js/application/trigger/TriggerRule.js' => '41b7b4f6', @@ -453,8 +453,8 @@ return array( 'rsrc/js/core/Favicon.js' => '7930776a', 'rsrc/js/core/FileUpload.js' => 'ab85e184', 'rsrc/js/core/Hovercard.js' => '074f0783', - 'rsrc/js/core/KeyboardShortcut.js' => 'c9749dcd', - 'rsrc/js/core/KeyboardShortcutManager.js' => '37b8a04a', + 'rsrc/js/core/KeyboardShortcut.js' => '1a844c06', + 'rsrc/js/core/KeyboardShortcutManager.js' => 'ef926938', 'rsrc/js/core/MultirowRowManager.js' => '5b54c823', 'rsrc/js/core/Notification.js' => 'a9b91e3f', 'rsrc/js/core/Prefab.js' => '5793d835', @@ -473,7 +473,7 @@ return array( 'rsrc/js/core/behavior-device.js' => '0cf79f45', 'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '7ad020a5', 'rsrc/js/core/behavior-fancy-datepicker.js' => '956f3eeb', - 'rsrc/js/core/behavior-file-tree.js' => 'ee82cedb', + 'rsrc/js/core/behavior-file-tree.js' => 'a61c2d11', 'rsrc/js/core/behavior-form.js' => '55d7b788', 'rsrc/js/core/behavior-gesture.js' => 'b58d1a2a', 'rsrc/js/core/behavior-global-drag-and-drop.js' => '1cab0e9a', @@ -481,7 +481,7 @@ return array( 'rsrc/js/core/behavior-history-install.js' => '6a1583a8', 'rsrc/js/core/behavior-hovercard.js' => '6c379000', 'rsrc/js/core/behavior-keyboard-pager.js' => '1325b731', - 'rsrc/js/core/behavior-keyboard-shortcuts.js' => '2cc87f49', + 'rsrc/js/core/behavior-keyboard-shortcuts.js' => '42c44e8b', 'rsrc/js/core/behavior-lightbox-attachments.js' => 'c7e748bf', 'rsrc/js/core/behavior-line-linker.js' => 'e15c8b1f', 'rsrc/js/core/behavior-linked-container.js' => '74446546', @@ -489,7 +489,7 @@ return array( 'rsrc/js/core/behavior-object-selector.js' => '98ef467f', 'rsrc/js/core/behavior-oncopy.js' => 'ff7b3f22', 'rsrc/js/core/behavior-phabricator-nav.js' => 'f166c949', - 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '2f80333f', + 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '54262396', 'rsrc/js/core/behavior-read-only-warning.js' => 'b9109f8f', 'rsrc/js/core/behavior-redirect.js' => '407ee861', 'rsrc/js/core/behavior-refresh-csrf.js' => '46116c01', @@ -511,7 +511,7 @@ return array( 'rsrc/js/core/behavior-workflow.js' => '9623adc1', 'rsrc/js/core/darkconsole/DarkLog.js' => '3b869402', 'rsrc/js/core/darkconsole/DarkMessage.js' => '26cd4b73', - 'rsrc/js/core/darkconsole/behavior-dark-console.js' => 'f39d968b', + 'rsrc/js/core/darkconsole/behavior-dark-console.js' => '457f4d16', 'rsrc/js/core/phtize.js' => '2f1db1ed', 'rsrc/js/phui/behavior-phui-dropdown-menu.js' => '5cf0501a', 'rsrc/js/phui/behavior-phui-file-upload.js' => 'e150bd50', @@ -598,7 +598,7 @@ return array( 'javelin-behavior-conpherence-pontificate' => '4ae58b5a', 'javelin-behavior-conpherence-search' => '91befbcc', 'javelin-behavior-countdown-timer' => '6a162524', - 'javelin-behavior-dark-console' => 'f39d968b', + 'javelin-behavior-dark-console' => '457f4d16', 'javelin-behavior-dashboard-async-panel' => '9c01e364', 'javelin-behavior-dashboard-move-panels' => 'a2ab19be', 'javelin-behavior-dashboard-query-panel-select' => '1e413dc9', @@ -639,20 +639,20 @@ return array( 'javelin-behavior-phabricator-active-nav' => '7353f43d', 'javelin-behavior-phabricator-autofocus' => '65bb0011', 'javelin-behavior-phabricator-clipboard-copy' => 'cf32921f', - 'javelin-behavior-phabricator-file-tree' => 'ee82cedb', + 'javelin-behavior-phabricator-file-tree' => 'a61c2d11', 'javelin-behavior-phabricator-gesture' => 'b58d1a2a', 'javelin-behavior-phabricator-gesture-example' => '242dedd0', 'javelin-behavior-phabricator-keyboard-pager' => '1325b731', - 'javelin-behavior-phabricator-keyboard-shortcuts' => '2cc87f49', + 'javelin-behavior-phabricator-keyboard-shortcuts' => '42c44e8b', 'javelin-behavior-phabricator-line-linker' => 'e15c8b1f', 'javelin-behavior-phabricator-nav' => 'f166c949', 'javelin-behavior-phabricator-notification-example' => '29819b75', 'javelin-behavior-phabricator-object-selector' => '98ef467f', 'javelin-behavior-phabricator-oncopy' => 'ff7b3f22', - 'javelin-behavior-phabricator-remarkup-assist' => '2f80333f', + 'javelin-behavior-phabricator-remarkup-assist' => '54262396', 'javelin-behavior-phabricator-reveal-content' => 'b105a3a6', 'javelin-behavior-phabricator-search-typeahead' => '1cb7d027', - 'javelin-behavior-phabricator-show-older-transactions' => '600f440c', + 'javelin-behavior-phabricator-show-older-transactions' => '8b5c7d65', 'javelin-behavior-phabricator-tooltips' => '73ecc1f8', 'javelin-behavior-phabricator-transaction-comment-form' => '2bdadf1a', 'javelin-behavior-phabricator-transaction-list' => '9cec214e', @@ -775,7 +775,7 @@ return array( 'phabricator-darkmessage' => '26cd4b73', 'phabricator-dashboard-css' => '5a205b9d', 'phabricator-diff-changeset' => '5a4e4a3b', - 'phabricator-diff-changeset-list' => '4769cfe7', + 'phabricator-diff-changeset-list' => 'f813ef26', 'phabricator-diff-inline' => '16e97ebc', 'phabricator-drag-and-drop-file-upload' => '4370900d', 'phabricator-draggable-list' => '0169e425', @@ -785,8 +785,8 @@ return array( 'phabricator-file-upload' => 'ab85e184', 'phabricator-filetree-view-css' => '56cdd875', 'phabricator-flag-css' => '2b77be8d', - 'phabricator-keyboard-shortcut' => 'c9749dcd', - 'phabricator-keyboard-shortcut-manager' => '37b8a04a', + 'phabricator-keyboard-shortcut' => '1a844c06', + 'phabricator-keyboard-shortcut-manager' => 'ef926938', 'phabricator-main-menu-view' => 'bcec20f0', 'phabricator-nav-view-css' => 'f8a0c1bf', 'phabricator-notification' => 'a9b91e3f', @@ -800,7 +800,7 @@ return array( 'phabricator-shaped-request' => 'abf88db8', 'phabricator-slowvote-css' => '1694baed', 'phabricator-source-code-view-css' => '03d7ac28', - 'phabricator-standard-page-view' => '8a295cb9', + 'phabricator-standard-page-view' => 'ed076e5a', 'phabricator-textareautils' => 'f340a484', 'phabricator-title' => '43bc9360', 'phabricator-tooltip' => '83754533', @@ -856,7 +856,7 @@ return array( 'phui-invisible-character-view-css' => 'c694c4a4', 'phui-left-right-css' => '68513c34', 'phui-lightbox-css' => '4ebf22da', - 'phui-list-view-css' => 'b05144dd', + 'phui-list-view-css' => '2f253c22', 'phui-object-box-css' => 'b8d7eea0', 'phui-oi-big-ui-css' => 'fa74cc35', 'phui-oi-color-css' => 'b517bfa0', @@ -1032,6 +1032,11 @@ return array( '16e97ebc' => array( 'javelin-dom', ), + '1a844c06' => array( + 'javelin-install', + 'javelin-util', + 'phabricator-keyboard-shortcut-manager', + ), '1b6acc2a' => array( 'javelin-magical-init', 'javelin-util', @@ -1160,13 +1165,6 @@ return array( 'javelin-request', 'phabricator-shaped-request', ), - '2cc87f49' => array( - 'javelin-behavior', - 'javelin-workflow', - 'javelin-json', - 'javelin-dom', - 'phabricator-keyboard-shortcut', - ), '2e255291' => array( 'javelin-install', 'javelin-util', @@ -1175,17 +1173,6 @@ return array( '2f1db1ed' => array( 'javelin-util', ), - '2f80333f' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - 'phabricator-phtize', - 'phabricator-textareautils', - 'javelin-workflow', - 'javelin-vector', - 'phuix-autocomplete', - 'javelin-mask', - ), '2fbe234d' => array( 'javelin-install', 'javelin-dom', @@ -1220,13 +1207,6 @@ return array( 'aphront-typeahead-control-css', 'phui-tag-view-css', ), - '37b8a04a' => array( - 'javelin-install', - 'javelin-util', - 'javelin-stratcom', - 'javelin-dom', - 'javelin-vector', - ), '3829a3cf' => array( 'javelin-behavior', 'javelin-uri', @@ -1279,6 +1259,13 @@ return array( 'javelin-behavior', 'javelin-uri', ), + '42c44e8b' => array( + 'javelin-behavior', + 'javelin-workflow', + 'javelin-json', + 'javelin-dom', + 'phabricator-keyboard-shortcut', + ), '4370900d' => array( 'javelin-install', 'javelin-util', @@ -1299,6 +1286,16 @@ return array( '43bc9360' => array( 'javelin-install', ), + '457f4d16' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-util', + 'javelin-dom', + 'javelin-request', + 'phabricator-keyboard-shortcut', + 'phabricator-darklog', + 'phabricator-darkmessage', + ), '46116c01' => array( 'javelin-request', 'javelin-behavior', @@ -1307,10 +1304,6 @@ return array( 'javelin-util', 'phabricator-busy', ), - '4769cfe7' => array( - 'javelin-install', - 'phuix-button-view', - ), '47a0728b' => array( 'javelin-behavior', 'javelin-dom', @@ -1401,6 +1394,17 @@ return array( '541f81c3' => array( 'javelin-install', ), + 54262396 => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-dom', + 'phabricator-phtize', + 'phabricator-textareautils', + 'javelin-workflow', + 'javelin-vector', + 'phuix-autocomplete', + 'javelin-mask', + ), '55a24e84' => array( 'javelin-install', 'javelin-dom', @@ -1494,12 +1498,6 @@ return array( '5faf27b9' => array( 'phuix-form-control-view', ), - '600f440c' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - 'phabricator-busy', - ), '60cd9241' => array( 'javelin-behavior', ), @@ -1674,6 +1672,12 @@ return array( 'javelin-dom', 'phabricator-draggable-list', ), + '8b5c7d65' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-dom', + 'phabricator-busy', + ), '8badee71' => array( 'javelin-install', 'javelin-util', @@ -1839,6 +1843,11 @@ return array( 'javelin-install', 'javelin-dom', ), + 'a61c2d11' => array( + 'javelin-behavior', + 'phabricator-keyboard-shortcut', + 'javelin-stratcom', + ), 'a9942052' => array( 'javelin-behavior', 'javelin-dom', @@ -2037,11 +2046,6 @@ return array( 'phuix-icon-view', 'phabricator-busy', ), - 'c9749dcd' => array( - 'javelin-install', - 'javelin-util', - 'phabricator-keyboard-shortcut-manager', - ), 'cf32921f' => array( 'javelin-behavior', 'javelin-dom', @@ -2130,16 +2134,18 @@ return array( 'ee77366f' => array( 'aphront-dialog-view-css', ), - 'ee82cedb' => array( - 'javelin-behavior', - 'phabricator-keyboard-shortcut', - 'javelin-stratcom', - ), 'ef836bf2' => array( 'javelin-behavior', 'javelin-dom', 'javelin-stratcom', ), + 'ef926938' => array( + 'javelin-install', + 'javelin-util', + 'javelin-stratcom', + 'javelin-dom', + 'javelin-vector', + ), 'f166c949' => array( 'javelin-behavior', 'javelin-behavior-device', @@ -2155,21 +2161,15 @@ return array( 'javelin-dom', 'javelin-vector', ), - 'f39d968b' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-util', - 'javelin-dom', - 'javelin-request', - 'phabricator-keyboard-shortcut', - 'phabricator-darklog', - 'phabricator-darkmessage', - ), 'f51e9c17' => array( 'javelin-behavior', 'javelin-stratcom', 'javelin-dom', ), + 'f813ef26' => array( + 'javelin-install', + 'phuix-button-view', + ), 'f84bcbf4' => array( 'javelin-behavior', 'javelin-stratcom', diff --git a/src/applications/help/controller/PhabricatorHelpKeyboardShortcutController.php b/src/applications/help/controller/PhabricatorHelpKeyboardShortcutController.php index 80bd259c48..0679d32b6a 100644 --- a/src/applications/help/controller/PhabricatorHelpKeyboardShortcutController.php +++ b/src/applications/help/controller/PhabricatorHelpKeyboardShortcutController.php @@ -21,8 +21,36 @@ final class PhabricatorHelpKeyboardShortcutController // close the dialog, so be explicit that escape works since it isn't // terribly discoverable. $keys[] = array( - 'keys' => array('esc'), + 'keys' => array('Esc'), 'description' => pht('Close any dialog, including this one.'), + 'group' => 'global', + ); + + $groups = array( + 'default' => array( + 'name' => pht('Page Shortcuts'), + 'icon' => 'fa-keyboard-o', + ), + 'diff-nav' => array( + 'name' => pht('Diff Navigation'), + 'icon' => 'fa-arrows', + ), + 'diff-vis' => array( + 'name' => pht('Hiding Content'), + 'icon' => 'fa-eye-slash', + ), + 'inline' => array( + 'name' => pht('Editing Inline Comments'), + 'icon' => 'fa-pencil', + ), + 'xactions' => array( + 'name' => pht('Comments'), + 'icon' => 'fa-comments-o', + ), + 'global' => array( + 'name' => pht('Global Shortcuts'), + 'icon' => 'fa-globe', + ), ); $stroke_map = array( @@ -35,31 +63,68 @@ final class PhabricatorHelpKeyboardShortcutController 'delete' => "\xE2\x8C\xAB", ); - $rows = array(); + $row_maps = array(); foreach ($keys as $shortcut) { $keystrokes = array(); foreach ($shortcut['keys'] as $stroke) { $stroke = idx($stroke_map, $stroke, $stroke); - $keystrokes[] = phutil_tag('kbd', array(), $stroke); + $keystrokes[] = phutil_tag( + 'span', + array( + 'class' => 'keyboard-shortcut-key', + ), + $stroke); } $keystrokes = phutil_implode_html(' or ', $keystrokes); - $rows[] = phutil_tag( + + $group_key = idx($shortcut, 'group'); + if (!isset($groups[$group_key])) { + $group_key = 'default'; + } + + $row = phutil_tag( 'tr', array(), array( phutil_tag('th', array(), $keystrokes), phutil_tag('td', array(), $shortcut['description']), )); + + $row_maps[$group_key][] = $row; } - $table = phutil_tag( - 'table', - array('class' => 'keyboard-shortcut-help'), - $rows); + $tab_group = id(new PHUITabGroupView()) + ->setVertical(true); + + foreach ($groups as $key => $group) { + $rows = idx($row_maps, $key); + if (!$rows) { + continue; + } + + $icon = id(new PHUIIconView()) + ->setIcon($group['icon']); + + $tab = id(new PHUITabView()) + ->setKey($key) + ->setName($group['name']) + ->setIcon($icon); + + $table = phutil_tag( + 'table', + array('class' => 'keyboard-shortcut-help'), + $rows); + + $tab->appendChild($table); + + $tab_group->addTab($tab); + } return $this->newDialog() ->setTitle(pht('Keyboard Shortcuts')) - ->appendChild($table) + ->setWidth(AphrontDialogView::WIDTH_FULL) + ->setFlush(true) + ->appendChild($tab_group) ->addCancelButton('#', pht('Close')); } diff --git a/src/view/phui/PHUIIconView.php b/src/view/phui/PHUIIconView.php index d907cb3343..e3f9f076c2 100644 --- a/src/view/phui/PHUIIconView.php +++ b/src/view/phui/PHUIIconView.php @@ -57,6 +57,10 @@ final class PHUIIconView extends AphrontTagView { return $this; } + public function getIconName() { + return $this->iconFont; + } + public function setBackground($color) { $this->iconBackground = $color; return $this; diff --git a/src/view/phui/PHUIListView.php b/src/view/phui/PHUIListView.php index 2a8180e5be..4c412ce23e 100644 --- a/src/view/phui/PHUIListView.php +++ b/src/view/phui/PHUIListView.php @@ -3,6 +3,7 @@ final class PHUIListView extends AphrontTagView { const NAVBAR_LIST = 'phui-list-navbar'; + const NAVBAR_VERTICAL = 'phui-list-navbar-vertical'; const SIDENAV_LIST = 'phui-list-sidenav'; const TABBAR_LIST = 'phui-list-tabbar'; @@ -180,8 +181,21 @@ final class PHUIListView extends AphrontTagView { $classes = array(); $classes[] = 'phui-list-view'; if ($this->type) { - $classes[] = $this->type; + switch ($this->type) { + case self::NAVBAR_LIST: + $classes[] = 'phui-list-navbar'; + $classes[] = 'phui-list-navbar-horizontal'; + break; + case self::NAVBAR_VERTICAL: + $classes[] = 'phui-list-navbar'; + $classes[] = 'phui-list-navbar-vertical'; + break; + default: + $classes[] = $this->type; + break; + } } + return array( 'class' => implode(' ', $classes), ); diff --git a/src/view/phui/PHUITabGroupView.php b/src/view/phui/PHUITabGroupView.php index 4a1963e050..65e9e1c821 100644 --- a/src/view/phui/PHUITabGroupView.php +++ b/src/view/phui/PHUITabGroupView.php @@ -4,6 +4,7 @@ final class PHUITabGroupView extends AphrontTagView { private $tabs = array(); private $selectedTab; + private $vertical; private $hideSingleTab; @@ -11,6 +12,15 @@ final class PHUITabGroupView extends AphrontTagView { return false; } + public function setVertical($vertical) { + $this->vertical = $vertical; + return $this; + } + + public function getVertical() { + return $this->vertical; + } + public function setHideSingleTab($hide_single_tab) { $this->hideSingleTab = $hide_single_tab; return $this; @@ -65,7 +75,13 @@ final class PHUITabGroupView extends AphrontTagView { protected function getTagAttributes() { $tab_map = mpull($this->tabs, 'getContentID', 'getKey'); + $classes = array(); + if ($this->getVertical()) { + $classes[] = 'phui-tab-group-view-vertical'; + } + return array( + 'class' => $classes, 'sigil' => 'phui-tab-group-view', 'meta' => array( 'tabMap' => $tab_map, @@ -76,8 +92,14 @@ final class PHUITabGroupView extends AphrontTagView { protected function getTagContent() { Javelin::initBehavior('phui-tab-group'); - $tabs = id(new PHUIListView()) - ->setType(PHUIListView::NAVBAR_LIST); + $tabs = new PHUIListView(); + + if ($this->getVertical()) { + $tabs->setType(PHUIListView::NAVBAR_VERTICAL); + } else { + $tabs->setType(PHUIListView::NAVBAR_LIST); + } + $content = array(); $selected_tab = $this->getSelectedTabKey(); @@ -107,6 +129,33 @@ final class PHUITabGroupView extends AphrontTagView { $tabs = null; } + if ($tabs && $this->getVertical()) { + $content = phutil_tag( + 'table', + array( + 'style' => 'width: 100%', + ), + phutil_tag( + 'tbody', + array(), + phutil_tag( + 'tr', + array(), + array( + phutil_tag( + 'td', + array( + 'class' => 'phui-tab-group-view-tab-column', + ), + $tabs), + phutil_tag( + 'td', + array(), + $content), + )))); + $tabs = null; + } + return array( $tabs, $content, diff --git a/src/view/phui/PHUITabView.php b/src/view/phui/PHUITabView.php index bcc80bf774..e50dbcbc4e 100644 --- a/src/view/phui/PHUITabView.php +++ b/src/view/phui/PHUITabView.php @@ -2,6 +2,7 @@ final class PHUITabView extends AphrontTagView { + private $icon; private $name; private $key; private $keyLocked; @@ -51,6 +52,15 @@ final class PHUITabView extends AphrontTagView { return $this->name; } + public function setIcon(PHUIIconView $icon) { + $this->icon = $icon; + return $this; + } + + public function getIcon() { + return $this->icon; + } + public function getContentID() { if ($this->contentID === null) { $this->contentID = celerity_generate_unique_node_id(); @@ -80,6 +90,11 @@ final class PHUITabView extends AphrontTagView { 'tabKey' => $this->getKey(), )); + $icon = $this->getIcon(); + if ($icon) { + $item->setIcon($icon->getIconName()); + } + $color = $this->getColor(); if ($color !== null) { $item->setStatusColor($color); diff --git a/webroot/rsrc/css/application/base/standard-page-view.css b/webroot/rsrc/css/application/base/standard-page-view.css index 755ed1d59c..6952710938 100644 --- a/webroot/rsrc/css/application/base/standard-page-view.css +++ b/webroot/rsrc/css/application/base/standard-page-view.css @@ -49,9 +49,13 @@ body.white-background { display: none; } +.keyboard-shortcut-help { + margin: 4px 12px; +} + .keyboard-shortcut-help td, .keyboard-shortcut-help th { - padding: 8px; + padding: 6px; vertical-align: middle; } @@ -60,6 +64,22 @@ body.white-background { color: {$greytext}; } +.keyboard-shortcut-key { + display: inline-block; + min-width: 1em; + min-height: 1em; + padding: 4px 6px; + font-weight: normal; + text-align: center; + text-decoration: none; + border-radius: 3px; + box-shadow: inset 0 -1px 0 rgba({$alphablue}, 0.08); + user-select: none; + color: {$darkgreytext}; + background: {$lightgreybackground}; + border: 1px solid {$lightgreyborder}; +} + .keyboard-focus-focus-reticle { background: rgba(255, 255, 211, 0.15); position: absolute; diff --git a/webroot/rsrc/css/phui/phui-list.css b/webroot/rsrc/css/phui/phui-list.css index 6932d9f29b..e6395302ea 100644 --- a/webroot/rsrc/css/phui/phui-list.css +++ b/webroot/rsrc/css/phui/phui-list.css @@ -100,28 +100,56 @@ .phui-list-view.phui-list-navbar { list-style: none; overflow: hidden; +} + +.phui-list-view.phui-list-navbar-horizontal { border-bottom: 1px solid {$thinblueborder}; } .phui-list-view.phui-list-navbar > li { list-style: none; - float: left; display: block; +} + +.phui-list-view.phui-list-navbar-horizontal > li { + float: left; border-right: 1px solid {$thinblueborder}; } .phui-list-navbar .phui-list-item-href { color: {$bluetext}; - padding: 8px 16px; line-height: 16px; } +.phui-list-navbar-horizontal .phui-list-item-href { + padding: 8px 16px; +} + +.phui-list-navbar-vertical .phui-list-item-href { + padding: 8px 12px; +} + +.phui-list-navbar-vertical { + box-shadow: 0 1px 0 rgba({$alphablue}, 0.05); +} + +.phui-list-navbar-vertical .phui-list-item-href { + display: block; + background: #ffffff; +} + .phui-list-navbar .phui-list-item-selected .phui-list-item-href { background: {$lightbluebackground}; color: {$darkbluetext}; font-weight: bold; } +.phui-tab-group-view-tab-column { + width: 220px; + border-right: 1px solid {$thinblueborder}; + background: {$lightgreybackground}; +} + .phui-list-navbar .phui-list-item-href:hover { background: rgba(100,100,100,.1); color: {$darkgreytext}; @@ -131,11 +159,19 @@ .phui-list-navbar .phui-list-item-icon { height: 14px; width: 14px; - display: block; font-size: 14px; + text-align: center; } -.device-phone .phui-list-view.phui-list-navbar > li { +.phui-list-navbar-vertical .phui-list-item-icon { + margin-right: 8px; +} + +.phui-list-navbar-horizontal .phui-list-item-icon { + display: block; +} + +.device-phone .phui-list-view.phui-list-navbar-horizontal > li { float: none; border: none; } diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index 862926bdfa..9f5af9a63b 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -158,6 +158,11 @@ JX.install('DiffChangesetList', { var label; + if (!standalone) { + label = pht('Jump to the table of contents.'); + this._installKey('t', 'diff-nav', label, this._ontoc); + } + label = pht('Jump to next change.'); this._installJumpKey('j', label, 1); @@ -187,32 +192,31 @@ JX.install('DiffChangesetList', { if (!standalone) { label = pht('Hide or show the current file.'); - this._installKey('h', label, this._onkeytogglefile); - - label = pht('Jump to the table of contents.'); - this._installKey('t', label, this._ontoc); + this._installKey('h', 'diff-vis', label, this._onkeytogglefile); } label = pht('Reply to selected inline comment or change.'); - this._installKey('r', label, JX.bind(this, this._onkeyreply, false)); + this._installKey('r', 'inline', label, + JX.bind(this, this._onkeyreply, false)); label = pht('Reply and quote selected inline comment.'); - this._installKey('R', label, JX.bind(this, this._onkeyreply, true)); + this._installKey('R', 'inline', label, + JX.bind(this, this._onkeyreply, true)); label = pht('Edit selected inline comment.'); - this._installKey('e', label, this._onkeyedit); + this._installKey('e', 'inline', label, this._onkeyedit); label = pht('Mark or unmark selected inline comment as done.'); - this._installKey('w', label, this._onkeydone); + this._installKey('w', 'inline', label, this._onkeydone); label = pht('Collapse or expand inline comment.'); - this._installKey('q', label, this._onkeycollapse); + this._installKey('q', 'diff-vis', label, this._onkeycollapse); label = pht('Hide or show all inline comments.'); - this._installKey('A', label, this._onkeyhideall); + this._installKey('A', 'diff-vis', label, this._onkeyhideall); label = pht('Open file in external editor.'); - this._installKey('\\', label, this._onkeyopeneditor); + this._installKey('\\', 'diff-nav', label, this._onkeyopeneditor); }, @@ -282,11 +286,12 @@ JX.install('DiffChangesetList', { } }, - _installKey: function(key, label, handler) { + _installKey: function(key, group, label, handler) { handler = JX.bind(this, this._ifawake, handler); return new JX.KeyboardShortcut(key, label) .setHandler(handler) + .setGroup(group) .register(); }, @@ -299,7 +304,7 @@ JX.install('DiffChangesetList', { }; var handler = JX.bind(this, this._onjumpkey, delta, options); - return this._installKey(key, label, handler); + return this._installKey(key, 'diff-nav', label, handler); }, _ontoc: function(manager) { diff --git a/webroot/rsrc/js/application/transactions/behavior-show-older-transactions.js b/webroot/rsrc/js/application/transactions/behavior-show-older-transactions.js index 74b17cd45c..5e76b1608f 100644 --- a/webroot/rsrc/js/application/transactions/behavior-show-older-transactions.js +++ b/webroot/rsrc/js/application/transactions/behavior-show-older-transactions.js @@ -114,6 +114,7 @@ JX.behavior('phabricator-show-older-transactions', function(config) { check_hash(); new JX.KeyboardShortcut(['@'], 'Show all older changes in the timeline.') + .setGroup('xactions') .setHandler(JX.bind(null, load_older, load_all_older_callback)) .register(); }); diff --git a/webroot/rsrc/js/core/KeyboardShortcut.js b/webroot/rsrc/js/core/KeyboardShortcut.js index 173a7b8ee2..9d34cce8ad 100644 --- a/webroot/rsrc/js/core/KeyboardShortcut.js +++ b/webroot/rsrc/js/core/KeyboardShortcut.js @@ -20,6 +20,7 @@ JX.install('KeyboardShortcut', { properties : { keys : null, + group: null, description : null, handler : null, tooltipHandler : null diff --git a/webroot/rsrc/js/core/KeyboardShortcutManager.js b/webroot/rsrc/js/core/KeyboardShortcutManager.js index 281c12a8f3..03406051a1 100644 --- a/webroot/rsrc/js/core/KeyboardShortcutManager.js +++ b/webroot/rsrc/js/core/KeyboardShortcutManager.js @@ -65,9 +65,11 @@ JX.install('KeyboardShortcutManager', { getShortcutDescriptions : function() { var desc = []; for (var ii = 0; ii < this._shortcuts.length; ii++) { + var shortcut = this._shortcuts[ii]; desc.push({ - keys : this._shortcuts[ii].getKeys(), - description : this._shortcuts[ii].getDescription() + keys : shortcut.getKeys(), + group: shortcut.getGroup(), + description : shortcut.getDescription() }); } return desc; diff --git a/webroot/rsrc/js/core/behavior-file-tree.js b/webroot/rsrc/js/core/behavior-file-tree.js index 69faf05f3c..ae4a8abf8d 100644 --- a/webroot/rsrc/js/core/behavior-file-tree.js +++ b/webroot/rsrc/js/core/behavior-file-tree.js @@ -8,6 +8,7 @@ JX.behavior('phabricator-file-tree', function() { new JX.KeyboardShortcut('f', 'Toggle file tree.') + .setGroup('diff-vis') .setHandler(function() { JX.Stratcom.invoke('differential-filetree-toggle'); }) diff --git a/webroot/rsrc/js/core/behavior-keyboard-shortcuts.js b/webroot/rsrc/js/core/behavior-keyboard-shortcuts.js index e1c6fa97d8..3a0f5163be 100644 --- a/webroot/rsrc/js/core/behavior-keyboard-shortcuts.js +++ b/webroot/rsrc/js/core/behavior-keyboard-shortcuts.js @@ -15,6 +15,7 @@ JX.behavior('phabricator-keyboard-shortcuts', function(config) { var workflow = null; new JX.KeyboardShortcut('?', pht('?')) + .setGroup('global') .setHandler(function(manager) { if (workflow) { // Already showing the dialog. @@ -32,6 +33,7 @@ JX.behavior('phabricator-keyboard-shortcuts', function(config) { if (config.searchID) { new JX.KeyboardShortcut('/', pht('/')) + .setGroup('global') .setHandler(function() { var search = JX.$(config.searchID); search.focus(); diff --git a/webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js b/webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js index e4d987c425..89d23ef60f 100644 --- a/webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js +++ b/webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js @@ -379,6 +379,7 @@ JX.behavior('phabricator-remarkup-assist', function(config) { if (config.canPin) { new JX.KeyboardShortcut('z', pht('key-help')) + .setGroup('xactions') .setHandler(function() { set_pinned_mode(root, !pinned); }) diff --git a/webroot/rsrc/js/core/darkconsole/behavior-dark-console.js b/webroot/rsrc/js/core/darkconsole/behavior-dark-console.js index a9f33d4594..fc7b04210b 100644 --- a/webroot/rsrc/js/core/darkconsole/behavior-dark-console.js +++ b/webroot/rsrc/js/core/darkconsole/behavior-dark-console.js @@ -259,6 +259,7 @@ JX.behavior('dark-console', function(config, statics) { function install_shortcut() { var desc = 'Toggle visibility of DarkConsole.'; new JX.KeyboardShortcut('`', desc) + .setGroup('global') .setHandler(function() { statics.visible = !statics.visible;