This is a portal style example using Drag & Drop event bubbling and animation.
//Use loader to grab the modules needed YUI().use('dd', 'anim', 'yql', 'cookie', 'json', function(Y) { //Make this an Event Target so we can bubble to it var Portal = function() { Portal.superclass.constructor.apply(this, arguments); }; Portal.NAME = 'portal'; Y.extend(Portal, Y.Base); //This is our new bubble target.. Y.Portal = new Portal(); //Setup some private variables.. var goingUp = false, lastY = 0, trans = {}; //The list of feeds that we are going to use var feeds = { 'ynews': { id: 'ynews', title: 'Yahoo! US News', url: 'rss.news.yahoo.com/rss/us' }, 'yui': { id: 'yui', title: 'YUI Blog', url: 'feeds.yuiblog.com/YahooUserInterfaceBlog' }, 'slashdot': { id: 'slashdot', title: 'Slashdot', url: 'rss.slashdot.org/Slashdot/slashdot' }, 'ajaxian': { id: 'ajaxian', title: 'Ajaxian', url: 'feeds.feedburner.com/ajaxian' }, 'daringfireball': { id: 'daringfireball', title: 'Daring Fireball', url: 'daringfireball.net/index.xml' }, 'smashing': { id: 'smashing', title: 'Smashing Magazine', url: 'www.smashingmagazine.com/wp-rss.php' } }; //Simple method for stopping event propagation //Using this so we can detach it later var stopper = function(e) { e.stopPropagation(); }; //Get the order, placement and minned state of the modules and save them to a cookie var _setCookies = function() { var dds = Y.DD.DDM._drags; var list = {}; //Walk all the drag elements Y.each(dds, function(v, k) { var par = v.get('node').get('parentNode'); //Find all the lists with drag items in them if (par.test('ul.list')) { if (!list[par.get('id')]) { list[par.get('id')] = []; } } }); //Walk the list Y.each(list, function(v, k) { //Get all the li's in the list var lis = Y.all('#' + k + ' li.item'); lis.each(function(v2, k2) { //Get the drag instance for the list item var dd = Y.DD.DDM.getDrag('#' + v2.get('id')); //Get the mod node var mod = dd.get('node').one('div.mod'); //Is it minimized var min = (mod.hasClass('minned')) ? true : false; //Setup the cookie data list[k][list[k].length] = { id: dd.get('data').id, min: min }; }); }); //JSON encode the cookie data var cookie = Y.JSON.stringify(list); //Set the sub-cookie Y.Cookie.setSub('yui', 'portal', cookie); }; //Helper method for creating the feed DD on the left var _createFeedDD = function(node, data) { //Create the DD instance var dd = new Y.DD.Drag({ node: node, data: data, bubbles: Y.Portal }).plug(Y.Plugin.DDProxy, { moveOnEnd: false, borderStyle: 'none' }); //Setup some stopper events dd.on('drag:start', _handleStart); dd.on('drag:end', stopper); dd.on('drag:drophit', stopper); }; //Handle the node:click event var _nodeClick = function(e) { //Is the target an href? if (e.target.test('a')) { var a = e.target, anim = null, div = a.get('parentNode').get('parentNode'); //Did they click on the min button if (a.hasClass('min')) { //Get some node references var ul = div.one('ul'), h2 = div.one('h2'), h = h2.get('offsetHeight'), hUL = ul.get('offsetHeight'), inner = div.one('div.inner'); //Create an anim instance on this node. anim = new Y.Anim({ node: inner }); //Is it expanded? if (!div.hasClass('minned')) { //Set the vars for collapsing it anim.setAttrs({ to: { height: 0 }, duration: '.25', easing: Y.Easing.easeOut, iteration: 1 }); //On the end, toggle the minned class //Then set the cookies for state anim.on('end', function() { div.toggleClass('minned'); _setCookies(); }); } else { //Set the vars for expanding it anim.setAttrs({ to: { height: (hUL) }, duration: '.25', easing: Y.Easing.easeOut, iteration: 1 }); //Toggle the minned class //Then set the cookies for state div.toggleClass('minned'); _setCookies(); } //Run the animation anim.run(); } //Was close clicked? if (a.hasClass('close')) { //Get some Node references.. var li = div.get('parentNode'), id = li.get('id'), dd = Y.DD.DDM.getDrag('#' + id), data = dd.get('data'), item = Y.one('#' + data.id); //Destroy the DD instance. dd.destroy(); //Setup the animation for making it disappear anim = new Y.Anim({ node: div, to: { opacity: 0 }, duration: '.25', easing: Y.Easing.easeOut }); anim.on('end', function() { //On end of the first anim, setup another to make it collapse var anim = new Y.Anim({ node: div, to: { height: 0 }, duration: '.25', easing: Y.Easing.easeOut }); anim.on('end', function() { //Remove it from the document li.get('parentNode').removeChild(li); item.removeClass('disabled'); //Setup a drag instance on the feed list _createFeedDD(item, data); _setCookies(); }); //Run the animation anim.run(); }); //Run the animation anim.run(); } //Stop the click e.halt(); } }; //This creates the module, either from a drag event or from the cookie load var setupModDD = function(mod, data, dd) { var node = mod; //Listen for the click so we can react to the buttons node.one('h2').on('click', _nodeClick); //Remove the event's on the original drag instance dd.detachAll('drag:start'); dd.detachAll('drag:end'); dd.detachAll('drag:drophit'); //It's a target dd.set('target', true); //Setup the handles dd.addHandle('h2').addInvalid('a'); //Remove the mouse listeners on this node dd._unprep(); //Update a new node dd.set('node', mod); //Reset the mouse handlers dd._prep(); Y.YQL('select * from feed where url="http:/'+'/' + data.url + '"', (function(mod) { return function(r) { if (r && r.query && r.query.results) { var inner = mod.one('div.inner'), html = ''; //Walk the list and create the news list Y.each(r.query.results, function(items) { Y.each(items, function(v, k) { if (k < 5) { if (v.title && v.title.content) { v.title = v.title.content; } if (v.link && (Y.Lang.isArray(v.link))) { v.link = v.link[0]; } if (v.link && (Y.Lang.isObject(v.link))) { v.link = v.link.href; } html += Y.Lang.sub('<li><a href="{link}" target="_blank">{title}</a>', v); } }); }); //Set the innerHTML of the module inner.set('innerHTML', '<ul>' + html + '</ul>'); if (Y.DD.DDM.activeDrag) { //If we are still dragging, update the proxy element too.. var proxy_inner = Y.DD.DDM.activeDrag.get('dragNode').one('div.inner'); proxy_inner.set('innerHTML', '<ul>' + html + '</ul>'); } } } })(mod)); }; //Helper method to create the markup for the module.. var createMod = function(feed) { var str = '<li class="item">' + '<div class="mod">' + '<h2><strong>' + feed.title + '</strong> <a title="minimize module" class="min" href="#">-</a>' + '<a title="close module" class="close" href="#">X</a></h2>' + '<div class="inner">' + ' <div class="loading">Feed loading, please wait..</div>' + '</div>' + '</div>' + '</li>'; return Y.Node.create(str); }; //Handle the start Drag event on the left side var _handleStart = function(e) { //Stop the event stopper(e); //Some private vars var drag = this, list3 = Y.one('#list1'), mod = createMod(drag.get('data')); //Add it to the first list list3.appendChild(mod); //Set the item on the left column disabled. drag.get('node').addClass('disabled'); //Set the node on the instance drag.set('node', mod); //Add some styles to the proxy node. drag.get('dragNode').setStyles({ opacity: '.5', borderStyle: 'none', width: '320px', height: '61px' }); //Update the innerHTML of the proxy with the innerHTML of the module drag.get('dragNode').set('innerHTML', drag.get('node').get('innerHTML')); //set the inner module to hidden drag.get('node').one('div.mod').setStyle('visibility', 'hidden'); //add a class for styling drag.get('node').addClass('moving'); //Setup the DD instance setupModDD(mod, drag.get('data'), drag); //Remove the listener this.detach('drag:start', _handleStart); }; //Walk through the feeds list and create the list on the left var feedList = Y.one('#feeds ul'); Y.each(feeds, function(v, k) { var li = Y.Node.create('<li id="' + k + '">' + v.title + '</li>'); feedList.appendChild(li); //Create the DD instance for this item _createFeedDD(li, v); }); //This does the calculations for when and where to move a module var _moveMod = function(drag, drop) { if (drag.get('node').hasClass('item')) { var dragNode = drag.get('node'), dropNode = drop.get('node'), append = false, padding = 30, xy = drag.mouseXY, region = drop.region, middle1 = region.top + ((region.bottom - region.top) / 2), middle2 = region.left + ((region.right - region.left) / 2), dir = false, dir1 = false, dir2 = false; //We could do something a little more fancy here, but we won't ;) if ((xy[1] < (region.top + padding))) { dir1 = 'top'; } if ((region.bottom - padding) < xy[1]) { dir1 = 'bottom'; } if ((region.right - padding) < xy[0]) { dir2 = 'right'; } if ((xy[0] < (region.left + padding))) { dir2 = 'left'; } dir = dir2; if (dir2 === false) { dir = dir1; } switch (dir) { case 'top': var next = dropNode.get('nextSibling'); if (next) { dropNode = next; } else { append = true; } break; case 'bottom': break; case 'right': case 'left': break; } if ((dropNode !== null) && dir) { if (dropNode && dropNode.get('parentNode')) { if (!append) { dropNode.get('parentNode').insertBefore(dragNode, dropNode); } else { dropNode.get('parentNode').appendChild(dragNode); } } } //Resync all the targets because something moved.. Y.Lang.later(50, Y, function() { Y.DD.DDM.syncActiveShims(true); }); } }; /* Handle the drop:enter event Now when we get a drop enter event, we check to see if the target is an LI, then we know it's out module. Here is where we move the module around in the DOM. */ Y.Portal.on('drop:enter', function(e) { if (!e.drag || !e.drop || (e.drop !== e.target)) { return false; } if (e.drop.get('node').get('tagName').toLowerCase() === 'li') { if (e.drop.get('node').hasClass('item')) { _moveMod(e.drag, e.drop); } } }); //Handle the drag:drag event //On drag we need to know if they are moved up or down so we can place the module in the proper DOM location. Y.Portal.on('drag:drag', function(e) { var y = e.target.mouseXY[1]; if (y < lastY) { goingUp = true; } else { goingUp = false; } lastY = y; }); /* Handle the drop:hit event Now that we have a drop on the target, we check to see if the drop was not on a LI. This means they dropped on the empty space of the UL. */ Y.Portal.on('drag:drophit', function(e) { var drop = e.drop.get('node'), drag = e.drag.get('node'); if (drop.get('tagName').toLowerCase() !== 'li') { if (!drop.contains(drag)) { drop.appendChild(drag); } } }); //Handle the drag:start event //Use some CSS here to make our dragging better looking. Y.Portal.on('drag:start', function(e) { var drag = e.target; if (drag.target) { drag.target.set('locked', true); } drag.get('dragNode').set('innerHTML', drag.get('node').get('innerHTML')); drag.get('dragNode').setStyle('opacity','.5'); drag.get('node').one('div.mod').setStyle('visibility', 'hidden'); drag.get('node').addClass('moving'); }); //Handle the drag:end event //Replace some of the styles we changed on start drag. Y.Portal.on('drag:end', function(e) { var drag = e.target; if (drag.target) { drag.target.set('locked', false); } drag.get('node').setStyle('visibility', ''); drag.get('node').one('div.mod').setStyle('visibility', ''); drag.get('node').removeClass('moving'); drag.get('dragNode').set('innerHTML', ''); _setCookies(); }); //Handle going over a UL, for empty lists Y.Portal.on('drop:over', function(e) { var drop = e.drop.get('node'), drag = e.drag.get('node'); if (drop.get('tagName').toLowerCase() !== 'li') { if (!drop.contains(drag)) { drop.appendChild(drag); Y.Lang.later(50, Y, function() { Y.DD.DDM.syncActiveShims(true); }); } } }); //Create simple targets for the main lists.. var uls = Y.all('#play ul.list'); uls.each(function(v, k) { var tar = new Y.DD.Drop({ node: v, padding: '20 0', bubbles: Y.Portal }); }); //Get the cookie data var cookie = Y.Cookie.getSub('yui', 'portal'); if (cookie) { //JSON parse the stored data var obj = Y.JSON.parse(cookie); //Walk the data Y.each(obj, function(v, k) { //Get the node for the list var list = Y.one('#' + k); //Walk the items in this list Y.each(v, function(v2, k2) { //Get the drag for it var drag = Y.DD.DDM.getDrag('#' + v2.id); //Create the module var mod = createMod(drag.get('data')); if (v2.min) { //If it's minned add some CSS mod.one('div.mod').addClass('minned'); mod.one('div.inner').setStyle('height', '0px'); } //Add it to the list list.appendChild(mod); //Set the drag listeners drag.get('node').addClass('disabled'); drag.set('node', mod); drag.set('dragNode', Y.DD.DDM._proxy); drag.detachAll('drag:start'); drag.detachAll('drag:end'); drag.detachAll('drag:drophit'); drag._unprep(); //Setup the new Drag listeners setupModDD(mod, drag.get('data'), drag); }); }); } });