Page MenuHomePhabricator

D20299.id.diff
No OneTemporary

D20299.id.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -10,7 +10,7 @@
'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 @@
'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 @@
'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 @@
'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 @@
'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 @@
'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 @@
'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 @@
'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 @@
'javelin-workflow',
'phuix-icon-view',
),
+ '101121be' => array(
+ 'javelin-install',
+ 'javelin-dom',
+ ),
'111bfd2d' => array(
'javelin-install',
),
@@ -1227,15 +1233,6 @@
'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 @@
'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 @@
'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 @@
'javelin-stratcom',
'javelin-dom',
),
- 'b65351bd' => array(
- 'javelin-install',
- ),
'b7b73831' => array(
'javelin-behavior',
'javelin-dom',
@@ -1906,6 +1880,18 @@
'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 @@
'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 @@
'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 @@
'javelin-dom',
'javelin-history',
),
+ 'e5461a51' => array(
+ 'phui-workcard-view-css',
+ ),
'e562708c' => array(
'javelin-install',
),
@@ -2068,14 +2077,12 @@
'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',
),
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -4094,6 +4094,7 @@
'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 @@
'PhabricatorProjectDefaultController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectDescriptionField' => 'PhabricatorProjectStandardCustomField',
'PhabricatorProjectDetailsProfileMenuItem' => 'PhabricatorProfileMenuItem',
+ 'PhabricatorProjectDropEffect' => 'Phobject',
'PhabricatorProjectEditController' => 'PhabricatorProjectController',
'PhabricatorProjectEditEngine' => 'PhabricatorEditEngine',
'PhabricatorProjectEditPictureController' => 'PhabricatorProjectController',
diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
--- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php
+++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
@@ -540,8 +540,8 @@
->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 @@
'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 @@
'headers' => $headers,
'headerKeys' => $header_keys,
'templateMap' => $templates,
- 'columnMaps' => $column_maps,
'orderMaps' => $vector_map,
'propertyMaps' => $properties,
+ 'columnTemplates' => $column_templates,
'boardID' => $board_id,
'projectPHID' => $project->getPHID(),
diff --git a/src/applications/project/icon/PhabricatorProjectDropEffect.php b/src/applications/project/icon/PhabricatorProjectDropEffect.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/icon/PhabricatorProjectDropEffect.php
@@ -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()),
+ );
+ }
+
+}
diff --git a/src/applications/project/order/PhabricatorProjectColumnHeader.php b/src/applications/project/order/PhabricatorProjectColumnHeader.php
--- a/src/applications/project/order/PhabricatorProjectColumnHeader.php
+++ b/src/applications/project/order/PhabricatorProjectColumnHeader.php
@@ -9,6 +9,7 @@
private $name;
private $icon;
private $editProperties;
+ private $dropEffects = array();
public function setOrderKey($order_key) {
$this->orderKey = $order_key;
@@ -64,6 +65,15 @@
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 @@
'template' => hsprintf('%s', $this->newView()),
'vector' => $this->getSortVector(),
'editProperties' => $this->getEditProperties(),
+ 'effects' => mpull($this->getDropEffects(), 'toDictionary'),
);
}
diff --git a/src/applications/project/order/PhabricatorProjectColumnOrder.php b/src/applications/project/order/PhabricatorProjectColumnOrder.php
--- a/src/applications/project/order/PhabricatorProjectColumnOrder.php
+++ b/src/applications/project/order/PhabricatorProjectColumnOrder.php
@@ -196,6 +196,10 @@
->setOrderKey($this->getColumnOrderKey());
}
+ final protected function newEffect() {
+ return new PhabricatorProjectDropEffect();
+ }
+
final public function toDictionary() {
return array(
'orderKey' => $this->getColumnOrderKey(),
diff --git a/src/applications/project/order/PhabricatorProjectColumnOwnerOrder.php b/src/applications/project/order/PhabricatorProjectColumnOwnerOrder.php
--- a/src/applications/project/order/PhabricatorProjectColumnOwnerOrder.php
+++ b/src/applications/project/order/PhabricatorProjectColumnOwnerOrder.php
@@ -122,16 +122,23 @@
$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 @@
'value' => $owner_phid,
));
+ if ($effect_content !== null) {
+ $header->addDropEffect(
+ $this->newEffect()
+ ->setIcon($owner_icon)
+ ->setColor($owner_color)
+ ->setContent($effect_content));
+ }
+
$headers[] = $header;
}
diff --git a/src/applications/project/order/PhabricatorProjectColumnPriorityOrder.php b/src/applications/project/order/PhabricatorProjectColumnPriorityOrder.php
--- a/src/applications/project/order/PhabricatorProjectColumnPriorityOrder.php
+++ b/src/applications/project/order/PhabricatorProjectColumnPriorityOrder.php
@@ -65,6 +65,14 @@
$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 @@
->setEditProperties(
array(
'value' => (int)$priority,
- ));
+ ))
+ ->addDropEffect($drop_effect);
$headers[] = $header;
}
diff --git a/src/applications/project/order/PhabricatorProjectColumnStatusOrder.php b/src/applications/project/order/PhabricatorProjectColumnStatusOrder.php
--- a/src/applications/project/order/PhabricatorProjectColumnStatusOrder.php
+++ b/src/applications/project/order/PhabricatorProjectColumnStatusOrder.php
@@ -72,6 +72,14 @@
$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 @@
->setEditProperties(
array(
'value' => $status_key,
- ));
+ ))
+ ->addDropEffect($drop_effect);
$headers[] = $header;
}
diff --git a/src/applications/project/storage/PhabricatorProjectColumn.php b/src/applications/project/storage/PhabricatorProjectColumn.php
--- a/src/applications/project/storage/PhabricatorProjectColumn.php
+++ b/src/applications/project/storage/PhabricatorProjectColumn.php
@@ -218,6 +218,41 @@
$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 )---------------------------------- */
diff --git a/src/applications/project/storage/PhabricatorProjectTrigger.php b/src/applications/project/storage/PhabricatorProjectTrigger.php
--- a/src/applications/project/storage/PhabricatorProjectTrigger.php
+++ b/src/applications/project/storage/PhabricatorProjectTrigger.php
@@ -170,6 +170,19 @@
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) {
diff --git a/src/applications/project/trigger/PhabricatorProjectTriggerInvalidRule.php b/src/applications/project/trigger/PhabricatorProjectTriggerInvalidRule.php
--- a/src/applications/project/trigger/PhabricatorProjectTriggerInvalidRule.php
+++ b/src/applications/project/trigger/PhabricatorProjectTriggerInvalidRule.php
@@ -19,4 +19,8 @@
return array();
}
+ protected function newDropEffects($value) {
+ return array();
+ }
+
}
diff --git a/src/applications/project/trigger/PhabricatorProjectTriggerManiphestStatusRule.php b/src/applications/project/trigger/PhabricatorProjectTriggerManiphestStatusRule.php
--- a/src/applications/project/trigger/PhabricatorProjectTriggerManiphestStatusRule.php
+++ b/src/applications/project/trigger/PhabricatorProjectTriggerManiphestStatusRule.php
@@ -38,4 +38,21 @@
);
}
+ 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),
+ );
+ }
+
}
diff --git a/src/applications/project/trigger/PhabricatorProjectTriggerRule.php b/src/applications/project/trigger/PhabricatorProjectTriggerRule.php
--- a/src/applications/project/trigger/PhabricatorProjectTriggerRule.php
+++ b/src/applications/project/trigger/PhabricatorProjectTriggerRule.php
@@ -40,6 +40,7 @@
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 @@
return $this->getObject()->getApplicationTransactionTemplate();
}
+ final public function getDropEffects() {
+ return $this->newDropEffects($this->getValue());
+ }
+
+ final protected function newEffect() {
+ return new PhabricatorProjectDropEffect();
+ }
+
}
diff --git a/src/applications/project/trigger/PhabricatorProjectTriggerUnknownRule.php b/src/applications/project/trigger/PhabricatorProjectTriggerUnknownRule.php
--- a/src/applications/project/trigger/PhabricatorProjectTriggerUnknownRule.php
+++ b/src/applications/project/trigger/PhabricatorProjectTriggerUnknownRule.php
@@ -19,4 +19,8 @@
return array();
}
+ protected function newDropEffects($value) {
+ return array();
+ }
+
}
diff --git a/webroot/rsrc/css/phui/workboards/phui-workpanel.css b/webroot/rsrc/css/phui/workboards/phui-workpanel.css
--- a/webroot/rsrc/css/phui/workboards/phui-workpanel.css
+++ b/webroot/rsrc/css/phui/workboards/phui-workpanel.css
@@ -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;
+}
diff --git a/webroot/rsrc/js/application/projects/WorkboardBoard.js b/webroot/rsrc/js/application/projects/WorkboardBoard.js
--- a/webroot/rsrc/js/application/projects/WorkboardBoard.js
+++ b/webroot/rsrc/js/application/projects/WorkboardBoard.js
@@ -39,6 +39,8 @@
_columns: null,
_headers: null,
_cards: null,
+ _dropPreviewNode: null,
+ _dropPreviewListNode: null,
getRoot: function() {
return this._root;
@@ -180,6 +182,8 @@
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 @@
}
},
- _findCardsInColumn: function(column_node) {
- return JX.DOM.scry(column_node, 'li', 'project-card');
- },
+ _didChangeDropTarget: function(src_list, src_node, dst_list, dst_node) {
+ var node = this._getDropPreviewNode();
- _onmovecard: function(list, item, after_node, src_list) {
- list.lock();
- JX.DOM.alterClass(item, 'drag-sending', true);
+ 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(list.getRootNode()).columnPHID;
+ var dst_phid = JX.Stratcom.getData(dst_list.getRootNode()).columnPHID;
- var item_phid = JX.Stratcom.getData(item).objectPHID;
- var data = {
- objectPHID: item_phid,
- columnPHID: dst_phid,
- order: this.getOrder()
- };
+ 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');
+ },
+
+ _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 @@
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 (before_data.headerKey) {
- 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;
}
- 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 @@
}
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 = [];
diff --git a/webroot/rsrc/js/application/projects/WorkboardColumn.js b/webroot/rsrc/js/application/projects/WorkboardColumn.js
--- a/webroot/rsrc/js/application/projects/WorkboardColumn.js
+++ b/webroot/rsrc/js/application/projects/WorkboardColumn.js
@@ -25,6 +25,7 @@
this._headers = {};
this._objects = [];
this._naturalOrder = [];
+ this._dropEffects = [];
},
members: {
@@ -40,6 +41,7 @@
_pointsContentNode: null,
_dirty: true,
_objects: null,
+ _dropEffects: null,
getPHID: function() {
return this._phid;
@@ -71,6 +73,15 @@
return this;
},
+ setDropEffects: function(effects) {
+ this._dropEffects = effects;
+ return this;
+ },
+
+ getDropEffects: function() {
+ return this._dropEffects;
+ },
+
getPointsNode: function() {
return this._pointsNode;
},
diff --git a/webroot/rsrc/js/application/projects/WorkboardDropEffect.js b/webroot/rsrc/js/application/projects/WorkboardDropEffect.js
new file mode 100644
--- /dev/null
+++ b/webroot/rsrc/js/application/projects/WorkboardDropEffect.js
@@ -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()]);
+ }
+ }
+});
diff --git a/webroot/rsrc/js/application/projects/WorkboardHeaderTemplate.js b/webroot/rsrc/js/application/projects/WorkboardHeaderTemplate.js
--- a/webroot/rsrc/js/application/projects/WorkboardHeaderTemplate.js
+++ b/webroot/rsrc/js/application/projects/WorkboardHeaderTemplate.js
@@ -14,7 +14,8 @@
template: null,
order: null,
vector: null,
- editProperties: null
+ editProperties: null,
+ dropEffects: []
},
members: {
diff --git a/webroot/rsrc/js/application/projects/behavior-project-boards.js b/webroot/rsrc/js/application/projects/behavior-project-boards.js
--- a/webroot/rsrc/js/application/projects/behavior-project-boards.js
+++ b/webroot/rsrc/js/application/projects/behavior-project-boards.js
@@ -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 @@
}
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 @@
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;
diff --git a/webroot/rsrc/js/core/DraggableList.js b/webroot/rsrc/js/core/DraggableList.js
--- a/webroot/rsrc/js/core/DraggableList.js
+++ b/webroot/rsrc/js/core/DraggableList.js
@@ -45,7 +45,8 @@
outerContainer: null,
hasInfiniteHeight: false,
compareOnMove: false,
- compareOnReorder: false
+ compareOnReorder: false,
+ targetChangeHandler: null
},
members : {
@@ -53,6 +54,7 @@
_dragging : null,
_locked : 0,
_target : null,
+ _lastTarget: null,
_targets : null,
_ghostHandler : null,
_ghostNode : null,
@@ -372,6 +374,19 @@
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 @@
}
}
+ this._didChangeTarget(target_list, cur_target);
+
this._updateAutoscroll(this._cursorPosition);
var f = JX.$V(this._frame);
@@ -673,6 +690,8 @@
group[ii]._clearTarget();
}
+ this._didChangeTarget(null, null);
+
JX.DOM.alterClass(dragging, 'drag-dragging', false);
JX.Tooltip.unlock();

File Metadata

Mime Type
text/plain
Expires
Thu, Mar 20, 8:56 AM (3 d, 5 h ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/35/qh/ck2637c4a2rdvgr4
Default Alt Text
D20299.id.diff (35 KB)

Event Timeline