mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-23 07:12:41 +01:00
Add UI elements for sortable tables
Summary: Allow AphrontTableView to render with sort indicators and links in its columns. Test Plan: Looked at UI example. Reviewers: btrahan Reviewed By: btrahan CC: aran, epriestley, sandra Maniphest Tasks: T994 Differential Revision: https://secure.phabricator.com/D1946
This commit is contained in:
parent
afed8bb929
commit
65cf34e2b8
6 changed files with 285 additions and 24 deletions
|
@ -135,7 +135,7 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'aphront-table-view-css' =>
|
'aphront-table-view-css' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/f4f39a2e/rsrc/css/aphront/table-view.css',
|
'uri' => '/res/3ff30c4f/rsrc/css/aphront/table-view.css',
|
||||||
'type' => 'css',
|
'type' => 'css',
|
||||||
'requires' =>
|
'requires' =>
|
||||||
array(
|
array(
|
||||||
|
@ -1962,7 +1962,7 @@ celerity_register_resource_map(array(
|
||||||
), array(
|
), array(
|
||||||
'packages' =>
|
'packages' =>
|
||||||
array(
|
array(
|
||||||
'78e8854e' =>
|
'05e42357' =>
|
||||||
array(
|
array(
|
||||||
'name' => 'core.pkg.css',
|
'name' => 'core.pkg.css',
|
||||||
'symbols' =>
|
'symbols' =>
|
||||||
|
@ -1987,7 +1987,7 @@ celerity_register_resource_map(array(
|
||||||
17 => 'aphront-pager-view-css',
|
17 => 'aphront-pager-view-css',
|
||||||
18 => 'phabricator-transaction-view-css',
|
18 => 'phabricator-transaction-view-css',
|
||||||
),
|
),
|
||||||
'uri' => '/res/pkg/78e8854e/core.pkg.css',
|
'uri' => '/res/pkg/05e42357/core.pkg.css',
|
||||||
'type' => 'css',
|
'type' => 'css',
|
||||||
),
|
),
|
||||||
'21d01ed8' =>
|
'21d01ed8' =>
|
||||||
|
@ -2134,17 +2134,17 @@ celerity_register_resource_map(array(
|
||||||
'reverse' =>
|
'reverse' =>
|
||||||
array(
|
array(
|
||||||
'aphront-attached-file-view-css' => '31583232',
|
'aphront-attached-file-view-css' => '31583232',
|
||||||
'aphront-crumbs-view-css' => '78e8854e',
|
'aphront-crumbs-view-css' => '05e42357',
|
||||||
'aphront-dialog-view-css' => '78e8854e',
|
'aphront-dialog-view-css' => '05e42357',
|
||||||
'aphront-form-view-css' => '78e8854e',
|
'aphront-form-view-css' => '05e42357',
|
||||||
'aphront-headsup-action-list-view-css' => '551249fc',
|
'aphront-headsup-action-list-view-css' => '551249fc',
|
||||||
'aphront-list-filter-view-css' => '78e8854e',
|
'aphront-list-filter-view-css' => '05e42357',
|
||||||
'aphront-pager-view-css' => '78e8854e',
|
'aphront-pager-view-css' => '05e42357',
|
||||||
'aphront-panel-view-css' => '78e8854e',
|
'aphront-panel-view-css' => '05e42357',
|
||||||
'aphront-side-nav-view-css' => '78e8854e',
|
'aphront-side-nav-view-css' => '05e42357',
|
||||||
'aphront-table-view-css' => '78e8854e',
|
'aphront-table-view-css' => '05e42357',
|
||||||
'aphront-tokenizer-control-css' => '78e8854e',
|
'aphront-tokenizer-control-css' => '05e42357',
|
||||||
'aphront-typeahead-control-css' => '78e8854e',
|
'aphront-typeahead-control-css' => '05e42357',
|
||||||
'differential-changeset-view-css' => '551249fc',
|
'differential-changeset-view-css' => '551249fc',
|
||||||
'differential-core-view-css' => '551249fc',
|
'differential-core-view-css' => '551249fc',
|
||||||
'differential-inline-comment-editor' => '0d08d81b',
|
'differential-inline-comment-editor' => '0d08d81b',
|
||||||
|
@ -2202,23 +2202,23 @@ celerity_register_resource_map(array(
|
||||||
'maniphest-task-detail-css' => '31583232',
|
'maniphest-task-detail-css' => '31583232',
|
||||||
'maniphest-task-summary-css' => '31583232',
|
'maniphest-task-summary-css' => '31583232',
|
||||||
'maniphest-transaction-detail-css' => '31583232',
|
'maniphest-transaction-detail-css' => '31583232',
|
||||||
'phabricator-app-buttons-css' => '78e8854e',
|
'phabricator-app-buttons-css' => '05e42357',
|
||||||
'phabricator-content-source-view-css' => '551249fc',
|
'phabricator-content-source-view-css' => '551249fc',
|
||||||
'phabricator-core-buttons-css' => '78e8854e',
|
'phabricator-core-buttons-css' => '05e42357',
|
||||||
'phabricator-core-css' => '78e8854e',
|
'phabricator-core-css' => '05e42357',
|
||||||
'phabricator-directory-css' => '78e8854e',
|
'phabricator-directory-css' => '05e42357',
|
||||||
'phabricator-drag-and-drop-file-upload' => '0d08d81b',
|
'phabricator-drag-and-drop-file-upload' => '0d08d81b',
|
||||||
'phabricator-dropdown-menu' => '21d01ed8',
|
'phabricator-dropdown-menu' => '21d01ed8',
|
||||||
'phabricator-jump-nav' => '78e8854e',
|
'phabricator-jump-nav' => '05e42357',
|
||||||
'phabricator-keyboard-shortcut' => '21d01ed8',
|
'phabricator-keyboard-shortcut' => '21d01ed8',
|
||||||
'phabricator-keyboard-shortcut-manager' => '21d01ed8',
|
'phabricator-keyboard-shortcut-manager' => '21d01ed8',
|
||||||
'phabricator-menu-item' => '21d01ed8',
|
'phabricator-menu-item' => '21d01ed8',
|
||||||
'phabricator-object-selector-css' => '551249fc',
|
'phabricator-object-selector-css' => '551249fc',
|
||||||
'phabricator-paste-file-upload' => '21d01ed8',
|
'phabricator-paste-file-upload' => '21d01ed8',
|
||||||
'phabricator-remarkup-css' => '78e8854e',
|
'phabricator-remarkup-css' => '05e42357',
|
||||||
'phabricator-shaped-request' => '0d08d81b',
|
'phabricator-shaped-request' => '0d08d81b',
|
||||||
'phabricator-standard-page-view' => '78e8854e',
|
'phabricator-standard-page-view' => '05e42357',
|
||||||
'phabricator-transaction-view-css' => '78e8854e',
|
'phabricator-transaction-view-css' => '05e42357',
|
||||||
'syntax-highlighting-css' => '78e8854e',
|
'syntax-highlighting-css' => '05e42357',
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
|
@ -807,6 +807,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorSlowvoteOption' => 'applications/slowvote/storage/option',
|
'PhabricatorSlowvoteOption' => 'applications/slowvote/storage/option',
|
||||||
'PhabricatorSlowvotePoll' => 'applications/slowvote/storage/poll',
|
'PhabricatorSlowvotePoll' => 'applications/slowvote/storage/poll',
|
||||||
'PhabricatorSlowvotePollController' => 'applications/slowvote/controller/poll',
|
'PhabricatorSlowvotePollController' => 'applications/slowvote/controller/poll',
|
||||||
|
'PhabricatorSortTableExample' => 'applications/uiexample/examples/sorttable',
|
||||||
'PhabricatorStandardPageView' => 'view/page/standard',
|
'PhabricatorStandardPageView' => 'view/page/standard',
|
||||||
'PhabricatorStatusController' => 'applications/status/base',
|
'PhabricatorStatusController' => 'applications/status/base',
|
||||||
'PhabricatorSymbolNameLinter' => 'infrastructure/lint/hook/xhpastsymbolname',
|
'PhabricatorSymbolNameLinter' => 'infrastructure/lint/hook/xhpastsymbolname',
|
||||||
|
@ -1562,6 +1563,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorSlowvoteOption' => 'PhabricatorSlowvoteDAO',
|
'PhabricatorSlowvoteOption' => 'PhabricatorSlowvoteDAO',
|
||||||
'PhabricatorSlowvotePoll' => 'PhabricatorSlowvoteDAO',
|
'PhabricatorSlowvotePoll' => 'PhabricatorSlowvoteDAO',
|
||||||
'PhabricatorSlowvotePollController' => 'PhabricatorSlowvoteController',
|
'PhabricatorSlowvotePollController' => 'PhabricatorSlowvoteController',
|
||||||
|
'PhabricatorSortTableExample' => 'PhabricatorUIExample',
|
||||||
'PhabricatorStandardPageView' => 'AphrontPageView',
|
'PhabricatorStandardPageView' => 'AphrontPageView',
|
||||||
'PhabricatorStatusController' => 'PhabricatorController',
|
'PhabricatorStatusController' => 'PhabricatorController',
|
||||||
'PhabricatorSymbolNameLinter' => 'ArcanistXHPASTLintNamingHook',
|
'PhabricatorSymbolNameLinter' => 'ArcanistXHPASTLintNamingHook',
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
final class PhabricatorSortTableExample extends PhabricatorUIExample {
|
||||||
|
|
||||||
|
public function getName() {
|
||||||
|
return 'Sortable Tables';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription() {
|
||||||
|
return 'Using sortable tables.';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderExample() {
|
||||||
|
|
||||||
|
$rows = array(
|
||||||
|
array(
|
||||||
|
'make' => 'Honda',
|
||||||
|
'model' => 'Civic',
|
||||||
|
'year' => 2004,
|
||||||
|
'price' => 3199,
|
||||||
|
'color' => 'Blue',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'make' => 'Ford',
|
||||||
|
'model' => 'Focus',
|
||||||
|
'year' => 2001,
|
||||||
|
'price' => 2549,
|
||||||
|
'color' => 'Red',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'make' => 'Toyota',
|
||||||
|
'model' => 'Camry',
|
||||||
|
'year' => 2009,
|
||||||
|
'price' => 4299,
|
||||||
|
'color' => 'Black',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'make' => 'NASA',
|
||||||
|
'model' => 'Shuttle',
|
||||||
|
'year' => 1998,
|
||||||
|
'price' => 1000000000,
|
||||||
|
'color' => '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(
|
||||||
|
'Make',
|
||||||
|
'Model',
|
||||||
|
'Year',
|
||||||
|
'Price',
|
||||||
|
'Color',
|
||||||
|
));
|
||||||
|
$table->setColumnClasses(
|
||||||
|
array(
|
||||||
|
'',
|
||||||
|
'wide',
|
||||||
|
'n',
|
||||||
|
'n',
|
||||||
|
'',
|
||||||
|
));
|
||||||
|
$table->makeSortable(
|
||||||
|
$request->getRequestURI(),
|
||||||
|
'sort',
|
||||||
|
$sort,
|
||||||
|
$reverse,
|
||||||
|
$orders);
|
||||||
|
|
||||||
|
$panel = new AphrontPanelView();
|
||||||
|
$panel->setHeader('Sortable Table of Vehicles');
|
||||||
|
$panel->appendChild($table);
|
||||||
|
|
||||||
|
return $panel;
|
||||||
|
}
|
||||||
|
}
|
16
src/applications/uiexample/examples/sorttable/__init__.php
Normal file
16
src/applications/uiexample/examples/sorttable/__init__.php
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/uiexample/examples/base');
|
||||||
|
phutil_require_module('phabricator', 'view/control/table');
|
||||||
|
phutil_require_module('phabricator', 'view/layout/panel');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorSortTableExample.php');
|
|
@ -27,6 +27,12 @@ final class AphrontTableView extends AphrontView {
|
||||||
protected $className;
|
protected $className;
|
||||||
protected $columnVisibility = array();
|
protected $columnVisibility = array();
|
||||||
|
|
||||||
|
protected $sortURI;
|
||||||
|
protected $sortParam;
|
||||||
|
protected $sortSelected;
|
||||||
|
protected $sortReverse;
|
||||||
|
protected $sortValues;
|
||||||
|
|
||||||
public function __construct(array $data) {
|
public function __construct(array $data) {
|
||||||
$this->data = $data;
|
$this->data = $data;
|
||||||
}
|
}
|
||||||
|
@ -66,6 +72,34 @@ final class AphrontTableView extends AphrontView {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a sorting parameter:
|
||||||
|
*
|
||||||
|
* list($sort, $reverse) = AphrontTableView::parseSortParam($sort_param);
|
||||||
|
*
|
||||||
|
* @param string Sort request parameter.
|
||||||
|
* @return pair Sort value, sort direction.
|
||||||
|
*/
|
||||||
|
public static function parseSort($sort) {
|
||||||
|
return array(ltrim($sort, '-'), preg_match('/^-/', $sort));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function makeSortable(
|
||||||
|
PhutilURI $base_uri,
|
||||||
|
$param,
|
||||||
|
$selected,
|
||||||
|
$reverse,
|
||||||
|
array $sort_values) {
|
||||||
|
|
||||||
|
$this->sortURI = $base_uri;
|
||||||
|
$this->sortParam = $param;
|
||||||
|
$this->sortSelected = $selected;
|
||||||
|
$this->sortReverse = $reverse;
|
||||||
|
$this->sortValues = array_values($sort_values);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function render() {
|
public function render() {
|
||||||
require_celerity_resource('aphront-table-view-css');
|
require_celerity_resource('aphront-table-view-css');
|
||||||
|
|
||||||
|
@ -80,7 +114,7 @@ final class AphrontTableView extends AphrontView {
|
||||||
$col_classes = array();
|
$col_classes = array();
|
||||||
foreach ($this->columnClasses as $key => $class) {
|
foreach ($this->columnClasses as $key => $class) {
|
||||||
if (strlen($class)) {
|
if (strlen($class)) {
|
||||||
$col_classes[] = ' class="'.$class.'"';
|
$col_classes[] = $class;
|
||||||
} else {
|
} else {
|
||||||
$col_classes[] = null;
|
$col_classes[] = null;
|
||||||
}
|
}
|
||||||
|
@ -88,21 +122,79 @@ final class AphrontTableView extends AphrontView {
|
||||||
|
|
||||||
$visibility = array_values($this->columnVisibility);
|
$visibility = array_values($this->columnVisibility);
|
||||||
$headers = $this->headers;
|
$headers = $this->headers;
|
||||||
|
$sort_values = $this->sortValues;
|
||||||
if ($headers) {
|
if ($headers) {
|
||||||
while (count($headers) > count($visibility)) {
|
while (count($headers) > count($visibility)) {
|
||||||
$visibility[] = true;
|
$visibility[] = true;
|
||||||
}
|
}
|
||||||
|
while (count($headers) > count($sort_values)) {
|
||||||
|
$sort_values[] = null;
|
||||||
|
}
|
||||||
$table[] = '<tr>';
|
$table[] = '<tr>';
|
||||||
foreach ($headers as $col_num => $header) {
|
foreach ($headers as $col_num => $header) {
|
||||||
if (!$visibility[$col_num]) {
|
if (!$visibility[$col_num]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$class = idx($col_classes, $col_num);
|
|
||||||
|
$classes = array();
|
||||||
|
|
||||||
|
if (!empty($col_classes[$col_num])) {
|
||||||
|
$classes[] = $col_classes[$col_num];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sort_values[$col_num] !== null) {
|
||||||
|
$classes[] = 'aphront-table-view-sortable';
|
||||||
|
|
||||||
|
$sort_value = $sort_values[$col_num];
|
||||||
|
$sort_glyph = "\xE2\x86\x93";
|
||||||
|
if ($sort_value == $this->sortSelected) {
|
||||||
|
if ($this->sortReverse) {
|
||||||
|
$sort_glyph = "\xE2\x86\x91";
|
||||||
|
} else if (!$this->sortReverse) {
|
||||||
|
$sort_value = '-'.$sort_value;
|
||||||
|
}
|
||||||
|
$classes[] = 'aphront-table-view-sortable-selected';
|
||||||
|
}
|
||||||
|
|
||||||
|
$sort_glyph = phutil_render_tag(
|
||||||
|
'span',
|
||||||
|
array(
|
||||||
|
'class' => 'aphront-table-view-sort-glyph',
|
||||||
|
),
|
||||||
|
$sort_glyph);
|
||||||
|
|
||||||
|
$header = phutil_render_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => $this->sortURI->alter($this->sortParam, $sort_value),
|
||||||
|
'class' => 'aphront-table-view-sort-link',
|
||||||
|
),
|
||||||
|
$sort_glyph.' '.$header);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($classes) {
|
||||||
|
$class = ' class="'.implode(' ', $classes).'"';
|
||||||
|
} else {
|
||||||
|
$class = null;
|
||||||
|
}
|
||||||
|
|
||||||
$table[] = '<th'.$class.'>'.$header.'</th>';
|
$table[] = '<th'.$class.'>'.$header.'</th>';
|
||||||
}
|
}
|
||||||
$table[] = '</tr>';
|
$table[] = '</tr>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($col_classes as $key => $value) {
|
||||||
|
|
||||||
|
if (($sort_values[$key] !== null) &&
|
||||||
|
($sort_values[$key] == $this->sortSelected)) {
|
||||||
|
$value = trim($value.' sorted-column');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value !== null) {
|
||||||
|
$col_classes[$key] = ' class="'.$value.'"';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$data = $this->data;
|
$data = $this->data;
|
||||||
if ($data) {
|
if ($data) {
|
||||||
$row_num = 0;
|
$row_num = 0;
|
||||||
|
|
|
@ -22,6 +22,19 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.aphront-table-view th a,
|
||||||
|
.aphront-table-view th a:hover,
|
||||||
|
.aphront-table-view th a:link {
|
||||||
|
padding: 4px 8px;
|
||||||
|
color: white;
|
||||||
|
display: block;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aphront-table-view th a:hover {
|
||||||
|
background: #3366aa;
|
||||||
|
}
|
||||||
|
|
||||||
.aphront-table-view td.header {
|
.aphront-table-view td.header {
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
background: #3b5998;
|
background: #3b5998;
|
||||||
|
@ -37,6 +50,14 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.aphront-table-view td.sorted-column {
|
||||||
|
background: #f6f6fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aphront-table-view tr.alt td.sorted-column {
|
||||||
|
background: #e0e0ef;
|
||||||
|
}
|
||||||
|
|
||||||
.aphront-table-view td.action {
|
.aphront-table-view td.action {
|
||||||
padding-top: 1px;
|
padding-top: 1px;
|
||||||
padding-bottom: 1px;
|
padding-bottom: 1px;
|
||||||
|
@ -106,3 +127,21 @@ span.single-display-line-content {
|
||||||
max-height: 64px;
|
max-height: 64px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.aphront-table-view th.aphront-table-view-sortable {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aphront-table-view-sort-glyph {
|
||||||
|
float: right;
|
||||||
|
padding-left: 8px;
|
||||||
|
color: #6677bb;
|
||||||
|
}
|
||||||
|
|
||||||
|
th a:hover .aphront-table-view-sort-glyph {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aphront-table-view-sortable-selected .aphront-table-view-sort-glyph {
|
||||||
|
display: block;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue