Commit be53c1bb authored by estade@chromium.org's avatar estade@chromium.org

ntp4: most visited dragging onto apps page

You can now:
- drag a most visited tile onto an apps page to get an app for it
- drag a most visited tile onto the trash (cool animations)
- blacklisting a most visited tile with the [x] looks better
- you don't see the trash icon show up for things that can't be trashed (as opposed to being able to drag onto trash but having it do nothing)

BUG=none
TEST=manual

Review URL: http://codereview.chromium.org/7592001

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@95856 0039d316-1c4b-4281-b951-d872f2087c98
parent 2398993e
...@@ -327,6 +327,26 @@ cr.define('ntp4', function() { ...@@ -327,6 +327,26 @@ cr.define('ntp4', function() {
menu.setupForApp(this); menu.setupForApp(this);
return menu.menu; return menu.menu;
}, },
/**
* Returns whether this element can be 'removed' from chrome (i.e. whether
* the user can drag it onto the trash and expect something to happen).
* @return {boolean} True if the app can be uninstalled.
*/
canBeRemoved: function() {
return this.appData_.can_uninstall;
},
/**
* Uninstalls the app after it's been dropped on the trash.
*/
removeFromChrome: function() {
chrome.send('uninstallApp', [this.appData_.id, true]);
var tile = this.parentNode;
tile.tilePage.cleanupDrag();
tile.parentNode.removeChild(tile);
},
}; };
var TilePage = ntp4.TilePage; var TilePage = ntp4.TilePage;
...@@ -384,7 +404,31 @@ cr.define('ntp4', function() { ...@@ -384,7 +404,31 @@ cr.define('ntp4', function() {
}, },
/** @inheritDoc */ /** @inheritDoc */
addOutsideData: function(dataTransfer, index) { addDragData: function(dataTransfer, index) {
var currentlyDraggingTile = ntp4.getCurrentlyDraggingTile();
if (currentlyDraggingTile) {
var tileContents = currentlyDraggingTile.firstChild;
if (tileContents.classList.contains('app')) {
this.tileGrid_.insertBefore(
currentlyDraggingTile,
this.tileElements_[index]);
this.tileMoved(currentlyDraggingTile);
} else if (tileContents.classList.contains('most-visited')) {
this.generateAppForLink(tileContents.data);
}
} else {
this.addOutsideData_(e.dataTransfer, index);
}
},
/**
* Adds drag data that has been dropped from a source that is not a tile.
* @param {Object} dataTransfer The data transfer object that holds drop
* data.
* @param {number} index The index for the new data.
* @private
*/
addOutsideData_: function(dataTransfer, index) {
var url = dataTransfer.getData('url'); var url = dataTransfer.getData('url');
assert(url); assert(url);
if (!url) if (!url)
...@@ -417,9 +461,12 @@ cr.define('ntp4', function() { ...@@ -417,9 +461,12 @@ cr.define('ntp4', function() {
/** /**
* Creates a new crx-less app manifest and installs it. * Creates a new crx-less app manifest and installs it.
* @param {Object} data The data object describing the link. Must have |url| * @param {Object} data The data object describing the link. Must have |url|
* and |title| members. * and |title| members.
* TODO(estade): pass along an index.
*/ */
generateAppForLink: function(data) { generateAppForLink: function(data) {
assert(data.url != undefined);
assert(data.title != undefined);
chrome.send('generateAppForLink', [data.url, data.title]); chrome.send('generateAppForLink', [data.url, data.title]);
}, },
......
...@@ -56,6 +56,10 @@ html[dir=rtl] .most-visited .close-button { ...@@ -56,6 +56,10 @@ html[dir=rtl] .most-visited .close-button {
-webkit-transition-delay: 0.5s; -webkit-transition-delay: 0.5s;
} }
.close-button:hover {
-webkit-transition-delay: 0;
}
.most-visited .favicon { .most-visited .favicon {
background: no-repeat 5px 50%; background: no-repeat 5px 50%;
background-size: 32px; background-size: 32px;
...@@ -147,3 +151,15 @@ html[dir=rtl] .most-visited .close-button { ...@@ -147,3 +151,15 @@ html[dir=rtl] .most-visited .close-button {
.filler .thumbnail-wrapper { .filler .thumbnail-wrapper {
visibility: visible; visibility: visible;
} }
/* 'finishing-drag' is the state we are in after dropping on the trash can.
* Override opacity of the tile to 1, so that the new tile animation
* occurs simultaneously with the trash animation. */
.tile.dragging.finishing-drag {
opacity: 1;
}
/* Don't display the new tile until there's something to show. */
.blacklisted {
opacity: 0;
}
...@@ -39,6 +39,10 @@ cr.define('ntp4', function() { ...@@ -39,6 +39,10 @@ cr.define('ntp4', function() {
return this.tile.index; return this.tile.index;
}, },
get data() {
return this.data_;
},
/** /**
* Clears the DOM hierarchy for this node, setting it back to the default * Clears the DOM hierarchy for this node, setting it back to the default
* for a blank thumbnail. * for a blank thumbnail.
...@@ -67,6 +71,12 @@ cr.define('ntp4', function() { ...@@ -67,6 +71,12 @@ cr.define('ntp4', function() {
* @param {Object} data A dictionary of relevant data for the page. * @param {Object} data A dictionary of relevant data for the page.
*/ */
updateForData: function(data) { updateForData: function(data) {
if (this.classList.contains('blacklisted') && data) {
// Animate appearance of new tile.
this.classList.add('new-tile-contents');
}
this.classList.remove('blacklisted');
if (!data || data.filler) { if (!data || data.filler) {
if (this.data_) if (this.data_)
this.reset(); this.reset();
...@@ -78,7 +88,6 @@ cr.define('ntp4', function() { ...@@ -78,7 +88,6 @@ cr.define('ntp4', function() {
this.data_ = data; this.data_ = data;
// TODO(estade): this shouldn't be focusable if the page isn't showing. // TODO(estade): this shouldn't be focusable if the page isn't showing.
this.tabIndex = 0; this.tabIndex = 0;
this.classList.remove('filler');
var faviconDiv = this.querySelector('.favicon'); var faviconDiv = this.querySelector('.favicon');
var faviconUrl = data.faviconUrl || var faviconUrl = data.faviconUrl ||
...@@ -99,6 +108,8 @@ cr.define('ntp4', function() { ...@@ -99,6 +108,8 @@ cr.define('ntp4', function() {
url(thumbnailUrl); url(thumbnailUrl);
this.href = data.url; this.href = data.url;
this.classList.remove('filler');
}, },
/** /**
...@@ -148,6 +159,7 @@ cr.define('ntp4', function() { ...@@ -148,6 +159,7 @@ cr.define('ntp4', function() {
chrome.send('blacklistURLFromMostVisited', [this.data_.url]); chrome.send('blacklistURLFromMostVisited', [this.data_.url]);
this.reset(); this.reset();
chrome.send('getMostVisited'); chrome.send('getMostVisited');
this.classList.add('blacklisted');
}, },
showUndoNotification_: function() { showUndoNotification_: function() {
...@@ -189,6 +201,32 @@ cr.define('ntp4', function() { ...@@ -189,6 +201,32 @@ cr.define('ntp4', function() {
this.style.right = x + 'px'; this.style.right = x + 'px';
this.style.top = y + 'px'; this.style.top = y + 'px';
}, },
/**
* Returns whether this element can be 'removed' from chrome (i.e. whether
* the user can drag it onto the trash and expect something to happen).
* @return {boolean} True, since most visited pages can always be
* blacklisted.
*/
canBeRemoved: function() {
return true;
},
/**
* Removes this element from chrome, i.e. blacklists it.
*/
removeFromChrome: function() {
this.blacklist_();
this.parentNode.classList.add('finishing-drag');
},
/**
* Called when a drag of this tile has ended (after all animations have
* finished).
*/
finalizeDrag: function() {
this.parentNode.classList.remove('finishing-drag');
},
}; };
var mostVisitedPageGridValues = { var mostVisitedPageGridValues = {
......
...@@ -159,7 +159,7 @@ body { ...@@ -159,7 +159,7 @@ body {
-webkit-transition-delay: 0, 0.2s; -webkit-transition-delay: 0, 0.2s;
} }
#footer.dragging-mode #trash { #footer.showing-trash-mode #trash {
opacity: 1; opacity: 1;
top: 0; top: 0;
-webkit-transition-delay: 0, 0; -webkit-transition-delay: 0, 0;
......
...@@ -459,7 +459,8 @@ cr.define('ntp4', function() { ...@@ -459,7 +459,8 @@ cr.define('ntp4', function() {
appendAppsPage(tempPage, ''); appendAppsPage(tempPage, '');
updateSliderCards(); updateSliderCards();
$('footer').classList.add('dragging-mode'); if (ntp4.getCurrentlyDraggingTile().firstChild.canBeRemoved())
$('footer').classList.add('showing-trash-mode');
} }
/** /**
...@@ -478,7 +479,7 @@ cr.define('ntp4', function() { ...@@ -478,7 +479,7 @@ cr.define('ntp4', function() {
saveAppPageName(tempPage, ''); saveAppPageName(tempPage, '');
} }
$('footer').classList.remove('dragging-mode'); $('footer').classList.remove('showing-trash-mode');
} }
/** /**
......
...@@ -16,11 +16,11 @@ ...@@ -16,11 +16,11 @@
/* Reserve space for the menu button even when it's hidden. */ /* Reserve space for the menu button even when it's hidden. */
#recently-closed-menu-button[hidden], #recently-closed-menu-button[hidden],
#footer.dragging-mode #recently-closed-menu-button { #footer.showing-trash-mode #recently-closed-menu-button {
visibility: hidden; visibility: hidden;
} }
#footer.dragging-mode #recently-closed-menu-button { #footer.showing-trash-mode #recently-closed-menu-button {
opacity: 0; opacity: 0;
-webkit-transition-delay: 0; -webkit-transition-delay: 0;
} }
......
...@@ -80,7 +80,13 @@ ...@@ -80,7 +80,13 @@
-webkit-transition: -webkit-transform 200ms; -webkit-transition: -webkit-transform 200ms;
} }
.tile.drag-representation.placing > *.deleting { /* When a drag finishes while we're not showing the page where the tile
* belongs, the tile shrinks to a dot. */
.tile.drag-representation.dropped-on-other-page > * {
-webkit-transform: scale(0);
}
.tile.drag-representation.deleting > * {
-webkit-transform: scale(0) rotate(360deg); -webkit-transform: scale(0) rotate(360deg);
-webkit-transition: -webkit-transform 600ms; -webkit-transition: -webkit-transform 600ms;
} }
......
...@@ -90,14 +90,6 @@ cr.define('ntp4', function() { ...@@ -90,14 +90,6 @@ cr.define('ntp4', function() {
* @private * @private
*/ */
onDragStart_: function(e) { onDragStart_: function(e) {
// TODO(estade): most visited dragging is disabled for now, remove this
// when it does something useful.
if (this.querySelector('.most-visited')) {
e.preventDefault();
e.stopPropagation();
return;
}
// The user may start dragging again during a previous drag's finishing // The user may start dragging again during a previous drag's finishing
// animation. // animation.
if (this.classList.contains('dragging')) if (this.classList.contains('dragging'))
...@@ -161,30 +153,32 @@ cr.define('ntp4', function() { ...@@ -161,30 +153,32 @@ cr.define('ntp4', function() {
setCurrentlyDraggingTile(null); setCurrentlyDraggingTile(null);
if (!this.tilePage) { // tilePage will be null if we've already been removed.
this.dragClone.firstChild.classList.add('deleting'); if (this.tilePage)
return; this.tilePage.positionTile_(this.index);
// Take an appropriate action with the drag clone.
if (this.landedOnTrash) {
this.dragClone.classList.add('deleting');
} else if (this.tilePage) {
if (this.tilePage.selected) {
// The tile's contents may have moved following the respositioning;
// adjust for that.
var contentDiffX = this.dragClone.firstChild.offsetLeft -
this.firstChild.offsetLeft;
var contentDiffY = this.dragClone.firstChild.offsetTop -
this.firstChild.offsetTop;
this.dragClone.style.left = (this.gridX + this.parentNode.offsetLeft -
contentDiffX) + 'px';
this.dragClone.style.top =
(this.gridY + this.parentNode.getBoundingClientRect().top -
contentDiffY) + 'px';
} else {
this.dragClone.classList.add('dropped-on-other-page');
}
} }
this.tilePage.positionTile_(this.index); this.landedOnTrash = false;
if (this.tilePage.selected) {
// The tile's contents may have moved following the respositioning;
// adjust for that.
var contentDiffX = this.dragClone.firstChild.offsetLeft -
this.firstChild.offsetLeft;
var contentDiffY = this.dragClone.firstChild.offsetTop -
this.firstChild.offsetTop;
this.dragClone.style.left = (this.gridX + this.parentNode.offsetLeft -
contentDiffX) + 'px';
this.dragClone.style.top =
(this.gridY + this.parentNode.getBoundingClientRect().top -
contentDiffY) + 'px';
} else {
// When we're showing another page and a drag fails or gets cancelled,
// the tile shrinks to a dot.
this.dragClone.firstChild.style.webkitTransform = 'scale(0)';
}
}, },
/** /**
...@@ -250,6 +244,8 @@ cr.define('ntp4', function() { ...@@ -250,6 +244,8 @@ cr.define('ntp4', function() {
clone.parentNode.removeChild(clone); clone.parentNode.removeChild(clone);
this.eventTracker.remove(clone, 'webkitTransitionEnd'); this.eventTracker.remove(clone, 'webkitTransitionEnd');
this.classList.remove('dragging'); this.classList.remove('dragging');
if (this.firstChild.finalizeDrag)
this.firstChild.finalizeDrag();
}, },
/** /**
...@@ -895,23 +891,20 @@ cr.define('ntp4', function() { ...@@ -895,23 +891,20 @@ cr.define('ntp4', function() {
if (!((index == this.dragItemIndex_) && this.withinPageDrag_)) { if (!((index == this.dragItemIndex_) && this.withinPageDrag_)) {
var adjustedIndex = this.currentDropIndex_ + var adjustedIndex = this.currentDropIndex_ +
(index > this.dragItemIndex_ ? 1 : 0); (index > this.dragItemIndex_ ? 1 : 0);
if (currentlyDraggingTile) { if (this.withinPageDrag_) {
var originalPage = currentlyDraggingTile.tilePage;
this.tileGrid_.insertBefore( this.tileGrid_.insertBefore(
currentlyDraggingTile, currentlyDraggingTile,
this.tileElements_[adjustedIndex]); this.tileElements_[adjustedIndex]);
if (originalPage != this)
originalPage.cleanupDrag();
this.tileMoved(currentlyDraggingTile); this.tileMoved(currentlyDraggingTile);
// Dropping the icon may cause topMargin to change, but changing it
// now would cause everything to move (annoying), so we leave it
// alone. The top margin will be re-calculated next time the window is
// resized or the page is selected.
} else { } else {
this.addOutsideData(e.dataTransfer, adjustedIndex); this.addDragData(e.dataTransfer, adjustedIndex);
currentlyDraggingTile.tilePage.cleanupDrag();
} }
// Dropping the icon may cause topMargin to change, but changing it
// now would cause everything to move (annoying), so we leave it
// alone. The top margin will be re-calculated next time the window is
// resized or the page is selected.
} }
this.classList.remove('animating-tile-page'); this.classList.remove('animating-tile-page');
...@@ -927,9 +920,8 @@ cr.define('ntp4', function() { ...@@ -927,9 +920,8 @@ cr.define('ntp4', function() {
if (originalPage == this) if (originalPage == this)
return; return;
this.tileGrid_.appendChild(currentlyDraggingTile); this.addDragData(null, this.tileElements_.length - 1);
originalPage.cleanupDrag(); originalPage.cleanupDrag();
this.tileMoved(currentlyDraggingTile);
}, },
/** /**
...@@ -985,14 +977,12 @@ cr.define('ntp4', function() { ...@@ -985,14 +977,12 @@ cr.define('ntp4', function() {
}, },
/** /**
* Called to accept a drag drop. * Called to accept a drag drop. Will not be called for in-page drops.
* @param {Object} dataTransfer The data transfer object that holds the drop * @param {Object} dataTransfer The data transfer object that holds the drop
* data. * data. This should only be used if currentlyDraggingTile is null.
* @param {number} index The tile index at which the drop occurred. * @param {number} index The tile index at which the drop occurred.
*/ */
addOutsideData: function(dataTransfer, index) { addDragData: function(dataTransfer, index) {
// This should not get called unless there is a non-default
// implementation.
assert(false); assert(false);
}, },
......
...@@ -33,11 +33,7 @@ cr.define('ntp4', function() { ...@@ -33,11 +33,7 @@ cr.define('ntp4', function() {
if (!tile) if (!tile)
return false; return false;
var app = tile.querySelector('.app'); return tile.firstChild.canBeRemoved();
if (!app)
return false;
return app.appData.can_uninstall;
}, },
/** /**
...@@ -67,15 +63,8 @@ cr.define('ntp4', function() { ...@@ -67,15 +63,8 @@ cr.define('ntp4', function() {
e.preventDefault(); e.preventDefault();
var tile = ntp4.getCurrentlyDraggingTile(); var tile = ntp4.getCurrentlyDraggingTile();
var app = tile.querySelector('.app'); tile.firstChild.removeFromChrome();
if (!app) tile.landedOnTrash = true;
return;
chrome.send('uninstallApp', [app.appData.id, true]);
var page = tile.tilePage;
tile.parentNode.removeChild(tile);
page.cleanupDrag();
}, },
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment