1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-22 14:52:41 +01:00

Preview the effects of a drag-and-drop operation on workboards

Summary:
Ref T10335. Ref T5474. When you drag-and-drop a card on a workboard, show a UI hint which lists all the things that the operation will do.

This shows: column moves; changes because of dragging a card to a different header; and changes which will be caused by triggers.

Not implemented here:

  - Actions are currently shown even if they have no effect. For example, if you drag a "Normal" task to a different column, it says "Change priority to Normal.". I plan to hide actions which have no effect, but figuring this out is a little bit tricky.
  - I'd like to make "trigger effects" vs "non-trigger effects" a little more clear in the future, probably.

Test Plan:
Dragged stuff between columns and headers, and into columns with triggers. Got appropriate preview text hints previewing what the action would do in the UI.

(This is tricky to take a screenshot of since it only shows up while the mouse cursor is down.)

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T10335, T5474

Differential Revision: https://secure.phabricator.com/D20299
This commit is contained in:
epriestley 2019-03-19 15:27:21 -07:00
parent 149f8cc959
commit 5dca1569b5
22 changed files with 522 additions and 101 deletions

View file

@ -10,7 +10,7 @@ return array(
'conpherence.pkg.css' => '3c8a0668',
'conpherence.pkg.js' => '020aebcf',
'core.pkg.css' => 'b797945d',
'core.pkg.js' => 'f9c2509b',
'core.pkg.js' => 'eaca003c',
'differential.pkg.css' => '8d8360fb',
'differential.pkg.js' => '67e02996',
'diffusion.pkg.css' => '42c75c37',
@ -178,7 +178,7 @@ return array(
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'e86de308',
'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98',
'rsrc/css/phui/workboards/phui-workcard.css' => '9e9eb0df',
'rsrc/css/phui/workboards/phui-workpanel.css' => 'c5b408ad',
'rsrc/css/phui/workboards/phui-workpanel.css' => 'e5461a51',
'rsrc/css/sprite-login.css' => '18b368a6',
'rsrc/css/sprite-tokens.css' => 'f1896dc5',
'rsrc/css/syntax/syntax-default.css' => '055fc231',
@ -408,15 +408,16 @@ return array(
'rsrc/js/application/phortune/phortune-credit-card-form.js' => 'd12d214f',
'rsrc/js/application/policy/behavior-policy-control.js' => '0eaa33a9',
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '9347f172',
'rsrc/js/application/projects/WorkboardBoard.js' => '9d59f098',
'rsrc/js/application/projects/WorkboardBoard.js' => 'ba6e36b0',
'rsrc/js/application/projects/WorkboardCard.js' => '0392a5d8',
'rsrc/js/application/projects/WorkboardCardTemplate.js' => '2a61f8d4',
'rsrc/js/application/projects/WorkboardColumn.js' => 'ec5c5ce0',
'rsrc/js/application/projects/WorkboardColumn.js' => 'c344eb3c',
'rsrc/js/application/projects/WorkboardController.js' => '42c7a5a7',
'rsrc/js/application/projects/WorkboardDropEffect.js' => '101121be',
'rsrc/js/application/projects/WorkboardHeader.js' => '111bfd2d',
'rsrc/js/application/projects/WorkboardHeaderTemplate.js' => 'b65351bd',
'rsrc/js/application/projects/WorkboardHeaderTemplate.js' => 'ebe83a6b',
'rsrc/js/application/projects/WorkboardOrderTemplate.js' => '03e8891f',
'rsrc/js/application/projects/behavior-project-boards.js' => '412af9d4',
'rsrc/js/application/projects/behavior-project-boards.js' => 'cd7c9d4f',
'rsrc/js/application/projects/behavior-project-create.js' => '34c53422',
'rsrc/js/application/projects/behavior-reorder-columns.js' => '8ac32fd9',
'rsrc/js/application/releeph/releeph-preview-branch.js' => '75184d68',
@ -437,7 +438,7 @@ return array(
'rsrc/js/application/uiexample/notification-example.js' => '29819b75',
'rsrc/js/core/Busy.js' => '5202e831',
'rsrc/js/core/DragAndDropFileUpload.js' => '4370900d',
'rsrc/js/core/DraggableList.js' => '8bc7d797',
'rsrc/js/core/DraggableList.js' => 'c9ad6f70',
'rsrc/js/core/Favicon.js' => '7930776a',
'rsrc/js/core/FileUpload.js' => 'ab85e184',
'rsrc/js/core/Hovercard.js' => '074f0783',
@ -657,7 +658,7 @@ return array(
'javelin-behavior-phuix-example' => 'c2c500a7',
'javelin-behavior-policy-control' => '0eaa33a9',
'javelin-behavior-policy-rule-editor' => '9347f172',
'javelin-behavior-project-boards' => '412af9d4',
'javelin-behavior-project-boards' => 'cd7c9d4f',
'javelin-behavior-project-create' => '34c53422',
'javelin-behavior-quicksand-blacklist' => '5a6f6a06',
'javelin-behavior-read-only-warning' => 'b9109f8f',
@ -729,13 +730,14 @@ return array(
'javelin-view-renderer' => '9aae2b66',
'javelin-view-visitor' => '308f9fe4',
'javelin-websocket' => 'fdc13e4e',
'javelin-workboard-board' => '9d59f098',
'javelin-workboard-board' => 'ba6e36b0',
'javelin-workboard-card' => '0392a5d8',
'javelin-workboard-card-template' => '2a61f8d4',
'javelin-workboard-column' => 'ec5c5ce0',
'javelin-workboard-column' => 'c344eb3c',
'javelin-workboard-controller' => '42c7a5a7',
'javelin-workboard-drop-effect' => '101121be',
'javelin-workboard-header' => '111bfd2d',
'javelin-workboard-header-template' => 'b65351bd',
'javelin-workboard-header-template' => 'ebe83a6b',
'javelin-workboard-order-template' => '03e8891f',
'javelin-workflow' => '958e9045',
'maniphest-report-css' => '3d53188b',
@ -761,7 +763,7 @@ return array(
'phabricator-diff-changeset-list' => '04023d82',
'phabricator-diff-inline' => 'a4a14a94',
'phabricator-drag-and-drop-file-upload' => '4370900d',
'phabricator-draggable-list' => '8bc7d797',
'phabricator-draggable-list' => 'c9ad6f70',
'phabricator-fatal-config-template-css' => '20babf50',
'phabricator-favicon' => '7930776a',
'phabricator-feed-css' => 'd8b6e3f8',
@ -860,7 +862,7 @@ return array(
'phui-workboard-color-css' => 'e86de308',
'phui-workboard-view-css' => '74fc9d98',
'phui-workcard-view-css' => '9e9eb0df',
'phui-workpanel-view-css' => 'c5b408ad',
'phui-workpanel-view-css' => 'e5461a51',
'phuix-action-list-view' => 'c68f183f',
'phuix-action-view' => 'aaa08f3b',
'phuix-autocomplete' => '8f139ef0',
@ -1001,6 +1003,10 @@ return array(
'javelin-workflow',
'phuix-icon-view',
),
'101121be' => array(
'javelin-install',
'javelin-dom',
),
'111bfd2d' => array(
'javelin-install',
),
@ -1227,15 +1233,6 @@ return array(
'javelin-behavior',
'javelin-uri',
),
'412af9d4' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-vector',
'javelin-stratcom',
'javelin-workflow',
'javelin-workboard-controller',
),
'4234f572' => array(
'syntax-default-css',
),
@ -1593,14 +1590,6 @@ return array(
'javelin-dom',
'javelin-typeahead-normalizer',
),
'8bc7d797' => array(
'javelin-install',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
'javelin-vector',
'javelin-magical-init',
),
'8c2ed2bf' => array(
'javelin-behavior',
'javelin-dom',
@ -1725,18 +1714,6 @@ return array(
'javelin-uri',
'phabricator-textareautils',
),
'9d59f098' => array(
'javelin-install',
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-workflow',
'phabricator-draggable-list',
'javelin-workboard-column',
'javelin-workboard-header-template',
'javelin-workboard-card-template',
'javelin-workboard-order-template',
),
'9f081f05' => array(
'javelin-behavior',
'javelin-dom',
@ -1885,9 +1862,6 @@ return array(
'javelin-stratcom',
'javelin-dom',
),
'b65351bd' => array(
'javelin-install',
),
'b7b73831' => array(
'javelin-behavior',
'javelin-dom',
@ -1906,6 +1880,18 @@ return array(
'javelin-uri',
'phabricator-notification',
),
'ba6e36b0' => array(
'javelin-install',
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-workflow',
'phabricator-draggable-list',
'javelin-workboard-column',
'javelin-workboard-header-template',
'javelin-workboard-card-template',
'javelin-workboard-order-template',
),
'bdce4d78' => array(
'javelin-install',
'javelin-util',
@ -1930,15 +1916,17 @@ return array(
'javelin-dom',
'phuix-button-view',
),
'c344eb3c' => array(
'javelin-install',
'javelin-workboard-card',
'javelin-workboard-header',
),
'c3703a16' => array(
'javelin-behavior',
'javelin-aphlict',
'phabricator-phtize',
'javelin-dom',
),
'c5b408ad' => array(
'phui-workcard-view-css',
),
'c687e867' => array(
'javelin-behavior',
'javelin-dom',
@ -1978,6 +1966,24 @@ return array(
'javelin-util',
'phabricator-keyboard-shortcut-manager',
),
'c9ad6f70' => array(
'javelin-install',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
'javelin-vector',
'javelin-magical-init',
),
'cd7c9d4f' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-vector',
'javelin-stratcom',
'javelin-workflow',
'javelin-workboard-controller',
'javelin-workboard-drop-effect',
),
'cf32921f' => array(
'javelin-behavior',
'javelin-dom',
@ -2038,6 +2044,9 @@ return array(
'javelin-dom',
'javelin-history',
),
'e5461a51' => array(
'phui-workcard-view-css',
),
'e562708c' => array(
'javelin-install',
),
@ -2068,14 +2077,12 @@ return array(
'javelin-install',
'javelin-event',
),
'ebe83a6b' => array(
'javelin-install',
),
'ec4e31c0' => array(
'phui-timeline-view-css',
),
'ec5c5ce0' => array(
'javelin-install',
'javelin-workboard-card',
'javelin-workboard-header',
),
'ee77366f' => array(
'aphront-dialog-view-css',
),

View file

@ -4094,6 +4094,7 @@ phutil_register_library_map(array(
'PhabricatorProjectDefaultController' => 'applications/project/controller/PhabricatorProjectDefaultController.php',
'PhabricatorProjectDescriptionField' => 'applications/project/customfield/PhabricatorProjectDescriptionField.php',
'PhabricatorProjectDetailsProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectDetailsProfileMenuItem.php',
'PhabricatorProjectDropEffect' => 'applications/project/icon/PhabricatorProjectDropEffect.php',
'PhabricatorProjectEditController' => 'applications/project/controller/PhabricatorProjectEditController.php',
'PhabricatorProjectEditEngine' => 'applications/project/engine/PhabricatorProjectEditEngine.php',
'PhabricatorProjectEditPictureController' => 'applications/project/controller/PhabricatorProjectEditPictureController.php',
@ -10219,6 +10220,7 @@ phutil_register_library_map(array(
'PhabricatorProjectDefaultController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectDescriptionField' => 'PhabricatorProjectStandardCustomField',
'PhabricatorProjectDetailsProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorProjectDropEffect' => 'Phobject',
'PhabricatorProjectEditController' => 'PhabricatorProjectController',
'PhabricatorProjectEditEngine' => 'PhabricatorEditEngine',
'PhabricatorProjectEditPictureController' => 'PhabricatorProjectController',

View file

@ -540,8 +540,8 @@ final class PhabricatorProjectBoardViewController
->setExcludedProjectPHIDs($select_phids);
$templates = array();
$column_maps = array();
$all_tasks = array();
$column_templates = array();
foreach ($visible_columns as $column_phid => $column) {
$column_tasks = $column_phids[$column_phid];
@ -606,18 +606,28 @@ final class PhabricatorProjectBoardViewController
'pointLimit' => $column->getPointLimit(),
));
$card_phids = array();
foreach ($column_tasks as $task) {
$object_phid = $task->getPHID();
$card = $rendering_engine->renderCard($object_phid);
$templates[$object_phid] = hsprintf('%s', $card->getItem());
$column_maps[$column_phid][] = $object_phid;
$card_phids[] = $object_phid;
$all_tasks[$object_phid] = $task;
}
$panel->setCards($cards);
$board->addPanel($panel);
$drop_effects = $column->getDropEffects();
$drop_effects = mpull($drop_effects, 'toDictionary');
$column_templates[] = array(
'columnPHID' => $column_phid,
'effects' => $drop_effects,
'cardPHIDs' => $card_phids,
);
}
$order_key = $this->sortKey;
@ -661,9 +671,9 @@ final class PhabricatorProjectBoardViewController
'headers' => $headers,
'headerKeys' => $header_keys,
'templateMap' => $templates,
'columnMaps' => $column_maps,
'orderMaps' => $vector_map,
'propertyMaps' => $properties,
'columnTemplates' => $column_templates,
'boardID' => $board_id,
'projectPHID' => $project->getPHID(),

View file

@ -0,0 +1,45 @@
<?php
final class PhabricatorProjectDropEffect
extends Phobject {
private $icon;
private $color;
private $content;
public function setIcon($icon) {
$this->icon = $icon;
return $this;
}
public function getIcon() {
return $this->icon;
}
public function setColor($color) {
$this->color = $color;
return $this;
}
public function getColor() {
return $this->color;
}
public function setContent($content) {
$this->content = $content;
return $this;
}
public function getContent() {
return $this->content;
}
public function toDictionary() {
return array(
'icon' => $this->getIcon(),
'color' => $this->getColor(),
'content' => hsprintf('%s', $this->getContent()),
);
}
}

View file

@ -9,6 +9,7 @@ final class PhabricatorProjectColumnHeader
private $name;
private $icon;
private $editProperties;
private $dropEffects = array();
public function setOrderKey($order_key) {
$this->orderKey = $order_key;
@ -64,6 +65,15 @@ final class PhabricatorProjectColumnHeader
return $this->editProperties;
}
public function addDropEffect(PhabricatorProjectDropEffect $effect) {
$this->dropEffects[] = $effect;
return $this;
}
public function getDropEffects() {
return $this->dropEffects;
}
public function toDictionary() {
return array(
'order' => $this->getOrderKey(),
@ -71,6 +81,7 @@ final class PhabricatorProjectColumnHeader
'template' => hsprintf('%s', $this->newView()),
'vector' => $this->getSortVector(),
'editProperties' => $this->getEditProperties(),
'effects' => mpull($this->getDropEffects(), 'toDictionary'),
);
}

View file

@ -196,6 +196,10 @@ abstract class PhabricatorProjectColumnOrder
->setOrderKey($this->getColumnOrderKey());
}
final protected function newEffect() {
return new PhabricatorProjectDropEffect();
}
final public function toDictionary() {
return array(
'orderKey' => $this->getColumnOrderKey(),

View file

@ -122,16 +122,23 @@ final class PhabricatorProjectColumnOwnerOrder
$header_key = $this->newHeaderKeyForOwnerPHID($owner_phid);
$owner_image = null;
$effect_content = null;
if ($owner_phid === null) {
$owner = null;
$sort_vector = $this->newSortVectorForUnowned();
$owner_name = pht('Not Assigned');
$effect_content = pht('Remove task assignee.');
} else {
$owner = idx($owner_users, $owner_phid);
if ($owner) {
$sort_vector = $this->newSortVectorForOwner($owner);
$owner_name = $owner->getUsername();
$owner_image = $owner->getProfileImageURI();
$effect_content = pht(
'Assign task to %s.',
phutil_tag('strong', array(), $owner_name));
} else {
$sort_vector = $this->newSortVectorForOwnerPHID($owner_phid);
$owner_name = pht('Unknown User ("%s")', $owner_phid);
@ -159,6 +166,14 @@ final class PhabricatorProjectColumnOwnerOrder
'value' => $owner_phid,
));
if ($effect_content !== null) {
$header->addDropEffect(
$this->newEffect()
->setIcon($owner_icon)
->setColor($owner_color)
->setContent($effect_content));
}
$headers[] = $header;
}

View file

@ -65,6 +65,14 @@ final class PhabricatorProjectColumnPriorityOrder
$icon_view = id(new PHUIIconView())
->setIcon($priority_icon, $priority_color);
$drop_effect = $this->newEffect()
->setIcon($priority_icon)
->setColor($priority_color)
->setContent(
pht(
'Change priority to %s.',
phutil_tag('strong', array(), $priority_name)));
$header = $this->newHeader()
->setHeaderKey($header_key)
->setSortVector($sort_vector)
@ -73,7 +81,8 @@ final class PhabricatorProjectColumnPriorityOrder
->setEditProperties(
array(
'value' => (int)$priority,
));
))
->addDropEffect($drop_effect);
$headers[] = $header;
}

View file

@ -72,6 +72,14 @@ final class PhabricatorProjectColumnStatusOrder
$icon_view = id(new PHUIIconView())
->setIcon($status_icon, $status_color);
$drop_effect = $this->newEffect()
->setIcon($status_icon)
->setColor($status_color)
->setContent(
pht(
'Change status to %s.',
phutil_tag('strong', array(), $status_name)));
$header = $this->newHeader()
->setHeaderKey($header_key)
->setSortVector($sort_vector)
@ -80,7 +88,8 @@ final class PhabricatorProjectColumnStatusOrder
->setEditProperties(
array(
'value' => $status_key,
));
))
->addDropEffect($drop_effect);
$headers[] = $header;
}

View file

@ -218,6 +218,41 @@ final class PhabricatorProjectColumn
$this->getProject()->getID());
}
public function getDropEffects() {
$effects = array();
$proxy = $this->getProxy();
if ($proxy && $proxy->isMilestone()) {
$effects[] = id(new PhabricatorProjectDropEffect())
->setIcon($proxy->getProxyColumnIcon())
->setColor('violet')
->setContent(
pht(
'Move to milestone %s.',
phutil_tag('strong', array(), $this->getDisplayName())));
} else {
$effects[] = id(new PhabricatorProjectDropEffect())
->setIcon('fa-columns')
->setColor('blue')
->setContent(
pht(
'Move to column %s.',
phutil_tag('strong', array(), $this->getDisplayName())));
}
if ($this->canHaveTrigger()) {
$trigger = $this->getTrigger();
if ($trigger) {
foreach ($trigger->getDropEffects() as $trigger_effect) {
$effects[] = $trigger_effect;
}
}
}
return $effects;
}
/* -( PhabricatorConduitResultInterface )---------------------------------- */

View file

@ -170,6 +170,19 @@ final class PhabricatorProjectTrigger
return $this->triggerRules;
}
public function getDropEffects() {
$effects = array();
$rules = $this->getTriggerRules();
foreach ($rules as $rule) {
foreach ($rule->getDropEffects() as $effect) {
$effects[] = $effect;
}
}
return $effects;
}
public function getRulesDescription() {
$rules = $this->getTriggerRules();
if (!$rules) {

View file

@ -19,4 +19,8 @@ final class PhabricatorProjectTriggerInvalidRule
return array();
}
protected function newDropEffects($value) {
return array();
}
}

View file

@ -38,4 +38,21 @@ final class PhabricatorProjectTriggerManiphestStatusRule
);
}
protected function newDropEffects($value) {
$status_name = ManiphestTaskStatus::getTaskStatusName($value);
$status_icon = ManiphestTaskStatus::getStatusIcon($value);
$status_color = ManiphestTaskStatus::getStatusColor($value);
$content = pht(
'Change status to %s.',
phutil_tag('strong', array(), $status_name));
return array(
$this->newEffect()
->setIcon($status_icon)
->setColor($status_color)
->setContent($content),
);
}
}

View file

@ -40,6 +40,7 @@ abstract class PhabricatorProjectTriggerRule
abstract public function getDescription();
abstract protected function assertValidRuleValue($value);
abstract protected function newDropTransactions($object, $value);
abstract protected function newDropEffects($value);
final public function getDropTransactions($object, $value) {
return $this->newDropTransactions($object, $value);
@ -86,4 +87,12 @@ abstract class PhabricatorProjectTriggerRule
return $this->getObject()->getApplicationTransactionTemplate();
}
final public function getDropEffects() {
return $this->newDropEffects($this->getValue());
}
final protected function newEffect() {
return new PhabricatorProjectDropEffect();
}
}

View file

@ -19,4 +19,8 @@ final class PhabricatorProjectTriggerUnknownRule
return array();
}
protected function newDropEffects($value) {
return array();
}
}

