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 83cf24b00a2a..178ffed3adbb 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 f4daeb7267f0..86984eed0391 100644 --- a/shared/elements/gaia_grid/js/grid_dragdrop.js +++ b/shared/elements/gaia_grid/js/grid_dragdrop.js @@ -53,6 +53,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));