At the Sea Sprint my task was to help build the drag and drop layout system for Deco, working along with Nathan van Gheem and Gonzalo Almeida. This was an area where a lot of work was needed. Deco used to have these capabilities, but they had been lost earlier this year when Rok Garbas did a lot of refactoring to make sure that all of Deco’s javascript and CSS resources could be loaded in a separate iframe, to provide isolation from the frontend site. So at the start of the sprint, it was possible to drag tiles into a column, but there was no way to arrange rows and columns using a GUI. By the end of the sprint, we had re-created the ability to add, arrange, and remove rows and columns.

A brief demo:

At the start of the sprint we were hoping that we could use one of the two existing codebases for creating tile layouts as a starting point, but unfortunately that didn’t seem possible. The first codebase is the one that Rob Gietema wrote for Deco several years ago. If you’ve seen screencasts of Deco in the past, this is what you saw. It provided an elegant user experience where the user could simply drag a tile around, and rows and columns would be created automatically if the tile was near the edge of an existing row or column. Unfortunately, this code was not written to support dragging between two separate iframes, and that is hard to retrofit into it (more on that below). In addition, several people who have been building Deco-like systems for clients have discovered downsides to creating rows and columns automatically. For one, it was too easy for inexperienced users to accidentally create rows and columns when they didn’t want to. Secondly, in some cases the action of creating layout (rows and columns) needs to be separated from the action of placing tiles in that layout so that different groups can be granted permission to do these different actions.

We also considered starting with the layout code in the collective.cover add-on that was created at the Cafecito sprint earlier this year. This implementation does separate the act of arranging rows and columns from the act of placing tiles in them. Unfortunately, it was also not built to handle drag and drop across multiple iframes. And it depends on the JQuery UI drag and drop library, which is too large and doesn’t perform well, according to Rok. So in the end we decided to do a new implementation using ideas from both of the existing ones.

Now, there is a dirty little secret about how we handle dragging things from the Deco toolbar iframe to the main content frame: the dragging only happens in the toolbar. When you start dragging, the toolbar expands to the entire size of the window (but transparent), and when you stop, it shrinks again. The problem was that we couldn’t get the drag and drop library (jquery.event.drag and jquery.event.drop) to bind events in both iframes, so the toolbar was not getting drag events when the mouse was over the content. I spent a few hours trying to get the library to bind events correctly before giving up, for the time being. So we went with the trick, invented by Rok, which allows us to receive all the events in the toolbar iframe, and then the mouse position is compared to the positions of the potential drop targets in the content frame.

One of the big questions was what grid system to use for positioning the rows and columns. The javascript needs to be able to make some assumptions about what sort of grid system is present in order to position things correctly. We decided to try using the grid from Twitter Bootstrap, since it is widely used and developed at this point. But which bootstrap grid? It has fixed and variable-width, responsive and nonresponsive versions. For now we are using the variable-width nonresponsive version. So it will resize with the window (or not resize if you fix the width of the visual portal wrapper), but not merge columns when the window gets narrow. However, in theory it should be possible to use a different grid system as long as it uses classes called “deco-row,” “deco-column,” and “deco-span1,” “deco-span2,” etc. At this point it assumes a 12-column grid, but that can easily be made configurable.

One thing that I think is nice about our implementation is how deleting columns and rows is handled. You can only delete a column if it has no tiles in it. Any empty columns show a message saying you can either add tiles or delete, where “delete” is a link that removes the column. This avoids the question of what to do with tiles that are in the column when it is deleted. And it avoids having hovering buttons for columns and rows in addition to tiles, which could get pretty visually confusing.

The implementation has a good start, but there’s plenty still to do. Next steps are:

1. Add acceptance tests using robotframework and unit tests using busterjs.

2. There are still a few bugs that result in a broken layout.

3. Make it smarter about sizing new columns (they should divide the column they are being dropped on, rather than starting at a width of 1 grid cell).

4. Figure out how to support undo and canceling changes to the layout. At this point the layout is saved when you add or remove a tile. So you can cancel your changes before that, but once you have changed tiles (which causes persistent changes) you can’t.

5. Ideally, do a bit of refactoring to turn the layout system into a more generally usable jquery plugin that could also be used by collective.cover.

Many thanks to the sprint organizers, sponsors, and attendees for contributing to such an enjoyable and productive weekend!

Filed October 4th, 2012 under Javascript, Technical, Report

No Comments


No comments yet.

Leave a comment

To comment on this blog you will need to log in or create an account first.