diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- 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 @@ '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 @@ '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 @@ '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 @@ '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 @@ '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 @@ '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 @@ 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 @@ 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 @@ 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 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -30,6 +30,7 @@ '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 @@ '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 --- a/src/applications/project/controller/PhabricatorProjectEditDetailsController.php +++ b/src/applications/project/controller/PhabricatorProjectEditDetailsController.php @@ -48,6 +48,7 @@ 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 @@ $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 @@ $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()) @@ -92,6 +95,10 @@ ->setNewValue($v_join); $xactions[] = id(new PhabricatorProjectTransaction()) + ->setTransactionType($type_icon) + ->setNewValue($v_icon); + + $xactions[] = id(new PhabricatorProjectTransaction()) ->setTransactionType($type_color) ->setNewValue($v_color); @@ -143,8 +150,19 @@ 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')) ->setName('color') diff --git a/src/applications/project/controller/PhabricatorProjectEditIconController.php b/src/applications/project/controller/PhabricatorProjectEditIconController.php --- a/src/applications/project/controller/PhabricatorProjectEditIconController.php +++ b/src/applications/project/controller/PhabricatorProjectEditIconController.php @@ -26,34 +26,22 @@ return new Aphront404Response(); } - $view_uri = '/tag/'.$project->getPrimarySlug().'/'; $edit_uri = $this->getApplicationURI('edit/'.$project->getID().'/'); + require_celerity_resource('project-icon-css'); + Javelin::initBehavior('phabricator-tooltips'); - if ($request->isFormPost()) { - $xactions = array(); + $project_icons = PhabricatorProjectIcon::getIconMap(); + if ($request->isFormPost()) { $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); + return id(new AphrontAjaxResponse())->setContent( + array( + 'value' => $v_icon, + 'display' => PhabricatorProjectIcon::renderIconForChooser($v_icon), + )); } - require_celerity_resource('project-icon-css'); - Javelin::initBehavior('phabricator-tooltips'); - - $project_icons = PhabricatorProjectIcon::getIconMap(); $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 --- a/src/applications/project/controller/PhabricatorProjectEditMainController.php +++ b/src/applications/project/controller/PhabricatorProjectEditMainController.php @@ -95,14 +95,6 @@ $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')) ->setIcon('fa-picture-o') ->setHref($this->getApplicationURI("picture/{$id}/")) diff --git a/src/applications/project/icon/PhabricatorProjectIcon.php b/src/applications/project/icon/PhabricatorProjectIcon.php --- a/src/applications/project/icon/PhabricatorProjectIcon.php +++ b/src/applications/project/icon/PhabricatorProjectIcon.php @@ -24,4 +24,18 @@ $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 --- /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 --- a/webroot/rsrc/css/phui/phui-form-view.css +++ b/webroot/rsrc/css/phui/phui-form-view.css @@ -465,3 +465,13 @@ .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 --- /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 --- a/webroot/rsrc/js/core/behavior-tooltip.js +++ b/webroot/rsrc/js/core/behavior-tooltip.js @@ -40,6 +40,11 @@ // 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.