1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-27 01:02:42 +01:00

Add basic support for new navigation menu

Summary:
Add a new left-side application menu. This menu shows which application you're in and provides a quick way to get to other applications.

On desktops, menus are always shown but the app menu can be collapsed to be very small.

On tablets, navigation buttons allow you to choose between the menus and the content.

On phones, navigation buttons allow you to choose between the app menu, the local menu, and the content.

This needs some code and UI cleanup, but has no effect yet so I think it's okay to land as-is, I'll clean it up a bit as I start integrating it. I want to play around with it a bit and see if it's good/useful or horrible anyway.

Test Plan: Will include screenshots.

Reviewers: vrana, btrahan, chad

Reviewed By: btrahan

CC: aran, alanh

Maniphest Tasks: T1569

Differential Revision: https://secure.phabricator.com/D3223
This commit is contained in:
epriestley 2012-08-11 07:06:12 -07:00
parent 7b068d3e46
commit d3fd790574
23 changed files with 705 additions and 111 deletions

View file

@ -7,6 +7,13 @@
*/ */
celerity_register_resource_map(array( celerity_register_resource_map(array(
'/rsrc/image/app/app_applications.png' =>
array(
'hash' => '0e83b7bea93bf92777e546ae6c7ac1cb',
'uri' => '/res/0e83b7be/rsrc/image/app/app_applications.png',
'disk' => '/rsrc/image/app/app_applications.png',
'type' => 'png',
),
'/rsrc/image/app/app_audit.png' => '/rsrc/image/app/app_audit.png' =>
array( array(
'hash' => '53340003d1daf306b64ed5ebb08bc204', 'hash' => '53340003d1daf306b64ed5ebb08bc204',
@ -140,6 +147,27 @@ celerity_register_resource_map(array(
'disk' => '/rsrc/image/bolt.png', 'disk' => '/rsrc/image/bolt.png',
'type' => 'png', 'type' => 'png',
), ),
'/rsrc/image/button_apps.png' =>
array(
'hash' => 'cc29f793afd01b15af613562225118f3',
'uri' => '/res/cc29f793/rsrc/image/button_apps.png',
'disk' => '/rsrc/image/button_apps.png',
'type' => 'png',
),
'/rsrc/image/button_content.png' =>
array(
'hash' => '87cc5797352097b4b3d7541e6c46f032',
'uri' => '/res/87cc5797/rsrc/image/button_content.png',
'disk' => '/rsrc/image/button_content.png',
'type' => 'png',
),
'/rsrc/image/button_menu.png' =>
array(
'hash' => '5742857c7734d9d25be1125f5737fe0e',
'uri' => '/res/5742857c/rsrc/image/button_menu.png',
'disk' => '/rsrc/image/button_menu.png',
'type' => 'png',
),
'/rsrc/image/credit_cards.png' => '/rsrc/image/credit_cards.png' =>
array( array(
'hash' => '681448de424ea159b6ea68af04c046ae', 'hash' => '681448de424ea159b6ea68af04c046ae',
@ -1153,7 +1181,7 @@ celerity_register_resource_map(array(
), ),
'javelin-behavior-device' => 'javelin-behavior-device' =>
array( array(
'uri' => '/res/bec84986/rsrc/js/application/core/behavior-device.js', 'uri' => '/res/37669d1a/rsrc/js/application/core/behavior-device.js',
'type' => 'js', 'type' => 'js',
'requires' => 'requires' =>
array( array(
@ -1614,7 +1642,7 @@ celerity_register_resource_map(array(
), ),
'javelin-behavior-phabricator-nav' => 'javelin-behavior-phabricator-nav' =>
array( array(
'uri' => '/res/adaae8ae/rsrc/js/application/core/behavior-phabricator-nav.js', 'uri' => '/res/cb8979b2/rsrc/js/application/core/behavior-phabricator-nav.js',
'type' => 'js', 'type' => 'js',
'requires' => 'requires' =>
array( array(
@ -1623,6 +1651,9 @@ celerity_register_resource_map(array(
2 => 'javelin-dom', 2 => 'javelin-dom',
3 => 'javelin-magical-init', 3 => 'javelin-magical-init',
4 => 'javelin-vector', 4 => 'javelin-vector',
5 => 'javelin-request',
6 => 'javelin-util',
7 => 'javelin-fx',
), ),
'disk' => '/rsrc/js/application/core/behavior-phabricator-nav.js', 'disk' => '/rsrc/js/application/core/behavior-phabricator-nav.js',
), ),
@ -1819,7 +1850,7 @@ celerity_register_resource_map(array(
), ),
'javelin-behavior-toggle-class' => 'javelin-behavior-toggle-class' =>
array( array(
'uri' => '/res/35b86b96/rsrc/js/application/core/behavior-toggle-class.js', 'uri' => '/res/fa818e0f/rsrc/js/application/core/behavior-toggle-class.js',
'type' => 'js', 'type' => 'js',
'requires' => 'requires' =>
array( array(
@ -2533,7 +2564,7 @@ celerity_register_resource_map(array(
), ),
'phabricator-nav-view-css' => 'phabricator-nav-view-css' =>
array( array(
'uri' => '/res/3443576d/rsrc/css/aphront/phabricator-nav-view.css', 'uri' => '/res/82636b80/rsrc/css/aphront/phabricator-nav-view.css',
'type' => 'css', 'type' => 'css',
'requires' => 'requires' =>
array( array(

View file

@ -882,6 +882,7 @@ phutil_register_library_map(array(
'PhabricatorPasteListController' => 'applications/paste/controller/PhabricatorPasteListController.php', 'PhabricatorPasteListController' => 'applications/paste/controller/PhabricatorPasteListController.php',
'PhabricatorPasteQuery' => 'applications/paste/query/PhabricatorPasteQuery.php', 'PhabricatorPasteQuery' => 'applications/paste/query/PhabricatorPasteQuery.php',
'PhabricatorPasteViewController' => 'applications/paste/controller/PhabricatorPasteViewController.php', 'PhabricatorPasteViewController' => 'applications/paste/controller/PhabricatorPasteViewController.php',
'PhabricatorPeopleAdjustSettingController' => 'applications/people/controller/PhabricatorPeopleAdjustSettingController.php',
'PhabricatorPeopleController' => 'applications/people/controller/PhabricatorPeopleController.php', 'PhabricatorPeopleController' => 'applications/people/controller/PhabricatorPeopleController.php',
'PhabricatorPeopleEditController' => 'applications/people/controller/PhabricatorPeopleEditController.php', 'PhabricatorPeopleEditController' => 'applications/people/controller/PhabricatorPeopleEditController.php',
'PhabricatorPeopleLdapController' => 'applications/people/controller/PhabricatorPeopleLdapController.php', 'PhabricatorPeopleLdapController' => 'applications/people/controller/PhabricatorPeopleLdapController.php',
@ -1964,6 +1965,7 @@ phutil_register_library_map(array(
'PhabricatorPasteListController' => 'PhabricatorPasteController', 'PhabricatorPasteListController' => 'PhabricatorPasteController',
'PhabricatorPasteQuery' => 'PhabricatorCursorPagedPolicyQuery', 'PhabricatorPasteQuery' => 'PhabricatorCursorPagedPolicyQuery',
'PhabricatorPasteViewController' => 'PhabricatorPasteController', 'PhabricatorPasteViewController' => 'PhabricatorPasteController',
'PhabricatorPeopleAdjustSettingController' => 'PhabricatorPeopleController',
'PhabricatorPeopleController' => 'PhabricatorController', 'PhabricatorPeopleController' => 'PhabricatorController',
'PhabricatorPeopleEditController' => 'PhabricatorPeopleController', 'PhabricatorPeopleEditController' => 'PhabricatorPeopleController',
'PhabricatorPeopleLdapController' => 'PhabricatorPeopleController', 'PhabricatorPeopleLdapController' => 'PhabricatorPeopleController',

View file

@ -42,6 +42,10 @@ final class PhabricatorApplicationAudit extends PhabricatorApplication {
); );
} }
public function getCoreApplicationOrder() {
return 0.130;
}
public function loadStatus(PhabricatorUser $user) { public function loadStatus(PhabricatorUser $user) {
$status = array(); $status = array();

View file

@ -62,6 +62,10 @@ abstract class PhabricatorApplication {
return true; return true;
} }
public function getCoreApplicationOrder() {
return null;
}
/* -( URI Routing )-------------------------------------------------------- */ /* -( URI Routing )-------------------------------------------------------- */

View file

@ -66,6 +66,10 @@ final class PhabricatorApplicationDifferential extends PhabricatorApplication {
); );
} }
public function getCoreApplicationOrder() {
return 0.100;
}
public function loadStatus(PhabricatorUser $user) { public function loadStatus(PhabricatorUser $user) {
$revisions = id(new DifferentialRevisionQuery()) $revisions = id(new DifferentialRevisionQuery())
->withResponsibleUsers(array($user->getPHID())) ->withResponsibleUsers(array($user->getPHID()))

View file

@ -72,5 +72,9 @@ final class PhabricatorApplicationDiffusion extends PhabricatorApplication {
); );
} }
public function getCoreApplicationOrder() {
return 0.120;
}
} }

View file

@ -34,6 +34,10 @@ final class PhabricatorApplicationManiphest extends PhabricatorApplication {
return celerity_get_resource_uri('/rsrc/image/app/app_maniphest.png'); return celerity_get_resource_uri('/rsrc/image/app/app_maniphest.png');
} }
public function getCoreApplicationOrder() {
return 0.110;
}
public function getFactObjectsForAnalysis() { public function getFactObjectsForAnalysis() {
return array( return array(
new ManiphestTask(), new ManiphestTask(),

View file

@ -84,6 +84,11 @@ abstract class ManiphestController extends PhabricatorController {
$nav->addLabel('Reports'); $nav->addLabel('Reports');
$nav->addFilter('report', 'Reports', '/maniphest/report/'); $nav->addFilter('report', 'Reports', '/maniphest/report/');
$nav->setFlexNav(true);
$nav->setShowApplicationMenu(true);
$nav->setCurrentApplication($this->getCurrentApplication());
$nav->setUser($this->getRequest()->getUser());
return $nav; return $nav;
} }

View file

@ -26,6 +26,10 @@ final class PhabricatorApplicationApplications extends PhabricatorApplication {
return 'Manage Applications'; return 'Manage Applications';
} }
public function getIconURI() {
return celerity_get_resource_uri('/rsrc/image/app/app_applications.png');
}
public function getRoutes() { public function getRoutes() {
return array( return array(
'/applications/' => array( '/applications/' => array(

View file

@ -34,6 +34,7 @@ final class PhabricatorApplicationSettings extends PhabricatorApplication {
return array( return array(
'/settings/' => array( '/settings/' => array(
'(?:page/(?P<page>[^/]+)/)?' => 'PhabricatorUserSettingsController', '(?:page/(?P<page>[^/]+)/)?' => 'PhabricatorUserSettingsController',
'adjust/' => 'PhabricatorPeopleAdjustSettingController',
), ),
); );
} }

View file

@ -0,0 +1,34 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorPeopleAdjustSettingController
extends PhabricatorPeopleController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$prefs = $user->loadPreferences();
$prefs->setPreference(
$request->getStr('key'),
$request->getStr('value'));
$prefs->save();
return id(new AphrontAjaxResponse())->setContent(array());
}
}

View file

@ -33,6 +33,9 @@ final class PhabricatorUserPreferences extends PhabricatorUserDAO {
const PREFERENCE_DIFFUSION_VIEW = 'diffusion-view'; const PREFERENCE_DIFFUSION_VIEW = 'diffusion-view';
const PREFERENCE_DIFFUSION_SYMBOLS = 'diffusion-symbols'; const PREFERENCE_DIFFUSION_SYMBOLS = 'diffusion-symbols';
const PREFERENCE_NAV_COLLAPSED = 'nav-collapsed';
const PREFERENCE_NAV_WIDTH = 'nav-width';
protected $userPHID; protected $userPHID;
protected $preferences = array(); protected $preferences = array();

View file

@ -53,5 +53,9 @@ final class PhabricatorApplicationPhriction extends PhabricatorApplication {
); );
} }
public function getCoreApplicationOrder() {
return 0.140;
}
} }

View file

@ -42,6 +42,24 @@ final class AphrontSideNavFilterView extends AphrontView {
private $selectedFilter = false; private $selectedFilter = false;
private $flexNav; private $flexNav;
private $flexible; private $flexible;
private $showApplicationMenu;
private $user;
private $currentApplication;
public function setCurrentApplication(PhabricatorApplication $current) {
$this->currentApplication = $current;
return $this;
}
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function setShowApplicationMenu($show_application_menu) {
$this->showApplicationMenu = $show_application_menu;
return $this;
}
public function setFlexNav($flex_nav) { public function setFlexNav($flex_nav) {
$this->flexNav = $flex_nav; $this->flexNav = $flex_nav;
@ -129,6 +147,13 @@ final class AphrontSideNavFilterView extends AphrontView {
$view = new AphrontSideNavView(); $view = new AphrontSideNavView();
$view->setFlexNav($this->flexNav); $view->setFlexNav($this->flexNav);
$view->setFlexible($this->flexible); $view->setFlexible($this->flexible);
$view->setShowApplicationMenu($this->showApplicationMenu);
if ($this->user) {
$view->setUser($this->user);
}
if ($this->currentApplication) {
$view->setCurrentApplication($this->currentApplication);
}
foreach ($this->items as $item) { foreach ($this->items as $item) {
list($type, $key, $name) = $item; list($type, $key, $name) = $item;
switch ($type) { switch ($type) {

View file

@ -18,9 +18,27 @@
final class AphrontSideNavView extends AphrontView { final class AphrontSideNavView extends AphrontView {
protected $items = array(); private $items = array();
protected $flexNav; private $flexNav;
protected $isFlexible; private $isFlexible;
private $showApplicationMenu;
private $user;
private $currentApplication;
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function setShowApplicationMenu($show_application_menu) {
$this->showApplicationMenu = $show_application_menu;
return $this;
}
public function setCurrentApplication(PhabricatorApplication $current) {
$this->currentApplication = $current;
return $this;
}
public function addNavItem($item) { public function addNavItem($item) {
$this->items[] = $item; $this->items[] = $item;
@ -42,20 +60,60 @@ final class AphrontSideNavView extends AphrontView {
$view->appendChild($this->items); $view->appendChild($this->items);
if ($this->flexNav) { if ($this->flexNav) {
$user = $this->user;
require_celerity_resource('phabricator-nav-view-css'); require_celerity_resource('phabricator-nav-view-css');
$nav_id = celerity_generate_unique_node_id(); $nav_classes = array();
$drag_id = celerity_generate_unique_node_id(); $nav_classes[] = 'phabricator-nav';
$app_id = celerity_generate_unique_node_id();
$nav_id = null;
$drag_id = null;
$content_id = celerity_generate_unique_node_id(); $content_id = celerity_generate_unique_node_id();
$collapse_id = null;
$expand_id = null;
$main_id = celerity_generate_unique_node_id();
$apps = $this->renderApplications();
$key = PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED;
if ($user->loadPreferences()->getPreference($key)) {
$nav_classes[] = 'phabricator-nav-app-collapsed';
}
$collapse_id = celerity_generate_unique_node_id();
$expand_id = celerity_generate_unique_node_id();
$collapse_button = phutil_render_tag(
'a',
array(
'href' => '#',
'class' => 'phabricator-nav-app-button-collapse',
'id' => $collapse_id,
),
'&laquo; Collapse');
$expand_button = phutil_render_tag(
'a',
array(
'href' => '#',
'class' => 'phabricator-nav-app-button-expand',
'id' => $expand_id,
),
'&raquo;');
$app_menu = phutil_render_tag(
'div',
array(
'class' => 'phabricator-nav-col phabricator-nav-app',
'id' => $app_id,
),
$apps->render()).
$expand_button.
$collapse_button;
if ($this->flexible) { if ($this->flexible) {
Javelin::initBehavior( $drag_id = celerity_generate_unique_node_id();
'phabricator-nav',
array(
'navID' => $nav_id,
'dragID' => $drag_id,
'contentID' => $content_id,
));
$flex_bar = phutil_render_tag( $flex_bar = phutil_render_tag(
'div', 'div',
array( array(
@ -67,24 +125,66 @@ final class AphrontSideNavView extends AphrontView {
$flex_bar = null; $flex_bar = null;
} }
return $nav_menu = null;
'<div class="phabricator-nav">'. if ($this->items) {
phutil_render_tag( $local_id = celerity_generate_unique_node_id();
'div', $nav_classes[] = 'has-local-nav';
array( $local_menu = phutil_render_tag(
'class' => 'phabricator-nav-col', 'div',
'id' => $nav_id, array(
), 'class' => 'phabricator-nav-col phabricator-nav-local',
$view->render()). 'id' => $local_id,
$flex_bar. ),
phutil_render_tag( $view->render());
'div', }
array(
'class' => 'phabricator-nav-content', Javelin::initBehavior(
'id' => $content_id, 'phabricator-nav',
), array(
$this->renderChildren()). 'mainID' => $main_id,
'appID' => $app_id,
'localID' => $local_id,
'dragID' => $drag_id,
'contentID' => $content_id,
'collapseID' => $collapse_id,
'expandID' => $expand_id,
'collapseKey' => $key,
));
$header_part =
'<div class="phabricator-nav-head">'.
'<div class="phabricator-nav-head-tablet">'.
'<a href="#" class="nav-button nav-button-w nav-button-menu" '.
'id="tablet-menu1"></a>'.
'<a href="#" class="nav-button nav-button-e nav-button-content '.
'nav-button-selected" id="tablet-menu2"></a>'.
'</div>'.
'<div class="phabricator-nav-head-phone">'.
'<a href="#" class="nav-button nav-button-w nav-button-apps" '.
'id="phone-menu1"></button>'.
'<a href="#" class="nav-button nav-button-menu" '.
'id="phone-menu2"></button>'.
'<a href="#" class="nav-button nav-button-e nav-button-content '.
'nav-button-selected" id="phone-menu3"></button>'.
'</div>'.
'</div>'; '</div>';
return $header_part.phutil_render_tag(
'div',
array(
'class' => implode(' ', $nav_classes),
'id' => $main_id,
),
$app_menu.
$local_menu.
$flex_bar.
phutil_render_tag(
'div',
array(
'class' => 'phabricator-nav-content',
'id' => $content_id,
),
$this->renderChildren()));
} else { } else {
require_celerity_resource('aphront-side-nav-view-css'); require_celerity_resource('aphront-side-nav-view-css');
@ -103,4 +203,52 @@ final class AphrontSideNavView extends AphrontView {
} }
} }
private function renderApplications() {
$core = array();
$current = $this->currentApplication;
$meta = null;
$applications = PhabricatorApplication::getAllInstalledApplications();
foreach ($applications as $application) {
if ($application instanceof PhabricatorApplicationApplications) {
$meta = $application;
continue;
}
if ($application->getCoreApplicationOrder() !== null) {
$core[] = $application;
}
}
$core = msort($core, 'getCoreApplicationOrder');
if ($meta) {
$core[] = $meta;
}
$core = mpull($core, null, 'getPHID');
if ($current && empty($core[$current->getPHID()])) {
array_unshift($core, $this->current);
}
$apps = array();
foreach ($core as $phid => $application) {
$classes = array();
$classes[] = 'phabricator-nav-app-item';
if ($current && $phid == $current->getPHID()) {
$classes[] = 'phabricator-nav-app-item-selected';
}
$apps[] = phutil_render_tag(
'a',
array(
'class' => implode(' ', $classes),
'href' => $application->getBaseURI(),
'style' => 'background-image: url('.$application->getIconURI().')',
),
phutil_escape_html($application->getName()));
}
return id(new AphrontNullView())->appendChild($apps);
}
} }

View file

@ -11,11 +11,9 @@
top: 44px; top: 44px;
left: 0; left: 0;
bottom: 0; bottom: 0;
width: 179px;
background: #e3e3e3;
border-right: 1px solid #999c9e; border-right: 1px solid #999c9e;
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.20); box-shadow: inset -3px 0 4px rgba(0, 0, 0, 0.05);
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
@ -23,6 +21,21 @@
white-space: nowrap; white-space: nowrap;
} }
.phabricator-nav-app {
width: 149px;
background: #d2d2d2;
}
.phabricator-nav-local {
width: 179px;
background: #ececec;
}
.device-tablet .phabricator-nav-local,
.device-phone .phabricator-nav-local {
width: 299px;
}
.phabricator-nav-drag { .phabricator-nav-drag {
position: fixed; position: fixed;
top: 44px; top: 44px;
@ -37,7 +50,7 @@
border-width: 0 1px 0 1px; border-width: 0 1px 0 1px;
border-color: #fff #999c9e #fff #999c9e; border-color: #fff #999c9e #fff #999c9e;
box-shadow: inset -1px 0px 2px rgba(0, 0, 0, 0.15); box-shadow: inset -1px 0px 1px rgba(0, 0, 0, 0.15);
background-image: url(/rsrc/image/divot.png); background-image: url(/rsrc/image/divot.png);
background-position: center; background-position: center;
@ -54,10 +67,30 @@
display: block; display: block;
} }
.phabricator-nav-content { .phabricator-nav-local {
margin-left: 180px; left: 150px;
} }
.device-desktop .phabricator-nav-app-collapsed .phabricator-nav-local {
left: 34px;
}
.phabricator-nav-content {
margin-left: 150px;
}
.has-local-nav .phabricator-nav-content {
margin-left: 330px;
}
.device-desktop .phabricator-nav-app-collapsed .phabricator-nav-content {
margin-left: 32px;
}
.device-desktop .has-local-nav.phabricator-nav-app-collapsed
.phabricator-nav-content {
margin-left: 212px;
}
.phabricator-nav-col span { .phabricator-nav-col span {
display: block; display: block;
@ -74,10 +107,183 @@
} }
.phabricator-nav-col a.aphront-side-nav-selected { .phabricator-nav-col a.aphront-side-nav-selected {
background: #a1bbe5; background-color: #a1bbe5;
} }
.phabricator-nav-col a:hover { .device-desktop .phabricator-nav-col a:hover {
background: #3875d7; background-color: #3875d7;
color: #ffffff; color: #ffffff;
} }
a.phabricator-nav-app-item {
color: #222222;
font-weight: normal;
padding: 4px;
padding-left: 37px;
vertical-align: middle;
line-height: 25px;
border-width: 1px 0px;
border-style: solid;
border-color: transparent;
background-size: auto 25px;
background-position: 4px 4px;
background-repeat: no-repeat;
}
a.phabricator-nav-app-item-selected {
background-color: #f3f3f3;
box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.20);
border-color: #b0b0b0;
}
a.phabricator-nav-app-button-expand,
a.phabricator-nav-app-button-collapse {
position: fixed;
display: none;
left: 0;
bottom: 0;
padding: 4px;
z-index: 2;
background: #d9d9d9;
line-height: 14px;
border-top: 1px solid #a9a9a9;
text-align: center;
font-size: 11px;
box-shadow: inset -1px -1px 3px rgba(0, 0, 0, 0.1);
color: #696969;
text-decoration: none;
}
.phabricator-nav-app-button-collapse {
width: 141px;
}
.phabricator-nav-app-button-expand {
width: 25px;
display: none;
}
.device-desktop .phabricator-nav-app-button-expand,
.device-desktop .phabricator-nav-app-button-collapse {
display: block;
}
.device-desktop .phabricator-nav-app-collapsed .phabricator-nav-app {
width: 33px;
}
.phabricator-nav-app-collapsed .phabricator-nav-app-button-collapse {
display: none;
}
.device-desktop .phabricator-nav-app-collapsed
.phabricator-nav-app-button-expand {
display: block;
}
.device-desktop .phabricator-nav-head {
display: none;
}
.device-tablet .phabricator-nav-col,
.device-phone .phabricator-nav-col {
position: absolute;
top: 0px;
}
.device-tablet .phabricator-nav-app,
.device-phone .phabricator-nav-app {
left: -450px;
}
.device-tablet .phabricator-nav-local,
.device-phone .phabricator-nav-local {
left: -300px;
}
.device-phone .phabricator-nav-head-tablet {
display: none;
}
.device-tablet .phabricator-nav-head-phone {
display: none;
}
.device-tablet .phabricator-nav,
.device-phone .phabricator-nav {
overflow-x: hidden;
position: relative;
}
.device-tablet .phabricator-nav-content,
.device-phone .phabricator-nav-content {
width: 100%;
}
.device-tablet .phabricator-nav-content,
.device-phone .phabricator-nav-content {
margin-left: 0;
position: relative;
}
.phabricator-nav-head {
display: block;
position: relative;
height: 43px;
background: #e6e6e6;
overflow: hidden;
border-bottom: 1px solid #9d9d9d;
text-align: center;
box-shadow: inset 0 0px 3px rgba(0, 0, 0, 0.30),
0px 1px 2px rgba(0, 0, 0, 0.10);
z-index: 3;
}
.nav-button {
background-color: #f3f3f3;
height: 32px;
width: 40px;
margin: 5px 0px;
display: inline-block;
border: 1px solid #999999;
box-shadow: inset -1px -1px 3px rgba(0, 0, 0, 0.10);
background-repeat: no-repeat;
}
.nav-button-selected {
background-color: #c9c9c9;
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.20);
}
.nav-button + .nav-button {
margin-left: -1px;
}
.nav-button-w {
border-radius: 6px 0 0 6px;
}
.nav-button-e {
border-radius: 0 6px 6px 0;
}
.nav-button-apps {
background-image: url(/rsrc/image/button_apps.png);
background-size: 24px auto;
background-position: center;
}
.nav-button-menu {
background-image: url(/rsrc/image/button_menu.png);
background-size: 24px auto;
background-position: center;
}
.nav-button-content {
background-image: url(/rsrc/image/button_content.png);
background-size: 24px auto;
background-position: center;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

View file

@ -31,6 +31,8 @@ JX.behavior('device', function(config) {
JX.DOM.alterClass(e, 'device-phone', (device == 'phone')); JX.DOM.alterClass(e, 'device-phone', (device == 'phone'));
JX.DOM.alterClass(e, 'device-tablet', (device == 'tablet')); JX.DOM.alterClass(e, 'device-tablet', (device == 'tablet'));
JX.DOM.alterClass(e, 'device-desktop', (device == 'desktop')); JX.DOM.alterClass(e, 'device-desktop', (device == 'desktop'));
JX.Stratcom.invoke('phabricator-device-change', device);
} }
JX.Stratcom.listen('resize', null, onresize); JX.Stratcom.listen('resize', null, onresize);

View file

@ -5,93 +5,202 @@
* javelin-dom * javelin-dom
* javelin-magical-init * javelin-magical-init
* javelin-vector * javelin-vector
* javelin-request
* javelin-util
* javelin-fx
* @javelin * @javelin
*/ */
JX.behavior('phabricator-nav', function(config) { JX.behavior('phabricator-nav', function(config) {
var dragging; var app = JX.$(config.appID);
var track;
var nav = JX.$(config.navID);
var drag = JX.$(config.dragID);
var content = JX.$(config.contentID); var content = JX.$(config.contentID);
var local = config.localID ? JX.$(config.localID) : null;
JX.enableDispatch(document.body, 'mousemove');
JX.DOM.listen(drag, 'mousedown', null, function(e) { // - Sliding Menu Animations ---------------------------------------------------
dragging = JX.$V(e);
// Show the "col-resize" cursor on the whole document while we're var animations = [];
// dragging, since the mouse will slip off the actual bar fairly often and function slide_menu(position) {
// we don't want it to flicker. var app_width = 150;
JX.DOM.alterClass(document.body, 'jx-drag-col', true); var local_width = local ? 300 : 0;
track = [ var shifts = {
{ 0: 0,
element: nav, 1: app_width - 10,
parameter: 'width', 2: app_width + local_width
start: JX.Vector.getDim(nav).x, };
scale: 1, var shift = shifts[position];
width: JX.Vector.getDim(nav).x, while (animations.length) {
minWidth: 150, animations.pop().stop();
minScale: 1 }
}, animations.push(build_animation(app, -shift));
{ local && animations.push(build_animation(local, -shift + app_width));
element: drag, animations.push(build_animation(content, -shift + app_width + local_width));
parameter: 'left',
start: JX.$V(drag).x,
scale: 1
},
{
element: content,
parameter: 'marginLeft',
start: parseInt(getComputedStyle(content).marginLeft, 10),
scale: 1,
width: JX.Vector.getDim(content).x, select_button(position);
minWidth: 300, }
minScale: -1
function build_animation(element, target) {
return new JX.FX(element)
.setDuration(100)
.start({left: [JX.$V(element).x, target]});
}
// - Sliding Menu Buttons ------------------------------------------------------
var button_positions = {
0: [JX.$('phone-menu1'), JX.$('tablet-menu1')],
1: [JX.$('phone-menu2')],
2: [JX.$('phone-menu3'), JX.$('tablet-menu2')]
};
for (var k in button_positions) {
for (var ii = 0; ii < button_positions[k].length; ii++) {
var onclick = function(p, e) {
e.kill();
slide_menu(p);
};
onclick = JX.bind(null, onclick, k);
JX.DOM.listen(
button_positions[k][ii],
['touchstart', 'mousedown'],
null,
onclick);
}
}
function select_button(position) {
for (var k in button_positions) {
for (var ii = 0; ii < button_positions[k].length; ii++) {
JX.DOM.alterClass(
button_positions[k][ii],
'nav-button-selected',
(k == position));
} }
]; }
}
e.kill();
// - Application Menu Collapse/Expand ------------------------------------------
function collapse(state, skip_save) {
JX.DOM.alterClass(
JX.$(config.mainID),
'phabricator-nav-app-collapsed',
state);
if (!skip_save) {
new JX.Request('/settings/adjust/', JX.bag)
.setData({key: config.collapseKey, value: state ? 1 : ''})
.send();
}
}
JX.DOM.listen(JX.$(config.collapseID), 'click', null, function(e) {
collapse(true);
}); });
JX.Stratcom.listen('mousemove', null, function(e) { JX.DOM.listen(JX.$(config.expandID), 'click', null, function(e) {
if (!dragging) { collapse(false);
return;
}
var dx = JX.$V(e).x - dragging.x;
var panel;
for (var k = 0; k < track.length; k++) {
panel = track[k];
if (!panel.minWidth) {
continue;
}
var new_width = panel.width + (dx * panel.minScale);
if (new_width < panel.minWidth) {
dx = (panel.minWidth - panel.width) * panel.minScale;
}
}
for (var k = 0; k < track.length; k++) {
panel = track[k];
var v = (panel.start + (dx * panel.scale));
panel.element.style[panel.parameter] = v + 'px';
}
}); });
JX.Stratcom.listen('mouseup', null, function(e) {
if (!dragging) {
return;
}
JX.DOM.alterClass(document.body, 'jx-drag-col', false); // - Flexible Navigation Column ------------------------------------------------
dragging = false;
if (config.dragID) {
var dragging;
var track;
var drag = JX.$(config.dragID);
JX.enableDispatch(document.body, 'mousemove');
JX.DOM.listen(drag, 'mousedown', null, function(e) {
dragging = JX.$V(e);
// Show the "col-resize" cursor on the whole document while we're
// dragging, since the mouse will slip off the actual bar fairly often and
// we don't want it to flicker.
JX.DOM.alterClass(document.body, 'jx-drag-col', true);
track = [
{
element: local,
parameter: 'width',
start: JX.Vector.getDim(local).x,
scale: 1,
width: JX.Vector.getDim(local).x,
minWidth: 150,
minScale: 1
},
{
element: drag,
parameter: 'left',
start: JX.$V(drag).x,
scale: 1
},
{
element: content,
parameter: 'marginLeft',
start: parseInt(getComputedStyle(content).marginLeft, 10),
scale: 1,
width: JX.Vector.getDim(content).x,
minWidth: 300,
minScale: -1
}
];
e.kill();
});
JX.Stratcom.listen('mousemove', null, function(e) {
if (!dragging) {
return;
}
var dx = JX.$V(e).x - dragging.x;
var panel;
for (var k = 0; k < track.length; k++) {
panel = track[k];
if (!panel.minWidth) {
continue;
}
var new_width = panel.width + (dx * panel.minScale);
if (new_width < panel.minWidth) {
dx = (panel.minWidth - panel.width) * panel.minScale;
}
}
for (var k = 0; k < track.length; k++) {
panel = track[k];
var v = (panel.start + (dx * panel.scale));
panel.element.style[panel.parameter] = v + 'px';
}
});
JX.Stratcom.listen('mouseup', null, function(e) {
if (!dragging) {
return;
}
JX.DOM.alterClass(document.body, 'jx-drag-col', false);
dragging = false;
});
}
// - Navigation Reset ----------------------------------------------------------
JX.Stratcom.listen('phabricator-device-change', null, function(device) {
app.style.left = '';
local && (local.style.left = '');
content.style.left = '';
select_button(2);
}); });
}); });

View file

@ -18,7 +18,7 @@
*/ */
JX.behavior('toggle-class', function() { JX.behavior('toggle-class', function() {
JX.Stratcom.listen( JX.Stratcom.listen(
'click', ['touchstart', 'mousedown'],
'jx-toggle-class', 'jx-toggle-class',
function(e) { function(e) {
e.kill(); e.kill();