Saturday, July 28, 2012

DRAG & DROP :

DRAG & DROP :
This example shows instead of select list of values you can drag and drop as shown below

SF Drag Drop MultiSelect List Demo

======================================

Visual force page (DemoSfDragDropList)

<apex:page controller="DemoSFDragDropList">
 <apex:sectionHeader title="http://www.jitenderbhatia.com" subtitle="SF Drag Drop MultiSelect List Demo"/>
 <c:SFDragDropList list1="India;United States;France;Germany;Japan" outputFieldId="list_2_serialised"/>
 <div style="clear:both">
 <apex:form >
     <input type="hidden" id="list_2_serialised" name="list_2_serialised" value="{!list2FinalItems}"/>
     <apex:commandButton action="{!showSelectedItems}" reRender="selectedItems" value="Show Selected Value" status="status"/>
 </apex:form>
 <apex:actionStatus id="status" startText="Fetching..."></apex:actionStatus>
 <apex:outputPanel id="selectedItems">
     <i>{!list2FinalItems}</i>
 </apex:outputPanel>
 </div>
</apex:page>


Apex Class(DemoSFDragDropList)

public class DemoSFDragDropList{
    public string list2FinalItems{get;set;}
    public DemoSFDragDropList(){
        
    }
    
    public pagereference showSelectedItems(){
        list2FinalItems = ApexPages.currentpage().getParameters().get('list_2_serialised');
        return null;
    }
    
    @istest
    private static void testthis(){
        DemoSFDragDropList cont = new DemoSFDragDropList();
        cont.showSelectedItems();    
    }
}


SFDragDropList  (Visualforce Component)

<apex:component controller="SFDragDropListController">
<apex:attribute name="list1" description="values for list one" type="string" assignTo="{!list1}" required="true"/>
<apex:attribute name="list2" description="values for list two" type="string" assignTo="{!list2}"/>
<apex:attribute name="outputFieldId" description="values which is stored in target list, fill in given textbox" type="string" assignTo="{!outputFieldId}" required="true"/>
<c:BaseDragDropComponent />
<script>
$(function(){
    mychange = function ( $list ){
        $( '#{!outputFieldId}').val( $.dds.serialize( 'list_2' ) );
    }
    $('ul').drag_drop_selectable({
        onListChange:mychange
    });
    $( '#{!outputFieldId}').val( $.dds.serialize( 'list_2' ) );
});
</script>

<div class='panel'>
<h2>Source List</h2>
<ul id="list_1">
    <apex:repeat value="{!list1Items}" var="item">
        <li id="{!item}">{!item}</li>
    </apex:repeat>
</ul>
</div>

<div class='panel'>
<h2>Target List</h2>
<ul id="list_2">
    <apex:repeat value="{!list2Items}" var="item">
        <li id="{!item}">{!item}</li>
    </apex:repeat>
</ul>
</div>
</apex:component>
                   

public class SFDragDropListController{
    public string list1 {get;set;}
    public string list2 {get;set;}
    public SFDragDropListController(){
    }
    
    public List<string> getList1Items(){
        return list1 != null ? list1.split(';') : null;        
    }
    
    public List<string> getList2Items(){
        return list2 != null ? list2.split(';') : null;        
    }
    
    @istest
    private static void testthis(){
        SFDragDropListController cont = new SFDragDropListController();
        cont.getList1Items();    
        cont.getList2Items();    
    }
}


BaseDragDropComponent

<apex:component >
<style type='text/css'>
.panel {float:left;width:300px;margin:20px;}

.panel ul {
    list-style-type:none;
    border:1px solid #ccc;
    background:#e6e6e6;
    padding:20px;
    min-height:150px;
    width:200px;
}

.panel li {
    display:block;
    border:1px solid #999;
    background:#fff;
    width:140px;
    padding:5px 10px;
    margin-bottom:5px;
}

.dds_selected {
    background:#ffc;
}
.dds_ghost {
    opacity:0.5;
}
.dds_move {
    background:#cfc;
}
.panel .dds_hover {
    background:#fc9;
    border:3px dashed #c96;
}

.holder {
    border:3px dashed #333;
    background:#fff;
}

