diff --git a/apps/sharedtest/test/unit/elements/gaia_grid/grid_dragdrop_test.js b/apps/sharedtest/test/unit/elements/gaia_grid/grid_dragdrop_test.js
index 79a6e2f55d2b..420a0ec61b03 100644
--- a/apps/sharedtest/test/unit/elements/gaia_grid/grid_dragdrop_test.js
+++ b/apps/sharedtest/test/unit/elements/gaia_grid/grid_dragdrop_test.js
@@ -9,6 +9,7 @@ require('/shared/elements/gaia_grid/js/items/grid_item.js');
require('/shared/elements/gaia_grid/js/items/divider.js');
require('/shared/elements/gaia_grid/js/items/icon.js');
require('/shared/elements/gaia_grid/js/items/bookmark.js');
+require('/shared/elements/gaia_grid/js/items/placeholder.js');
require('/shared/elements/gaia_grid/script.js');
suite('GaiaGrid > DragDrop', function() {
diff --git a/dev_apps/collection/view.html b/dev_apps/collection/view.html
index 2dc653439893..8b562a55d288 100644
--- a/dev_apps/collection/view.html
+++ b/dev_apps/collection/view.html
@@ -16,6 +16,7 @@
+
diff --git a/dev_apps/home2/index.html b/dev_apps/home2/index.html
index 1da4324b7736..1909d4c36e85 100644
--- a/dev_apps/home2/index.html
+++ b/dev_apps/home2/index.html
@@ -36,6 +36,7 @@
+
diff --git a/dev_apps/home2/js/stores/item.js b/dev_apps/home2/js/stores/item.js
index 5c715fdb0a06..470a4e5bebf7 100644
--- a/dev_apps/home2/js/stores/item.js
+++ b/dev_apps/home2/js/stores/item.js
@@ -108,7 +108,10 @@
newTxn(DB_ITEM_STORE, 'readwrite', function(txn, store) {
store.clear();
for (var i = 0, iLen = entries.length; i < iLen; i++) {
- store.put(entries[i].detail);
+ var entry = entries[i];
+ if (entry.persistToDB) {
+ store.put(entry.detail);
+ }
}
}, callback);
},
diff --git a/dev_apps/home2/test/marionette/lib/home2.js b/dev_apps/home2/test/marionette/lib/home2.js
index 8ab8d27b4897..a65c649da6d4 100644
--- a/dev_apps/home2/test/marionette/lib/home2.js
+++ b/dev_apps/home2/test/marionette/lib/home2.js
@@ -30,7 +30,7 @@ Home2.URL = 'app://home2.gaiamobile.org';
Home2.Selectors = {
search: '#search',
- firstIcon: '#icons div.icon',
+ firstIcon: '#icons div.icon:not(.placeholder)',
dividers: '#icons div.divider'
};
diff --git a/shared/elements/gaia_grid/js/grid_dragdrop.js b/shared/elements/gaia_grid/js/grid_dragdrop.js
index d127df3e5447..f94d5b935e0b 100644
--- a/shared/elements/gaia_grid/js/grid_dragdrop.js
+++ b/shared/elements/gaia_grid/js/grid_dragdrop.js
@@ -52,6 +52,13 @@
return 1 + activeScaleAdjust;
},
+ /**
+ * Returns true if we are currently dragging an icon.
+ */
+ get inDragAction() {
+ return this.target && this.target.classList.contains('active');
+ },
+
/**
* Begins the drag/drop interaction.
* Enlarges the icon.
diff --git a/shared/elements/gaia_grid/js/grid_view.js b/shared/elements/gaia_grid/js/grid_view.js
index 1cf296f41f8b..0085775ee4f3 100644
--- a/shared/elements/gaia_grid/js/grid_view.js
+++ b/shared/elements/gaia_grid/js/grid_view.js
@@ -3,6 +3,7 @@
/* global GridDragDrop */
/* global GridLayout */
/* global GridZoom */
+/* global Placeholder */
(function(exports) {
@@ -145,16 +146,72 @@
}
},
+ /**
+ * Removes placeholders from the grid.
+ */
+ removeAllPlaceholders: function() {
+ var toSplice = [];
+ var previousItem;
+ this.items.forEach(function(item, idx) {
+ if (item instanceof Placeholder) {
+
+ // If the previous item is a divider, and we are in edit mode
+ // we do not remove the placeholder. This is so the section will
+ // remain even if the user drags the icon around. Bug 1014982
+ if (previousItem && previousItem instanceof Divider &&
+ this.dragdrop && this.dragdrop.inDragAction) {
+ return;
+ }
+
+ toSplice.push(idx);
+ }
+
+ previousItem = item;
+ }, this);
+
+ toSplice.reverse().forEach(function(idx) {
+ var item = this.items[idx];
+ item.element && item.element.parentNode.removeChild(item.element);
+ this.items.splice(idx, 1);
+ }, this);
+ },
+
+ /**
+ * Creates placeholders and injects them into the grid.
+ * @param {Array} coordinates [x,y] coordinates on the grid of the first
+ * item in grid units.
+ * @param {Integer} idx The position of the first placeholder.
+ * @param {Integer} idx The number of placeholders to create.
+ */
+ createPlaceholders: function(coordinates, idx, count) {
+ for (var i = 0; i < count; i++) {
+ var itemCoords = [
+ coordinates[0] + i,
+ coordinates[1]
+ ];
+
+ var item = new Placeholder();
+ this.items.splice(idx + i, 0, item);
+ item.render(itemCoords, idx + i);
+ }
+ },
+
/**
* Renders all icons.
* Positions app icons and dividers accoriding to available space
* on the grid.
+ * @param {Integer} from The index to start rendering from.
*/
render: function(from) {
var self = this;
+ this.removeAllPlaceholders();
this.cleanItems();
- from = from || 0;
+
+ // Start rendering from one before the drop target. If not,
+ // we may drop over the divider and miss rendering an icon.
+ from = from - 1 || 0;
+
// TODO This variable should be an argument of this method. See
// https://bugzilla.mozilla.org/show_bug.cgi?id=1010742#c4
var to = this.items.length - 1;
@@ -183,9 +240,19 @@
// step the y-axis.
if (x > 0 && item.gridWidth > 1 &&
x + item.gridWidth >= this.layout.perRow) {
+
+ // Insert placeholders to fill remaining space
+ var remaining = this.layout.perRow - x;
+ this.createPlaceholders([x, y], idx, remaining);
+
+ // Increment the current index due to divider insertion
+ idx += remaining;
+ to += remaining;
+ item = this.items[idx];
+
// Step the y-axis by the size of the last row.
// For now we just check the height of the last item.
- var lastItem = this.items[idx - 1];
+ var lastItem = this.items[idx - (remaining + 1)];
step(lastItem);
}
diff --git a/shared/elements/gaia_grid/js/items/grid_item.js b/shared/elements/gaia_grid/js/items/grid_item.js
index f94fb40c986f..76dfdbc456d5 100644
--- a/shared/elements/gaia_grid/js/items/grid_item.js
+++ b/shared/elements/gaia_grid/js/items/grid_item.js
@@ -32,6 +32,11 @@
scale: 1,
+ /**
+ * Whether or not this icon will persist to the database.
+ */
+ persistToDB: true,
+
/**
* Returns a reference to the current grid.
* We can currently only have one grid per page.
diff --git a/shared/elements/gaia_grid/js/items/placeholder.js b/shared/elements/gaia_grid/js/items/placeholder.js
new file mode 100644
index 000000000000..559079cccf91
--- /dev/null
+++ b/shared/elements/gaia_grid/js/items/placeholder.js
@@ -0,0 +1,70 @@
+'use strict';
+/* global GridItem */
+
+(function(exports) {
+
+ /**
+ * The placeholder represents an empty place on the grid.
+ * This is generally used to detect distance from an empty spot on the grid.
+ */
+ function Placeholder() {
+ this.detail = {
+ type: 'placeholder',
+ index: 0
+ };
+ }
+
+ Placeholder.prototype = {
+
+ __proto__: GridItem.prototype,
+
+ /**
+ * Returns the height in pixels of each icon.
+ */
+ get pixelHeight() {
+ return this.grid.layout.gridItemHeight;
+ },
+
+ /**
+ * Width in grid units for each icon.
+ */
+ gridWidth: 1,
+
+ /**
+ * Placeholders do not save. They are re-generated on render calls.
+ */
+ persistToDB: false,
+
+ get name() {
+ return '';
+ },
+
+ /**
+ * Renders a transparent placeholder.
+ * @param {Array} coordinates Grid coordinates to render to.
+ * @param {Number} index The index of the items list of this item.
+ */
+ render: function(coordinates, index) {
+ this.scale = this.grid.layout.percent;
+
+ // Generate an element if we need to
+ if (!this.element) {
+ var tile = document.createElement('div');
+ //tile.style.backgroundColor = '#ff0000'; // Useful for debugging.
+ tile.className = 'icon placeholder';
+ tile.style.height = this.grid.layout.gridItemHeight + 'px';
+ this.element = tile;
+ this.grid.element.appendChild(tile);
+ }
+
+ var x = this.x = coordinates[0] * this.grid.layout.gridItemWidth;
+ var y = this.y = this.grid.layout.offsetY;
+ this.setPosition(index);
+
+ this.transform(x, y, this.grid.layout.percent);
+ },
+ };
+
+ exports.Placeholder = Placeholder;
+
+}(window));