diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php index afa6c456a6..abbf1d77e2 100644 --- a/resources/celerity/packages.php +++ b/resources/celerity/packages.php @@ -93,6 +93,7 @@ return array( 'phabricator-core-css', 'phabricator-zindex-css', 'phui-button-css', + 'phui-button-simple-css', 'phui-theme-css', 'phabricator-standard-page-view', 'aphront-dialog-view-css', @@ -203,9 +204,6 @@ return array( 'javelin-behavior-differential-user-select', 'javelin-behavior-aphront-more', - 'phabricator-scroll-objective', - 'phabricator-scroll-objective-list', - 'phabricator-diff-inline', 'phabricator-diff-changeset', 'phabricator-diff-changeset-list', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index ce3cc27784..13e447c48d 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -728,8 +728,10 @@ phutil_register_library_map(array( 'DiffusionGitSSHWorkflow' => 'applications/diffusion/ssh/DiffusionGitSSHWorkflow.php', 'DiffusionGitUploadPackSSHWorkflow' => 'applications/diffusion/ssh/DiffusionGitUploadPackSSHWorkflow.php', 'DiffusionHistoryController' => 'applications/diffusion/controller/DiffusionHistoryController.php', + 'DiffusionHistoryListView' => 'applications/diffusion/view/DiffusionHistoryListView.php', 'DiffusionHistoryQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php', 'DiffusionHistoryTableView' => 'applications/diffusion/view/DiffusionHistoryTableView.php', + 'DiffusionHistoryView' => 'applications/diffusion/view/DiffusionHistoryView.php', 'DiffusionHovercardEngineExtension' => 'applications/diffusion/engineextension/DiffusionHovercardEngineExtension.php', 'DiffusionInlineCommentController' => 'applications/diffusion/controller/DiffusionInlineCommentController.php', 'DiffusionInlineCommentPreviewController' => 'applications/diffusion/controller/DiffusionInlineCommentPreviewController.php', @@ -1393,10 +1395,6 @@ phutil_register_library_map(array( 'HeraldTranscriptTestCase' => 'applications/herald/storage/__tests__/HeraldTranscriptTestCase.php', 'HeraldUtilityActionGroup' => 'applications/herald/action/HeraldUtilityActionGroup.php', 'Javelin' => 'infrastructure/javelin/Javelin.php', - 'JavelinReactorUIExample' => 'applications/uiexample/examples/JavelinReactorUIExample.php', - 'JavelinUIExample' => 'applications/uiexample/examples/JavelinUIExample.php', - 'JavelinViewExampleServerView' => 'applications/uiexample/examples/JavelinViewExampleServerView.php', - 'JavelinViewUIExample' => 'applications/uiexample/examples/JavelinViewUIExample.php', 'LegalpadController' => 'applications/legalpad/controller/LegalpadController.php', 'LegalpadCreateDocumentsCapability' => 'applications/legalpad/capability/LegalpadCreateDocumentsCapability.php', 'LegalpadDAO' => 'applications/legalpad/storage/LegalpadDAO.php', @@ -1808,6 +1806,7 @@ phutil_register_library_map(array( 'PHUIUserAvailabilityView' => 'applications/calendar/view/PHUIUserAvailabilityView.php', 'PHUIWorkboardView' => 'view/phui/PHUIWorkboardView.php', 'PHUIWorkpanelView' => 'view/phui/PHUIWorkpanelView.php', + 'PHUIXComponentsExample' => 'applications/uiexample/examples/PHUIXComponentsExample.php', 'PassphraseAbstractKey' => 'applications/passphrase/keys/PassphraseAbstractKey.php', 'PassphraseConduitAPIMethod' => 'applications/passphrase/conduit/PassphraseConduitAPIMethod.php', 'PassphraseController' => 'applications/passphrase/controller/PassphraseController.php', @@ -2144,7 +2143,6 @@ phutil_register_library_map(array( 'PhabricatorBadgesTransactionComment' => 'applications/badges/storage/PhabricatorBadgesTransactionComment.php', 'PhabricatorBadgesTransactionQuery' => 'applications/badges/query/PhabricatorBadgesTransactionQuery.php', 'PhabricatorBadgesViewController' => 'applications/badges/controller/PhabricatorBadgesViewController.php', - 'PhabricatorBarePageUIExample' => 'applications/uiexample/examples/PhabricatorBarePageUIExample.php', 'PhabricatorBarePageView' => 'view/page/PhabricatorBarePageView.php', 'PhabricatorBaseURISetupCheck' => 'applications/config/check/PhabricatorBaseURISetupCheck.php', 'PhabricatorBcryptPasswordHasher' => 'infrastructure/util/password/PhabricatorBcryptPasswordHasher.php', @@ -2159,7 +2157,6 @@ phutil_register_library_map(array( 'PhabricatorBuiltinDraftEngine' => 'applications/transactions/draft/PhabricatorBuiltinDraftEngine.php', 'PhabricatorBuiltinPatchList' => 'infrastructure/storage/patch/PhabricatorBuiltinPatchList.php', 'PhabricatorBulkContentSource' => 'infrastructure/daemon/contentsource/PhabricatorBulkContentSource.php', - 'PhabricatorBusyUIExample' => 'applications/uiexample/examples/PhabricatorBusyUIExample.php', 'PhabricatorCacheDAO' => 'applications/cache/storage/PhabricatorCacheDAO.php', 'PhabricatorCacheEngine' => 'applications/system/engine/PhabricatorCacheEngine.php', 'PhabricatorCacheEngineExtension' => 'applications/system/engine/PhabricatorCacheEngineExtension.php', @@ -2522,6 +2519,7 @@ phutil_register_library_map(array( 'PhabricatorCustomFieldStorageQuery' => 'infrastructure/customfield/query/PhabricatorCustomFieldStorageQuery.php', 'PhabricatorCustomFieldStringIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldStringIndexStorage.php', 'PhabricatorCustomLogoConfigType' => 'applications/config/custom/PhabricatorCustomLogoConfigType.php', + 'PhabricatorCustomUIFooterConfigType' => 'applications/config/custom/PhabricatorCustomUIFooterConfigType.php', 'PhabricatorDaemon' => 'infrastructure/daemon/PhabricatorDaemon.php', 'PhabricatorDaemonBulkJobController' => 'applications/daemon/controller/PhabricatorDaemonBulkJobController.php', 'PhabricatorDaemonBulkJobListController' => 'applications/daemon/controller/PhabricatorDaemonBulkJobListController.php', @@ -3027,7 +3025,6 @@ phutil_register_library_map(array( 'PhabricatorLiskFulltextEngineExtension' => 'applications/search/engineextension/PhabricatorLiskFulltextEngineExtension.php', 'PhabricatorLiskSearchEngineExtension' => 'applications/search/engineextension/PhabricatorLiskSearchEngineExtension.php', 'PhabricatorLiskSerializer' => 'infrastructure/storage/lisk/PhabricatorLiskSerializer.php', - 'PhabricatorListFilterUIExample' => 'applications/uiexample/examples/PhabricatorListFilterUIExample.php', 'PhabricatorLocalDiskFileStorageEngine' => 'applications/files/engine/PhabricatorLocalDiskFileStorageEngine.php', 'PhabricatorLocalTimeTestCase' => 'view/__tests__/PhabricatorLocalTimeTestCase.php', 'PhabricatorLocaleScopeGuard' => 'infrastructure/internationalization/scope/PhabricatorLocaleScopeGuard.php', @@ -3993,7 +3990,6 @@ phutil_register_library_map(array( 'PhabricatorSlowvoteVoteController' => 'applications/slowvote/controller/PhabricatorSlowvoteVoteController.php', 'PhabricatorSlug' => 'infrastructure/util/PhabricatorSlug.php', 'PhabricatorSlugTestCase' => 'infrastructure/util/__tests__/PhabricatorSlugTestCase.php', - 'PhabricatorSortTableUIExample' => 'applications/uiexample/examples/PhabricatorSortTableUIExample.php', 'PhabricatorSourceCodeView' => 'view/layout/PhabricatorSourceCodeView.php', 'PhabricatorSpaceEditField' => 'applications/transactions/editfield/PhabricatorSpaceEditField.php', 'PhabricatorSpacesApplication' => 'applications/spaces/application/PhabricatorSpacesApplication.php', @@ -4157,7 +4153,6 @@ phutil_register_library_map(array( 'PhabricatorTokensCurtainExtension' => 'applications/tokens/engineextension/PhabricatorTokensCurtainExtension.php', 'PhabricatorTokensSettingsPanel' => 'applications/settings/panel/PhabricatorTokensSettingsPanel.php', 'PhabricatorTokensToken' => 'applications/tokens/storage/PhabricatorTokensToken.php', - 'PhabricatorTooltipUIExample' => 'applications/uiexample/examples/PhabricatorTooltipUIExample.php', 'PhabricatorTransactionChange' => 'applications/transactions/data/PhabricatorTransactionChange.php', 'PhabricatorTransactionRemarkupChange' => 'applications/transactions/data/PhabricatorTransactionRemarkupChange.php', 'PhabricatorTransactions' => 'applications/transactions/constants/PhabricatorTransactions.php', @@ -5704,8 +5699,10 @@ phutil_register_library_map(array( ), 'DiffusionGitUploadPackSSHWorkflow' => 'DiffusionGitSSHWorkflow', 'DiffusionHistoryController' => 'DiffusionController', + 'DiffusionHistoryListView' => 'DiffusionHistoryView', 'DiffusionHistoryQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', - 'DiffusionHistoryTableView' => 'DiffusionView', + 'DiffusionHistoryTableView' => 'DiffusionHistoryView', + 'DiffusionHistoryView' => 'DiffusionView', 'DiffusionHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension', 'DiffusionInlineCommentController' => 'PhabricatorInlineCommentController', 'DiffusionInlineCommentPreviewController' => 'PhabricatorInlineCommentPreviewController', @@ -6478,10 +6475,6 @@ phutil_register_library_map(array( 'HeraldTranscriptTestCase' => 'PhabricatorTestCase', 'HeraldUtilityActionGroup' => 'HeraldActionGroup', 'Javelin' => 'Phobject', - 'JavelinReactorUIExample' => 'PhabricatorUIExample', - 'JavelinUIExample' => 'PhabricatorUIExample', - 'JavelinViewExampleServerView' => 'AphrontView', - 'JavelinViewUIExample' => 'PhabricatorUIExample', 'LegalpadController' => 'PhabricatorController', 'LegalpadCreateDocumentsCapability' => 'PhabricatorPolicyCapability', 'LegalpadDAO' => 'PhabricatorLiskDAO', @@ -6946,6 +6939,7 @@ phutil_register_library_map(array( 'PHUIUserAvailabilityView' => 'AphrontTagView', 'PHUIWorkboardView' => 'AphrontTagView', 'PHUIWorkpanelView' => 'AphrontTagView', + 'PHUIXComponentsExample' => 'PhabricatorUIExample', 'PassphraseAbstractKey' => 'Phobject', 'PassphraseConduitAPIMethod' => 'ConduitAPIMethod', 'PassphraseController' => 'PhabricatorController', @@ -7337,7 +7331,6 @@ phutil_register_library_map(array( 'PhabricatorBadgesTransactionComment' => 'PhabricatorApplicationTransactionComment', 'PhabricatorBadgesTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorBadgesViewController' => 'PhabricatorBadgesProfileController', - 'PhabricatorBarePageUIExample' => 'PhabricatorUIExample', 'PhabricatorBarePageView' => 'AphrontPageView', 'PhabricatorBaseURISetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorBcryptPasswordHasher' => 'PhabricatorPasswordHasher', @@ -7352,7 +7345,6 @@ phutil_register_library_map(array( 'PhabricatorBuiltinDraftEngine' => 'PhabricatorDraftEngine', 'PhabricatorBuiltinPatchList' => 'PhabricatorSQLPatchList', 'PhabricatorBulkContentSource' => 'PhabricatorContentSource', - 'PhabricatorBusyUIExample' => 'PhabricatorUIExample', 'PhabricatorCacheDAO' => 'PhabricatorLiskDAO', 'PhabricatorCacheEngine' => 'Phobject', 'PhabricatorCacheEngineExtension' => 'Phobject', @@ -7779,6 +7771,7 @@ phutil_register_library_map(array( 'PhabricatorCustomFieldStorageQuery' => 'Phobject', 'PhabricatorCustomFieldStringIndexStorage' => 'PhabricatorCustomFieldIndexStorage', 'PhabricatorCustomLogoConfigType' => 'PhabricatorConfigOptionType', + 'PhabricatorCustomUIFooterConfigType' => 'PhabricatorConfigJSONOptionType', 'PhabricatorDaemon' => 'PhutilDaemon', 'PhabricatorDaemonBulkJobController' => 'PhabricatorDaemonController', 'PhabricatorDaemonBulkJobListController' => 'PhabricatorDaemonBulkJobController', @@ -8352,7 +8345,6 @@ phutil_register_library_map(array( 'PhabricatorLiskFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension', 'PhabricatorLiskSearchEngineExtension' => 'PhabricatorSearchEngineExtension', 'PhabricatorLiskSerializer' => 'Phobject', - 'PhabricatorListFilterUIExample' => 'PhabricatorUIExample', 'PhabricatorLocalDiskFileStorageEngine' => 'PhabricatorFileStorageEngine', 'PhabricatorLocalTimeTestCase' => 'PhabricatorTestCase', 'PhabricatorLocaleScopeGuard' => 'Phobject', @@ -9517,7 +9509,6 @@ phutil_register_library_map(array( 'PhabricatorSlowvoteVoteController' => 'PhabricatorSlowvoteController', 'PhabricatorSlug' => 'Phobject', 'PhabricatorSlugTestCase' => 'PhabricatorTestCase', - 'PhabricatorSortTableUIExample' => 'PhabricatorUIExample', 'PhabricatorSourceCodeView' => 'AphrontView', 'PhabricatorSpaceEditField' => 'PhabricatorEditField', 'PhabricatorSpacesApplication' => 'PhabricatorApplication', @@ -9698,7 +9689,6 @@ phutil_register_library_map(array( 'PhabricatorFlaggableInterface', 'PhabricatorConduitResultInterface', ), - 'PhabricatorTooltipUIExample' => 'PhabricatorUIExample', 'PhabricatorTransactionChange' => 'Phobject', 'PhabricatorTransactionRemarkupChange' => 'PhabricatorTransactionChange', 'PhabricatorTransactions' => 'Phobject', diff --git a/src/applications/auth/controller/config/PhabricatorAuthListController.php b/src/applications/auth/controller/config/PhabricatorAuthListController.php index ae5808ed78..c5d4f7ad77 100644 --- a/src/applications/auth/controller/config/PhabricatorAuthListController.php +++ b/src/applications/auth/controller/config/PhabricatorAuthListController.php @@ -103,7 +103,7 @@ final class PhabricatorAuthListController $button = id(new PHUIButtonView()) ->setTag('a') - ->setColor(PHUIButtonView::SIMPLE) + ->setButtonType(PHUIButtonView::BUTTONTYPE_SIMPLE) ->setHref($this->getApplicationURI('config/new/')) ->setIcon('fa-plus') ->setDisabled(!$can_manage) diff --git a/src/applications/config/custom/PhabricatorCustomUIFooterConfigType.php b/src/applications/config/custom/PhabricatorCustomUIFooterConfigType.php new file mode 100644 index 0000000000..d9961e7454 --- /dev/null +++ b/src/applications/config/custom/PhabricatorCustomUIFooterConfigType.php @@ -0,0 +1,41 @@ + $item) { + if (!is_array($item)) { + throw new Exception( + pht( + 'Footer item with index "%s" is invalid: each item must be a '. + 'dictionary describing a footer item.', + $idx)); + } + + try { + PhutilTypeSpec::checkMap( + $item, + array( + 'name' => 'string', + 'href' => 'optional string', + )); + } catch (Exception $ex) { + throw new Exception( + pht( + 'Footer item with index "%s" is invalid: %s', + $idx, + $ex->getMessage())); + } + } + } + + +} diff --git a/src/applications/config/option/PhabricatorUIConfigOptions.php b/src/applications/config/option/PhabricatorUIConfigOptions.php index 4f93946ce2..cef3fbd342 100644 --- a/src/applications/config/option/PhabricatorUIConfigOptions.php +++ b/src/applications/config/option/PhabricatorUIConfigOptions.php @@ -46,6 +46,7 @@ final class PhabricatorUIConfigOptions EOJSON; $logo_type = 'custom:PhabricatorCustomLogoConfigType'; + $footer_type = 'custom:PhabricatorCustomUIFooterConfigType'; return array( $this->newOption('ui.header-color', 'enum', 'blindigo') @@ -63,7 +64,7 @@ EOJSON; "Phabricator logo in the site header.\n\n". " - **Wordmark**: Choose new text to display next to the logo. ". "By default, the header displays //Phabricator//.\n\n")), - $this->newOption('ui.footer-items', 'list', array()) + $this->newOption('ui.footer-items', $footer_type, array()) ->setSummary( pht( 'Allows you to add footer links on most pages.')) diff --git a/src/applications/differential/view/DifferentialChangesetDetailView.php b/src/applications/differential/view/DifferentialChangesetDetailView.php index e80dc20ec7..a1a3de26bb 100644 --- a/src/applications/differential/view/DifferentialChangesetDetailView.php +++ b/src/applications/differential/view/DifferentialChangesetDetailView.php @@ -204,7 +204,6 @@ final class DifferentialChangesetDetailView extends AphrontView { 'loaded' => $this->getLoaded(), 'undoTemplates' => hsprintf('%s', $renderer->renderUndoTemplates()), 'displayPath' => hsprintf('%s', $display_parts), - 'objectiveName' => basename($display_filename), 'icon' => $display_icon, ), 'class' => $class, diff --git a/src/applications/differential/view/DifferentialChangesetListView.php b/src/applications/differential/view/DifferentialChangesetListView.php index 880fcb872d..b8090b88a2 100644 --- a/src/applications/differential/view/DifferentialChangesetListView.php +++ b/src/applications/differential/view/DifferentialChangesetListView.php @@ -203,15 +203,11 @@ final class DifferentialChangesetListView extends AphrontView { $this->requireResource('aphront-tooltip-css'); - $show_objectives = - PhabricatorEnv::getEnvConfig('phabricator.show-prototypes'); - $this->initBehavior( 'differential-populate', array( 'changesetViewIDs' => $ids, 'inlineURI' => $this->inlineURI, - 'showObjectives' => $show_objectives, 'pht' => array( 'Open in Editor' => pht('Open in Editor'), 'Show All Context' => pht('Show All Context'), diff --git a/src/applications/diffusion/controller/DiffusionHistoryController.php b/src/applications/diffusion/controller/DiffusionHistoryController.php index 1a29a4263a..8ee6c848a7 100644 --- a/src/applications/diffusion/controller/DiffusionHistoryController.php +++ b/src/applications/diffusion/controller/DiffusionHistoryController.php @@ -26,11 +26,6 @@ final class DiffusionHistoryController extends DiffusionController { 'limit' => $pager->getPageSize() + 1, ); - if (!$request->getBool('copies')) { - $params['needDirectChanges'] = true; - $params['needChildChanges'] = true; - } - $history_results = $this->callConduitWithDiffusionRequest( 'diffusion.historyquery', $params); @@ -39,27 +34,12 @@ final class DiffusionHistoryController extends DiffusionController { $history = $pager->sliceResults($history); - $show_graph = !strlen($drequest->getPath()); - $history_table = id(new DiffusionHistoryTableView()) - ->setUser($request->getUser()) + $history_list = id(new DiffusionHistoryListView()) + ->setViewer($viewer) ->setDiffusionRequest($drequest) ->setHistory($history); - $history_table->loadRevisions(); - - if ($show_graph) { - $history_table->setParents($history_results['parents']); - $history_table->setIsHead(!$pager->getOffset()); - $history_table->setIsTail(!$pager->getHasMorePages()); - } - - $history_header = $this->buildHistoryHeader($drequest); - $history_panel = id(new PHUIObjectBoxView()) - ->setHeader($history_header) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->setTable($history_table) - ->setPager($pager); - + $history_list->loadRevisions(); $header = $this->buildHeader($drequest); $crumbs = $this->buildCrumbs( @@ -70,44 +50,32 @@ final class DiffusionHistoryController extends DiffusionController { )); $crumbs->setBorder(true); + $title = array( + pht('History'), + $repository->getDisplayName(), + ); + + $pager = id(new PHUIBoxView()) + ->addClass('mlb') + ->appendChild($pager); + $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setFooter(array( - $history_panel, + $history_list, + $pager, )); return $this->newPage() - ->setTitle( - array( - pht('History'), - $repository->getDisplayName(), - )) + ->setTitle($title) ->setCrumbs($crumbs) - ->appendChild( - array( - $view, - )); + ->appendChild($view); } private function buildHeader(DiffusionRequest $drequest) { $viewer = $this->getViewer(); $tag = $this->renderCommitHashTag($drequest); - - $header = id(new PHUIHeaderView()) - ->setUser($viewer) - ->setPolicyObject($drequest->getRepository()) - ->addTag($tag) - ->setHeader($this->renderPathLinks($drequest, $mode = 'history')) - ->setHeaderIcon('fa-clock-o'); - - return $header; - - } - - private function buildHistoryHeader(DiffusionRequest $drequest) { - $viewer = $this->getViewer(); - $browse_uri = $drequest->generateURI( array( 'action' => 'browse', @@ -117,36 +85,18 @@ final class DiffusionHistoryController extends DiffusionController { ->setTag('a') ->setText(pht('Browse')) ->setHref($browse_uri) - ->setIcon('fa-files-o'); - - // TODO: Sometimes we do have a change view, we need to look at the most - // recent history entry to figure it out. - - $request = $this->getRequest(); - if ($request->getBool('copies')) { - $branch_name = pht('Hide Copies/Branches'); - $branch_uri = $request->getRequestURI() - ->alter('offset', null) - ->alter('copies', null); - } else { - $branch_name = pht('Show Copies/Branches'); - $branch_uri = $request->getRequestURI() - ->alter('offset', null) - ->alter('copies', true); - } - - $branch_button = id(new PHUIButtonView()) - ->setTag('a') - ->setText($branch_name) - ->setIcon('fa-code-fork') - ->setHref($branch_uri); + ->setIcon('fa-code'); $header = id(new PHUIHeaderView()) - ->setHeader(pht('History')) - ->addActionLink($browse_button) - ->addActionLink($branch_button); + ->setUser($viewer) + ->setPolicyObject($drequest->getRepository()) + ->addTag($tag) + ->setHeader($this->renderPathLinks($drequest, $mode = 'history')) + ->setHeaderIcon('fa-clock-o') + ->addActionLink($browse_button); return $header; + } } diff --git a/src/applications/diffusion/view/DiffusionCommitListView.php b/src/applications/diffusion/view/DiffusionCommitListView.php index b3f2a9fb0c..f1d1ebc8e4 100644 --- a/src/applications/diffusion/view/DiffusionCommitListView.php +++ b/src/applications/diffusion/view/DiffusionCommitListView.php @@ -25,6 +25,28 @@ final class DiffusionCommitListView extends AphrontView { return $this->commits; } + public function setHandles(array $handles) { + assert_instances_of($handles, 'PhabricatorObjectHandle'); + $this->handles = $handles; + return $this; + } + + private function getRequiredHandlePHIDs() { + $phids = array(); + foreach ($this->history as $item) { + $data = $item->getCommitData(); + if ($data) { + if ($data->getCommitDetail('authorPHID')) { + $phids[$data->getCommitDetail('authorPHID')] = true; + } + if ($data->getCommitDetail('committerPHID')) { + $phids[$data->getCommitDetail('committerPHID')] = true; + } + } + } + return array_keys($phids); + } + private function getCommitDescription($phid) { if ($this->commits === null) { return pht('(Unknown Commit)'); @@ -114,12 +136,10 @@ final class DiffusionCommitListView extends AphrontView { if ($author_phid) { $author_name = $handles[$author_phid]->renderLink(); $author_image_uri = $handles[$author_phid]->getImageURI(); - $author_image_href = $handles[$author_phid]->getURI(); } else { $author_name = $commit->getCommitData()->getAuthorName(); $author_image_uri = celerity_get_resource_uri('/rsrc/image/people/user0.png'); - $author_image_href = null; } $commit_tag = id(new PHUITagView()) @@ -134,7 +154,6 @@ final class DiffusionCommitListView extends AphrontView { ->setDisabled($commit->isUnreachable()) ->setDescription($message) ->setImageURI($author_image_uri) - ->setImageHref($author_image_href) ->addByline(pht('Author: %s', $author_name)) ->addIcon('none', $committed) ->addAttribute($commit_tag); diff --git a/src/applications/diffusion/view/DiffusionHistoryListView.php b/src/applications/diffusion/view/DiffusionHistoryListView.php new file mode 100644 index 0000000000..533e15d644 --- /dev/null +++ b/src/applications/diffusion/view/DiffusionHistoryListView.php @@ -0,0 +1,181 @@ +getDiffusionRequest(); + $viewer = $this->getUser(); + $repository = $drequest->getRepository(); + + require_celerity_resource('diffusion-history-css'); + Javelin::initBehavior('phabricator-tooltips'); + + $buildables = $this->loadBuildables( + mpull($this->getHistory(), 'getCommit')); + + $show_revisions = PhabricatorApplication::isClassInstalledForViewer( + 'PhabricatorDifferentialApplication', + $viewer); + + $handles = $viewer->loadHandles($this->getRequiredHandlePHIDs()); + + $show_builds = PhabricatorApplication::isClassInstalledForViewer( + 'PhabricatorHarbormasterApplication', + $this->getUser()); + + $cur_date = null; + $view = array(); + foreach ($this->getHistory() as $history) { + $epoch = $history->getEpoch(); + $new_date = phabricator_date($history->getEpoch(), $viewer); + if ($cur_date !== $new_date) { + $date = ucfirst( + phabricator_relative_date($history->getEpoch(), $viewer)); + $header = id(new PHUIHeaderView()) + ->setHeader($date); + $list = id(new PHUIObjectItemListView()) + ->setFlush(true) + ->addClass('diffusion-history-list'); + + $view[] = id(new PHUIObjectBoxView()) + ->setHeader($header) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->setObjectList($list); + } + + if ($epoch) { + $committed = $viewer->formatShortDateTime($epoch); + } else { + $committed = null; + } + + $data = $history->getCommitData(); + $author_phid = $committer = $committer_phid = null; + if ($data) { + $author_phid = $data->getCommitDetail('authorPHID'); + $committer_phid = $data->getCommitDetail('committerPHID'); + $committer = $data->getCommitDetail('committer'); + } + + if ($author_phid && isset($handles[$author_phid])) { + $author_name = $handles[$author_phid]->renderLink(); + $author_image = $handles[$author_phid]->getImageURI(); + } else { + $author_name = self::renderName($history->getAuthorName()); + $author_image = + celerity_get_resource_uri('/rsrc/image/people/user0.png'); + } + + $different_committer = false; + if ($committer_phid) { + $different_committer = ($committer_phid != $author_phid); + } else if ($committer != '') { + $different_committer = ($committer != $history->getAuthorName()); + } + if ($different_committer) { + if ($committer_phid && isset($handles[$committer_phid])) { + $committer = $handles[$committer_phid]->renderLink(); + } else { + $committer = self::renderName($committer); + } + $author_name = hsprintf('%s / %s', $author_name, $committer); + } + + // We can show details once the message and change have been imported. + $partial_import = PhabricatorRepositoryCommit::IMPORTED_MESSAGE | + PhabricatorRepositoryCommit::IMPORTED_CHANGE; + + $commit = $history->getCommit(); + if ($commit && $commit->isPartiallyImported($partial_import) && $data) { + $commit_desc = $history->getSummary(); + } else { + $commit_desc = phutil_tag('em', array(), pht("Importing\xE2\x80\xA6")); + } + + $browse_button = $this->linkBrowse( + $history->getPath(), + array( + 'commit' => $history->getCommitIdentifier(), + 'branch' => $drequest->getBranch(), + 'type' => $history->getFileType(), + ), + true); + + $diff_tag = null; + if ($show_revisions && $commit) { + $d_id = idx($this->getRevisions(), $commit->getPHID()); + if ($d_id) { + $diff_tag = id(new PHUITagView()) + ->setName('D'.$d_id) + ->setType(PHUITagView::TYPE_SHADE) + ->setColor(PHUITagView::COLOR_BLUE) + ->setHref('/D'.$d_id) + ->addClass('diffusion-differential-tag') + ->setSlimShady(true); + } + } + + $build_view = null; + if ($show_builds) { + $buildable = idx($buildables, $commit->getPHID()); + if ($buildable !== null) { + $status = $buildable->getBuildableStatus(); + $icon = HarbormasterBuildable::getBuildableStatusIcon($status); + $color = HarbormasterBuildable::getBuildableStatusColor($status); + $name = HarbormasterBuildable::getBuildableStatusName($status); + $build_view = id(new PHUIButtonView()) + ->setTag('a') + ->setText($name) + ->setIcon($icon) + ->setColor($color) + ->setHref('/'.$buildable->getMonogram()) + ->addClass('mmr') + ->setButtonType(PHUIButtonView::BUTTONTYPE_SIMPLE) + ->addClass('diffusion-list-build-status'); + } + } + + $message = null; + $commit_link = $repository->getCommitURI( + $history->getCommitIdentifier()); + + $commit_name = $repository->formatCommitName( + $history->getCommitIdentifier(), $local = true); + + $committed = phabricator_datetime($commit->getEpoch(), $viewer); + $author_name = phutil_tag( + 'strong', + array( + 'class' => 'diffusion-history-author-name', + ), + $author_name); + $authored = pht('%s on %s.', $author_name, $committed); + + $commit_tag = id(new PHUITagView()) + ->setName($commit_name) + ->setType(PHUITagView::TYPE_SHADE) + ->setColor(PHUITagView::COLOR_INDIGO) + ->setSlimShady(true); + + $item = id(new PHUIObjectItemView()) + ->setHeader($commit_desc) + ->setHref($commit_link) + ->setDisabled($commit->isUnreachable()) + ->setDescription($message) + ->setImageURI($author_image) + ->addAttribute(array($commit_tag, ' ', $diff_tag)) // For Copy Pasta + ->addAttribute($authored) + ->setSideColumn(array( + $build_view, + $browse_button, + )); + + $list->addItem($item); + $cur_date = $new_date; + } + + + return $view; + } + +} diff --git a/src/applications/diffusion/view/DiffusionHistoryTableView.php b/src/applications/diffusion/view/DiffusionHistoryTableView.php index bb3aee37da..3885bbf47c 100644 --- a/src/applications/diffusion/view/DiffusionHistoryTableView.php +++ b/src/applications/diffusion/view/DiffusionHistoryTableView.php @@ -1,87 +1,14 @@ history = $history; - return $this; - } - - public function loadRevisions() { - $commit_phids = array(); - foreach ($this->history as $item) { - if ($item->getCommit()) { - $commit_phids[] = $item->getCommit()->getPHID(); - } - } - - // TODO: Get rid of this. - $this->revisions = id(new DifferentialRevision()) - ->loadIDsByCommitPHIDs($commit_phids); - return $this; - } - - public function setHandles(array $handles) { - assert_instances_of($handles, 'PhabricatorObjectHandle'); - $this->handles = $handles; - return $this; - } - - private function getRequiredHandlePHIDs() { - $phids = array(); - foreach ($this->history as $item) { - $data = $item->getCommitData(); - if ($data) { - if ($data->getCommitDetail('authorPHID')) { - $phids[$data->getCommitDetail('authorPHID')] = true; - } - if ($data->getCommitDetail('committerPHID')) { - $phids[$data->getCommitDetail('committerPHID')] = true; - } - } - } - return array_keys($phids); - } - - public function setParents(array $parents) { - $this->parents = $parents; - return $this; - } - - public function setIsHead($is_head) { - $this->isHead = $is_head; - return $this; - } - - public function setIsTail($is_tail) { - $this->isTail = $is_tail; - return $this; - } - - public function setFilterParents($filter_parents) { - $this->filterParents = $filter_parents; - return $this; - } - - public function getFilterParents() { - return $this->filterParents; - } +final class DiffusionHistoryTableView extends DiffusionHistoryView { public function render() { $drequest = $this->getDiffusionRequest(); $viewer = $this->getUser(); - $buildables = $this->loadBuildables(mpull($this->history, 'getCommit')); + $buildables = $this->loadBuildables( + mpull($this->getHistory(), 'getCommit')); $has_any_build = false; $show_revisions = PhabricatorApplication::isClassInstalledForViewer( @@ -91,14 +18,14 @@ final class DiffusionHistoryTableView extends DiffusionView { $handles = $viewer->loadHandles($this->getRequiredHandlePHIDs()); $graph = null; - if ($this->parents) { - $parents = $this->parents; + if ($this->getParents()) { + $parents = $this->getParents(); // If we're filtering parents, remove relationships which point to // commits that are not part of the visible graph. Otherwise, we get // a big tree of nonsense when viewing release branches like "stable" // versus "master". - if ($this->filterParents) { + if ($this->getFilterParents()) { foreach ($parents as $key => $nodes) { foreach ($nodes as $nkey => $node) { if (empty($parents[$node])) { @@ -109,8 +36,8 @@ final class DiffusionHistoryTableView extends DiffusionView { } $graph = id(new PHUIDiffGraphView()) - ->setIsHead($this->isHead) - ->setIsTail($this->isTail) + ->setIsHead($this->getIsHead()) + ->setIsTail($this->getIsTail()) ->renderGraph($parents); } @@ -120,7 +47,7 @@ final class DiffusionHistoryTableView extends DiffusionView { $rows = array(); $ii = 0; - foreach ($this->history as $history) { + foreach ($this->getHistory() as $history) { $epoch = $history->getEpoch(); if ($epoch) { @@ -209,7 +136,7 @@ final class DiffusionHistoryTableView extends DiffusionView { $build, $audit_view, ($commit ? - self::linkRevision(idx($this->revisions, $commit->getPHID())) : + self::linkRevision(idx($this->getRevisions(), $commit->getPHID())) : null), $author, $summary, diff --git a/src/applications/diffusion/view/DiffusionHistoryView.php b/src/applications/diffusion/view/DiffusionHistoryView.php new file mode 100644 index 0000000000..56a0f3b75f --- /dev/null +++ b/src/applications/diffusion/view/DiffusionHistoryView.php @@ -0,0 +1,101 @@ +history = $history; + return $this; + } + + public function getHistory() { + return $this->history; + } + + public function loadRevisions() { + $commit_phids = array(); + foreach ($this->history as $item) { + if ($item->getCommit()) { + $commit_phids[] = $item->getCommit()->getPHID(); + } + } + + // TODO: Get rid of this. + $this->revisions = id(new DifferentialRevision()) + ->loadIDsByCommitPHIDs($commit_phids); + return $this; + } + + public function getRevisions() { + return $this->revisions; + } + + public function setHandles(array $handles) { + assert_instances_of($handles, 'PhabricatorObjectHandle'); + $this->handles = $handles; + return $this; + } + + public function getRequiredHandlePHIDs() { + $phids = array(); + foreach ($this->history as $item) { + $data = $item->getCommitData(); + if ($data) { + if ($data->getCommitDetail('authorPHID')) { + $phids[$data->getCommitDetail('authorPHID')] = true; + } + if ($data->getCommitDetail('committerPHID')) { + $phids[$data->getCommitDetail('committerPHID')] = true; + } + } + } + return array_keys($phids); + } + + public function setParents(array $parents) { + $this->parents = $parents; + return $this; + } + + public function getParents() { + return $this->parents; + } + + public function setIsHead($is_head) { + $this->isHead = $is_head; + return $this; + } + + public function getIsHead() { + return $this->isHead; + } + + public function setIsTail($is_tail) { + $this->isTail = $is_tail; + return $this; + } + + public function getIsTail() { + return $this->isTail; + } + + public function setFilterParents($filter_parents) { + $this->filterParents = $filter_parents; + return $this; + } + + public function getFilterParents() { + return $this->filterParents; + } + + public function render() {} + +} diff --git a/src/applications/diffusion/view/DiffusionView.php b/src/applications/diffusion/view/DiffusionView.php index 5aa1fad33c..1d89a9dbd7 100644 --- a/src/applications/diffusion/view/DiffusionView.php +++ b/src/applications/diffusion/view/DiffusionView.php @@ -58,7 +58,10 @@ abstract class DiffusionView extends AphrontView { id(new PHUIIconView())->setIcon('fa-history bluegrey')); } - final public function linkBrowse($path, array $details = array()) { + final public function linkBrowse( + $path, + array $details = array(), + $button = false) { require_celerity_resource('diffusion-icons-css'); Javelin::initBehavior('phabricator-tooltips'); @@ -111,6 +114,15 @@ abstract class DiffusionView extends AphrontView { ); } + if ($button) { + return id(new PHUIButtonView()) + ->setText(pht('Browse')) + ->setIcon('fa-code') + ->setHref($href) + ->setTag('a') + ->setButtonType(PHUIButtonView::BUTTONTYPE_SIMPLE); + } + return javelin_tag( 'a', array( @@ -168,7 +180,7 @@ abstract class DiffusionView extends AphrontView { 'sigil' => 'has-tooltip', 'meta' => array( 'tip' => $email->getAddress(), - 'align' => 'E', + 'align' => 'S', 'size' => 'auto', ), ), @@ -177,30 +189,24 @@ abstract class DiffusionView extends AphrontView { return hsprintf('%s', $name); } - final protected function renderBuildable(HarbormasterBuildable $buildable) { + final protected function renderBuildable( + HarbormasterBuildable $buildable) { $status = $buildable->getBuildableStatus(); + Javelin::initBehavior('phabricator-tooltips'); $icon = HarbormasterBuildable::getBuildableStatusIcon($status); $color = HarbormasterBuildable::getBuildableStatusColor($status); $name = HarbormasterBuildable::getBuildableStatusName($status); - $icon_view = id(new PHUIIconView()) - ->setIcon($icon.' '.$color); + return id(new PHUIIconView()) + ->setIcon($icon.' '.$color) + ->addSigil('has-tooltip') + ->setHref('/'.$buildable->getMonogram()) + ->setMetadata( + array( + 'tip' => $name, + )); - $tooltip_view = javelin_tag( - 'span', - array( - 'sigil' => 'has-tooltip', - 'meta' => array('tip' => $name), - ), - $icon_view); - - Javelin::initBehavior('phabricator-tooltips'); - - return phutil_tag( - 'a', - array('href' => '/'.$buildable->getMonogram()), - $tooltip_view); } final protected function loadBuildables(array $commits) { diff --git a/src/applications/guides/view/PhabricatorGuideListView.php b/src/applications/guides/view/PhabricatorGuideListView.php index 04ab7c3058..089f325e0c 100644 --- a/src/applications/guides/view/PhabricatorGuideListView.php +++ b/src/applications/guides/view/PhabricatorGuideListView.php @@ -30,7 +30,7 @@ final class PhabricatorGuideListView extends AphrontView { ->setText(pht('Skip')) ->setTag('a') ->setHref($skip_href) - ->setColor(PHUIButtonView::SIMPLE); + ->setButtonType(PHUIButtonView::BUTTONTYPE_SIMPLE); $list_item->setSideColumn($skip); } $list->addItem($list_item); diff --git a/src/applications/maniphest/storage/ManiphestTransaction.php b/src/applications/maniphest/storage/ManiphestTransaction.php index e2739c1d09..6301241050 100644 --- a/src/applications/maniphest/storage/ManiphestTransaction.php +++ b/src/applications/maniphest/storage/ManiphestTransaction.php @@ -40,6 +40,22 @@ final class ManiphestTransaction return parent::shouldGenerateOldValue(); } + public function shouldHideForFeed() { + // NOTE: Modular transactions don't currently support this, and it has + // very few callsites, and it's publish-time rather than display-time. + // This should probably become a supported, display-time behavior. For + // discussion, see T12787. + + // Hide "alice created X, a task blocking Y." from feed because it + // will almost always appear adjacent to "alice created Y". + $is_new = $this->getMetadataValue('blocker.new'); + if ($is_new) { + return true; + } + + return parent::shouldHideForFeed(); + } + public function getRequiredHandlePHIDs() { $phids = parent::getRequiredHandlePHIDs(); diff --git a/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php index b1c20af9e6..8953664f27 100644 --- a/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php +++ b/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php @@ -17,10 +17,6 @@ final class ManiphestTaskPointsTransaction $object->setPoints($value); } - public function shouldHideForFeed() { - return true; - } - public function shouldHide() { if (!ManiphestTaskPoints::getIsEnabled()) { return true; diff --git a/src/applications/maniphest/xaction/ManiphestTaskUnblockTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskUnblockTransaction.php index 905554a3a3..1c7a88dec7 100644 --- a/src/applications/maniphest/xaction/ManiphestTaskUnblockTransaction.php +++ b/src/applications/maniphest/xaction/ManiphestTaskUnblockTransaction.php @@ -9,15 +9,6 @@ final class ManiphestTaskUnblockTransaction return null; } - public function shouldHideForFeed() { - // Hide "alice created X, a task blocking Y." from feed because it - // will almost always appear adjacent to "alice created Y". - $is_new = $this->getMetadataValue('blocker.new'); - if ($is_new) { - return true; - } - } - public function getActionName() { $old = $this->getOldValue(); $new = $this->getNewValue(); diff --git a/src/applications/notification/query/PhabricatorNotificationSearchEngine.php b/src/applications/notification/query/PhabricatorNotificationSearchEngine.php index 11b935eb50..0ee7327bfc 100644 --- a/src/applications/notification/query/PhabricatorNotificationSearchEngine.php +++ b/src/applications/notification/query/PhabricatorNotificationSearchEngine.php @@ -85,12 +85,12 @@ final class PhabricatorNotificationSearchEngine $viewer = $this->requireViewer(); $image = id(new PHUIIconView()) - ->setIcon('fa-eye-slash'); + ->setIcon('fa-bell-o'); $button = id(new PHUIButtonView()) ->setTag('a') ->addSigil('workflow') - ->setColor(PHUIButtonView::SIMPLE) + ->setColor(PHUIButtonView::GREY) ->setIcon($image) ->setText(pht('Mark All Read')); diff --git a/src/applications/nuance/application/PhabricatorNuanceApplication.php b/src/applications/nuance/application/PhabricatorNuanceApplication.php index cd268dd95e..dc18a6585f 100644 --- a/src/applications/nuance/application/PhabricatorNuanceApplication.php +++ b/src/applications/nuance/application/PhabricatorNuanceApplication.php @@ -18,11 +18,6 @@ final class PhabricatorNuanceApplication extends PhabricatorApplication { return true; } - public function isLaunchable() { - // Try to hide this even more for now. - return false; - } - public function getBaseURI() { return '/nuance/'; } diff --git a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php index a1c10cd5e9..ce411aad35 100644 --- a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php +++ b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php @@ -398,13 +398,36 @@ final class PhabricatorOwnersPackageQuery } } + // At each strength level, drop weak packages if there are also strong + // packages of the same strength. + $strength_map = igroup($matches, 'strength'); + foreach ($strength_map as $strength => $package_list) { + $any_strong = false; + foreach ($package_list as $package_id => $package) { + if (!$package['weak']) { + $any_strong = true; + break; + } + } + if ($any_strong) { + foreach ($package_list as $package_id => $package) { + if ($package['weak']) { + unset($matches[$package_id]); + } + } + } + } + $matches = isort($matches, 'strength'); $matches = array_reverse($matches); - $first_id = null; + $strongest = null; foreach ($matches as $package_id => $match) { - if ($first_id === null) { - $first_id = $package_id; + if ($strongest === null) { + $strongest = $match['strength']; + } + + if ($match['strength'] === $strongest) { continue; } diff --git a/src/applications/owners/storage/PhabricatorOwnersPackage.php b/src/applications/owners/storage/PhabricatorOwnersPackage.php index 2b22a7a145..5f7b4f28c1 100644 --- a/src/applications/owners/storage/PhabricatorOwnersPackage.php +++ b/src/applications/owners/storage/PhabricatorOwnersPackage.php @@ -297,27 +297,54 @@ final class PhabricatorOwnersPackage // a more specific package. if ($weak) { foreach ($path_packages as $match => $packages) { + + // Group packages by length. + $length_map = array(); + foreach ($packages as $package_id => $package) { + $length_map[$package['length']][$package_id] = $package; + } + + // For each path length, remove all weak packages if there are any + // strong packages of the same length. This makes sure that if there + // are one or more strong claims on a particular path, only those + // claims stand. + foreach ($length_map as $package_list) { + $any_strong = false; + foreach ($package_list as $package_id => $package) { + if (!isset($weak[$package_id])) { + $any_strong = true; + break; + } + } + + if ($any_strong) { + foreach ($package_list as $package_id => $package) { + if (isset($weak[$package_id])) { + unset($packages[$package_id]); + } + } + } + } + $packages = isort($packages, 'length'); $packages = array_reverse($packages, true); - $first = null; + $best_length = null; foreach ($packages as $package_id => $package) { - // If this is the first package we've encountered, note it and - // continue. We're iterating over the packages from longest to - // shortest match, so this package always has the strongest claim - // on the path. - if ($first === null) { - $first = $package_id; + // If this is the first package we've encountered, note its length. + // We're iterating over the packages from longest to shortest match, + // so packages of this length always have the best claim on the path. + if ($best_length === null) { + $best_length = $package['length']; + } + + // If this package has the same length as the best length, its claim + // stands. + if ($package['length'] === $best_length) { continue; } - // If this is the first package we saw, its claim stands even if it - // is a weak package. - if ($first === $package_id) { - continue; - } - - // If this is a weak package and not the first package we saw, + // If this is a weak package and does not have the best length, // cede its claim to the stronger package. if (isset($weak[$package_id])) { unset($packages[$package_id]); diff --git a/src/applications/owners/storage/__tests__/PhabricatorOwnersPackageTestCase.php b/src/applications/owners/storage/__tests__/PhabricatorOwnersPackageTestCase.php index 7d24fc6e8d..f21f09b44e 100644 --- a/src/applications/owners/storage/__tests__/PhabricatorOwnersPackageTestCase.php +++ b/src/applications/owners/storage/__tests__/PhabricatorOwnersPackageTestCase.php @@ -100,6 +100,95 @@ final class PhabricatorOwnersPackageTestCase extends PhabricatorTestCase { PhabricatorOwnersPackage::findLongestPathsPerPackage($rows, $paths)); + // Test cases where multiple packages own the same path, with various + // dominion rules. + + $main_c = 'src/applications/main/main.c'; + + $rules = array( + // All claims strong. + array( + PhabricatorOwnersPackage::DOMINION_STRONG, + PhabricatorOwnersPackage::DOMINION_STRONG, + PhabricatorOwnersPackage::DOMINION_STRONG, + ), + // All claims weak. + array( + PhabricatorOwnersPackage::DOMINION_WEAK, + PhabricatorOwnersPackage::DOMINION_WEAK, + PhabricatorOwnersPackage::DOMINION_WEAK, + ), + // Mixture of strong and weak claims, strong first. + array( + PhabricatorOwnersPackage::DOMINION_STRONG, + PhabricatorOwnersPackage::DOMINION_STRONG, + PhabricatorOwnersPackage::DOMINION_WEAK, + ), + // Mixture of strong and weak claims, weak first. + array( + PhabricatorOwnersPackage::DOMINION_WEAK, + PhabricatorOwnersPackage::DOMINION_STRONG, + PhabricatorOwnersPackage::DOMINION_STRONG, + ), + ); + + foreach ($rules as $rule_idx => $rule) { + $rows = array( + array( + 'id' => 1, + 'excluded' => 0, + 'dominion' => $rule[0], + 'path' => $main_c, + ), + array( + 'id' => 2, + 'excluded' => 0, + 'dominion' => $rule[1], + 'path' => $main_c, + ), + array( + 'id' => 3, + 'excluded' => 0, + 'dominion' => $rule[2], + 'path' => $main_c, + ), + ); + + $paths = array( + $main_c => $pvalue, + ); + + // If one or more packages have strong dominion, they should own the + // path. If not, all the packages with weak dominion should own the + // path. + $strong = array(); + $weak = array(); + foreach ($rule as $idx => $dominion) { + if ($dominion == PhabricatorOwnersPackage::DOMINION_STRONG) { + $strong[] = $idx + 1; + } else { + $weak[] = $idx + 1; + } + } + + if ($strong) { + $expect = $strong; + } else { + $expect = $weak; + } + + $expect = array_fill_keys($expect, strlen($main_c)); + $actual = PhabricatorOwnersPackage::findLongestPathsPerPackage( + $rows, + $paths); + + ksort($actual); + + $this->assertEqual( + $expect, + $actual, + pht('Ruleset "%s" for Identical Ownership', $rule_idx)); + } } } diff --git a/src/applications/people/controller/PhabricatorPeopleProfileTasksController.php b/src/applications/people/controller/PhabricatorPeopleProfileTasksController.php index 47cc605bb8..b843af8fc7 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileTasksController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileTasksController.php @@ -61,6 +61,7 @@ final class PhabricatorPeopleProfileTasksController ->withStatuses($open) ->needProjectPHIDs(true) ->setLimit(100) + ->setGroupBy(ManiphestTaskQuery::GROUP_PRIORITY) ->execute(); $handles = ManiphestTaskListView::loadTaskHandles($viewer, $tasks); diff --git a/src/applications/phame/query/PhameBlogSearchEngine.php b/src/applications/phame/query/PhameBlogSearchEngine.php index 3d23a9763d..eaef32af1c 100644 --- a/src/applications/phame/query/PhameBlogSearchEngine.php +++ b/src/applications/phame/query/PhameBlogSearchEngine.php @@ -98,7 +98,7 @@ final class PhameBlogSearchEngine ->setTag('a') ->setText('New Post') ->setHref($this->getApplicationURI('/post/edit/?blog='.$id)) - ->setColor(PHUIButtonView::SIMPLE); + ->setButtonType(PHUIButtonView::BUTTONTYPE_SIMPLE); $item->setSideColumn($button); } diff --git a/src/applications/pholio/xaction/PholioImageReplaceTransaction.php b/src/applications/pholio/xaction/PholioImageReplaceTransaction.php index e6d45dfc7a..4978fa9768 100644 --- a/src/applications/pholio/xaction/PholioImageReplaceTransaction.php +++ b/src/applications/pholio/xaction/PholioImageReplaceTransaction.php @@ -62,7 +62,19 @@ final class PholioImageReplaceTransaction } public function extractFilePHIDs($object, $value) { - return array($value); + $file_phids = array(); + + $editor = $this->getEditor(); + $images = $editor->getNewImages(); + foreach ($images as $image) { + if ($image->getPHID() !== $value) { + continue; + } + + $file_phids[] = $image->getFilePHID(); + } + + return $file_phids; } } diff --git a/src/applications/settings/application/PhabricatorSettingsApplication.php b/src/applications/settings/application/PhabricatorSettingsApplication.php index d5add43d46..e5e0034e1a 100644 --- a/src/applications/settings/application/PhabricatorSettingsApplication.php +++ b/src/applications/settings/application/PhabricatorSettingsApplication.php @@ -22,10 +22,6 @@ final class PhabricatorSettingsApplication extends PhabricatorApplication { return false; } - public function isLaunchable() { - return false; - } - public function getRoutes() { $panel_pattern = '(?:page/(?P[^/]+)/(?:(?Psaved)/)?)?'; diff --git a/src/applications/uiexample/controller/PhabricatorUIExampleRenderController.php b/src/applications/uiexample/controller/PhabricatorUIExampleRenderController.php index 6be978a6a9..025b9511b4 100644 --- a/src/applications/uiexample/controller/PhabricatorUIExampleRenderController.php +++ b/src/applications/uiexample/controller/PhabricatorUIExampleRenderController.php @@ -17,9 +17,14 @@ final class PhabricatorUIExampleRenderController extends PhabricatorController { $nav = new AphrontSideNavFilterView(); $nav->setBaseURI(new PhutilURI($this->getApplicationURI('view/'))); - foreach ($classes as $class => $obj) { - $name = $obj->getName(); - $nav->addFilter($class, $name); + $groups = mgroup($classes, 'getCategory'); + ksort($groups); + foreach ($groups as $group => $group_classes) { + $nav->addLabel($group); + foreach ($group_classes as $class => $obj) { + $name = $obj->getName(); + $nav->addFilter($class, $name); + } } $selected = $nav->selectFilter($id, head_key($classes)); diff --git a/src/applications/uiexample/examples/JavelinReactorUIExample.php b/src/applications/uiexample/examples/JavelinReactorUIExample.php deleted file mode 100644 index 9e47545072..0000000000 --- a/src/applications/uiexample/examples/JavelinReactorUIExample.php +++ /dev/null @@ -1,95 +0,0 @@ - true), - ), - array( - pht('Reactive focus detector generates a boolean dynamic value'), - 'ReactorFocusExample', - 'phabricator-uiexample-reactor-focus', - array(), - ), - array( - pht('Reactive input box, with normal and calmed output'), - 'ReactorInputExample', - 'phabricator-uiexample-reactor-input', - array('init' => 'Initial value'), - ), - array( - pht('Reactive mouseover detector generates a boolean dynamic value'), - 'ReactorMouseoverExample', - 'phabricator-uiexample-reactor-mouseover', - array(), - ), - array( - pht('Reactive radio buttons generate a string dynamic value'), - 'ReactorRadioExample', - 'phabricator-uiexample-reactor-radio', - array(), - ), - array( - pht('Reactive select box generates a string dynamic value'), - 'ReactorSelectExample', - 'phabricator-uiexample-reactor-select', - array(), - ), - array( - pht( - '%s makes the class of an element a string dynamic value', - 'sendclass'), - 'ReactorSendClassExample', - 'phabricator-uiexample-reactor-sendclass', - array(), - ), - array( - pht( - '%s makes some properties of an object into dynamic values', - 'sendproperties'), - 'ReactorSendPropertiesExample', - 'phabricator-uiexample-reactor-sendproperties', - array(), - ), - ); - - foreach ($examples as $example) { - list($desc, $name, $resource, $params) = $example; - $template = new AphrontJavelinView(); - $template - ->setName($name) - ->setParameters($params) - ->setCelerityResource($resource); - $rows[] = array($desc, $template->render()); - } - - $table = new AphrontTableView($rows); - - $panel = new PHUIObjectBoxView(); - $panel->setHeaderText(pht('Example')); - $panel->appendChild($table); - - return $panel; - } -} diff --git a/src/applications/uiexample/examples/JavelinUIExample.php b/src/applications/uiexample/examples/JavelinUIExample.php deleted file mode 100644 index c85a2d4cde..0000000000 --- a/src/applications/uiexample/examples/JavelinUIExample.php +++ /dev/null @@ -1,66 +0,0 @@ -getRequest(); - $user = $request->getUser(); - - // toggle-class - - $container_id = celerity_generate_unique_node_id(); - $button_red_id = celerity_generate_unique_node_id(); - $button_blue_id = celerity_generate_unique_node_id(); - - $button_red = javelin_tag( - 'a', - array( - 'class' => 'button', - 'sigil' => 'jx-toggle-class', - 'href' => '#', - 'id' => $button_red_id, - 'meta' => array( - 'map' => array( - $container_id => 'jxui-red-border', - $button_red_id => 'jxui-active', - ), - ), - ), - pht('Toggle Red Border')); - - $button_blue = javelin_tag( - 'a', - array( - 'class' => 'button jxui-active', - 'sigil' => 'jx-toggle-class', - 'href' => '#', - 'id' => $button_blue_id, - 'meta' => array( - 'state' => true, - 'map' => array( - $container_id => 'jxui-blue-background', - $button_blue_id => 'jxui-active', - ), - ), - ), - pht('Toggle Blue Background')); - - $div = phutil_tag( - 'div', - array( - 'id' => $container_id, - 'class' => 'jxui-example-container jxui-blue-background', - ), - array($button_red, $button_blue)); - - return array($div); - } -} diff --git a/src/applications/uiexample/examples/JavelinViewExampleServerView.php b/src/applications/uiexample/examples/JavelinViewExampleServerView.php deleted file mode 100644 index 2d59917a7c..0000000000 --- a/src/applications/uiexample/examples/JavelinViewExampleServerView.php +++ /dev/null @@ -1,14 +0,0 @@ - 'server-view', - ), - $this->renderChildren()); - } - -} diff --git a/src/applications/uiexample/examples/JavelinViewUIExample.php b/src/applications/uiexample/examples/JavelinViewUIExample.php deleted file mode 100644 index f7df1749b4..0000000000 --- a/src/applications/uiexample/examples/JavelinViewUIExample.php +++ /dev/null @@ -1,45 +0,0 @@ -getRequest(); - - $init = $request->getStr('init'); - - $parent_server_template = new JavelinViewExampleServerView(); - - $parent_client_template = new AphrontJavelinView(); - $parent_client_template - ->setName('JavelinViewExample') - ->setCelerityResource('phabricator-uiexample-javelin-view'); - - $child_server_template = new JavelinViewExampleServerView(); - - $child_client_template = new AphrontJavelinView(); - $child_client_template - ->setName('JavelinViewExample') - ->setCelerityResource('phabricator-uiexample-javelin-view'); - - $parent_server_template->appendChild($parent_client_template); - $parent_client_template->appendChild($child_server_template); - $child_server_template->appendChild($child_client_template); - $child_client_template->appendChild(pht('Hey, it worked.')); - - $panel = new PHUIObjectBoxView(); - $panel->setHeaderText(pht('Example')); - $panel->appendChild( - phutil_tag_div('ml', $parent_server_template)); - - return $panel; - } -} diff --git a/src/applications/uiexample/examples/MacroEmojiExample.php b/src/applications/uiexample/examples/MacroEmojiExample.php index fddf20bc32..036a5a8932 100644 --- a/src/applications/uiexample/examples/MacroEmojiExample.php +++ b/src/applications/uiexample/examples/MacroEmojiExample.php @@ -3,11 +3,15 @@ final class MacroEmojiExample extends PhabricatorUIExample { public function getName() { - return pht('Emoji Support'); + return pht('Emoji'); } public function getDescription() { - return pht('Shiny happy people holding hands'); + return pht('Shiny happy people holding hands.'); + } + + public function getCategory() { + return pht('Catalogs'); } public function renderExample() { diff --git a/src/applications/uiexample/examples/PHUIBadgeExample.php b/src/applications/uiexample/examples/PHUIBadgeExample.php index 14eb3469d0..703595b76c 100644 --- a/src/applications/uiexample/examples/PHUIBadgeExample.php +++ b/src/applications/uiexample/examples/PHUIBadgeExample.php @@ -10,6 +10,10 @@ final class PHUIBadgeExample extends PhabricatorUIExample { return pht('Celebrate the moments of your life.'); } + public function getCategory() { + return pht('Single Use'); + } + public function renderExample() { $badges1 = array(); diff --git a/src/applications/uiexample/examples/PHUIBoxExample.php b/src/applications/uiexample/examples/PHUIBoxExample.php index 7a2674da94..4a551de707 100644 --- a/src/applications/uiexample/examples/PHUIBoxExample.php +++ b/src/applications/uiexample/examples/PHUIBoxExample.php @@ -62,11 +62,11 @@ final class PHUIBoxExample extends PhabricatorUIExample { ); $button = id(new PHUIButtonView()) - ->setTag('a') - ->setColor(PHUIButtonView::SIMPLE) - ->setIcon('fa-heart') - ->setText(pht('Such Wow')) - ->addClass(PHUI::MARGIN_SMALL_RIGHT); + ->setTag('a') + ->setButtonType(PHUIButtonView::BUTTONTYPE_SIMPLE) + ->setIcon('fa-heart') + ->setText(pht('Such Wow')) + ->addClass(PHUI::MARGIN_SMALL_RIGHT); $badge1 = id(new PHUIBadgeMiniView()) ->setIcon('fa-bug') diff --git a/src/applications/uiexample/examples/PHUIButtonBarExample.php b/src/applications/uiexample/examples/PHUIButtonBarExample.php index 1501770dcf..d7992aa830 100644 --- a/src/applications/uiexample/examples/PHUIButtonBarExample.php +++ b/src/applications/uiexample/examples/PHUIButtonBarExample.php @@ -36,7 +36,7 @@ final class PHUIButtonBarExample extends PhabricatorUIExample { foreach ($icons as $text => $icon) { $button = id(new PHUIButtonView()) ->setTag('a') - ->setColor(PHUIButtonView::SIMPLE) + ->setButtonType(PHUIButtonView::BUTTONTYPE_SIMPLE) ->setTitle($text) ->setText($text); @@ -47,7 +47,7 @@ final class PHUIButtonBarExample extends PhabricatorUIExample { foreach ($icons as $text => $icon) { $button = id(new PHUIButtonView()) ->setTag('a') - ->setColor(PHUIButtonView::SIMPLE) + ->setButtonType(PHUIButtonView::BUTTONTYPE_SIMPLE) ->setTitle($text) ->setTooltip($text) ->setIcon($icon); diff --git a/src/applications/uiexample/examples/PHUIButtonExample.php b/src/applications/uiexample/examples/PHUIButtonExample.php index 900125cbc2..aed5d5bf08 100644 --- a/src/applications/uiexample/examples/PHUIButtonExample.php +++ b/src/applications/uiexample/examples/PHUIButtonExample.php @@ -106,18 +106,61 @@ final class PHUIButtonExample extends PhabricatorUIExample { $column = array(); $icons = array( - 'Comment' => 'fa-comment', - 'Give Token' => 'fa-trophy', - 'Reverse Time' => 'fa-clock-o', - 'Implode Earth' => 'fa-exclamation-triangle red', + array( + 'text' => pht('Comment'), + 'icon' => 'fa-comment', + ), + array( + 'text' => pht('Give Token'), + 'icon' => 'fa-trophy', + ), + array( + 'text' => pht('Reverse Time'), + 'icon' => 'fa-clock-o', + ), + array( + 'text' => pht('Implode Earth'), + 'icon' => 'fa-exclamation-triangle', + ), + array( + 'icon' => 'fa-rocket', + ), + array( + 'icon' => 'fa-clipboard', + ), + array( + 'icon' => 'fa-upload', + ), + array( + 'icon' => 'fa-street-view', + ), + array( + 'text' => pht('Copy "Quack" to Clipboard'), + 'icon' => 'fa-clipboard', + 'copy' => pht('Quack'), + ), ); - foreach ($icons as $text => $icon) { - $column[] = id(new PHUIButtonView()) + foreach ($icons as $text => $spec) { + $button = id(new PHUIButtonView()) ->setTag('a') ->setColor(PHUIButtonView::GREY) - ->setIcon($icon) - ->setText($text) + ->setIcon(idx($spec, 'icon')) + ->setText(idx($spec, 'text')) ->addClass(PHUI::MARGIN_SMALL_RIGHT); + + $copy = idx($spec, 'copy'); + if ($copy !== null) { + Javelin::initBehavior('phabricator-clipboard-copy'); + + $button->addClass('clipboard-copy'); + $button->addSigil('clipboard-copy'); + $button->setMetadata( + array( + 'text' => $copy, + )); + } + + $column[] = $button; } $layout3 = id(new AphrontMultiColumnView()) @@ -129,18 +172,22 @@ final class PHUIButtonExample extends PhabricatorUIExample { 'Subscribe' => 'fa-check-circle bluegrey', 'Edit' => 'fa-pencil bluegrey', ); - $colors = array( - PHUIButtonView::SIMPLE, + $designs = array( + PHUIButtonView::BUTTONTYPE_SIMPLE, ); + $colors = array('', 'red', 'green', 'yellow'); $column = array(); - foreach ($colors as $color) { - foreach ($icons as $text => $icon) { - $column[] = id(new PHUIButtonView()) - ->setTag('a') - ->setColor($color) - ->setIcon($icon) - ->setText($text) - ->addClass(PHUI::MARGIN_SMALL_RIGHT); + foreach ($designs as $design) { + foreach ($colors as $color) { + foreach ($icons as $text => $icon) { + $column[] = id(new PHUIButtonView()) + ->setTag('a') + ->setButtonType($design) + ->setColor($color) + ->setIcon($icon) + ->setText($text) + ->addClass(PHUI::MARGIN_SMALL_RIGHT); + } } } diff --git a/src/applications/uiexample/examples/PHUIColorPalletteExample.php b/src/applications/uiexample/examples/PHUIColorPalletteExample.php index 0e3193d24d..a8910b930d 100644 --- a/src/applications/uiexample/examples/PHUIColorPalletteExample.php +++ b/src/applications/uiexample/examples/PHUIColorPalletteExample.php @@ -10,6 +10,10 @@ final class PHUIColorPalletteExample extends PhabricatorUIExample { return pht('A Standard Palette of Colors for use.'); } + public function getCategory() { + return pht('Catalogs'); + } + public function renderExample() { $colors = array( diff --git a/src/applications/uiexample/examples/PHUIFeedStoryExample.php b/src/applications/uiexample/examples/PHUIFeedStoryExample.php index 375c6e42fd..0df757cada 100644 --- a/src/applications/uiexample/examples/PHUIFeedStoryExample.php +++ b/src/applications/uiexample/examples/PHUIFeedStoryExample.php @@ -8,7 +8,11 @@ final class PHUIFeedStoryExample extends PhabricatorUIExample { public function getDescription() { return pht( - 'An outlandish exaggeration of intricate tales from around the realm'); + 'An outlandish exaggeration of intricate tales from around the realm.'); + } + + public function getCategory() { + return pht('Single Use'); } public function renderExample() { diff --git a/src/applications/uiexample/examples/PHUIHovercardUIExample.php b/src/applications/uiexample/examples/PHUIHovercardUIExample.php index 8673441d09..8cdec56d68 100644 --- a/src/applications/uiexample/examples/PHUIHovercardUIExample.php +++ b/src/applications/uiexample/examples/PHUIHovercardUIExample.php @@ -12,6 +12,10 @@ final class PHUIHovercardUIExample extends PhabricatorUIExample { phutil_tag('tt', array(), 'PHUIHovercardView')); } + public function getCategory() { + return pht('Single Use'); + } + public function renderExample() { $request = $this->getRequest(); $user = $request->getUser(); diff --git a/src/applications/uiexample/examples/PHUIIconExample.php b/src/applications/uiexample/examples/PHUIIconExample.php index 1bf96ce8e5..35395fbff0 100644 --- a/src/applications/uiexample/examples/PHUIIconExample.php +++ b/src/applications/uiexample/examples/PHUIIconExample.php @@ -10,6 +10,10 @@ final class PHUIIconExample extends PhabricatorUIExample { return pht('Easily render icons or images with links and sprites.'); } + public function getCategory() { + return pht('Catalogs'); + } + private function listTransforms() { return array( 'ph-rotate-90', diff --git a/src/applications/uiexample/examples/PHUIXComponentsExample.php b/src/applications/uiexample/examples/PHUIXComponentsExample.php new file mode 100644 index 0000000000..0b6d90f28f --- /dev/null +++ b/src/applications/uiexample/examples/PHUIXComponentsExample.php @@ -0,0 +1,139 @@ + 'fa-rocket', + ), + array( + 'icon' => 'fa-cloud', + 'color' => 'indigo', + ), + ); + + foreach ($icons as $spec) { + $icon = new PHUIIconView(); + + $icon->setIcon(idx($spec, 'icon'), idx($spec, 'color')); + + $client_id = celerity_generate_unique_node_id(); + + $server_view = $icon; + $client_view = javelin_tag( + 'div', + array( + 'id' => $client_id, + )); + + Javelin::initBehavior( + 'phuix-example', + array( + 'type' => 'icon', + 'id' => $client_id, + 'spec' => $spec, + )); + + $content[] = id(new AphrontMultiColumnView()) + ->addColumn($server_view) + ->addColumn($client_view); + } + + + $buttons = array( + array( + 'text' => pht('Submit'), + ), + array( + 'text' => pht('Activate'), + 'icon' => 'fa-rocket', + ), + array( + 'type' => PHUIButtonView::BUTTONTYPE_SIMPLE, + 'text' => pht('3 / 5 Comments'), + 'icon' => 'fa-comment', + ), + array( + 'color' => PHUIButtonView::GREEN, + 'text' => pht('Environmental!'), + ), + array( + 'icon' => 'fa-cog', + ), + array( + 'icon' => 'fa-cog', + 'type' => PHUIButtonView::BUTTONTYPE_SIMPLE, + ), + array( + 'text' => array('2 + 2', ' ', '=', ' ', '4'), + ), + array( + 'color' => PHUIButtonView::GREY, + 'text' => pht('Cancel'), + ), + array( + 'text' => array(''), + ), + ); + + foreach ($buttons as $spec) { + $button = new PHUIButtonView(); + + if (idx($spec, 'text') !== null) { + $button->setText($spec['text']); + } + + if (idx($spec, 'icon') !== null) { + $button->setIcon($spec['icon']); + } + + if (idx($spec, 'type') !== null) { + $button->setButtonType($spec['type']); + } + + if (idx($spec, 'color') !== null) { + $button->setColor($spec['color']); + } + + $client_id = celerity_generate_unique_node_id(); + + $server_view = $button; + $client_view = javelin_tag( + 'div', + array( + 'id' => $client_id, + )); + + Javelin::initBehavior( + 'phuix-example', + array( + 'type' => 'button', + 'id' => $client_id, + 'spec' => $spec, + )); + + $content[] = id(new AphrontMultiColumnView()) + ->addColumn($server_view) + ->addColumn($client_view); + } + + return id(new PHUIBoxView()) + ->appendChild($content) + ->addMargin(PHUI::MARGIN_LARGE); + } +} diff --git a/src/applications/uiexample/examples/PhabricatorBarePageUIExample.php b/src/applications/uiexample/examples/PhabricatorBarePageUIExample.php deleted file mode 100644 index 3f377edadd..0000000000 --- a/src/applications/uiexample/examples/PhabricatorBarePageUIExample.php +++ /dev/null @@ -1,25 +0,0 @@ -appendChild( - phutil_tag( - 'h1', - array(), - $this->getDescription())); - - $response = new AphrontWebpageResponse(); - $response->setContent($view->render()); - return $response; - } -} diff --git a/src/applications/uiexample/examples/PhabricatorBusyUIExample.php b/src/applications/uiexample/examples/PhabricatorBusyUIExample.php deleted file mode 100644 index ab23e5cf79..0000000000 --- a/src/applications/uiexample/examples/PhabricatorBusyUIExample.php +++ /dev/null @@ -1,17 +0,0 @@ -getRequest(); $viewer = $request->getUser(); diff --git a/src/applications/uiexample/examples/PhabricatorGestureUIExample.php b/src/applications/uiexample/examples/PhabricatorGestureUIExample.php index 1adb7dee16..2adfb795b2 100644 --- a/src/applications/uiexample/examples/PhabricatorGestureUIExample.php +++ b/src/applications/uiexample/examples/PhabricatorGestureUIExample.php @@ -14,6 +14,10 @@ final class PhabricatorGestureUIExample extends PhabricatorUIExample { phutil_tag('tt', array(), 'touchable')); } + public function getCategory() { + return pht('Technical'); + } + public function renderExample() { $id = celerity_generate_unique_node_id(); diff --git a/src/applications/uiexample/examples/PhabricatorListFilterUIExample.php b/src/applications/uiexample/examples/PhabricatorListFilterUIExample.php deleted file mode 100644 index 6c934a3e47..0000000000 --- a/src/applications/uiexample/examples/PhabricatorListFilterUIExample.php +++ /dev/null @@ -1,35 +0,0 @@ -setUser($this->getRequest()->getUser()); - $form - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Query'))) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Search'))); - - $filter->appendChild($form); - - - return $filter; - } -} diff --git a/src/applications/uiexample/examples/PhabricatorNotificationUIExample.php b/src/applications/uiexample/examples/PhabricatorNotificationUIExample.php index 1b825693fa..5d15d884cc 100644 --- a/src/applications/uiexample/examples/PhabricatorNotificationUIExample.php +++ b/src/applications/uiexample/examples/PhabricatorNotificationUIExample.php @@ -12,6 +12,10 @@ final class PhabricatorNotificationUIExample extends PhabricatorUIExample { phutil_tag('tt', array(), 'JX.Notification')); } + public function getCategory() { + return pht('Technical'); + } + public function renderExample() { require_celerity_resource('phabricator-notification-css'); Javelin::initBehavior('phabricator-notification-example'); diff --git a/src/applications/uiexample/examples/PhabricatorRemarkupUIExample.php b/src/applications/uiexample/examples/PhabricatorRemarkupUIExample.php index 46f0262bbb..5044821d82 100644 --- a/src/applications/uiexample/examples/PhabricatorRemarkupUIExample.php +++ b/src/applications/uiexample/examples/PhabricatorRemarkupUIExample.php @@ -11,6 +11,10 @@ final class PhabricatorRemarkupUIExample extends PhabricatorUIExample { 'Demonstrates the visual appearance of various Remarkup elements.'); } + public function getCategory() { + return pht('Technical'); + } + public function renderExample() { $viewer = $this->getRequest()->getUser(); diff --git a/src/applications/uiexample/examples/PhabricatorSetupIssueUIExample.php b/src/applications/uiexample/examples/PhabricatorSetupIssueUIExample.php index e5370d30cf..d6386e59d4 100644 --- a/src/applications/uiexample/examples/PhabricatorSetupIssueUIExample.php +++ b/src/applications/uiexample/examples/PhabricatorSetupIssueUIExample.php @@ -10,6 +10,10 @@ final class PhabricatorSetupIssueUIExample extends PhabricatorUIExample { return pht('Setup errors and warnings.'); } + public function getCategory() { + return pht('Single Use'); + } + public function renderExample() { $request = $this->getRequest(); $user = $request->getUser(); diff --git a/src/applications/uiexample/examples/PhabricatorSortTableUIExample.php b/src/applications/uiexample/examples/PhabricatorSortTableUIExample.php deleted file mode 100644 index 113c38b92a..0000000000 --- a/src/applications/uiexample/examples/PhabricatorSortTableUIExample.php +++ /dev/null @@ -1,96 +0,0 @@ - 'Honda', - 'model' => 'Civic', - 'year' => 2004, - 'price' => 3199, - 'color' => pht('Blue'), - ), - array( - 'make' => 'Ford', - 'model' => 'Focus', - 'year' => 2001, - 'price' => 2549, - 'color' => pht('Red'), - ), - array( - 'make' => 'Toyota', - 'model' => 'Camry', - 'year' => 2009, - 'price' => 4299, - 'color' => pht('Black'), - ), - array( - 'make' => 'NASA', - 'model' => 'Shuttle', - 'year' => 1998, - 'price' => 1000000000, - 'color' => pht('White'), - ), - ); - - $request = $this->getRequest(); - - $orders = array( - 'make', - 'model', - 'year', - 'price', - ); - - $sort = $request->getStr('sort'); - list($sort, $reverse) = AphrontTableView::parseSort($sort); - if (!in_array($sort, $orders)) { - $sort = 'make'; - } - - $rows = isort($rows, $sort); - if ($reverse) { - $rows = array_reverse($rows); - } - - $table = new AphrontTableView($rows); - $table->setHeaders( - array( - pht('Make'), - pht('Model'), - pht('Year'), - pht('Price'), - pht('Color'), - )); - $table->setColumnClasses( - array( - '', - 'wide', - 'n', - 'n', - '', - )); - $table->makeSortable( - $request->getRequestURI(), - 'sort', - $sort, - $reverse, - $orders); - - $panel = new PHUIObjectBoxView(); - $panel->setHeaderText(pht('Sortable Table of Vehicles')); - $panel->setTable($table); - - return $panel; - } -} diff --git a/src/applications/uiexample/examples/PhabricatorTooltipUIExample.php b/src/applications/uiexample/examples/PhabricatorTooltipUIExample.php deleted file mode 100644 index 373161612a..0000000000 --- a/src/applications/uiexample/examples/PhabricatorTooltipUIExample.php +++ /dev/null @@ -1,102 +0,0 @@ - array( - 'tip' => 'Hi', - ), - 'lorem (north)' => array( - 'tip' => $lorem, - ), - 'lorem (east)' => array( - 'tip' => $lorem, - 'align' => 'E', - ), - 'lorem (south)' => array( - 'tip' => $lorem, - 'align' => 'S', - ), - 'lorem (west)' => array( - 'tip' => $lorem, - 'align' => 'W', - ), - 'lorem (large, north)' => array( - 'tip' => $lorem, - 'size' => 300, - ), - 'lorem (large, east)' => array( - 'tip' => $lorem, - 'size' => 300, - 'align' => 'E', - ), - 'lorem (large, west)' => array( - 'tip' => $lorem, - 'size' => 300, - 'align' => 'W', - ), - 'lorem (large, south)' => array( - 'tip' => $lorem, - 'size' => 300, - 'align' => 'S', - ), - 'overflow (north)' => array( - 'tip' => $overflow, - ), - 'overflow (east)' => array( - 'tip' => $overflow, - 'align' => 'E', - ), - 'overflow (south)' => array( - 'tip' => $overflow, - 'align' => 'S', - ), - 'overflow (west)' => array( - 'tip' => $overflow, - 'align' => 'W', - ), - ); - - $content = array(); - foreach ($metas as $key => $meta) { - $content[] = javelin_tag( - 'div', - array( - 'sigil' => 'has-tooltip', - 'meta' => $meta, - 'style' => $style, - ), - $key); - } - - return $content; - } -} diff --git a/src/applications/uiexample/examples/PhabricatorUIExample.php b/src/applications/uiexample/examples/PhabricatorUIExample.php index 5326cba3fe..7c84f6c9e4 100644 --- a/src/applications/uiexample/examples/PhabricatorUIExample.php +++ b/src/applications/uiexample/examples/PhabricatorUIExample.php @@ -17,6 +17,10 @@ abstract class PhabricatorUIExample extends Phobject { abstract public function getDescription(); abstract public function renderExample(); + public function getCategory() { + return pht('General'); + } + protected function createBasicDummyHandle($name, $type, $fullname = null, $uri = null) { diff --git a/src/view/form/control/AphrontFormPolicyControl.php b/src/view/form/control/AphrontFormPolicyControl.php index 2ce13491af..6f343bf00e 100644 --- a/src/view/form/control/AphrontFormPolicyControl.php +++ b/src/view/form/control/AphrontFormPolicyControl.php @@ -324,7 +324,7 @@ final class AphrontFormPolicyControl extends AphrontFormControl { javelin_tag( 'a', array( - 'class' => 'grey button dropdown has-icon policy-control', + 'class' => 'grey button dropdown has-icon has-text policy-control', 'href' => '#', 'mustcapture' => true, 'sigil' => 'policy-control', diff --git a/src/view/phui/PHUIButtonBarView.php b/src/view/phui/PHUIButtonBarView.php index add91bbc8f..78443929d8 100644 --- a/src/view/phui/PHUIButtonBarView.php +++ b/src/view/phui/PHUIButtonBarView.php @@ -29,7 +29,7 @@ final class PHUIButtonBarView extends AphrontTagView { } protected function getTagContent() { - require_celerity_resource('phui-button-css'); + require_celerity_resource('phui-button-bar-css'); $i = 1; $j = count($this->buttons); diff --git a/src/view/phui/PHUIButtonView.php b/src/view/phui/PHUIButtonView.php index ac3f8aabe0..2e269bd9af 100644 --- a/src/view/phui/PHUIButtonView.php +++ b/src/view/phui/PHUIButtonView.php @@ -5,11 +5,13 @@ final class PHUIButtonView extends AphrontTagView { const GREEN = 'green'; const GREY = 'grey'; const DISABLED = 'disabled'; - const SIMPLE = 'simple'; const SMALL = 'small'; const BIG = 'big'; + const BUTTONTYPE_DEFAULT = 'buttontype.default'; + const BUTTONTYPE_SIMPLE = 'buttontype.simple'; + private $size; private $text; private $subtext; @@ -25,6 +27,7 @@ final class PHUIButtonView extends AphrontTagView { private $tooltip; private $noCSS; private $hasCaret; + private $buttonType = self::BUTTONTYPE_DEFAULT; public function setName($name) { $this->name = $name; @@ -103,6 +106,15 @@ final class PHUIButtonView extends AphrontTagView { return $this->hasCaret; } + public function setButtonType($button_type) { + $this->buttonType = $button_type; + return $this; + } + + public function getButtonType() { + return $this->buttonType; + } + public function setIcon($icon, $first = true) { if (!($icon instanceof PHUIIconView)) { $icon = id(new PHUIIconView()) @@ -141,6 +153,7 @@ final class PHUIButtonView extends AphrontTagView { protected function getTagAttributes() { require_celerity_resource('phui-button-css'); + require_celerity_resource('phui-button-simple-css'); $classes = array(); $classes[] = 'button'; @@ -161,6 +174,10 @@ final class PHUIButtonView extends AphrontTagView { $classes[] = 'has-icon'; } + if ($this->text !== null) { + $classes[] = 'has-text'; + } + if ($this->iconFirst == false) { $classes[] = 'icon-last'; } @@ -169,6 +186,15 @@ final class PHUIButtonView extends AphrontTagView { $classes[] = 'disabled'; } + switch ($this->getButtonType()) { + case self::BUTTONTYPE_DEFAULT: + // Nothing special for default buttons. + break; + case self::BUTTONTYPE_SIMPLE: + $classes[] = 'simple'; + break; + } + $sigil = null; $meta = null; if ($this->tooltip) { @@ -204,10 +230,24 @@ final class PHUIButtonView extends AphrontTagView { $subtext = null; if ($this->subtext) { $subtext = phutil_tag( - 'div', array('class' => 'phui-button-subtext'), $this->subtext); + 'div', + array( + 'class' => 'phui-button-subtext', + ), + $this->subtext); + } + + if ($this->text !== null) { + $text = phutil_tag( + 'div', + array( + 'class' => 'phui-button-text', + ), + array( + $text, + $subtext, + )); } - $text = phutil_tag( - 'div', array('class' => 'phui-button-text'), array($text, $subtext)); } $caret = null; diff --git a/src/view/phui/PHUIDocumentViewPro.php b/src/view/phui/PHUIDocumentViewPro.php index d60cd78d44..51ecc3526e 100644 --- a/src/view/phui/PHUIDocumentViewPro.php +++ b/src/view/phui/PHUIDocumentViewPro.php @@ -79,7 +79,7 @@ final class PHUIDocumentViewPro extends AphrontTagView { $toc[] = id(new PHUIButtonView()) ->setTag('a') ->setIcon('fa-align-left') - ->setColor(PHUIButtonView::SIMPLE) + ->setButtonType(PHUIButtonView::BUTTONTYPE_SIMPLE) ->addClass('phui-document-toc') ->addSigil('jx-toggle-class') ->setMetaData(array( diff --git a/webroot/rsrc/css/aphront/tokenizer.css b/webroot/rsrc/css/aphront/tokenizer.css index 7116ce3ace..3f3f904e22 100644 --- a/webroot/rsrc/css/aphront/tokenizer.css +++ b/webroot/rsrc/css/aphront/tokenizer.css @@ -181,4 +181,5 @@ a.jx-tokenizer-token-invalid:hover { .button.tokenizer-browse-button .phui-icon-view { top: 7px; left: 9px; + position: absolute; } diff --git a/webroot/rsrc/css/application/base/main-menu-view.css b/webroot/rsrc/css/application/base/main-menu-view.css index eb5cfad8c7..4de30fb8ce 100644 --- a/webroot/rsrc/css/application/base/main-menu-view.css +++ b/webroot/rsrc/css/application/base/main-menu-view.css @@ -247,6 +247,7 @@ a.phabricator-core-user-menu .caret:before { font-size: 15px; top: 4px; left: 8px; + position: absolute; } .phabricator-main-menu-search-dropdown .caret { diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css index 6a29e1269b..f1be2b5f99 100644 --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -409,36 +409,7 @@ tr.differential-inline-loading { color: {$greytext}; } -.scroll-objective-list { - position: fixed; - right: 0; - width: 24px; - top: 48px; - bottom: 48px; - background: rgba(255, 255, 255, 0.50); - border-style: solid; - border-color: rgba(255, 255, 255, 0.95); - border-width: 1px 0 1px 1px; - box-shadow: -1px 0 2px rgba(255, 255, 255, 0.10); - overflow: hidden; -} - -.scroll-objective-list.has-aesthetic-scrollbar { - /* For now, hide this element on systems with aesthetic scrollbars. */ - display: none; -} - -.scroll-objective { - display: block; - position: absolute; - box-sizing: border-box; - cursor: pointer; - text-align: middle; - left: 7px; -} - -.scroll-objective .phui-icon-view { - text-shadow: 1px 1px 4px rgba(0, 0, 0, 0.25); - display: block; - height: 14px; +.diff-banner-has-unsaved, +.diff-banner-has-unsubmitted { + background: {$sh-yellowbackground}; } diff --git a/webroot/rsrc/css/application/diffusion/diffusion-history.css b/webroot/rsrc/css/application/diffusion/diffusion-history.css index b340cd913d..fd22f9b1a5 100644 --- a/webroot/rsrc/css/application/diffusion/diffusion-history.css +++ b/webroot/rsrc/css/application/diffusion/diffusion-history.css @@ -3,7 +3,7 @@ */ .diffusion-history-list .phui-oi-link { - color: {$darkbluetext}; + color: #000; font-size: {$biggerfontsize}; } @@ -11,6 +11,10 @@ border-color: transparent; } +.diffusion-history-list .phui-oi-attribute .phui-tag-indigo a { + color: {$indigo}; +} + .diffusion-history-message { background-color: {$bluebackground}; padding: 16px; @@ -18,3 +22,21 @@ border-radius: 5px; color: {$darkbluetext}; } + +.diffusion-history-list .phui-oi-attribute { + font-size: {$smallerfontsize}; + letter-spacing: 0.01em; +} + +.diffusion-history-author-name a { + color: {$darkbluetext}; +} + +.diffusion-history-list .diffusion-differential-tag { + margin-left: 4px; +} + +a.phui-tag-view:hover.diffusion-differential-tag .phui-tag-core { + border-color: transparent; + text-decoration: underline; +} diff --git a/webroot/rsrc/css/core/core.css b/webroot/rsrc/css/core/core.css index 01c4414454..d7145bb485 100644 --- a/webroot/rsrc/css/core/core.css +++ b/webroot/rsrc/css/core/core.css @@ -173,3 +173,16 @@ hr { height: 2px; background: {$sky}; } + +.clipboard-copy { + visibility: hidden; +} + +.supports-clipboard .clipboard-copy { + visibility: visible; +} + +.clipboard-buffer { + position: absolute; + left: -9999px; +} diff --git a/webroot/rsrc/css/core/z-index.css b/webroot/rsrc/css/core/z-index.css index 04b013386d..75cd394988 100644 --- a/webroot/rsrc/css/core/z-index.css +++ b/webroot/rsrc/css/core/z-index.css @@ -97,10 +97,6 @@ div.phui-calendar-day-event { z-index: 6; } -.scroll-objective-list { - z-index: 6; -} - .conpherence-durable-column { z-index: 7; } diff --git a/webroot/rsrc/css/phui/button/phui-button-bar.css b/webroot/rsrc/css/phui/button/phui-button-bar.css new file mode 100644 index 0000000000..17de25e808 --- /dev/null +++ b/webroot/rsrc/css/phui/button/phui-button-bar.css @@ -0,0 +1,61 @@ +/** + * @provides phui-button-bar-css + * @requires phui-button-css + * @requires phui-button-simple-css + */ + +.phui-button-bar-borderless .button { + border: 0; + background-color: transparent; + background-image: none; + padding-left: 10px; + padding-right: 10px; +} + +.phui-button-bar-borderless .button .phui-icon-view { + font-size: 15px; + color: rgba({$alphagrey},.4); +} + +.phui-button-bar-borderless .button:hover { + background-color: transparent; + background-image: none; + border-radius: 3px; +} + +.phui-button-bar-borderless .button:hover .phui-icon-view { + color: rgba({$alphagrey},.9); +} + +.phui-button-bar-borderless .button { + border: 0; +} + +.phui-button-bar .phui-button-bar-first { + border-top-right-radius: 0px; + border-bottom-right-radius: 0px; +} + +.phui-button-bar .phui-button-bar-middle { + border-radius: 0; + border-left: none; +} + +.phui-button-bar .phui-button-bar-last { + border-left: none; + border-top-left-radius: 0px; + border-bottom-left-radius: 0px; +} + +.phui-button-bar .button.simple:hover { + border-color: {$lightblueborder}; + background-color: #fff; + background-image: none; + color: {$sky}; +} + +.phui-button-bar .button.simple:hover .phui-icon-view { + border-color: {$lightblueborder}; + color: {$sky}; + background-image: none; +} diff --git a/webroot/rsrc/css/phui/button/phui-button-simple.css b/webroot/rsrc/css/phui/button/phui-button-simple.css new file mode 100644 index 0000000000..fa0a8d11b2 --- /dev/null +++ b/webroot/rsrc/css/phui/button/phui-button-simple.css @@ -0,0 +1,131 @@ +/** + * @provides phui-button-simple-css + * @requires phui-button-css + */ + + +/* - Basic -------------------------------------------------------------------*/ + +button.simple, +input[type="submit"].simple, +a.simple, +a.simple:visited { + background: #fff; + color: {$bluetext}; + border: 1px solid {$lightblueborder}; +} + +button.simple .phui-icon-view, +input[type="submit"].simple .phui-icon-view, +a.simple .phui-icon-view, +a.simple:visited .phui-icon-view { + color: {$lightbluetext}; +} + +a.button.simple:hover, +button.simple:hover { + border-color: {$blueborder}; + background-image: none; + background-color: #fff; + transition: 0s; +} + +a.simple.current { + background: {$lightblue}; +} + + +/* - Red --------------------------------------------------------------------*/ + +button.simple.red, +input[type="submit"].simple.red, +a.simple.red, +a.simple.red:visited { + background: {$sh-redbackground}; + color: {$redtext}; + border: 1px solid {$sh-redborder}; +} + +button.simple.red .phui-icon-view, +input[type="submit"].simple.red .phui-icon-view, +a.simple.red .phui-icon-view, +a.simple.red:visited .phui-icon-view { + color: {$redtext}; +} + +a.button.simple.red:hover, +button.simple.red:hover { + border-color: {$sh-redtext}; + background-image: none; + background-color: {$sh-redbackground}; + transition: 0s; +} + +/* - Green ------------------------------------------------------------------*/ + +button.simple.green, +input[type="submit"].simple.green, +a.simple.green, +a.simple.green:visited { + background: {$sh-greenbackground}; + color: {$greentext}; + border: 1px solid {$sh-greenborder}; +} + +button.simple.green .phui-icon-view, +input[type="submit"].simple.green .phui-icon-view, +a.simple.green .phui-icon-view, +a.simple.green:visited .phui-icon-view { + color: {$greentext}; +} + +a.button.simple.green:hover, +button.simple.green:hover { + border-color: {$sh-greentext}; + background-image: none; + background-color: {$sh-greenbackground}; + transition: 0s; +} + +/* - Yellow -----------------------------------------------------------------*/ + +button.simple.yellow, +input[type="submit"].simple.yellow, +a.simple.yellow, +a.simple.yellow:visited { + background-color: {$sh-yellowbackground}; + color: {$sh-yellowtext}; + border: 1px solid {$sh-yellowborder}; +} + +button.simple.yellow .phui-icon-view, +input[type="submit"].simple.yellow .phui-icon-view, +a.simple.yellow .phui-icon-view, +a.simple.yellow:visited .phui-icon-view { + color: {$sh-yellowicon}; +} + +a.button.simple.yellow:hover, +button.simple.yellow:hover { + border-color: {$sh-yellowtext}; + background-image: none; + background-color: {$sh-yellowbackground}; + transition: 0s; +} + + +/* - Misc -------------------------------------------------------------------*/ + +a.button.simple .phui-icon-view { + border: none; +} + +a.button.simple.phuix-dropdown-open { + background-color: #fff; + color: {$blue}; + box-shadow: none; +} + +a.button.simple.phuix-dropdown-open:hover .phui-icon-view { + color: {$blue}; +} diff --git a/webroot/rsrc/css/phui/phui-button.css b/webroot/rsrc/css/phui/button/phui-button.css similarity index 69% rename from webroot/rsrc/css/phui/phui-button.css rename to webroot/rsrc/css/phui/button/phui-button.css index e9d446a0ac..d8183febd2 100644 --- a/webroot/rsrc/css/phui/phui-button.css +++ b/webroot/rsrc/css/phui/button/phui-button.css @@ -41,7 +41,7 @@ input[type="submit"] { font-weight: bold; font-size: {$normalfontsize}; display: inline-block; - padding: 4px 16px 5px; + padding: 4px 14px 5px; text-align: center; white-space: nowrap; border-radius: 3px; @@ -69,8 +69,8 @@ a.icon:visited { } button.green, -a.green, -a.green:visited { +a.green.button, +a.green.button:visited { background-color: {$green}; border-color: {$green}; background-image: linear-gradient(to bottom, #23BB5B, #139543); @@ -86,26 +86,6 @@ a.grey:visited { color: {$darkgreytext}; } -button.simple, -input[type="submit"].simple, -a.simple, -a.simple:visited { - background: #fff; - color: {$bluetext}; - border: 1px solid {$lightblueborder}; -} - -a.simple.current { - background: {$lightblue}; -} - -button.simple .phui-icon-view, -input[type="submit"].simple .phui-icon-view, -a.simple .phui-icon-view, -a.simple:visited .phui-icon-view { - color: {$lightbluetext}; -} - a.disabled, button.disabled, button[disabled] { @@ -143,34 +123,6 @@ button.green:hover { transition: 0.1s; } -a.button.simple:hover, -button.simple:hover { - background-color: {$lightblue}; - background-image: linear-gradient(to bottom, {$blue}, {$blue}); - color: #fff; - transition: 0s; -} - -a.button.simple:hover .phui-icon-view, -button.simple:hover .phui-icon-view { - color: #fff; - transition: 0.1s; -} - -a.button.simple .phui-icon-view { - border: none; -} - -a.button.simple.phuix-dropdown-open { - background-color: #fff; - color: {$blue}; - box-shadow: none; -} - -a.button.simple.phuix-dropdown-open:hover .phui-icon-view { - color: {$blue}; -} - body a.button.disabled:hover, body button.disabled:hover, body a.button.disabled:active, @@ -301,7 +253,7 @@ a.policy-control .phui-button-text { position: relative; } -.button .phui-icon-view { +.button.has-text .phui-icon-view { display: inline-block; position: absolute; top: 7px; @@ -313,10 +265,6 @@ a.policy-control .phui-button-text { right: 10px; } -.phui-button-bar .button .phui-icon-view { - left: 14px; -} - .button.has-icon .phui-button-text { margin-left: 16px; } @@ -345,66 +293,3 @@ a.policy-control .phui-button-text { font-weight: normal; } -/* PHUI Button Bar */ - -.phui-button-bar-borderless .button { - border: 0; - background-color: transparent; - background-image: none; - padding-left: 10px; - padding-right: 10px; -} - -.phui-button-bar-borderless .button .phui-icon-view { - font-size: 15px; - color: rgba({$alphagrey},.4); -} - -.phui-button-bar-borderless .button:hover { - background-color: transparent; - background-image: none; - border-radius: 3px; -} - -.phui-button-bar-borderless .button:hover .phui-icon-view { - color: rgba({$alphagrey},.9); -} - -.phui-button-bar-borderless .button { - border: 0; -} - -.phui-button-bar a.button.has-icon { - display: inline-block; - height: 18px; - width: 6px; -} - -.phui-button-bar .phui-button-bar-first { - border-top-right-radius: 0px; - border-bottom-right-radius: 0px; -} - -.phui-button-bar .phui-button-bar-middle { - border-radius: 0; - border-left: none; -} - -.phui-button-bar .phui-button-bar-last { - border-left: none; - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; -} - -.phui-button-bar .button.simple:hover { - border-color: {$lightblueborder}; - background-color: #fff; - background-image: none; - color: {$sky}; -} - -.phui-button-bar .button.simple:hover .phui-icon-view { - border-color: {$lightblueborder}; - color: {$sky}; - background-image: none; -} diff --git a/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css b/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css index 788b0e73ec..f6d1589a9a 100644 --- a/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css +++ b/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css @@ -182,6 +182,10 @@ ul.phui-oi-list-view { vertical-align: top; } +.phui-oi-col2.phui-oi-side-column { + width: 200px; +} + .device-phone .phui-oi-col1, .device-phone .phui-oi-col2 { display: block; diff --git a/webroot/rsrc/css/phui/phui-icon.css b/webroot/rsrc/css/phui/phui-icon.css index 627b7632fe..5fd7fd97bc 100644 --- a/webroot/rsrc/css/phui/phui-icon.css +++ b/webroot/rsrc/css/phui/phui-icon.css @@ -144,7 +144,7 @@ a.phui-icon-circle.hover-red:hover .phui-icon-view { color: {$red}; } -a.phui-icon-circle .phui-icon-view.phui-icon-circle-state-icon { +.phui-icon-circle .phui-icon-view.phui-icon-circle-state-icon { position: absolute; width: 14px; height: 14px; diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index cd7f888a40..b5ebe4ce70 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -32,7 +32,6 @@ JX.install('DiffChangeset', { this._rightID = data.right; this._displayPath = JX.$H(data.displayPath); - this._objectiveName = data.objectiveName; this._icon = data.icon; this._inlines = []; @@ -62,8 +61,6 @@ JX.install('DiffChangeset', { _displayPath: null, _changesetList: null, - _objective: null, - _objectiveName: null, _icon: null, getLeftChangesetID: function() { @@ -76,23 +73,9 @@ JX.install('DiffChangeset', { setChangesetList: function(list) { this._changesetList = list; - - var objectives = list.getObjectives(); - this._objective = objectives.newObjective() - .setAnchor(this._node); - - this._updateObjective(); - return this; }, - _updateObjective: function() { - this._objective - .setIcon(this.getIcon()) - .setColor(this.getColor()) - .setTooltip(this.getObjectiveName()); - }, - getIcon: function() { if (!this._visible) { return 'fa-file-o'; @@ -109,10 +92,6 @@ JX.install('DiffChangeset', { return 'blue'; }, - getObjectiveName: function() { - return this._objectiveName; - }, - getChangesetList: function() { return this._changesetList; }, @@ -501,12 +480,17 @@ JX.install('DiffChangeset', { // diff with a large number of changes don't constantly have the text // area scrolled off the bottom of the screen until the entire diff loads. // - // There are two three major cases here: + // There are several major cases here: // // - If we're near the top of the document, never scroll. - // - If we're near the bottom of the document, always scroll. - // - Otherwise, scroll if the changes were above the midline of the - // viewport. + // - If we're near the bottom of the document, always scroll, unless + // we have an anchor. + // - Otherwise, scroll if the changes were above (or, at least, + // almost entirely above) the viewport. + // + // We don't scroll if the changes were just near the top of the viewport + // because this makes us scroll incorrectly when an anchored change is + // visible. See T12779. var target = this._node; @@ -529,17 +513,39 @@ JX.install('DiffChangeset', { var target_pos = JX.Vector.getPos(target); var target_dim = JX.Vector.getDim(target); - var target_mid = (target_pos.y + (target_dim.y / 2)); + var target_bot = (target_pos.y + target_dim.y); - var view_mid = (old_pos.y + (old_view.y / 2)); - var above_mid = (target_mid < view_mid); + // Detect if the changeset is entirely (or, at least, almost entirely) + // above us. The height here is roughly the height of the persistent + // banner. + var above_screen = (target_bot < old_pos.y + 64); + + // If we have a URL anchor and are currently nearby, stick to it + // no matter what. + var on_target = null; + if (window.location.hash) { + try { + var anchor = JX.$(window.location.hash.replace('#', '')); + if (anchor) { + var anchor_pos = JX.$V(anchor); + if ((anchor_pos.y > old_pos.y) && + (anchor_pos.y < old_pos.y + 96)) { + on_target = anchor; + } + } + } catch (ignored) { + // If we have a bogus anchor, just ignore it. + } + } var frame = this._getContentFrame(); JX.DOM.setContent(frame, JX.$H(response.changeset)); if (this._stabilize) { - if (!near_top) { - if (near_bot || above_mid) { + if (on_target) { + JX.DOM.scrollToPosition(old_pos.x, JX.$V(on_target).y - 60); + } else if (!near_top) { + if (near_bot || above_screen) { // Figure out how much taller the document got. var delta = (JX.Vector.getDocument().y - old_dim.y); JX.DOM.scrollToPosition(old_pos.x, old_pos.y + delta); @@ -570,7 +576,6 @@ JX.install('DiffChangeset', { JX.Stratcom.invoke('differential-inline-comment-refresh'); - this._objective.show(); this._rebuildAllInlines(); JX.Stratcom.invoke('resize'); @@ -695,6 +700,11 @@ JX.install('DiffChangeset', { return null; }, + getInlines: function() { + this._rebuildAllInlines(); + return this._inlines; + }, + _rebuildAllInlines: function() { var rows = JX.DOM.scry(this._node, 'tr'); for (var ii = 0; ii < rows.length; ii++) { @@ -723,11 +733,6 @@ JX.install('DiffChangeset', { JX.DOM.appendContent(diff.parentNode, undo); } - this._updateObjective(); - for (var ii = 0; ii < this._inlines.length; ii++) { - this._inlines[ii].updateObjective(); - } - JX.Stratcom.invoke('resize'); }, diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index c344c96de8..b79f05296e 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -1,7 +1,6 @@ /** * @provides phabricator-diff-changeset-list * @requires javelin-install - * phabricator-scroll-objective-list * @javelin */ @@ -9,7 +8,6 @@ JX.install('DiffChangesetList', { construct: function() { this._changesets = []; - this._objectives = new JX.ScrollObjectiveList(); var onload = JX.bind(this, this._ifawake, this._onload); JX.Stratcom.listen('click', 'differential-load', onload); @@ -70,7 +68,7 @@ JX.install('DiffChangesetList', { var onrangedown = JX.bind(this, this._ifawake, this._onrangedown); JX.Stratcom.listen( - ['touchstart', 'mousedown'], + 'mousedown', ['differential-changeset', 'tag:th'], onrangedown); @@ -80,15 +78,9 @@ JX.install('DiffChangesetList', { ['differential-changeset', 'tag:th'], onrangemove); - var onrangetouchmove = JX.bind(this, this._ifawake, this._onrangetouchmove); - JX.Stratcom.listen( - 'touchmove', - null, - onrangetouchmove); - var onrangeup = JX.bind(this, this._ifawake, this._onrangeup); JX.Stratcom.listen( - ['touchend', 'mouseup'], + 'mouseup', null, onrangeup); }, @@ -102,7 +94,6 @@ JX.install('DiffChangesetList', { _initialized: false, _asleep: true, _changesets: null, - _objectives: null, _cursorItem: null, @@ -120,7 +111,6 @@ JX.install('DiffChangesetList', { _rangeTarget: null, _bannerNode: null, - _showObjectives: false, sleep: function() { this._asleep = true; @@ -128,8 +118,6 @@ JX.install('DiffChangesetList', { this._redrawFocus(); this._redrawSelection(); this.resetHover(); - - this._objectives.hide(); }, wake: function() { @@ -138,10 +126,6 @@ JX.install('DiffChangesetList', { this._redrawFocus(); this._redrawSelection(); - if (this._showObjectives) { - this._objectives.show(); - } - if (this._initialized) { return; } @@ -198,19 +182,10 @@ JX.install('DiffChangesetList', { this._installKey('q', label, this._onkeyhide); }, - setShowObjectives: function(show) { - this._showObjectives = show; - return this; - }, - isAsleep: function() { return this._asleep; }, - getObjectives: function() { - return this._objectives; - }, - newChangesetForNode: function(node) { var changeset = JX.DiffChangeset.getForNode(node); @@ -538,24 +513,9 @@ JX.install('DiffChangesetList', { }, _setSelectionState: function(item, manager) { - // If we had an inline selected before, we need to update it after - // changing our selection to clear the selected state. Then, update the - // new one to add the selected state. - var old_inline = this.getSelectedInline(); - this._cursorItem = item; this._redrawSelection(manager, true); - var new_inline = this.getSelectedInline(); - - if (old_inline) { - old_inline.updateObjective(); - } - - if (new_inline) { - new_inline.updateObjective(); - } - return this; }, @@ -858,6 +818,11 @@ JX.install('DiffChangesetList', { this._redrawSelection(); this._redrawHover(); + // Force a banner redraw after a resize event. Particularly, this makes + // sure the inline state updates immediately after an inline edit + // operation, even if the changeset itself has not changed. + this._bannerChangeset = null; + this._redrawBanner(); }, @@ -1181,8 +1146,8 @@ JX.install('DiffChangesetList', { }, _onrangedown: function(e) { - // NOTE: We're allowing touch events through, including "touchstart". We - // need to kill the "touchstart" event so the page doesn't scroll. + // NOTE: We're allowing "mousedown" from a touch event through so users + // can leave inlines on a single line. if (e.isRightButton()) { return; } @@ -1272,31 +1237,6 @@ JX.install('DiffChangesetList', { this._setHoverRange(this._rangeOrigin, this._rangeTarget); }, - _onrangetouchmove: function(e) { - if (!this._rangeActive) { - return; - } - - // NOTE: The target of a "touchmove" event is bogus. Use dark magic to - // identify the actual target. Some day, this might move into the core - // libraries. If this doesn't work, just bail. - - var target; - try { - var raw_event = e.getRawEvent(); - var touch = raw_event.touches[0]; - target = document.elementFromPoint(touch.clientX, touch.clientY); - } catch (ex) { - return; - } - - if (!JX.DOM.isType(target, 'th')) { - return; - } - - this._updateRange(target, false); - }, - _onrangeup: function(e) { if (!this._rangeActive) { return; @@ -1343,6 +1283,43 @@ JX.install('DiffChangesetList', { return; } + var changesets = this._changesets; + var unsaved = []; + var unsubmitted = []; + var undone = []; + var all = []; + + for (var ii = 0; ii < changesets.length; ii++) { + var inlines = changesets[ii].getInlines(); + for (var jj = 0; jj < inlines.length; jj++) { + var inline = inlines[jj]; + + if (inline.isDeleted()) { + continue; + } + + all.push(inline); + + if (inline.isEditing()) { + unsaved.push(inline); + } else if (inline.isDraft()) { + unsubmitted.push(inline); + } else if (!inline.isDone()) { + undone.push(inline); + } + } + } + + JX.DOM.alterClass( + node, + 'diff-banner-has-unsaved', + !!unsaved.length); + + JX.DOM.alterClass( + node, + 'diff-banner-has-unsubmitted', + !!unsubmitted.length); + var icon = new JX.PHUIXIconView() .setIcon(changeset.getIcon()) .getNode(); diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js index bfc2a3e258..6d897c27ae 100644 --- a/webroot/rsrc/js/application/diff/DiffInline.js +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -29,7 +29,6 @@ JX.install('DiffInline', { _isLoading: false, _changeset: null, - _objective: null, _isDraft: null, _isFixed: null, @@ -38,7 +37,6 @@ JX.install('DiffInline', { bindToRow: function(row) { this._row = row; - this._objective.setAnchor(this._row); var row_data = JX.Stratcom.getData(row); row_data.inline = this; @@ -80,11 +78,25 @@ JX.install('DiffInline', { this.setInvisible(false); - this.updateObjective(); - return this; }, + isDraft: function() { + return this._isDraft; + }, + + isDone: function() { + return this._isFixed; + }, + + isEditing: function() { + return this._isEditing; + }, + + isDeleted: function() { + return this._isDeleted; + }, + bindToRange: function(data) { this._displaySide = data.displaySide; this._number = parseInt(data.number, 10); @@ -171,14 +183,6 @@ JX.install('DiffInline', { setChangeset: function(changeset) { this._changeset = changeset; - - var objectives = changeset.getChangesetList().getObjectives(); - - // Create this inline's objective, but don't show it yet. - this._objective = objectives.newObjective() - .setCallback(JX.bind(this, this._onobjective)) - .hide(); - return this; }, @@ -188,84 +192,9 @@ JX.install('DiffInline', { setEditing: function(editing) { this._isEditing = editing; - this.updateObjective(); return this; }, - _onobjective: function() { - this.getChangeset().getChangesetList().selectInline(this); - }, - - updateObjective: function() { - var objective = this._objective; - - if (this.isHidden() || this._isDeleted) { - objective.hide(); - return; - } - - // If this is a new comment which we aren't editing, don't show anything: - // the use started a comment or reply, then cancelled it. - if (this._isNew && !this._isEditing) { - objective.hide(); - return; - } - - var changeset = this.getChangeset(); - if (!changeset.isVisible()) { - objective.hide(); - return; - } - - var pht = changeset.getChangesetList().getTranslations(); - - var icon = 'fa-comment'; - var color = 'bluegrey'; - var tooltip = this._snippet; - var anchor = this._row; - var should_stack = false; - - if (this._isEditing) { - icon = 'fa-star'; - color = 'pink'; - tooltip = pht('Editing Comment'); - - // If we're editing, anchor to the row with the editor instead of the - // actual comment row (which is invisible and can have a misleading - // position). - anchor = this._row.nextSibling; - } else if (this._isDraft) { - // This inline is an unsubmitted draft. - icon = 'fa-pencil'; - color = 'indigo'; - } else if (this._isFixed) { - // This inline has been marked done. - icon = 'fa-check'; - color = 'grey'; - } else if (this._isGhost) { - icon = 'fa-comment-o'; - color = 'grey'; - } else if (this._replyToCommentPHID) { - icon = 'fa-reply'; - should_stack = true; - } - - if (changeset.getChangesetList().getSelectedInline() === this) { - // TODO: Maybe add some other kind of effect here, since we're only - // using color to show this? - color = 'yellow'; - } - - - objective - .setAnchor(anchor) - .setIcon(icon) - .setColor(color) - .setTooltip(tooltip) - .setShouldStack(should_stack) - .show(); - }, - canReply: function() { if (!this._hasAction('reply')) { return false; @@ -316,7 +245,6 @@ JX.install('DiffInline', { JX.Stratcom.getData(row).inline = this; this._row = row; - this._objective.setAnchor(this._row); this._id = null; this._phid = null; @@ -759,8 +687,6 @@ JX.install('DiffInline', { this.getChangeset().getChangesetList().redrawPreview(); } - this.updateObjective(); - this.getChangeset().getChangesetList().redrawCursor(); this.getChangeset().getChangesetList().resetHover(); diff --git a/webroot/rsrc/js/application/diff/ScrollObjective.js b/webroot/rsrc/js/application/diff/ScrollObjective.js deleted file mode 100644 index 1e596bd816..0000000000 --- a/webroot/rsrc/js/application/diff/ScrollObjective.js +++ /dev/null @@ -1,141 +0,0 @@ -/** - * @provides phabricator-scroll-objective - * @requires javelin-dom - * javelin-util - * javelin-stratcom - * javelin-install - * javelin-workflow - * @javelin - */ - - -JX.install('ScrollObjective', { - - construct : function() { - var node = this.getNode(); - - var onclick = JX.bind(this, this._onclick); - JX.DOM.listen(node, 'click', null, onclick); - }, - - members: { - _list: null, - - _node: null, - _anchor: null, - - _visible: false, - _callback: false, - _stack: false, - - getNode: function() { - if (!this._node) { - var attributes = { - className: 'scroll-objective' - }; - - var content = this._getIconObject().getNode(); - - var node = JX.$N('div', attributes, content); - - this._node = node; - } - - return this._node; - }, - - setCallback: function(callback) { - this._callback = callback; - return this; - }, - - setObjectiveList: function(list) { - this._list = list; - return this; - }, - - _getIconObject: function() { - if (!this._iconObject) { - this._iconObject = new JX.PHUIXIconView(); - } - return this._iconObject; - }, - - _onclick: function(e) { - (this._callback && this._callback(e)); - - if (e.getPrevented()) { - return; - } - - e.kill(); - - // This is magic to account for the banner, and should probably be made - // less hard-coded. - var buffer = 48; - - JX.DOM.scrollToPosition(null, JX.$V(this.getAnchor()).y - buffer); - }, - - setAnchor: function(node) { - this._anchor = node; - return this; - }, - - getAnchor: function() { - return this._anchor; - }, - - setIcon: function(icon) { - this._getIconObject().setIcon(icon); - return this; - }, - - setColor: function(color) { - this._getIconObject().setColor(color); - return this; - }, - - setTooltip: function(tip) { - var node = this._getIconObject().getNode(); - JX.Stratcom.addSigil(node, 'has-tooltip'); - JX.Stratcom.getData(node).tip = tip; - JX.Stratcom.getData(node).align = 'W'; - JX.Stratcom.getData(node).size = 'auto'; - return this; - }, - - - /** - * Should this objective always stack immediately under the previous - * objective? - * - * This allows related objectives (like "comment, reply, reply") to be - * rendered in a tight sequence. - */ - setShouldStack: function(stack) { - this._stack = stack; - return this; - }, - - shouldStack: function() { - return this._stack; - }, - - show: function() { - this._visible = true; - return this; - }, - - hide: function() { - this._visible = false; - return this; - }, - - isVisible: function() { - return this._visible; - } - - } - -}); diff --git a/webroot/rsrc/js/application/diff/ScrollObjectiveList.js b/webroot/rsrc/js/application/diff/ScrollObjectiveList.js deleted file mode 100644 index ce47877d1a..0000000000 --- a/webroot/rsrc/js/application/diff/ScrollObjectiveList.js +++ /dev/null @@ -1,150 +0,0 @@ -/** - * @provides phabricator-scroll-objective-list - * @requires javelin-dom - * javelin-util - * javelin-stratcom - * javelin-install - * javelin-workflow - * javelin-scrollbar - * phabricator-scroll-objective - * @javelin - */ - - -JX.install('ScrollObjectiveList', { - - construct : function() { - this._objectives = []; - - var onresize = JX.bind(this, this._dirty); - JX.Stratcom.listen('resize', null, onresize); - }, - - members: { - _objectives: null, - _visible: false, - _trigger: null, - - newObjective: function() { - var objective = new JX.ScrollObjective() - .setObjectiveList(this); - - this._objectives.push(objective); - this._getNode().appendChild(objective.getNode()); - - this._dirty(); - - return objective; - }, - - show: function() { - this._visible = true; - this._dirty(); - return this; - }, - - hide: function() { - this._visible = false; - this._dirty(); - return this; - }, - - _getNode: function() { - if (!this._node) { - var node = new JX.$N('div', {className: 'scroll-objective-list'}); - this._node = node; - } - return this._node; - }, - - _dirty: function() { - if (this._trigger !== null) { - return; - } - - this._trigger = setTimeout(JX.bind(this, this._redraw), 0); - }, - - _redraw: function() { - this._trigger = null; - - var node = this._getNode(); - - var is_visible = - (this._visible) && - (JX.Device.getDevice() == 'desktop') && - (this._objectives.length); - - if (!is_visible) { - JX.DOM.remove(node); - return; - } - - document.body.appendChild(node); - - // If we're on OSX without a mouse or some other system with zero-width - // trackpad-style scrollbars, adjust the display appropriately. - var aesthetic = (JX.Scrollbar.getScrollbarControlWidth() === 0); - JX.DOM.alterClass(node, 'has-aesthetic-scrollbar', aesthetic); - - var d = JX.Vector.getDocument(); - - var list_dimensions = JX.Vector.getDim(node); - var icon_height = 16; - var list_y = (list_dimensions.y - icon_height); - - var ii; - var offset; - - // First, build a list of all the items we're going to show. - var items = []; - for (ii = 0; ii < this._objectives.length; ii++) { - var objective = this._objectives[ii]; - var objective_node = objective.getNode(); - - var anchor = objective.getAnchor(); - if (!anchor || !objective.isVisible()) { - JX.DOM.remove(objective_node); - continue; - } - - offset = (JX.$V(anchor).y / d.y) * (list_y); - - items.push({ - offset: offset, - node: objective_node, - objective: objective - }); - } - - // Now, sort it from top to bottom. - items.sort(function(u, v) { - return u.offset - v.offset; - }); - - // Lay out the items in the objective list, leaving a minimum amount - // of space between them so they do not overlap. - var min = null; - for (ii = 0; ii < items.length; ii++) { - var item = items[ii]; - - offset = item.offset; - - if (min !== null) { - if (item.objective.shouldStack()) { - offset = min; - } else { - offset = Math.max(offset, min); - } - } - min = offset + 15; - - item.node.style.top = offset + 'px'; - node.appendChild(item.node); - } - - } - - } - -}); diff --git a/webroot/rsrc/js/application/differential/behavior-populate.js b/webroot/rsrc/js/application/differential/behavior-populate.js index b1b5bd415c..e5b0d5039d 100644 --- a/webroot/rsrc/js/application/differential/behavior-populate.js +++ b/webroot/rsrc/js/application/differential/behavior-populate.js @@ -60,8 +60,7 @@ JX.behavior('differential-populate', function(config, statics) { var changeset_list = new JX.DiffChangesetList() .setTranslations(JX.phtize(config.pht)) - .setInlineURI(config.inlineURI) - .setShowObjectives(config.showObjectives); + .setInlineURI(config.inlineURI); // Install and activate the current page. var page_id = JX.Quicksand.getCurrentPageID(); diff --git a/webroot/rsrc/js/application/uiexample/JavelinViewExample.js b/webroot/rsrc/js/application/uiexample/JavelinViewExample.js deleted file mode 100644 index a8e17923b2..0000000000 --- a/webroot/rsrc/js/application/uiexample/JavelinViewExample.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @provides phabricator-uiexample-javelin-view - * @requires javelin-install - * javelin-dom - * javelin-view - */ - -JX.install('JavelinViewExample', { - extend: 'View', - members: { - render: function(rendered_children) { - return JX.$N( - 'div', - { className: 'client-view' }, - rendered_children - ); - } - } -}); diff --git a/webroot/rsrc/js/application/uiexample/ReactorButtonExample.js b/webroot/rsrc/js/application/uiexample/ReactorButtonExample.js deleted file mode 100644 index 41982f192f..0000000000 --- a/webroot/rsrc/js/application/uiexample/ReactorButtonExample.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @provides phabricator-uiexample-reactor-button - * @requires javelin-install - * javelin-dom - * javelin-util - * javelin-dynval - * javelin-reactor-dom - */ - -JX.install('ReactorButtonExample', { - extend: 'View', - members: { - render: function() { - var button = JX.$N('button', {}, 'Fun'); - var clicks = JX.RDOM.clickPulses(button); - - var time = JX.RDOM.time(); - - // function snapshot(pulses, dynval) { - // return new DynVal( - // pulses.transform(JX.bind(dynval, dynval.getValueNow)), - // dynval.getValueNow() - // ); - // } - // - // Below could be... - // time.snapshot(clicks) - // clicks.snapshot(time) - - var snapshot_time = new JX.DynVal( - clicks.transform(JX.bind(time, time.getValueNow)), - time.getValueNow() - ); - - return [button, JX.RDOM.$DT(snapshot_time)]; - } - } -}); diff --git a/webroot/rsrc/js/application/uiexample/ReactorCheckboxExample.js b/webroot/rsrc/js/application/uiexample/ReactorCheckboxExample.js deleted file mode 100644 index 983f6d96ab..0000000000 --- a/webroot/rsrc/js/application/uiexample/ReactorCheckboxExample.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @provides phabricator-uiexample-reactor-checkbox - * @requires javelin-install - * javelin-dom - * javelin-reactor-dom - */ - -JX.install('ReactorCheckboxExample', { - extend: 'View', - members: { - render: function() { - var checkbox = JX.$N('input', {type: 'checkbox'}); - - return [checkbox, JX.RDOM.$DT(JX.RDOM.checkbox(checkbox))]; - } - } -}); diff --git a/webroot/rsrc/js/application/uiexample/ReactorFocusExample.js b/webroot/rsrc/js/application/uiexample/ReactorFocusExample.js deleted file mode 100644 index f4c53b2865..0000000000 --- a/webroot/rsrc/js/application/uiexample/ReactorFocusExample.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @provides phabricator-uiexample-reactor-focus - * @requires javelin-install - * javelin-dom - * javelin-reactor-dom - */ - -JX.install('ReactorFocusExample', { - extend: 'View', - members: { - render: function() { - var input = JX.$N('input'); - return [input, JX.RDOM.$DT(JX.RDOM.hasFocus(input))]; - } - } -}); diff --git a/webroot/rsrc/js/application/uiexample/ReactorInputExample.js b/webroot/rsrc/js/application/uiexample/ReactorInputExample.js deleted file mode 100644 index 70f4c9fb70..0000000000 --- a/webroot/rsrc/js/application/uiexample/ReactorInputExample.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @provides phabricator-uiexample-reactor-input - * @requires javelin-install - * javelin-reactor-dom - * javelin-view-html - * javelin-view-interpreter - * javelin-view-renderer - */ - -JX.install('ReactorInputExample', { - extend: 'View', - members: { - render: function() { - var html = JX.HTMLView.registerToInterpreter(new JX.ViewInterpreter()); - - var raw_input = JX.ViewRenderer.render( - html.input({ value: this.getAttr('init') }) - ); - var input = JX.RDOM.input(raw_input); - - return JX.ViewRenderer.render( - html.div( - raw_input, - html.br(), - html.span(JX.RDOM.$DT(input)), - html.br(), - html.span(JX.RDOM.$DT(input.calm(500))) - ) - ); - } - } -}); diff --git a/webroot/rsrc/js/application/uiexample/ReactorMouseoverExample.js b/webroot/rsrc/js/application/uiexample/ReactorMouseoverExample.js deleted file mode 100644 index 5f9656e2f3..0000000000 --- a/webroot/rsrc/js/application/uiexample/ReactorMouseoverExample.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @provides phabricator-uiexample-reactor-mouseover - * @requires javelin-install - * javelin-dom - * javelin-reactor-dom - */ - -JX.install('ReactorMouseoverExample', { - extend: 'View', - members: { - render: function() { - var target = JX.$N('span', 'mouseover me '); - return [target, JX.RDOM.$DT(JX.RDOM.isMouseOver(target))]; - } - } -}); diff --git a/webroot/rsrc/js/application/uiexample/ReactorRadioExample.js b/webroot/rsrc/js/application/uiexample/ReactorRadioExample.js deleted file mode 100644 index 3e99e0e674..0000000000 --- a/webroot/rsrc/js/application/uiexample/ReactorRadioExample.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @provides phabricator-uiexample-reactor-radio - * @requires javelin-install - * javelin-dom - * javelin-reactor-dom - */ - -JX.install('ReactorRadioExample', { - extend: 'View', - members: { - render: function() { - var radio_one = JX.$N('input', {type: 'radio', name: 'n', value: 'one'}); - var radio_two = JX.$N('input', {type: 'radio', name: 'n', value: 'two'}); - - radio_one.checked = true; - - return [ - radio_one, - radio_two, - JX.RDOM.$DT(JX.RDOM.radio([radio_one, radio_two])) - ]; - } - } -}); diff --git a/webroot/rsrc/js/application/uiexample/ReactorSelectExample.js b/webroot/rsrc/js/application/uiexample/ReactorSelectExample.js deleted file mode 100644 index 628fbed122..0000000000 --- a/webroot/rsrc/js/application/uiexample/ReactorSelectExample.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @provides phabricator-uiexample-reactor-select - * @requires javelin-install - * javelin-dom - * javelin-reactor-dom - */ - -JX.install('ReactorSelectExample', { - extend: 'View', - members: { - render: function() { - var select = JX.$N('select', {}, [ - JX.$N('option', { value: 'goat' }, 'Goat'), - JX.$N('option', { value: 'bat' }, 'Bat'), - JX.$N('option', { value: 'duck' }, 'Duck') - ]); - - return [select, JX.RDOM.$DT(JX.RDOM.select(select))]; - } - } -}); diff --git a/webroot/rsrc/js/application/uiexample/ReactorSendClassExample.js b/webroot/rsrc/js/application/uiexample/ReactorSendClassExample.js deleted file mode 100644 index 62e44b7d0c..0000000000 --- a/webroot/rsrc/js/application/uiexample/ReactorSendClassExample.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @provides phabricator-uiexample-reactor-sendclass - * @requires javelin-install - * javelin-dom - * javelin-reactor-dom - */ - -JX.install('ReactorSendClassExample', { - extend: 'View', - members: { - render: function() { - var input = JX.$N('input', { type: 'checkbox' }); - var span = JX.$N('a', 'Hey'); - JX.RDOM.sendClass(JX.RDOM.checkbox(input), span, 'disabled'); - return [input, span]; - } - } -}); diff --git a/webroot/rsrc/js/application/uiexample/ReactorSendPropertiesExample.js b/webroot/rsrc/js/application/uiexample/ReactorSendPropertiesExample.js deleted file mode 100644 index f297e413dc..0000000000 --- a/webroot/rsrc/js/application/uiexample/ReactorSendPropertiesExample.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @provides phabricator-uiexample-reactor-sendproperties - * @requires javelin-install - * javelin-dom - * javelin-reactor-dom - */ - -JX.install('ReactorSendPropertiesExample', { - extend: 'View', - members: { - render: function() { - var color = JX.$N('input', {value: '#fff000'}); - var title = JX.$N('input', {value: 'seen on hover'}); - var target = JX.$N('span', 'Change my color and title'); - - JX.RDOM.sendProps(target, { - style: { - backgroundColor: JX.RDOM.input(color) - }, - title: JX.RDOM.input(title) - }); - - return [color, title, target]; - } - } -}); diff --git a/webroot/rsrc/js/application/uiexample/busy-example.js b/webroot/rsrc/js/application/uiexample/busy-example.js deleted file mode 100644 index 64ee390112..0000000000 --- a/webroot/rsrc/js/application/uiexample/busy-example.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @provides javelin-behavior-phabricator-busy-example - * @requires phabricator-busy - * javelin-behavior - */ - -JX.behavior('phabricator-busy-example', function() { - JX.Busy.start(); -}); diff --git a/webroot/rsrc/js/core/behavior-copy.js b/webroot/rsrc/js/core/behavior-copy.js new file mode 100644 index 0000000000..4457f6e022 --- /dev/null +++ b/webroot/rsrc/js/core/behavior-copy.js @@ -0,0 +1,43 @@ +/** + * @provides javelin-behavior-phabricator-clipboard-copy + * @requires javelin-behavior + * javelin-dom + * javelin-stratcom + * @javelin + */ + +JX.behavior('phabricator-clipboard-copy', function() { + + if (!document.queryCommandSupported) { + return; + } + + if (!document.queryCommandSupported('copy')) { + return; + } + + JX.DOM.alterClass(document.body, 'supports-clipboard', true); + + JX.Stratcom.listen('click', 'clipboard-copy', function(e) { + e.kill(); + + var data = e.getNodeData('clipboard-copy'); + var attr = { + value: data.text || '', + className: 'clipboard-buffer' + }; + + var node = JX.$N('textarea', attr); + document.body.appendChild(node); + + try { + node.select(); + document.execCommand('copy'); + } catch (ignored) { + // Ignore any errors we hit. + } + + JX.DOM.remove(node); + }); + +}); diff --git a/webroot/rsrc/js/phuix/PHUIXButtonView.js b/webroot/rsrc/js/phuix/PHUIXButtonView.js new file mode 100644 index 0000000000..67b59f24f1 --- /dev/null +++ b/webroot/rsrc/js/phuix/PHUIXButtonView.js @@ -0,0 +1,110 @@ +/** + * @provides phuix-button-view + * @requires javelin-install + * javelin-dom + */ +JX.install('PHUIXButtonView', { + + statics: { + BUTTONTYPE_DEFAULT: 'buttontype.default', + BUTTONTYPE_SIMPLE: 'buttontype.simple' + }, + + members: { + _node: null, + _textNode: null, + + _iconView: null, + _color: null, + _buttonType: null, + + setIcon: function(icon) { + this.getIconView().setIcon(icon); + return this; + }, + + getIconView: function() { + if (!this._iconView) { + this._iconView = new JX.PHUIXIconView(); + this._redraw(); + } + return this._iconView; + }, + + setColor: function(color) { + var node = this.getNode(); + + if (this._color) { + JX.DOM.alterClass(node, this._color, false); + } + this._color = color; + JX.DOM.alterClass(node, this._color, true); + + return this; + }, + + setButtonType: function(button_type) { + var self = JX.PHUIXButtonView; + + this._buttonType = button_type; + var node = this.getNode(); + + var is_simple = (this._buttonType == self.BUTTONTYPE_SIMPLE); + JX.DOM.alterClass(node, 'simple', is_simple); + + return this; + }, + + setText: function(text) { + JX.DOM.setContent(this._getTextNode(), text); + return this; + }, + + getNode: function() { + if (!this._node) { + var attrs = { + className: 'button' + }; + + this._node = JX.$N('button', attrs); + + this._redraw(); + } + + return this._node; + }, + + _getTextNode: function() { + if (!this._textNode) { + var attrs = { + className: 'phui-button-text' + }; + + this._textNode = JX.$N('div', attrs); + } + + return this._textNode; + }, + + _redraw: function() { + var node = this.getNode(); + + var icon = this._iconView; + var text = this._textNode; + + var content = []; + if (icon) { + content.push(icon.getNode()); + } + + if (text) { + content.push(text); + } + + JX.DOM.alterClass(node, 'has-icon', !!icon); + JX.DOM.alterClass(node, 'has-text', !!text); + JX.DOM.setContent(node, content); + } + } + +}); diff --git a/webroot/rsrc/js/phuix/PHUIXExample.js b/webroot/rsrc/js/phuix/PHUIXExample.js new file mode 100644 index 0000000000..87f36b04c3 --- /dev/null +++ b/webroot/rsrc/js/phuix/PHUIXExample.js @@ -0,0 +1,65 @@ +/** + * @provides javelin-behavior-phuix-example + * @requires javelin-install + * javelin-dom + * phuix-button-view + */ + +JX.behavior('phuix-example', function(config) { + var node; + var spec; + var defaults; + + switch (config.type) { + case 'button': + var button = new JX.PHUIXButtonView(); + defaults = { + text: null, + icon: null, + type: null, + color: null + }; + + spec = JX.copy(defaults, config.spec); + + if (spec.text !== null) { + button.setText(spec.text); + } + + if (spec.icon !== null) { + button.setIcon(spec.icon); + } + + if (spec.type !== null) { + button.setButtonType(spec.type); + } + + if (spec.color !== null) { + button.setColor(spec.color); + } + + node = button.getNode(); + break; + case 'icon': + var icon = new JX.PHUIXIconView(); + defaults = { + icon: null, + color: null + }; + + spec = JX.copy(defaults, config.spec); + + if (spec.icon !== null) { + icon.setIcon(spec.icon); + } + + if (spec.color !== null) { + icon.setColor(spec.color); + } + + node = icon.getNode(); + break; + } + + JX.DOM.setContent(JX.$(config.id), node); +});