mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-29 10:12:41 +01:00
Make bulk editor working set editable and more homogenous
Summary: Ref T13025. See PHI50. Fixes T11286. Ref T10005. Begin modernizing the bulk editor. For T10005 ("move the bulk editor to modern infrastructure"), rewrite the rendering of the editable set so that it is application-agnostic and can work with any kind of object. For T11286 ("let users de-select items in the working set"), make the working set editable. Test Plan: {F5302158} - Deselected some objects, applied an edit, saw the edit apply to only selected objects. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13025, T11286, T10005 Differential Revision: https://secure.phabricator.com/D18805
This commit is contained in:
parent
3e983b583d
commit
ad659627b3
6 changed files with 175 additions and 29 deletions
|
@ -9,7 +9,7 @@ return array(
|
|||
'names' => array(
|
||||
'conpherence.pkg.css' => 'e68cf1fa',
|
||||
'conpherence.pkg.js' => '15191c65',
|
||||
'core.pkg.css' => 'fdb27ef9',
|
||||
'core.pkg.css' => '5be8063f',
|
||||
'core.pkg.js' => '4c79d74f',
|
||||
'darkconsole.pkg.js' => '1f9a31bc',
|
||||
'differential.pkg.css' => '45951e9e',
|
||||
|
@ -135,14 +135,14 @@ return array(
|
|||
'rsrc/css/phui/object-item/phui-oi-color.css' => 'cd2b9b77',
|
||||
'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => '08f4ccc3',
|
||||
'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '9d9685d6',
|
||||
'rsrc/css/phui/object-item/phui-oi-list-view.css' => 'bf094950',
|
||||
'rsrc/css/phui/object-item/phui-oi-list-view.css' => '73c5f5c4',
|
||||
'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => 'a8beebea',
|
||||
'rsrc/css/phui/phui-action-list.css' => 'f7f61a34',
|
||||
'rsrc/css/phui/phui-action-panel.css' => 'b4798122',
|
||||
'rsrc/css/phui/phui-badge.css' => '22c0cf4f',
|
||||
'rsrc/css/phui/phui-basic-nav-view.css' => '98c11ab3',
|
||||
'rsrc/css/phui/phui-big-info-view.css' => 'acc3492c',
|
||||
'rsrc/css/phui/phui-box.css' => '9f3745fb',
|
||||
'rsrc/css/phui/phui-box.css' => '4bd6cdb9',
|
||||
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
|
||||
'rsrc/css/phui/phui-cms.css' => '504b4b23',
|
||||
'rsrc/css/phui/phui-comment-form.css' => 'ac68149f',
|
||||
|
@ -523,6 +523,7 @@ return array(
|
|||
'rsrc/js/core/phtize.js' => 'd254d646',
|
||||
'rsrc/js/phui/behavior-phui-dropdown-menu.js' => 'b95d6f7d',
|
||||
'rsrc/js/phui/behavior-phui-file-upload.js' => 'b003d4fb',
|
||||
'rsrc/js/phui/behavior-phui-selectable-list.js' => '464259a2',
|
||||
'rsrc/js/phui/behavior-phui-submenu.js' => 'a6f7a73b',
|
||||
'rsrc/js/phui/behavior-phui-tab-group.js' => '0a0b10e9',
|
||||
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
|
||||
|
@ -673,6 +674,7 @@ return array(
|
|||
'javelin-behavior-phui-dropdown-menu' => 'b95d6f7d',
|
||||
'javelin-behavior-phui-file-upload' => 'b003d4fb',
|
||||
'javelin-behavior-phui-hovercards' => 'bcaccd64',
|
||||
'javelin-behavior-phui-selectable-list' => '464259a2',
|
||||
'javelin-behavior-phui-submenu' => 'a6f7a73b',
|
||||
'javelin-behavior-phui-tab-group' => '0a0b10e9',
|
||||
'javelin-behavior-phuix-example' => '68af71ca',
|
||||
|
@ -820,7 +822,7 @@ return array(
|
|||
'phui-badge-view-css' => '22c0cf4f',
|
||||
'phui-basic-nav-view-css' => '98c11ab3',
|
||||
'phui-big-info-view-css' => 'acc3492c',
|
||||
'phui-box-css' => '9f3745fb',
|
||||
'phui-box-css' => '4bd6cdb9',
|
||||
'phui-button-bar-css' => 'f1ff5494',
|
||||
'phui-button-css' => '1863cc6e',
|
||||
'phui-button-simple-css' => '8e1baf68',
|
||||
|
@ -860,7 +862,7 @@ return array(
|
|||
'phui-oi-color-css' => 'cd2b9b77',
|
||||
'phui-oi-drag-ui-css' => '08f4ccc3',
|
||||
'phui-oi-flush-ui-css' => '9d9685d6',
|
||||
'phui-oi-list-view-css' => 'bf094950',
|
||||
'phui-oi-list-view-css' => '73c5f5c4',
|
||||
'phui-oi-simple-ui-css' => 'a8beebea',
|
||||
'phui-pager-css' => 'edcbc226',
|
||||
'phui-pinboard-view-css' => '2495140e',
|
||||
|
@ -1226,6 +1228,11 @@ return array(
|
|||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
),
|
||||
'464259a2' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
),
|
||||
'469c0d9e' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
|
|
@ -89,12 +89,7 @@ final class ManiphestBatchEditController extends ManiphestController {
|
|||
->setURI($job->getMonitorURI());
|
||||
}
|
||||
|
||||
$handles = ManiphestTaskListView::loadTaskHandles($viewer, $tasks);
|
||||
|
||||
$list = new ManiphestTaskListView();
|
||||
$list->setTasks($tasks);
|
||||
$list->setUser($viewer);
|
||||
$list->setHandles($handles);
|
||||
$list = $this->newBulkObjectList($tasks);
|
||||
|
||||
$template = new AphrontTokenizerTemplateView();
|
||||
$template = $template->render();
|
||||
|
@ -142,21 +137,8 @@ final class ManiphestBatchEditController extends ManiphestController {
|
|||
'statusMap' => ManiphestTaskStatus::getTaskStatusMap(),
|
||||
));
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->addHiddenInput('board', $board_id)
|
||||
->setID('maniphest-batch-edit-form');
|
||||
|
||||
foreach ($tasks as $task) {
|
||||
$form->appendChild(
|
||||
phutil_tag(
|
||||
'input',
|
||||
array(
|
||||
'type' => 'hidden',
|
||||
'name' => 'batch[]',
|
||||
'value' => $task->getID(),
|
||||
)));
|
||||
}
|
||||
$form = id(new PHUIFormLayoutView())
|
||||
->setUser($viewer);
|
||||
|
||||
$form->appendChild(
|
||||
phutil_tag(
|
||||
|
@ -166,6 +148,7 @@ final class ManiphestBatchEditController extends ManiphestController {
|
|||
'name' => 'actions',
|
||||
'id' => 'batch-form-actions',
|
||||
)));
|
||||
|
||||
$form->appendChild(
|
||||
id(new PHUIFormInsetView())
|
||||
->setTitle(pht('Actions'))
|
||||
|
@ -210,17 +193,63 @@ final class ManiphestBatchEditController extends ManiphestController {
|
|||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setForm($form);
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setFooter(array(
|
||||
|
||||
$complete_form = phabricator_form(
|
||||
$viewer,
|
||||
array(
|
||||
'action' => $request->getRequestURI(),
|
||||
'method' => 'POST',
|
||||
'id' => 'maniphest-batch-edit-form',
|
||||
),
|
||||
array(
|
||||
phutil_tag(
|
||||
'input',
|
||||
array(
|
||||
'type' => 'hidden',
|
||||
'name' => 'board',
|
||||
'value' => $board_id,
|
||||
)),
|
||||
$task_box,
|
||||
$form_box,
|
||||
));
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setFooter($complete_form);
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
->setCrumbs($crumbs)
|
||||
->appendChild($view);
|
||||
}
|
||||
|
||||
private function newBulkObjectList(array $objects) {
|
||||
$viewer = $this->getViewer();
|
||||
$objects = mpull($objects, null, 'getPHID');
|
||||
|
||||
$handles = $viewer->loadHandles(array_keys($objects));
|
||||
|
||||
$status_closed = PhabricatorObjectHandle::STATUS_CLOSED;
|
||||
|
||||
$list = id(new PHUIObjectItemListView())
|
||||
->setViewer($viewer)
|
||||
->setFlush(true);
|
||||
|
||||
foreach ($objects as $phid => $object) {
|
||||
$handle = $handles[$phid];
|
||||
|
||||
$is_closed = ($handle->getStatus() === $status_closed);
|
||||
|
||||
$item = id(new PHUIObjectItemView())
|
||||
->setHeader($handle->getFullName())
|
||||
->setHref($handle->getURI())
|
||||
->setDisabled($is_closed)
|
||||
->setSelectable('batch[]', $object->getID(), true);
|
||||
|
||||
$list->addItem($item);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ final class PHUIObjectItemView extends AphrontTagView {
|
|||
private $coverImage;
|
||||
private $description;
|
||||
|
||||
private $selectableName;
|
||||
private $selectableValue;
|
||||
private $isSelected;
|
||||
|
||||
public function setDisabled($disabled) {
|
||||
$this->disabled = $disabled;
|
||||
return $this;
|
||||
|
@ -160,6 +164,13 @@ final class PHUIObjectItemView extends AphrontTagView {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setSelectable($name, $value, $is_selected) {
|
||||
$this->selectableName = $name;
|
||||
$this->selectableValue = $value;
|
||||
$this->isSelected = $is_selected;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setEpoch($epoch) {
|
||||
$date = phabricator_datetime($epoch, $this->getUser());
|
||||
$this->addIcon('none', $date);
|
||||
|
@ -239,6 +250,8 @@ final class PHUIObjectItemView extends AphrontTagView {
|
|||
}
|
||||
|
||||
protected function getTagAttributes() {
|
||||
$sigils = array();
|
||||
|
||||
$item_classes = array();
|
||||
$item_classes[] = 'phui-oi';
|
||||
|
||||
|
@ -286,6 +299,17 @@ final class PHUIObjectItemView extends AphrontTagView {
|
|||
throw new Exception(pht('Invalid effect!'));
|
||||
}
|
||||
|
||||
if ($this->isSelected) {
|
||||
$item_classes[] = 'phui-oi-selected';
|
||||
}
|
||||
|
||||
if ($this->selectableName !== null) {
|
||||
$item_classes[] = 'phui-oi-selectable';
|
||||
$sigils[] = 'phui-oi-selectable';
|
||||
|
||||
Javelin::initBehavior('phui-selectable-list');
|
||||
}
|
||||
|
||||
if ($this->getGrippable()) {
|
||||
$item_classes[] = 'phui-oi-grippable';
|
||||
}
|
||||
|
@ -300,6 +324,7 @@ final class PHUIObjectItemView extends AphrontTagView {
|
|||
|
||||
return array(
|
||||
'class' => $item_classes,
|
||||
'sigil' => $sigils,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -628,6 +653,24 @@ final class PHUIObjectItemView extends AphrontTagView {
|
|||
$countdown);
|
||||
}
|
||||
|
||||
if ($this->selectableName !== null) {
|
||||
$checkbox = phutil_tag(
|
||||
'input',
|
||||
array(
|
||||
'type' => 'checkbox',
|
||||
'name' => $this->selectableName,
|
||||
'value' => $this->selectableValue,
|
||||
'checked' => ($this->isSelected ? 'checked' : null),
|
||||
));
|
||||
|
||||
$column0 = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'phui-oi-col0 phui-oi-checkbox',
|
||||
),
|
||||
$checkbox);
|
||||
}
|
||||
|
||||
$column1 = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
|
|
|
@ -664,3 +664,22 @@ ul.phui-oi-list-view .phui-oi-selected
|
|||
padding: 0 8px 8px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.phui-oi-col0.phui-oi-checkbox {
|
||||
width: 28px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.phui-oi-selectable {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
/* When the list selection state can be toggled on the client (as in the bulk
|
||||
editor), keep the border color consistent to make the interaction feel more
|
||||
robust. */
|
||||
ul.phui-oi-list-view .phui-oi-selectable
|
||||
.phui-oi-frame {
|
||||
border-color: {$blueborder};
|
||||
}
|
||||
|
|
|
@ -103,6 +103,10 @@ body.device .phui-box-blue-property.phui-object-box.phui-object-box-collapsed
|
|||
padding: 2px 8px;
|
||||
}
|
||||
|
||||
.phui-box-blue-property .phui-oi-list-view.phui-oi-list-flush {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body .phui-box-blue-property.phui-object-box.phui-object-box-collapsed {
|
||||
padding: 0;
|
||||
}
|
||||
|
|
44
webroot/rsrc/js/phui/behavior-phui-selectable-list.js
Normal file
44
webroot/rsrc/js/phui/behavior-phui-selectable-list.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* @provides javelin-behavior-phui-selectable-list
|
||||
* @requires javelin-behavior
|
||||
* javelin-stratcom
|
||||
* javelin-dom
|
||||
*/
|
||||
|
||||
JX.behavior('phui-selectable-list', function() {
|
||||
|
||||
JX.Stratcom.listen('click', 'phui-oi-selectable', function(e) {
|
||||
if (!e.isNormalClick()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the user clicked a link, ignore it.
|
||||
if (e.getNode('tag:a')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var root = e.getNode('phui-oi-selectable');
|
||||
|
||||
// If the user did not click the checkbox, pretend they did. This makes
|
||||
// the entire element a click target to make changing the selection set a
|
||||
// bit easier.
|
||||
if (!e.getNode('tag:input')) {
|
||||
var checkbox = getCheckbox(root);
|
||||
checkbox.checked = !checkbox.checked;
|
||||
|
||||
e.kill();
|
||||
}
|
||||
|
||||
setTimeout(JX.bind(null, redraw, root), 0);
|
||||
});
|
||||
|
||||
function getCheckbox(root) {
|
||||
return JX.DOM.find(root, 'input');
|
||||
}
|
||||
|
||||
function redraw(root) {
|
||||
var checkbox = getCheckbox(root);
|
||||
JX.DOM.alterClass(root, 'phui-oi-selected', !!checkbox.checked);
|
||||
}
|
||||
|
||||
});
|
Loading…
Reference in a new issue