A brief demo: http://www.screencast.com/t/1BFjd89Xio
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 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!