</style>
<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js'></script>
<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jqueryui/1.5.3/jquery-ui.min.js'></script>
<script type='text/javascript'>
/**
 *   Multi-Select And Drag
 *
 *   Not elegant solution to this problem, but the problem, despite being easily
 *   desribed is not simple. This code is more a proof of concept, but should be
 *   extendable by anyone with the time / inclination, there I grant permission
 *   for it to be re-used in accodance with the MIT license:
 *
 *   Copyright (c) 2009 Chris Walker (http://thechriswalker.net/)
 *
 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 *   of this software and associated documentation files (the "Software"), to deal
 *   in the Software without restriction, including without limitation the rights
 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *   copies of the Software, and to permit persons to whom the Software is
 *   furnished to do so, subject to the following conditions:
 *
 *   The above copyright notice and this permission notice shall be included in
 *   all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *   THE SOFTWARE.
 */
(function($){
    $.fn.drag_drop_selectable = function( options ){
        $.fn.captureKeys();
        var $_this = this;
        var settings = $.extend({},$.fn.drag_drop_selectable.defaults,options||{});
        return $(this).each(function(i){
            var $list = $(this);
            var list_id = $.fn.drag_drop_selectable.unique++;
            $.fn.drag_drop_selectable.stack[list_id]={"selected":[ ],"all":[ ]};//we hold all as well as selected so we can invert and stuff...
            $list.attr('dds',list_id);
            $.fn.drag_drop_selectable.settings[list_id] = settings;
            $list.find('li')
            //make all list elements selectable with click and ctrl+click.
            .each(function(){
                var $item = $(this);
                //add item to list!
                var item_id = $.fn.drag_drop_selectable.unique++;
                $item.attr('dds',item_id);
                $.fn.drag_drop_selectable.stack[list_id].all.push(item_id);
                $(this).bind('click.dds_select',function(e){
                    if($.fn.isPressed(CTRL_KEY) || ($.fn.drag_drop_selectable.stack[$.fn.drag_drop_selectable.getListId( $(this).attr('dds') )].selected.length == 1 && $(this).hasClass('dds_selected'))){
                        //ctrl pressed add to selection
                        $.fn.drag_drop_selectable.toggle(item_id);
                    }else{
                        //ctrl not pressed make new selection
                        $.fn.drag_drop_selectable.replace(item_id);
                    }
                }).bind('dds.select',function(){
                    $(this).addClass('dds_selected').addClass( $.fn.drag_drop_selectable.settings[$.fn.drag_drop_selectable.getListId($(this).attr('dds'))].selectClass );

                }).bind('dds.deselect',function(){
                    $(this).removeClass('dds_selected').removeClass( $.fn.drag_drop_selectable.settings[$.fn.drag_drop_selectable.getListId($(this).attr('dds'))].selectClass );;
                }).css({cursor:'pointer'});
            })
            //OK so they are selectable. now I need to make them draggable, in such a way that they pick up their friends when dragged. hmmm how do I do that?
            .draggable({
                 helper:function(){
                    $clicked = $(this);
                    if( ! $clicked.hasClass('dds_selected') ){
                        //trigger the click function.
                        $clicked.trigger('click.dds_select');
                    }
                    var list = $.fn.drag_drop_selectable.getListId($clicked.attr('dds'));
                    var $helper = $('<div dds_list="'+list+'"><div style="margin-top:-'+$.fn.drag_drop_selectable.getMarginForDragging( $clicked )+'px;" /></div>').append( $.fn.drag_drop_selectable.getSelectedForDragging( $clicked.attr('dds') ) );
                        $.fn.drag_drop_selectable.getListItems( list ).filter('.dds_selected').addClass($.fn.drag_drop_selectable.settings[list].ghostClass);
                    return $helper;
                 },
                 distance:5, //give bit of leeway to allow selecting with click.
                 revert:'invalid',
                 cursor:'move',
                 stop:function(e, ui){
                    var list = $.fn.drag_drop_selectable.getListId($clicked.attr('dds'));
                    $.fn.drag_drop_selectable.getListItems( list ).filter('.dds_selected').removeClass($.fn.drag_drop_selectable.settings[list].ghostClass);
                 }
            });
            $list.droppable({
                drop:function(e,ui){
                    var oldlist = parseInt(ui.helper.attr('dds_list'));
                    ui.helper.find('li.dds_selected').each(function(){
                        var iid = parseInt( $(this).attr('dds_drag') );
                        $.fn.drag_drop_selectable.moveBetweenLists( iid, oldlist, list_id );
                    });

                    //now call callbacks!
                    if( $.fn.drag_drop_selectable.settings[oldlist] && typeof($.fn.drag_drop_selectable.settings[oldlist].onListChange) == 'function'){
                        setTimeout(function(){ $.fn.drag_drop_selectable.settings[oldlist].onListChange( $('ul[dds='+oldlist+']') ); },50);
                    }
                    if( $.fn.drag_drop_selectable.settings[list_id] && typeof($.fn.drag_drop_selectable.settings[list_id].onListChange) == 'function'){
                        setTimeout(function(){ $.fn.drag_drop_selectable.settings[list_id].onListChange( $('ul[dds='+list_id+']') ); },50);
                    }


                },
                accept:function(d){
                    if( $.fn.drag_drop_selectable.getListId( d.attr('dds') ) == $(this).attr('dds')){
                        return false;
                    }
                    return true;
                },
                hoverClass:$.fn.drag_drop_selectable.settings[list_id].hoverClass,
                tolerance:'pointer'
            });
        });
    };
    $.fn.drag_drop_selectable.moveBetweenLists=function(item_id, old_list_id, new_list_id){
        //first deselect.
        $.fn.drag_drop_selectable.deselect(parseInt(item_id));
        //now remove from stack
        $.fn.drag_drop_selectable.stack[old_list_id].all.splice( $.inArray( parseInt(item_id),$.fn.drag_drop_selectable.stack[old_list_id].all ),1);
        //now add to new stack.
        $.fn.drag_drop_selectable.stack[new_list_id].all.push( parseInt(item_id) );
        //now move DOM Object.
        $('ul[dds='+old_list_id+']').find('li[dds='+item_id+']').removeClass($.fn.drag_drop_selectable.settings[old_list_id].ghostClass).appendTo( $('ul[dds='+new_list_id+']') );
    };
    $.fn.drag_drop_selectable.getSelectedForDragging=function(item_id){
        var list = $.fn.drag_drop_selectable.getListId( item_id );
        var $others = $.fn.drag_drop_selectable.getListItems( list ).clone().each(function(){
            $(this).not('.dds_selected').css({visibility:'hidden'});
            $(this).filter('.dds_selected').addClass( $.fn.drag_drop_selectable.settings[list].moveClass ).css({opacity:$.fn.drag_drop_selectable.settings[list].moveOpacity});;
            $(this).attr('dds_drag',$(this).attr('dds'))
            $(this).attr('dds','');
        });
        return $others;
    };
    $.fn.drag_drop_selectable.getMarginForDragging=function($item){
        //find this items offset and the first items offset.
        var this_offset = $item.position().top;
        var first_offset = $.fn.drag_drop_selectable.getListItems( $.fn.drag_drop_selectable.getListId( $item.attr('dds') ) ).eq(0).position().top;
        return this_offset-first_offset;
    }

    $.fn.drag_drop_selectable.toggle=function(id){
        if(!$.fn.drag_drop_selectable.isSelected(id)){
            $.fn.drag_drop_selectable.select(id);
        }else{
            $.fn.drag_drop_selectable.deselect(id);
        }
    };
    $.fn.drag_drop_selectable.select=function(id){
        if(!$.fn.drag_drop_selectable.isSelected(id)){
            var list = $.fn.drag_drop_selectable.getListId(id);
            $.fn.drag_drop_selectable.stack[list].selected.push(id);
            $('[dds='+id+']').trigger('dds.select');
        }
    };
    $.fn.drag_drop_selectable.deselect=function(id){
        if($.fn.drag_drop_selectable.isSelected(id)){
            var list = $.fn.drag_drop_selectable.getListId(id);
            $.fn.drag_drop_selectable.stack[list].selected.splice($.inArray(id,$.fn.drag_drop_selectable.stack[list].selected),1);
            $('[dds='+id+']').trigger('dds.deselect');
        }
    };
    $.fn.drag_drop_selectable.isSelected=function(id){
        return $('li[dds='+id+']').hasClass('dds_selected');
    };
    $.fn.drag_drop_selectable.replace=function(id){
        //find the list this is in!
        var list = $.fn.drag_drop_selectable.getListId(id);
        $.fn.drag_drop_selectable.selectNone(list);
        $.fn.drag_drop_selectable.stack[list].selected.push(id);
        $('[dds='+id+']').trigger('dds.select');
    };
    $.fn.drag_drop_selectable.selectNone=function(list_id){
        $.fn.drag_drop_selectable.getListItems(list_id).each(function(){
            $.fn.drag_drop_selectable.deselect( $(this).attr('dds') );
        });return false;
    };
    $.fn.drag_drop_selectable.selectAll=function(list_id){
        $.fn.drag_drop_selectable.getListItems(list_id).each(function(){
            $.fn.drag_drop_selectable.select( $(this).attr('dds') );
        });return false;
    };
    $.fn.drag_drop_selectable.selectInvert=function(list_id){
        $.fn.drag_drop_selectable.getListItems(list_id).each(function(){
            $.fn.drag_drop_selectable.toggle( $(this).attr('dds') );
        });return false;
    };
    $.fn.drag_drop_selectable.getListItems=function(list_id){
        return $('ul[dds='+list_id+'] li');
    };
    $.fn.drag_drop_selectable.getListId=function(item_id){
        return parseInt($('li[dds='+item_id+']').parent('ul').eq(0).attr('dds'));
    };
    $.fn.drag_drop_selectable.serializeArray=function( list_id ){
        var out = [];
        $.fn.drag_drop_selectable.getListItems(list_id).each(function(){
            out.push($(this).attr('id'));
        });
        return out;
    };
    $.fn.drag_drop_selectable.serialize=function( list_id ){
            return $.fn.drag_drop_selectable.serializeArray( list_id ).join(";");
    };

    $.fn.drag_drop_selectable.unique=0;
    $.fn.drag_drop_selectable.stack=[];
    $.fn.drag_drop_selectable.defaults={
        moveOpacity: 0.8, //opacity of moving items
        ghostClass: 'dds_ghost', //class for "left-behind" item.
        hoverClass: 'dds_hover', //class for acceptable drop targets on hover
        moveClass:  'dds_move', //class to apply to items whilst moving them.
        selectedClass: 'dds_selected', //this default will be aplied any way, but the overridden one too.
        onListChange: function(list){ console.log( list.attr('id') );} //called once when the list changes
    }
    $.fn.drag_drop_selectable.settings=[];


    $.extend({
        dds:{
                selectAll:function(id){ return $.fn.drag_drop_selectable.selectAll($('#'+id).attr('dds')); },
                selectNone:function(id){ return $.fn.drag_drop_selectable.selectNone($('#'+id).attr('dds')); },
                selectInvert:function(id){ return $.fn.drag_drop_selectable.selectInvert($('#'+id).attr('dds')); },
                serialize:function(id){ return $.fn.drag_drop_selectable.serialize($('#'+id).attr('dds')); }
            }
    });

    var CTRL_KEY = 17;
    var ALT_KEY = 18;
    var SHIFT_KEY = 16;
    var META_KEY = 92;
    $.fn.captureKeys=function(){
        if($.fn.captureKeys.capturing){ return; }
        $(document).keydown(function(e){
            if(e.keyCode == CTRL_KEY ){ $.fn.captureKeys.stack.CTRL_KEY  = true  }
            if(e.keyCode == SHIFT_KEY){ $.fn.captureKeys.stack.SHIFT_KEY = true  }
            if(e.keyCode == ALT_KEY  ){ $.fn.captureKeys.stack.ALT_KEY   = true  }
            if(e.keyCode == META_KEY ){ $.fn.captureKeys.stack.META_KEY  = true  }
        }).keyup(function(e){
            if(e.keyCode == CTRL_KEY ){ $.fn.captureKeys.stack.CTRL_KEY  = false }
            if(e.keyCode == SHIFT_KEY){ $.fn.captureKeys.stack.SHIFT_KEY = false }
            if(e.keyCode == ALT_KEY  ){ $.fn.captureKeys.stack.ALT_KEY   = false }
            if(e.keyCode == META_KEY ){ $.fn.captureKeys.stack.META_KEY  = false }
        });
    };
    $.fn.captureKeys.stack={ CTRL_KEY:false, SHIFT_KEY:false, ALT_KEY:false, META_KEY:false }
    $.fn.captureKeys.capturing=false;
    $.fn.isPressed=function(key){
        switch(key){
            case  CTRL_KEY: return $.fn.captureKeys.stack.CTRL_KEY;
            case   ALT_KEY: return $.fn.captureKeys.stack.ALT_KEY;
            case SHIFT_KEY: return $.fn.captureKeys.stack.SHIFT_KEY;
            case  META_KEY: return $.fn.captureKeys.stack.META_KEY;
            default: return false;
        }
    }
})(jQuery);

</script>
</apex:component>


1 comment:

  1. Hi
    whenever we select/deselect values the target container box is getting expandable(height increases) & un-expandable(height decreases when unselect).I need to avoid this and have a scroll bar how to do this

    ReplyDelete