From 195def15b0caf9b7e56ee53dea14d3ef6028985c Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 26 Jun 2014 09:41:07 -0700 Subject: [PATCH] Move project icon editing into "Edit Details" Summary: Ref T5482. Instead of editing icons and details seaparetly, use a bunch of Javascript to pop a dialog instead. Test Plan: {F170528} Reviewers: chad Reviewed By: chad Subscribers: epriestley Maniphest Tasks: T5482 Differential Revision: https://secure.phabricator.com/D9743 --- resources/celerity/map.php | 35 ++++--- src/__phutil_library_map__.php | 2 + ...habricatorProjectEditDetailsController.php | 18 ++++ .../PhabricatorProjectEditIconController.php | 34 +++---- .../PhabricatorProjectEditMainController.php | 8 -- .../project/icon/PhabricatorProjectIcon.php | 14 +++ .../AphrontFormChooseButtonControl.php | 96 +++++++++++++++++++ webroot/rsrc/css/phui/phui-form-view.css | 10 ++ .../rsrc/js/core/behavior-choose-control.js | 31 ++++++ webroot/rsrc/js/core/behavior-tooltip.js | 5 + 10 files changed, 209 insertions(+), 44 deletions(-) create mode 100644 src/view/form/control/AphrontFormChooseButtonControl.php create mode 100644 webroot/rsrc/js/core/behavior-choose-control.js diff --git a/resources/celerity/map.php b/resources/celerity/map.php index a867d37396..d3929d1840 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,8 +7,8 @@ return array( 'names' => array( - 'core.pkg.css' => 'ead20778', - 'core.pkg.js' => '8c184823', + 'core.pkg.css' => 'b2a7a97c', + 'core.pkg.js' => '834b4eda', 'darkconsole.pkg.js' => 'df001cab', 'differential.pkg.css' => '4a93db37', 'differential.pkg.js' => 'd1443567', @@ -127,7 +127,7 @@ return array( 'rsrc/css/phui/phui-document.css' => 'a5615198', 'rsrc/css/phui/phui-feed-story.css' => 'e2c9bc83', 'rsrc/css/phui/phui-fontkit.css' => 'de84aa4a', - 'rsrc/css/phui/phui-form-view.css' => 'ed856191', + 'rsrc/css/phui/phui-form-view.css' => 'ebac1b1d', 'rsrc/css/phui/phui-form.css' => 'b78ec020', 'rsrc/css/phui/phui-header-view.css' => 'a2071a67', 'rsrc/css/phui/phui-icon.css' => 'd8526aa1', @@ -450,6 +450,7 @@ return array( 'rsrc/js/core/behavior-active-nav.js' => 'e379b58e', 'rsrc/js/core/behavior-audio-source.js' => '59b251eb', 'rsrc/js/core/behavior-autofocus.js' => '7319e029', + 'rsrc/js/core/behavior-choose-control.js' => '6153c708', 'rsrc/js/core/behavior-crop.js' => 'fa0f4fc2', 'rsrc/js/core/behavior-dark-console.js' => '357b6e9b', 'rsrc/js/core/behavior-device.js' => '03d6ed07', @@ -481,7 +482,7 @@ return array( 'rsrc/js/core/behavior-select-on-click.js' => '4e3e79a6', 'rsrc/js/core/behavior-toggle-class.js' => 'a82a7769', 'rsrc/js/core/behavior-tokenizer.js' => 'b3a4b884', - 'rsrc/js/core/behavior-tooltip.js' => '40b3be97', + 'rsrc/js/core/behavior-tooltip.js' => '3ee3408b', 'rsrc/js/core/behavior-watch-anchor.js' => '06e05112', 'rsrc/js/core/behavior-workflow.js' => '0a3f3021', 'rsrc/js/core/phtize.js' => 'd254d646', @@ -553,6 +554,7 @@ return array( 'javelin-behavior-audit-preview' => 'd835b03a', 'javelin-behavior-balanced-payment-form' => '3b3e1664', 'javelin-behavior-boards-dropdown' => '0ec56e1d', + 'javelin-behavior-choose-control' => '6153c708', 'javelin-behavior-config-reorder-fields' => '14a827de', 'javelin-behavior-conpherence-menu' => 'f0a41b9f', 'javelin-behavior-conpherence-pontificate' => '85ab3c8e', @@ -623,7 +625,7 @@ return array( 'javelin-behavior-phabricator-reveal-content' => '60821bc7', 'javelin-behavior-phabricator-search-typeahead' => '5a376f34', 'javelin-behavior-phabricator-show-all-transactions' => '7c273581', - 'javelin-behavior-phabricator-tooltips' => '40b3be97', + 'javelin-behavior-phabricator-tooltips' => '3ee3408b', 'javelin-behavior-phabricator-transaction-comment-form' => '9f7309fb', 'javelin-behavior-phabricator-transaction-list' => '71f66c08', 'javelin-behavior-phabricator-watch-anchor' => '06e05112', @@ -772,7 +774,7 @@ return array( 'phui-font-icon-base-css' => 'eb84f033', 'phui-fontkit-css' => 'de84aa4a', 'phui-form-css' => 'b78ec020', - 'phui-form-view-css' => 'ed856191', + 'phui-form-view-css' => 'ebac1b1d', 'phui-header-view-css' => 'a2071a67', 'phui-icon-view-css' => 'd8526aa1', 'phui-image-mask-css' => '5a8b09c8', @@ -1133,6 +1135,13 @@ return array( 4 => 'javelin-util', 5 => 'javelin-uri', ), + '3ee3408b' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-behavior-device', + 2 => 'javelin-stratcom', + 3 => 'phabricator-tooltip', + ), '40a6a403' => array( 0 => 'javelin-install', @@ -1152,13 +1161,6 @@ return array( 8 => 'phuix-action-list-view', 9 => 'phuix-action-view', ), - '40b3be97' => - array( - 0 => 'javelin-behavior', - 1 => 'javelin-behavior-device', - 2 => 'javelin-stratcom', - 3 => 'phabricator-tooltip', - ), '41e47dea' => array( 0 => 'javelin-install', @@ -1293,6 +1295,13 @@ return array( 1 => 'javelin-stratcom', 2 => 'javelin-dom', ), + '6153c708' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-stratcom', + 2 => 'javelin-dom', + 3 => 'javelin-workflow', + ), '62e18640' => array( 0 => 'javelin-install', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index df26fb9f41..c65ae681ee 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -30,6 +30,7 @@ phutil_register_library_map(array( 'AphrontException' => 'aphront/exception/AphrontException.php', 'AphrontFileResponse' => 'aphront/response/AphrontFileResponse.php', 'AphrontFormCheckboxControl' => 'view/form/control/AphrontFormCheckboxControl.php', + 'AphrontFormChooseButtonControl' => 'view/form/control/AphrontFormChooseButtonControl.php', 'AphrontFormControl' => 'view/form/control/AphrontFormControl.php', 'AphrontFormCropControl' => 'view/form/control/AphrontFormCropControl.php', 'AphrontFormDateControl' => 'view/form/control/AphrontFormDateControl.php', @@ -2731,6 +2732,7 @@ phutil_register_library_map(array( 'AphrontException' => 'Exception', 'AphrontFileResponse' => 'AphrontResponse', 'AphrontFormCheckboxControl' => 'AphrontFormControl', + 'AphrontFormChooseButtonControl' => 'AphrontFormControl', 'AphrontFormControl' => 'AphrontView', 'AphrontFormCropControl' => 'AphrontFormControl', 'AphrontFormDateControl' => 'AphrontFormControl', diff --git a/src/applications/project/controller/PhabricatorProjectEditDetailsController.php b/src/applications/project/controller/PhabricatorProjectEditDetailsController.php index f4cb1894df..3852f7dee6 100644 --- a/src/applications/project/controller/PhabricatorProjectEditDetailsController.php +++ b/src/applications/project/controller/PhabricatorProjectEditDetailsController.php @@ -48,6 +48,7 @@ final class PhabricatorProjectEditDetailsController unset($project_slugs[$v_primary_slug]); $v_slugs = $project_slugs; $v_color = $project->getColor(); + $v_icon = $project->getIcon(); $validation_exception = null; @@ -61,6 +62,7 @@ final class PhabricatorProjectEditDetailsController $v_edit = $request->getStr('can_edit'); $v_join = $request->getStr('can_join'); $v_color = $request->getStr('color'); + $v_icon = $request->getStr('icon'); $xactions = $field_list->buildFieldTransactionsFromRequest( new PhabricatorProjectTransaction(), @@ -69,6 +71,7 @@ final class PhabricatorProjectEditDetailsController $type_name = PhabricatorProjectTransaction::TYPE_NAME; $type_slugs = PhabricatorProjectTransaction::TYPE_SLUGS; $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY; + $type_icon = PhabricatorProjectTransaction::TYPE_ICON; $type_color = PhabricatorProjectTransaction::TYPE_COLOR; $xactions[] = id(new PhabricatorProjectTransaction()) @@ -91,6 +94,10 @@ final class PhabricatorProjectEditDetailsController ->setTransactionType(PhabricatorTransactions::TYPE_JOIN_POLICY) ->setNewValue($v_join); + $xactions[] = id(new PhabricatorProjectTransaction()) + ->setTransactionType($type_icon) + ->setNewValue($v_icon); + $xactions[] = id(new PhabricatorProjectTransaction()) ->setTransactionType($type_color) ->setNewValue($v_color); @@ -143,7 +150,18 @@ final class PhabricatorProjectEditDetailsController array(PhabricatorProject::DEFAULT_COLOR)) + $shades; unset($shades[PHUITagView::COLOR_DISABLED]); + $icon_uri = $this->getApplicationURI('icon/'.$project->getID().'/'); + $icon_display = PhabricatorProjectIcon::renderIconForChooser($v_icon); + $form + ->appendChild( + id(new AphrontFormChooseButtonControl()) + ->setLabel(pht('Icon')) + ->setName('icon') + ->setDisplayValue($icon_display) + ->setButtonText(pht('Choose Icon...')) + ->setChooseURI($icon_uri) + ->setValue($v_icon)) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel(pht('Color')) diff --git a/src/applications/project/controller/PhabricatorProjectEditIconController.php b/src/applications/project/controller/PhabricatorProjectEditIconController.php index 6b4f2abf41..b715d6ae6b 100644 --- a/src/applications/project/controller/PhabricatorProjectEditIconController.php +++ b/src/applications/project/controller/PhabricatorProjectEditIconController.php @@ -26,34 +26,22 @@ final class PhabricatorProjectEditIconController return new Aphront404Response(); } - $view_uri = '/tag/'.$project->getPrimarySlug().'/'; $edit_uri = $this->getApplicationURI('edit/'.$project->getID().'/'); - - if ($request->isFormPost()) { - $xactions = array(); - - $v_icon = $request->getStr('icon'); - - $type_icon = PhabricatorProjectTransaction::TYPE_ICON; - $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType($type_icon) - ->setNewValue($v_icon); - - $editor = id(new PhabricatorProjectTransactionEditor()) - ->setActor($viewer) - ->setContentSourceFromRequest($request) - ->setContinueOnMissingFields(true) - ->setContinueOnNoEffect(true); - - $editor->applyTransactions($project, $xactions); - - return id(new AphrontReloadResponse())->setURI($edit_uri); - } - require_celerity_resource('project-icon-css'); Javelin::initBehavior('phabricator-tooltips'); $project_icons = PhabricatorProjectIcon::getIconMap(); + + if ($request->isFormPost()) { + $v_icon = $request->getStr('icon'); + + return id(new AphrontAjaxResponse())->setContent( + array( + 'value' => $v_icon, + 'display' => PhabricatorProjectIcon::renderIconForChooser($v_icon), + )); + } + $ii = 0; $buttons = array(); foreach ($project_icons as $icon => $label) { diff --git a/src/applications/project/controller/PhabricatorProjectEditMainController.php b/src/applications/project/controller/PhabricatorProjectEditMainController.php index fe59a0ef27..9af77bafd0 100644 --- a/src/applications/project/controller/PhabricatorProjectEditMainController.php +++ b/src/applications/project/controller/PhabricatorProjectEditMainController.php @@ -93,14 +93,6 @@ final class PhabricatorProjectEditMainController ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); - $view->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Edit Icon')) - ->setIcon($project->getIcon()) - ->setHref($this->getApplicationURI("icon/{$id}/")) - ->setDisabled(!$can_edit) - ->setWorkflow(true)); - $view->addAction( id(new PhabricatorActionView()) ->setName(pht('Edit Picture')) diff --git a/src/applications/project/icon/PhabricatorProjectIcon.php b/src/applications/project/icon/PhabricatorProjectIcon.php index 8729db9e4e..976b9b19e4 100644 --- a/src/applications/project/icon/PhabricatorProjectIcon.php +++ b/src/applications/project/icon/PhabricatorProjectIcon.php @@ -24,4 +24,18 @@ final class PhabricatorProjectIcon extends Phobject { $map = self::getIconMap(); return $map[$key]; } + + public static function renderIconForChooser($icon) { + $project_icons = PhabricatorProjectIcon::getIconMap(); + + return phutil_tag( + 'span', + array(), + array( + id(new PHUIIconView())->setIconFont($icon), + ' ', + idx($project_icons, $icon, pht('Unknown Icon')), + )); + } + } diff --git a/src/view/form/control/AphrontFormChooseButtonControl.php b/src/view/form/control/AphrontFormChooseButtonControl.php new file mode 100644 index 0000000000..d48f01fc3b --- /dev/null +++ b/src/view/form/control/AphrontFormChooseButtonControl.php @@ -0,0 +1,96 @@ +displayValue = $display_value; + return $this; + } + + public function getDisplayValue() { + return $this->displayValue; + } + + public function setButtonText($text) { + $this->buttonText = $text; + return $this; + } + + public function setChooseURI($choose_uri) { + $this->chooseURI = $choose_uri; + return $this; + } + + protected function getCustomControlClass() { + return 'aphront-form-control-choose-button'; + } + + protected function renderInput() { + Javelin::initBehavior('choose-control'); + + $input_id = celerity_generate_unique_node_id(); + $display_id = celerity_generate_unique_node_id(); + + $display_value = $this->displayValue; + $button = javelin_tag( + 'a', + array( + 'href' => '#', + 'class' => 'button grey', + 'sigil' => 'aphront-form-choose-button', + ), + nonempty($this->buttonText, pht('Choose...'))); + + $display_cell = phutil_tag( + 'td', + array( + 'class' => 'aphront-form-choose-display-cell', + 'id' => $display_id, + ), + $display_value); + + $button_cell = phutil_tag( + 'td', + array( + 'class' => 'aphront-form-choose-button-cell', + ), + $button); + + $row = phutil_tag( + 'tr', + array(), + array($display_cell, $button_cell)); + + $layout = javelin_tag( + 'table', + array( + 'class' => 'aphront-form-choose-table', + 'sigil' => 'aphront-form-choose', + 'meta' => array( + 'uri' => $this->chooseURI, + 'inputID' => $input_id, + 'displayID' => $display_id, + ), + ), + $row); + + $hidden_input = phutil_tag( + 'input', + array( + 'type' => 'hidden', + 'name' => $this->getName(), + 'value' => $this->getValue(), + 'id' => $input_id, + )); + + return array( + $hidden_input, + $layout, + ); + } + +} diff --git a/webroot/rsrc/css/phui/phui-form-view.css b/webroot/rsrc/css/phui/phui-form-view.css index 10d7cfdae5..9394a784cd 100644 --- a/webroot/rsrc/css/phui/phui-form-view.css +++ b/webroot/rsrc/css/phui/phui-form-view.css @@ -465,3 +465,13 @@ table.aphront-form-control-checkbox-layout th { .device-desktop .text-with-submit-control-submit { width: 180px; } + + +.aphront-form-choose-table td { + vertical-align: middle; + padding: 4px 0; +} + +.aphront-form-choose-table .aphront-form-choose-button-cell { + padding: 4px 8px; +} diff --git a/webroot/rsrc/js/core/behavior-choose-control.js b/webroot/rsrc/js/core/behavior-choose-control.js new file mode 100644 index 0000000000..3652e1305b --- /dev/null +++ b/webroot/rsrc/js/core/behavior-choose-control.js @@ -0,0 +1,31 @@ +/** + * @provides javelin-behavior-choose-control + * @requires javelin-behavior + * javelin-stratcom + * javelin-dom + * javelin-workflow + */ + +JX.behavior('choose-control', function() { + + JX.Stratcom.listen( + 'click', + 'aphront-form-choose-button', + function(e) { + e.kill(); + + var data = e.getNodeData('aphront-form-choose'); + + var params = { + value: JX.$(data.inputID).value + }; + + new JX.Workflow(data.uri, params) + .setHandler(function(r) { + JX.$(data.inputID).value = r.value; + JX.DOM.setContent(JX.$(data.displayID), JX.$H(r.display)); + }) + .start(); + }); + +}); diff --git a/webroot/rsrc/js/core/behavior-tooltip.js b/webroot/rsrc/js/core/behavior-tooltip.js index 0a35430e66..fd88956aef 100644 --- a/webroot/rsrc/js/core/behavior-tooltip.js +++ b/webroot/rsrc/js/core/behavior-tooltip.js @@ -40,6 +40,11 @@ JX.behavior('phabricator-tooltips', function() { // example, submitting an inline comment in Differential). See T4586. JX.Stratcom.listen('keydown', null, wipe); + + // Hide tips on mouseup. This removes tips on buttons in dialogs after the + // buttons are clicked. + JX.Stratcom.listen('mouseup', null, wipe); + // When we leave the page, hide any visible tooltips. If we don't do this, // clicking a link with a tooltip and then hitting "back" will give you a // phantom tooltip.