From f76c3bbc22e0ae009a623041dd80ef3c594f19b2 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 31 Mar 2011 17:06:33 -0700 Subject: [PATCH] AphrontPagerView and UIExamples. --- src/__celerity_resource_map__.php | 54 ++++-- src/__phutil_library_map__.php | 9 + ...AphrontDefaultApplicationConfiguration.php | 6 + .../base/PhabricatorUIExampleController.php | 34 ++++ .../uiexample/controller/base/__init__.php | 15 ++ .../PhabricatorUIExampleRenderController.php | 91 ++++++++++ .../uiexample/controller/render/__init__.php | 18 ++ .../examples/base/PhabricatorUIExample.php | 36 ++++ .../uiexample/examples/base/__init__.php | 10 + .../pager/PhabricatorUIPagerExample.php | 94 ++++++++++ .../uiexample/examples/pager/__init__.php | 15 ++ src/view/control/pager/AphrontPagerView.php | 171 ++++++++++++++++++ src/view/control/pager/__init__.php | 15 ++ webroot/rsrc/css/aphront/pager-view.css | 18 ++ .../css/application/uiexample/example.css | 22 +++ 15 files changed, 590 insertions(+), 18 deletions(-) create mode 100644 src/applications/uiexample/controller/base/PhabricatorUIExampleController.php create mode 100644 src/applications/uiexample/controller/base/__init__.php create mode 100644 src/applications/uiexample/controller/render/PhabricatorUIExampleRenderController.php create mode 100644 src/applications/uiexample/controller/render/__init__.php create mode 100644 src/applications/uiexample/examples/base/PhabricatorUIExample.php create mode 100644 src/applications/uiexample/examples/base/__init__.php create mode 100644 src/applications/uiexample/examples/pager/PhabricatorUIPagerExample.php create mode 100644 src/applications/uiexample/examples/pager/__init__.php create mode 100644 src/view/control/pager/AphrontPagerView.php create mode 100644 src/view/control/pager/__init__.php create mode 100644 webroot/rsrc/css/aphront/pager-view.css create mode 100644 webroot/rsrc/css/application/uiexample/example.css diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 8f0f5e7ef9..14574b38e4 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -61,6 +61,15 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/css/aphront/headsup-action-list-view.css', ), + 'aphront-pager-view-css' => + array( + 'uri' => '/res/73ec8cd5/rsrc/css/aphront/pager-view.css', + 'type' => 'css', + 'requires' => + array( + ), + 'disk' => '/rsrc/css/aphront/pager-view.css', + ), 'aphront-panel-view-css' => array( 'uri' => '/res/63672373/rsrc/css/aphront/panel-view.css', @@ -90,7 +99,7 @@ celerity_register_resource_map(array( ), 'aphront-table-view-css' => array( - 'uri' => '/res/7bf17fb8/rsrc/css/aphront/table-view.css', + 'uri' => '/res/03724b05/rsrc/css/aphront/table-view.css', 'type' => 'css', 'requires' => array( @@ -288,6 +297,15 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/css/application/people/profile.css', ), + 'phabricator-ui-example-css' => + array( + 'uri' => '/res/b2593f53/rsrc/css/application/uiexample/example.css', + 'type' => 'css', + 'requires' => + array( + ), + 'disk' => '/rsrc/css/application/uiexample/example.css', + ), 'phabricator-core-buttons-css' => array( 'uri' => '/res/53b4f712/rsrc/css/core/buttons.css', @@ -455,7 +473,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-diffusion-pull-lastmodified' => array( - 'uri' => '/res/1c95a58e/rsrc/js/application/diffusion/behavior-pull-lastmodified.js', + 'uri' => '/res/6a5e7374/rsrc/js/application/diffusion/behavior-pull-lastmodified.js', 'type' => 'js', 'requires' => array( @@ -571,7 +589,7 @@ celerity_register_resource_map(array( ), array ( 'packages' => array ( - 'aa9bbfd1' => + '930fd839' => array ( 'name' => 'core.pkg.css', 'symbols' => @@ -591,7 +609,7 @@ celerity_register_resource_map(array( 12 => 'phabricator-remarkup-css', 13 => 'syntax-highlighting-css', ), - 'uri' => '/res/pkg/aa9bbfd1/core.pkg.css', + 'uri' => '/res/pkg/930fd839/core.pkg.css', 'type' => 'css', ), '76f3c1f8' => @@ -638,20 +656,20 @@ celerity_register_resource_map(array( ), 'reverse' => array ( - 'phabricator-core-css' => 'aa9bbfd1', - 'phabricator-core-buttons-css' => 'aa9bbfd1', - 'phabricator-standard-page-view' => 'aa9bbfd1', - 'aphront-dialog-view-css' => 'aa9bbfd1', - 'aphront-form-view-css' => 'aa9bbfd1', - 'aphront-panel-view-css' => 'aa9bbfd1', - 'aphront-side-nav-view-css' => 'aa9bbfd1', - 'aphront-table-view-css' => 'aa9bbfd1', - 'aphront-crumbs-view-css' => 'aa9bbfd1', - 'aphront-tokenizer-control-css' => 'aa9bbfd1', - 'aphront-typeahead-control-css' => 'aa9bbfd1', - 'phabricator-directory-css' => 'aa9bbfd1', - 'phabricator-remarkup-css' => 'aa9bbfd1', - 'syntax-highlighting-css' => 'aa9bbfd1', + 'phabricator-core-css' => '930fd839', + 'phabricator-core-buttons-css' => '930fd839', + 'phabricator-standard-page-view' => '930fd839', + 'aphront-dialog-view-css' => '930fd839', + 'aphront-form-view-css' => '930fd839', + 'aphront-panel-view-css' => '930fd839', + 'aphront-side-nav-view-css' => '930fd839', + 'aphront-table-view-css' => '930fd839', + 'aphront-crumbs-view-css' => '930fd839', + 'aphront-tokenizer-control-css' => '930fd839', + 'aphront-typeahead-control-css' => '930fd839', + 'phabricator-directory-css' => '930fd839', + 'phabricator-remarkup-css' => '930fd839', + 'syntax-highlighting-css' => '930fd839', 'differential-core-view-css' => '76f3c1f8', 'differential-changeset-view-css' => '76f3c1f8', 'differential-revision-detail-css' => '76f3c1f8', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index e51e47883a..61ed5fef58 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -41,6 +41,7 @@ phutil_register_library_map(array( 'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql', 'AphrontNullView' => 'view/null', 'AphrontPageView' => 'view/page/base', + 'AphrontPagerView' => 'view/control/pager', 'AphrontPanelView' => 'view/layout/panel', 'AphrontQueryConnectionException' => 'storage/exception/connection', 'AphrontQueryConnectionLostException' => 'storage/exception/connectionlost', @@ -385,6 +386,10 @@ phutil_register_library_map(array( 'PhabricatorTimelineIterator' => 'infrastructure/daemon/timeline/cursor/iterator', 'PhabricatorTypeaheadCommonDatasourceController' => 'applications/typeahead/controller/common', 'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/base', + 'PhabricatorUIExample' => 'applications/uiexample/examples/base', + 'PhabricatorUIExampleController' => 'applications/uiexample/controller/base', + 'PhabricatorUIExampleRenderController' => 'applications/uiexample/controller/render', + 'PhabricatorUIPagerExample' => 'applications/uiexample/examples/pager', 'PhabricatorUser' => 'applications/people/storage/user', 'PhabricatorUserDAO' => 'applications/people/storage/base', 'PhabricatorUserOAuthInfo' => 'applications/people/storage/useroauthinfo', @@ -453,6 +458,7 @@ phutil_register_library_map(array( 'AphrontMySQLDatabaseConnection' => 'AphrontDatabaseConnection', 'AphrontNullView' => 'AphrontView', 'AphrontPageView' => 'AphrontView', + 'AphrontPagerView' => 'AphrontView', 'AphrontPanelView' => 'AphrontView', 'AphrontQueryConnectionException' => 'AphrontQueryException', 'AphrontQueryConnectionLostException' => 'AphrontQueryRecoverableException', @@ -714,6 +720,9 @@ phutil_register_library_map(array( 'PhabricatorTimelineEventData' => 'PhabricatorTimelineDAO', 'PhabricatorTypeaheadCommonDatasourceController' => 'PhabricatorTypeaheadDatasourceController', 'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController', + 'PhabricatorUIExampleController' => 'PhabricatorController', + 'PhabricatorUIExampleRenderController' => 'PhabricatorUIExampleController', + 'PhabricatorUIPagerExample' => 'PhabricatorUIExample', 'PhabricatorUser' => 'PhabricatorUserDAO', 'PhabricatorUserDAO' => 'PhabricatorLiskDAO', 'PhabricatorUserOAuthInfo' => 'PhabricatorUserDAO', diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php index fac46706f9..16665dd9cc 100755 --- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php +++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php @@ -245,6 +245,12 @@ class AphrontDefaultApplicationConfiguration '/preferences/' => array( '$' => 'PhabricatorEditPreferencesController' ), + + '/uiexample/' => array( + '$' => 'PhabricatorUIExampleRenderController', + 'view/(?P[^/]+)/$' => 'PhabricatorUIExampleRenderController', + ), + ); } diff --git a/src/applications/uiexample/controller/base/PhabricatorUIExampleController.php b/src/applications/uiexample/controller/base/PhabricatorUIExampleController.php new file mode 100644 index 0000000000..7e47ca9b75 --- /dev/null +++ b/src/applications/uiexample/controller/base/PhabricatorUIExampleController.php @@ -0,0 +1,34 @@ +buildStandardPageView(); + + $page->setApplicationName('UI Examples'); + $page->setBaseURI('/uiexample/'); + $page->setTitle(idx($data, 'title')); + $page->setGlyph("\xE2\x8F\x9A"); + $page->appendChild($view); + + $response = new AphrontWebpageResponse(); + return $response->setContent($page->render()); + } + +} diff --git a/src/applications/uiexample/controller/base/__init__.php b/src/applications/uiexample/controller/base/__init__.php new file mode 100644 index 0000000000..ef93f3f5a7 --- /dev/null +++ b/src/applications/uiexample/controller/base/__init__.php @@ -0,0 +1,15 @@ +class = idx($data, 'class'); + } + + public function processRequest() { + + $classes = id(new PhutilSymbolLoader()) + ->setAncestorClass('PhabricatorUIExample') + ->selectAndLoadSymbols(); + $classes = ipull($classes, 'name', 'name'); + + $selected = null; + foreach ($classes as $class => $ignored) { + $classes[$class] = newv($class, array()); + if ($this->class == $class) { + $selected = $class; + } + } + + if (!$selected) { + reset($classes); + $selected = key($classes); + } + + $nav = new AphrontSideNavView(); + + foreach ($classes as $class => $obj) { + $name = $obj->getName(); + + $nav->addNavItem( + phutil_render_tag( + 'a', + array( + 'href' => '/uiexample/view/'.$name.'/', + 'class' => ($selected == $class) + ? 'aphront-side-nav-selected' + : null, + ), + phutil_escape_html($obj->getName()))); + } + + require_celerity_resource('phabricator-ui-example-css'); + + $example = $classes[$selected]; + $example->setRequest($this->getRequest()); + + + $nav->appendChild( + '
'. + '

'. + phutil_escape_html($example->getName()). + ' ('.get_class($example).')'. + '

'. + '

' + .$example->getDescription(). + '

'. + '
'); + + $nav->appendChild($example->renderExample()); + + return $this->buildStandardPageResponse( + $nav, + array( + 'title' => 'UI Example', + )); + } + +} diff --git a/src/applications/uiexample/controller/render/__init__.php b/src/applications/uiexample/controller/render/__init__.php new file mode 100644 index 0000000000..740806ae10 --- /dev/null +++ b/src/applications/uiexample/controller/render/__init__.php @@ -0,0 +1,18 @@ +request = $request; + return $this; + } + + public function getRequest() { + return $this->request; + } + + abstract public function getName(); + abstract public function getDescription(); + abstract public function renderExample(); + +} diff --git a/src/applications/uiexample/examples/base/__init__.php b/src/applications/uiexample/examples/base/__init__.php new file mode 100644 index 0000000000..a1f5c22161 --- /dev/null +++ b/src/applications/uiexample/examples/base/__init__.php @@ -0,0 +1,10 @@ +AphrontPagerView to create a control which allows '. + 'users to paginate through large amounts of content.'; + } + + public function renderExample() { + + $request = $this->getRequest(); + + $offset = (int)$request->getInt('offset'); + $page_size = 20; + $item_count = 173; + + $rows = array(); + for ($ii = $offset; $ii < min($item_count, $offset + $page_size); $ii++) { + $rows[] = array( + 'Item #'.($ii + 1), + ); + } + + $table = new AphrontTableView($rows); + $table->setHeaders( + array( + 'Item', + )); + $panel = new AphrontPanelView(); + $panel->appendChild($table); + + $panel->appendChild( + '

'. + 'Use AphrontPagerView to render a pager element.'. + '

'); + + $pager = new AphrontPagerView(); + $pager->setPageSize($page_size); + $pager->setOffset($offset); + $pager->setCount($item_count); + $pager->setURI($request->getRequestURI(), 'offset'); + $panel->appendChild($pager); + + $panel->appendChild( + '

'. + 'You can show more or fewer pages of surrounding context.'. + '

'); + + $many_pages_pager = new AphrontPagerView(); + $many_pages_pager->setPageSize($page_size); + $many_pages_pager->setOffset($offset); + $many_pages_pager->setCount($item_count); + $many_pages_pager->setURI($request->getRequestURI(), 'offset'); + $many_pages_pager->setSurroundingPages(7); + $panel->appendChild($many_pages_pager); + + $panel->appendChild( + '

'. + 'When it is prohibitively expensive or complex to attain a complete '. + 'count of the items, you can select one extra item and set '. + 'hasMorePages(true) if it exists, creating an inexact pager.'. + '

'); + + $inexact_pager = new AphrontPagerView(); + $inexact_pager->setPageSize($page_size); + $inexact_pager->setOffset($offset); + $inexact_pager->setHasMorePages($offset < ($item_count - $page_size)); + $inexact_pager->setURI($request->getRequestURI(), 'offset'); + $panel->appendChild($inexact_pager); + + return $panel; + } +} diff --git a/src/applications/uiexample/examples/pager/__init__.php b/src/applications/uiexample/examples/pager/__init__.php new file mode 100644 index 0000000000..deaef516ed --- /dev/null +++ b/src/applications/uiexample/examples/pager/__init__.php @@ -0,0 +1,15 @@ +pageSize = max(1, $page_size); + return $this; + } + + final public function setOffset($offset) { + $this->offset = max(0, $offset); + return $this; + } + + final public function getOffset() { + return $this->offset; + } + + final public function getPageSize() { + return $this->pageSize; + } + + final public function setCount($count) { + $this->count = $count; + return $this; + } + + final public function setHasMorePages($has_more) { + $this->hasMorePages = $has_more; + return $this; + } + + final public function setURI(PhutilURI $uri, $paging_parameter) { + $this->uri = $uri; + $this->pagingParameter = $paging_parameter; + return $this; + } + + final public function setSurroundingPages($pages) { + $this->surroundingPages = max(0, $pages); + return $this; + } + + private function computeCount() { + if ($this->count !== null) { + return $this->count; + } + return $this->getOffset() + + $this->getPageSize() + + ($this->hasMorePages ? 1 : 0); + } + + private function isExactCountKnown() { + return $this->count !== null; + } + + public function render() { + + require_celerity_resource('aphront-pager-view-css'); + + $page = (int)floor($this->getOffset() / $this->getPageSize()); + $last = ((int)ceil($this->computeCount() / $this->getPageSize())) - 1; + $near = $this->surroundingPages; + + $min = $page - $near; + $max = $page + $near; + + // Limit the window size to no larger than the number of available pages. + if ($max - $min > $last) { + $max = $min + $last; + if ($max == $min) { + return '
'; + } + } + + // Slide the window so it is entirely over displayable pages. + if ($min < 0) { + $max += 0 - $min; + $min += 0 - $min; + } + + if ($max > $last) { + $min -= $max - $last; + $max -= $max - $last; + } + + + // Build up a list of tuples which describe the + // links we'll display, then render them all at once. + + $links = array(); + + if ($min > 0) { + $links[] = array(0, 'First', null); + } + + if ($page > 0) { + $links[] = array($page - 1, 'Prev', null); + } + + for ($ii = $min; $ii <= $max; $ii++) { + $links[] = array($ii, $ii + 1, ($ii == $page) ? 'current' : null); + } + + if ($page < $last && $last > 0) { + $links[] = array($page + 1, 'Next', null); + } + + if ($max < ($last - 1)) { + $links[] = array($last, 'Last', null); + } + + $base_uri = $this->uri; + $parameter = $this->pagingParameter; + $page_size = $this->getPageSize(); + + // Convert tuples into rendered nodes. + $rendered_links = array(); + foreach ($links as $link) { + list($index, $label, $class) = $link; + // Use a 1-based sequence for display so that the number in the URI is + // the same as the page number you're on. + if ($index == 0) { + // No need for the first page to say page=1. + $display_index = null; + } else { + $display_index = $index * $page_size; + } + $link = $base_uri->alter($parameter, $display_index); + $rendered_links[] = phutil_render_tag( + 'a', + array( + 'href' => $link, + 'class' => $class, + ), + $label); + } + + return + '
'. + implode('', $rendered_links). + '
'; + } + +} diff --git a/src/view/control/pager/__init__.php b/src/view/control/pager/__init__.php new file mode 100644 index 0000000000..488d68a205 --- /dev/null +++ b/src/view/control/pager/__init__.php @@ -0,0 +1,15 @@ +