View file

@ -178,3 +178,39 @@
margin-left: 36px;
overflow: hidden;
}
.workboard-drop-preview {
pointer-events: none;
position: absolute;
bottom: 12px;
right: 12px;
width: 300px;
border-radius: 3px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
border: 1px solid {$lightblueborder};
padding: 4px 0;
}
.workboard-drop-preview:hover {
opacity: 0.25;
}
.workboard-drop-preview li {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin: 4px 8px;
color: {$greytext};
}
.workboard-drop-preview li .phui-icon-view {
position: relative;
display: inline-block;
text-align: center;
width: 24px;
height: 18px;
padding-top: 6px;
border-radius: 3px;
background: {$bluebackground};
margin-right: 6px;
}

View file

@ -39,6 +39,8 @@ JX.install('WorkboardBoard', {
_columns: null,
_headers: null,
_cards: null,
_dropPreviewNode: null,
_dropPreviewListNode: null,
getRoot: function() {
return this._root;
@ -180,6 +182,8 @@ JX.install('WorkboardBoard', {
list.setCompareOnReorder(true);
}
list.setTargetChangeHandler(JX.bind(this, this._didChangeDropTarget));
list.listen('didDrop', JX.bind(this, this._onmovecard, list));
lists.push(list);
@ -190,23 +194,89 @@ JX.install('WorkboardBoard', {
}
},
_didChangeDropTarget: function(src_list, src_node, dst_list, dst_node) {
var node = this._getDropPreviewNode();
if (!dst_list) {
// The card is being dragged into a dead area, like the left menu.
JX.DOM.remove(node);
return;
}
if (dst_node === false) {
// The card is being dragged over itself, so dropping it won't
// affect anything.
JX.DOM.remove(node);
return;
}
var src_phid = JX.Stratcom.getData(src_list.getRootNode()).columnPHID;
var dst_phid = JX.Stratcom.getData(dst_list.getRootNode()).columnPHID;
var src_column = this.getColumn(src_phid);
var dst_column = this.getColumn(dst_phid);
var effects = [];
if (src_column !== dst_column) {
effects = effects.concat(dst_column.getDropEffects());
}
var context = this._getDropContext(dst_node);
if (context.headerKey) {
var header = this.getHeaderTemplate(context.headerKey);
effects = effects.concat(header.getDropEffects());
}
if (!effects.length) {
JX.DOM.remove(node);
return;
}
var items = [];
for (var ii = 0; ii < effects.length; ii++) {
var effect = effects[ii];
items.push(effect.newNode());
}
JX.DOM.setContent(this._getDropPreviewListNode(), items);
document.body.appendChild(node);
},
_getDropPreviewNode: function() {
if (!this._dropPreviewNode) {
var attributes = {
className: 'workboard-drop-preview'
};
var content = [
this._getDropPreviewListNode()
];
this._dropPreviewNode = JX.$N('div', attributes, content);
}
return this._dropPreviewNode;
},
_getDropPreviewListNode: function() {
if (!this._dropPreviewListNode) {
var attributes = {};
this._dropPreviewListNode = JX.$N('ul', attributes);
}
return this._dropPreviewListNode;
},
_findCardsInColumn: function(column_node) {
return JX.DOM.scry(column_node, 'li', 'project-card');
},
_onmovecard: function(list, item, after_node, src_list) {
list.lock();
JX.DOM.alterClass(item, 'drag-sending', true);
var src_phid = JX.Stratcom.getData(src_list.getRootNode()).columnPHID;
var dst_phid = JX.Stratcom.getData(list.getRootNode()).columnPHID;
var item_phid = JX.Stratcom.getData(item).objectPHID;
var data = {
objectPHID: item_phid,
columnPHID: dst_phid,
order: this.getOrder()
};
_getDropContext: function(after_node, item) {
var header_key;
var before_phid;
var after_phid;
// We're going to send an "afterPHID" and a "beforePHID" if the card
// was dropped immediately adjacent to another card. If a card was
@ -231,26 +301,28 @@ JX.install('WorkboardBoard', {
if (after_data) {
if (after_data.objectPHID) {
data.afterPHID = after_data.objectPHID;
after_phid = after_data.objectPHID;
}
}
var before_data;
var before_card = item.nextSibling;
while (before_card) {
before_data = JX.Stratcom.getData(before_card);
if (before_data.objectPHID) {
break;
if (item) {
var before_data;
var before_card = item.nextSibling;
while (before_card) {
before_data = JX.Stratcom.getData(before_card);
if (before_data.objectPHID) {
break;
}
if (before_data.headerKey) {
break;
}
before_card = before_card.nextSibling;
}
if (before_data.headerKey) {
break;
}
before_card = before_card.nextSibling;
}
if (before_data) {
if (before_data.objectPHID) {
data.beforePHID = before_data.objectPHID;
if (before_data) {
if (before_data.objectPHID) {
before_phid = before_data.objectPHID;
}
}
}
@ -265,12 +337,44 @@ JX.install('WorkboardBoard', {
}
if (header_data) {
var header_key = header_data.headerKey;
if (header_key) {
var properties = this.getHeaderTemplate(header_key)
.getEditProperties();
data.header = JX.JSON.stringify(properties);
}
header_key = header_data.headerKey;
}
return {
headerKey: header_key,
afterPHID: after_phid,
beforePHID: before_phid
};
},
_onmovecard: function(list, item, after_node, src_list) {
list.lock();
JX.DOM.alterClass(item, 'drag-sending', true);
var src_phid = JX.Stratcom.getData(src_list.getRootNode()).columnPHID;
var dst_phid = JX.Stratcom.getData(list.getRootNode()).columnPHID;
var item_phid = JX.Stratcom.getData(item).objectPHID;
var data = {
objectPHID: item_phid,
columnPHID: dst_phid,
order: this.getOrder()
};
var context = this._getDropContext(after_node);
if (context.afterPHID) {
data.afterPHID = context.afterPHID;
}
if (context.beforePHID) {
data.beforePHID = context.beforePHID;
}
if (context.headerKey) {
var properties = this.getHeaderTemplate(context.headerKey)
.getEditProperties();
data.header = JX.JSON.stringify(properties);
}
var visible_phids = [];

View file

@ -25,6 +25,7 @@ JX.install('WorkboardColumn', {
this._headers = {};
this._objects = [];
this._naturalOrder = [];
this._dropEffects = [];
},
members: {
@ -40,6 +41,7 @@ JX.install('WorkboardColumn', {
_pointsContentNode: null,
_dirty: true,
_objects: null,
_dropEffects: null,
getPHID: function() {
return this._phid;
@ -71,6 +73,15 @@ JX.install('WorkboardColumn', {
return this;
},
setDropEffects: function(effects) {
this._dropEffects = effects;
return this;
},
getDropEffects: function() {
return this._dropEffects;
},
getPointsNode: function() {
return this._pointsNode;
},

View file

@ -0,0 +1,35 @@
/**
* @provides javelin-workboard-drop-effect
* @requires javelin-install
* javelin-dom
* @javelin
*/
JX.install('WorkboardDropEffect', {
properties: {
icon: null,
color: null,
content: null
},
statics: {
newFromDictionary: function(map) {
return new JX.WorkboardDropEffect()
.setIcon(map.icon)
.setColor(map.color)
.setContent(JX.$H(map.content));
}
},
members: {
newNode: function() {
var icon = new JX.PHUIXIconView()
.setIcon(this.getIcon())
.setColor(this.getColor())
.getNode();
return JX.$N('li', {}, [icon, this.getContent()]);
}
}
});

View file

@ -14,7 +14,8 @@ JX.install('WorkboardHeaderTemplate', {
template: null,
order: null,
vector: null,
editProperties: null
editProperties: null,
dropEffects: []
},
members: {

View file

@ -7,6 +7,7 @@
* javelin-stratcom
* javelin-workflow
* javelin-workboard-controller
* javelin-workboard-drop-effect
*/
JX.behavior('project-boards', function(config, statics) {
@ -88,12 +89,24 @@ JX.behavior('project-boards', function(config, statics) {
}
var ii;
var column_maps = config.columnMaps;
for (var column_phid in column_maps) {
var column = board.getColumn(column_phid);
var column_map = column_maps[column_phid];
for (ii = 0; ii < column_map.length; ii++) {
column.newCard(column_map[ii]);
var jj;
var effects;
for (ii = 0; ii < config.columnTemplates.length; ii++) {
var spec = config.columnTemplates[ii];
var column = board.getColumn(spec.columnPHID);
effects = [];
for (jj = 0; jj < spec.effects.length; jj++) {
effects.push(
JX.WorkboardDropEffect.newFromDictionary(
spec.effects[jj]));
}
column.setDropEffects(effects);
for (jj = 0; jj < spec.cardPHIDs.length; jj++) {
column.newCard(spec.cardPHIDs[jj]);
}
}
@ -115,11 +128,19 @@ JX.behavior('project-boards', function(config, statics) {
for (ii = 0; ii < headers.length; ii++) {
var header = headers[ii];
effects = [];
for (jj = 0; jj < header.effects.length; jj++) {
effects.push(
JX.WorkboardDropEffect.newFromDictionary(
header.effects[jj]));
}
board.getHeaderTemplate(header.key)
.setOrder(header.order)
.setNodeHTMLTemplate(header.template)
.setVector(header.vector)
.setEditProperties(header.editProperties);
.setEditProperties(header.editProperties)
.setDropEffects(effects);
}
var orders = config.orders;

View file

@ -45,7 +45,8 @@ JX.install('DraggableList', {
outerContainer: null,
hasInfiniteHeight: false,
compareOnMove: false,
compareOnReorder: false
compareOnReorder: false,
targetChangeHandler: null
},
members : {
@ -53,6 +54,7 @@ JX.install('DraggableList', {
_dragging : null,
_locked : 0,
_target : null,
_lastTarget: null,
_targets : null,
_ghostHandler : null,
_ghostNode : null,
@ -372,6 +374,19 @@ JX.install('DraggableList', {
return this;
},
_didChangeTarget: function(dst_list, dst_node) {
if (dst_node === this._lastTarget) {
return;
}
this._lastTarget = dst_node;
var handler = this.getTargetChangeHandler();
if (handler) {
handler(this, this._dragging, dst_list, dst_node);
}
},
_setIsDropTarget: function(is_target) {
var root = this.getRootNode();
JX.DOM.alterClass(root, 'drag-target-list', is_target);
@ -540,6 +555,8 @@ JX.install('DraggableList', {
}
}
this._didChangeTarget(target_list, cur_target);
this._updateAutoscroll(this._cursorPosition);
var f = JX.$V(this._frame);
@ -673,6 +690,8 @@ JX.install('DraggableList', {
group[ii]._clearTarget();
}
this._didChangeTarget(null, null);
JX.DOM.alterClass(dragging, 'drag-dragging', false);
JX.Tooltip.unlock();