Page MenuHomePhabricator

D15056.diff
No OneTemporary

D15056.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -7,7 +7,7 @@
*/
return array(
'names' => array(
- 'core.pkg.css' => 'c61091b0',
+ 'core.pkg.css' => '7fce81fc',
'core.pkg.js' => '573e6664',
'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => '2de124c9',
@@ -143,7 +143,7 @@
'rsrc/css/phui/phui-object-item-list-view.css' => '26c30d3f',
'rsrc/css/phui/phui-pager.css' => 'bea33d23',
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
- 'rsrc/css/phui/phui-profile-menu.css' => 'a26fa598',
+ 'rsrc/css/phui/phui-profile-menu.css' => '72d69773',
'rsrc/css/phui/phui-property-list-view.css' => '27b2849e',
'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591',
'rsrc/css/phui/phui-spacing.css' => '042804d6',
@@ -500,6 +500,7 @@
'rsrc/js/core/phtize.js' => 'd254d646',
'rsrc/js/phui/behavior-phui-dropdown-menu.js' => '54733475',
'rsrc/js/phui/behavior-phui-object-box-tabs.js' => '2bfa2836',
+ 'rsrc/js/phui/behavior-phui-profile-menu.js' => 'bf2c93d6',
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262',
'rsrc/js/phuix/PHUIXAutocomplete.js' => '21dc9144',
@@ -648,6 +649,7 @@
'javelin-behavior-pholio-mock-view' => 'fbe497e7',
'javelin-behavior-phui-dropdown-menu' => '54733475',
'javelin-behavior-phui-object-box-tabs' => '2bfa2836',
+ 'javelin-behavior-phui-profile-menu' => 'bf2c93d6',
'javelin-behavior-policy-control' => 'ae45872f',
'javelin-behavior-policy-rule-editor' => '5e9f347c',
'javelin-behavior-project-boards' => 'ba4fa35c',
@@ -817,7 +819,7 @@
'phui-object-item-list-view-css' => '26c30d3f',
'phui-pager-css' => 'bea33d23',
'phui-pinboard-view-css' => '2495140e',
- 'phui-profile-menu-css' => 'a26fa598',
+ 'phui-profile-menu-css' => '72d69773',
'phui-property-list-view-css' => '27b2849e',
'phui-remarkup-preview-css' => '1a8f2591',
'phui-spacing-css' => '042804d6',
@@ -1772,6 +1774,11 @@
'javelin-util',
'javelin-request',
),
+ 'bf2c93d6' => array(
+ 'javelin-behavior',
+ 'javelin-stratcom',
+ 'javelin-dom',
+ ),
'bff6884b' => array(
'javelin-install',
'javelin-dom',
diff --git a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php
--- a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php
+++ b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php
@@ -191,14 +191,29 @@
// Background color for "dark" themes.
'page.background.dark' => '#ebecee',
+ // NOTE: We can't just do these with an alpha channel because the
+ // fixed items in the footer may render on top of other items, so the
+ // backgrounds must be opaque.
+
+ // This is the base background color.
'menu.profile.background' => '#525868',
+
+ // This is premultiplied 7.5% alpha.
+ 'menu.profile.background.hover' => '#4c5160',
+
+ // This is premultiplied 15% alpha.
+ 'menu.profile.background.selected' => '#464b59',
+
'menu.profile.text' => '#c6c7cb',
'menu.profile.text.selected' => '#ffffff',
- 'menu.profile.icon' => '#ffffff',
'menu.profile.icon.disabled' => '#b9bcc2',
'menu.main.height' => '44px',
+ 'menu.profile.width' => '240px',
+ 'menu.profile.width.collapsed' => '80px',
+ 'menu.profile.item.height' => '46px',
+
);
}
diff --git a/src/applications/project/controller/PhabricatorProjectController.php b/src/applications/project/controller/PhabricatorProjectController.php
--- a/src/applications/project/controller/PhabricatorProjectController.php
+++ b/src/applications/project/controller/PhabricatorProjectController.php
@@ -4,6 +4,7 @@
private $project;
private $profileMenu;
+ private $profilePanelEngine;
protected function setProject(PhabricatorProject $project) {
$this->project = $project;
@@ -98,14 +99,8 @@
protected function getProfileMenu() {
if (!$this->profileMenu) {
- $project = $this->getProject();
- if ($project) {
- $viewer = $this->getViewer();
-
- $engine = id(new PhabricatorProjectProfilePanelEngine())
- ->setViewer($viewer)
- ->setProfileObject($project);
-
+ $engine = $this->getProfilePanelEngine();
+ if ($engine) {
$this->profileMenu = $engine->buildNavigation();
}
}
@@ -131,4 +126,24 @@
return $crumbs;
}
+ protected function getProfilePanelEngine() {
+ if (!$this->profilePanelEngine) {
+ $viewer = $this->getViewer();
+ $project = $this->getProject();
+ if ($project) {
+ $engine = id(new PhabricatorProjectProfilePanelEngine())
+ ->setViewer($viewer)
+ ->setProfileObject($project);
+ $this->profilePanelEngine = $engine;
+ }
+ }
+ return $this->profilePanelEngine;
+ }
+
+ protected function setProfilePanelEngine(
+ PhabricatorProjectProfilePanelEngine $engine) {
+ $this->profilePanelEngine = $engine;
+ return $this;
+ }
+
}
diff --git a/src/applications/project/controller/PhabricatorProjectPanelController.php b/src/applications/project/controller/PhabricatorProjectPanelController.php
--- a/src/applications/project/controller/PhabricatorProjectPanelController.php
+++ b/src/applications/project/controller/PhabricatorProjectPanelController.php
@@ -12,10 +12,13 @@
$viewer = $this->getViewer();
$project = $this->getProject();
- return id(new PhabricatorProjectProfilePanelEngine())
+ $engine = id(new PhabricatorProjectProfilePanelEngine())
->setProfileObject($project)
- ->setController($this)
- ->buildResponse();
+ ->setController($this);
+
+ $this->setProfilePanelEngine($engine);
+
+ return $engine->buildResponse();
}
}
diff --git a/src/applications/search/engine/PhabricatorProfilePanelEngine.php b/src/applications/search/engine/PhabricatorProfilePanelEngine.php
--- a/src/applications/search/engine/PhabricatorProfilePanelEngine.php
+++ b/src/applications/search/engine/PhabricatorProfilePanelEngine.php
@@ -6,6 +6,7 @@
private $profileObject;
private $panels;
private $controller;
+ private $navigation;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
@@ -147,6 +148,10 @@
}
public function buildNavigation() {
+ if ($this->navigation) {
+ return $this->navigation;
+ }
+
$nav = id(new AphrontSideNavFilterView())
->setIsProfileMenu(true)
->setBaseURI(new PhutilURI($this->getPanelURI('')));
@@ -185,14 +190,15 @@
}
}
- $configure_item = $this->newConfigureMenuItem();
- if ($configure_item) {
- $nav->addMenuItem($configure_item);
+ $more_items = $this->newAutomaticMenuItems($nav);
+ foreach ($more_items as $item) {
+ $nav->addMenuItem($item);
}
$nav->selectFilter(null);
- return $nav;
+ $this->navigation = $nav;
+ return $this->navigation;
}
private function getPanels() {
@@ -301,26 +307,112 @@
}
}
- private function newConfigureMenuItem() {
- if (!$this->isPanelEngineConfigurable()) {
- return null;
+ private function newAutomaticMenuItems(AphrontSideNavFilterView $nav) {
+ $items = array();
+
+ // NOTE: We're adding a spacer item for the fixed footer, so that if the
+ // menu taller than the page content you can still scroll down the page far
+ // enough to access the last item without the content being obscured by the
+ // fixed items.
+ $items[] = id(new PHUIListItemView())
+ ->setHideInApplicationMenu(true)
+ ->addClass('phui-profile-menu-spacer');
+
+ if ($this->isPanelEngineConfigurable()) {
+ $viewer = $this->getViewer();
+ $object = $this->getProfileObject();
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $object,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $expanded_edit_icon = id(new PHUIIconView())
+ ->addClass('phui-list-item-icon')
+ ->addClass('phui-profile-menu-visible-when-expanded')
+ ->setIconFont('fa-pencil');
+
+ $collapsed_edit_icon = id(new PHUIIconView())
+ ->addClass('phui-list-item-icon')
+ ->addClass('phui-profile-menu-visible-when-collapsed')
+ ->setIconFont('fa-pencil')
+ ->addSigil('has-tooltip')
+ ->setMetadata(
+ array(
+ 'tip' => pht('Edit Menu'),
+ 'align' => 'E',
+ ));
+
+ $items[] = id(new PHUIListItemView())
+ ->setName('Edit Menu')
+ ->setKey('panel.configure')
+ ->addIcon($expanded_edit_icon)
+ ->addIcon($collapsed_edit_icon)
+ ->addClass('phui-profile-menu-footer')
+ ->addClass('phui-profile-menu-footer-1')
+ ->setHref($this->getPanelURI('configure/'))
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(!$can_edit);
}
+ $collapse_id = celerity_generate_unique_node_id();
+
$viewer = $this->getViewer();
- $object = $this->getProfileObject();
- $can_edit = PhabricatorPolicyFilter::hasCapability(
- $viewer,
- $object,
- PhabricatorPolicyCapability::CAN_EDIT);
+ $collapse_key =
+ PhabricatorUserPreferences::PREFERENCE_PROFILE_MENU_COLLAPSED;
+
+ $preferences = $viewer->loadPreferences();
+ $is_collapsed = $preferences->getPreference($collapse_key, false);
+
+ if ($is_collapsed) {
+ $nav->addClass('phui-profile-menu-collapsed');
+ } else {
+ $nav->addClass('phui-profile-menu-expanded');
+ }
+
+ if ($viewer->isLoggedIn()) {
+ $settings_uri = '/settings/adjust/?key='.$collapse_key;
+ } else {
+ $settings_uri = null;
+ }
+
+ Javelin::initBehavior(
+ 'phui-profile-menu',
+ array(
+ 'menuID' => $nav->getMainID(),
+ 'collapseID' => $collapse_id,
+ 'isCollapsed' => $is_collapsed,
+ 'settingsURI' => $settings_uri,
+ ));
- return id(new PHUIListItemView())
- ->setName('Configure Menu')
- ->setKey('panel.configure')
- ->setIcon('fa-gear')
- ->setHref($this->getPanelURI('configure/'))
- ->setDisabled(!$can_edit)
- ->setWorkflow(!$can_edit);
+ $collapse_icon = id(new PHUIIconView())
+ ->addClass('phui-list-item-icon')
+ ->addClass('phui-profile-menu-visible-when-expanded')
+ ->setIconFont('fa-angle-left');
+
+ $expand_icon = id(new PHUIIconView())
+ ->addClass('phui-list-item-icon')
+ ->addClass('phui-profile-menu-visible-when-collapsed')
+ ->addSigil('has-tooltip')
+ ->setMetadata(
+ array(
+ 'tip' => pht('Expand'),
+ 'align' => 'E',
+ ))
+ ->setIconFont('fa-angle-right');
+
+ $items[] = id(new PHUIListItemView())
+ ->setName('Collapse')
+ ->addIcon($collapse_icon)
+ ->addIcon($expand_icon)
+ ->setID($collapse_id)
+ ->addClass('phui-profile-menu-footer')
+ ->addClass('phui-profile-menu-footer-2')
+ ->setHideInApplicationMenu(true)
+ ->setHref('#');
+
+ return $items;
}
public function getConfigureURI() {
diff --git a/src/applications/settings/storage/PhabricatorUserPreferences.php b/src/applications/settings/storage/PhabricatorUserPreferences.php
--- a/src/applications/settings/storage/PhabricatorUserPreferences.php
+++ b/src/applications/settings/storage/PhabricatorUserPreferences.php
@@ -41,6 +41,8 @@
const PREFERENCE_RESOURCE_POSTPROCESSOR = 'resource-postprocessor';
const PREFERENCE_DESKTOP_NOTIFICATIONS = 'desktop-notifications';
+ const PREFERENCE_PROFILE_MENU_COLLAPSED = 'profile-menu.collapsed';
+
// These are in an unusual order for historic reasons.
const MAILTAG_PREFERENCE_NOTIFY = 0;
const MAILTAG_PREFERENCE_EMAIL = 1;
diff --git a/src/view/layout/AphrontSideNavFilterView.php b/src/view/layout/AphrontSideNavFilterView.php
--- a/src/view/layout/AphrontSideNavFilterView.php
+++ b/src/view/layout/AphrontSideNavFilterView.php
@@ -27,6 +27,7 @@
private $crumbs;
private $classes = array();
private $menuID;
+ private $mainID;
private $isProfileMenu;
private $footer = array();
@@ -168,6 +169,13 @@
return $this;
}
+ public function getMainID() {
+ if (!$this->mainID) {
+ $this->mainID = celerity_generate_unique_node_id();
+ }
+ return $this->mainID;
+ }
+
public function render() {
if ($this->menu->getItems()) {
if (!$this->baseURI) {
@@ -212,7 +220,7 @@
$local_id = null;
$background_id = null;
$local_menu = null;
- $main_id = celerity_generate_unique_node_id();
+ $main_id = $this->getMainID();
if ($this->flexible) {
$drag_id = celerity_generate_unique_node_id();
diff --git a/src/view/layout/PHUIApplicationMenuView.php b/src/view/layout/PHUIApplicationMenuView.php
--- a/src/view/layout/PHUIApplicationMenuView.php
+++ b/src/view/layout/PHUIApplicationMenuView.php
@@ -75,8 +75,11 @@
$profile_menu = $this->getProfileMenu();
if ($profile_menu) {
foreach ($profile_menu->getMenu()->getItems() as $item) {
+ if ($item->getHideInApplicationMenu()) {
+ continue;
+ }
+
$item = clone $item;
- $item->setRenderNameAsTooltip(false);
$view->addMenuItem($item);
}
}
diff --git a/src/view/phui/PHUIListItemView.php b/src/view/phui/PHUIListItemView.php
--- a/src/view/phui/PHUIListItemView.php
+++ b/src/view/phui/PHUIListItemView.php
@@ -28,6 +28,17 @@
private $aural;
private $profileImage;
private $indented;
+ private $hideInApplicationMenu;
+ private $icons = array();
+
+ public function setHideInApplicationMenu($hide) {
+ $this->hideInApplicationMenu = $hide;
+ return $this;
+ }
+
+ public function getHideInApplicationMenu() {
+ return $this->hideInApplicationMenu;
+ }
public function setDropdownMenu(PhabricatorActionListView $actions) {
Javelin::initBehavior('phui-dropdown-menu');
@@ -150,6 +161,15 @@
return $this;
}
+ public function addIcon(PHUIIconView $icon) {
+ $this->icons[] = $icon;
+ return $this;
+ }
+
+ public function getIcons() {
+ return $this->icons;
+ }
+
protected function getTagName() {
return 'li';
}
@@ -274,6 +294,8 @@
$classes[] = 'phui-list-item-indented';
}
+ $icons = $this->getIcons();
+
return javelin_tag(
$this->href ? 'a' : 'div',
array(
@@ -285,6 +307,7 @@
array(
$aural,
$icon,
+ $icons,
$this->renderChildren(),
$name,
));
diff --git a/webroot/rsrc/css/phui/phui-profile-menu.css b/webroot/rsrc/css/phui/phui-profile-menu.css
--- a/webroot/rsrc/css/phui/phui-profile-menu.css
+++ b/webroot/rsrc/css/phui/phui-profile-menu.css
@@ -16,12 +16,17 @@
display: table-cell;
position: relative;
vertical-align: top;
- width: 240px;
- max-width: 240px;
+ width: {$menu.profile.width};
+ max-width: {$menu.profile.width};
margin-top: 0;
overflow: hidden;
}
+.device-desktop .phui-profile-menu-collapsed .phabricator-nav-local {
+ width: {$menu.profile.width.collapsed};
+ max-width: {$menu.profile.width.collapsed};
+}
+
.device-desktop .phui-profile-menu .phabricator-nav-content {
display: table-cell;
margin-left: 0;
@@ -47,23 +52,47 @@
line-height: 22px;
overflow: hidden;
text-overflow: ellipsis;
+
+ /* NOTE: We must have an opaque background on these items so the footer
+ items appear opaque when the render over normal items. */
+ background: {$menu.profile.background};
}
.phui-profile-menu .phabricator-side-menu .phui-list-item-icon,
.phui-profile-menu .phabricator-side-menu
.phui-list-item-href .phui-icon-view {
position: absolute;
- left: 13px;
top: 12px;
+ left: 13px;
font-size: 20px;
width: 22px;
height: 22px;
line-height: 22px;
text-align: center;
- color: {$menu.profile.icon};
+ color: {$menu.profile.text};
background-size: 100%;
}
+.phui-profile-menu .phui-profile-menu-collapsed .phui-list-item-href {
+ text-align: center;
+ padding: 42px 8px 12px;
+ font-size: 11px;
+ line-height: 13px;
+}
+
+.phui-profile-menu .phui-profile-menu-collapsed .phui-list-item-name {
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.phui-profile-menu .phui-profile-menu-collapsed .phui-list-item-icon,
+.phui-profile-menu .phui-profile-menu-collapsed
+ .phui-list-item-href .phui-icon-view {
+ top: 10px;
+ left: 29px;
+}
+
.phui-profile-menu .phabricator-side-menu
.phui-list-item-disabled
.phui-list-item-icon {
@@ -76,7 +105,16 @@
.device-desktop .phui-profile-menu .phabricator-side-menu
.phui-list-item-href:hover {
- background-color: rgba(0, 0, 0, 0.075);
+ background-color: {$menu.profile.background.hover};
+ color: {$menu.profile.text.selected};
+}
+
+.phui-profile-menu .phabricator-side-menu
+ .phui-list-item-selected
+ .phui-list-item-icon,
+.device-desktop .phui-profile-menu .phabricator-side-menu
+ .phui-list-item-href:hover
+ .phui-list-item-icon {
color: {$menu.profile.text.selected};
}
@@ -85,7 +123,7 @@
.device-desktop .phui-profile-menu .phabricator-side-menu
.phui-list-item-selected
.phui-list-item-href:hover {
- background-color: rgba(0, 0, 0, 0.150);
+ background-color: {$menu.profile.background.selected};
color: {$menu.profile.text.selected};
}
@@ -107,3 +145,56 @@
font-size: 12px;
color: {$menu.profile.text};
}
+
+.phui-profile-menu .phabricator-side-menu .phui-profile-menu-spacer {
+ box-sizing: border-box;
+ height: {$menu.profile.item.height};
+}
+
+.phui-profile-menu .phabricator-side-menu .phui-profile-menu-footer {
+ position: fixed;
+ box-sizing: border-box;
+ width: {$menu.profile.width};
+ bottom: 0px;
+}
+
+.phui-profile-menu .phabricator-side-menu .phui-profile-menu-footer-1 {
+ left: 0;
+}
+
+.phui-profile-menu .phabricator-side-menu .phui-profile-menu-footer-2 {
+ left: 120px;
+}
+
+.phui-profile-menu .phui-profile-menu-collapsed .phui-profile-menu-footer {
+ width: 40px;
+ height: {$menu.profile.item.height};
+ bottom: 0px;
+}
+
+.phui-profile-menu .phui-profile-menu-collapsed .phui-profile-menu-footer-1 {
+ left: 0;
+}
+
+.phui-profile-menu .phui-profile-menu-collapsed .phui-profile-menu-footer-2 {
+ left: 40px;
+}
+
+
+.phui-profile-menu .phui-profile-menu-collapsed .phui-profile-menu-footer
+ .phui-list-item-name {
+ display: none;
+}
+
+.phui-profile-menu .phui-profile-menu-collapsed .phui-profile-menu-footer
+ .phui-list-item-icon {
+ top: 10px;
+ left: 10px;
+}
+
+.phui-profile-menu .phui-profile-menu-expanded
+ .phui-profile-menu-visible-when-collapsed,
+.phui-profile-menu .phui-profile-menu-collapsed
+ .phui-profile-menu-visible-when-expanded {
+ display: none;
+}
diff --git a/webroot/rsrc/js/phui/behavior-phui-profile-menu.js b/webroot/rsrc/js/phui/behavior-phui-profile-menu.js
new file mode 100644
--- /dev/null
+++ b/webroot/rsrc/js/phui/behavior-phui-profile-menu.js
@@ -0,0 +1,28 @@
+/**
+ * @provides javelin-behavior-phui-profile-menu
+ * @requires javelin-behavior
+ * javelin-stratcom
+ * javelin-dom
+ */
+
+JX.behavior('phui-profile-menu', function(config) {
+ var menu_node = JX.$(config.menuID);
+ var collapse_node = JX.$(config.collapseID);
+
+ var is_collapsed = config.isCollapsed;
+
+ JX.DOM.listen(collapse_node, 'click', null, function(e) {
+ is_collapsed = !is_collapsed;
+ JX.DOM.alterClass(menu_node, 'phui-profile-menu-collapsed', is_collapsed);
+ JX.DOM.alterClass(menu_node, 'phui-profile-menu-expanded', !is_collapsed);
+
+ if (config.settingsURI) {
+ new JX.Request(config.settingsURI)
+ .setData({value: (is_collapsed ? 1 : 0)})
+ .send();
+ }
+
+ e.kill();
+ });
+
+});

File Metadata

Mime Type
text/plain
Expires
Sun, Mar 16, 11:52 PM (5 d, 15 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7389278
Default Alt Text
D15056.diff (19 KB)

Event Timeline