Changeset 13095

Show
Ignore:
Timestamp:
03/14/08 14:16:03 (10 months ago)
Author:
dante
Message:

fixes #6214 - Fixes ff2 display issue with DropDownSelect? widget. proxy commit from Nathan Toone. gracias.

Location:
dojox/trunk/form
Files:
3 modified

Legend:

Unmodified
Added
Removed
  • dojox/trunk/form/DropDownSelect.js

    r13085 r13095  
    1313        baseClass: "dojoxDropDownSelect", 
    1414 
    15         // store:  
    16         //              A store to keep track of our options internally 
    17         store: null, 
     15        /*===== 
     16        dojox.form.__SelectOption = function(){ 
     17                //      value: String 
     18                //              The value of the option.  Setting to empty (or missing) will 
     19                //              place a separator at that location 
     20                //      label: String 
     21                //              The label for our option.  It can contain html tags. 
     22                this.value = value; 
     23                this.label = label; 
     24        } 
     25        =====*/ 
     26 
     27        // options: dojox.form.__SelectOption[] 
     28        //              our set of options 
     29        options: null, 
    1830         
    1931        // emptyLabel: string 
     
    2537        _isPopulated: false, 
    2638 
    27         // _numSeps: number 
    28         //              The number of separator we have added (to ensure uniqueness) 
    29         _numSeps: 0, 
    30  
    31         _addMenuItem: function(/* item */ item){ 
    32                 // summary: 
    33                 //              For the given datastore item, add a menu item to our dropdown 
    34                 //              If the item doesn't have a label (or if the label is an empty 
    35                 //              string), then a separator is added in that place. 
    36                 var store = this.store, menu = this.dropDown; 
    37  
    38                 // Check that our item is loaded 
    39                 if(!store.isItemLoaded(item)){ 
    40                         store.loadItem({item: item,  
    41                                                         onComplete: function(i){ 
    42                                                                                 this._addMenuItem(i); 
    43                                                         }, 
    44                                                         onError: function(e){throw e;}, 
    45                                                         scope: this}); 
    46                         return; 
    47                 } 
    48                 if(!store.getLabel(item)){ 
     39        _addMenuItem: function(/* dojox.form.__SelectOption */ option){ 
     40                // summary: 
     41                //              For the given option, add a menu item to our dropdown 
     42                //              If the option doesn't have a value, then a separator is added  
     43                //              in that place. 
     44                var menu = this.dropDown; 
     45 
     46                if(!option.value){ 
    4947                        // We are a separator (no label set for it) 
    5048                        menu.addChild(new dijit.MenuSeparator()); 
    5149                }else{ 
    52                         // Just a regular menu item 
    53                         var click = dojo.hitch(this, function(){this._setValue(item);}); 
     50                        // Just a regular menu option 
     51                        var click = dojo.hitch(this, function(){this.setAttribute("value", option);}); 
    5452                        menu.addChild(new dijit.MenuItem({ 
    55                                                                         id: this.id + "_item_" + store.getIdentity(item), 
    56                                                                         label: store.getLabel(item), 
     53                                                                        id: this.id + "_item_" + option.value, 
     54                                                                        label: option.label, 
    5755                                                                        onClick: click})); 
    5856                } 
    5957        }, 
    6058 
    61         _resetMenu: function(){ 
    62                 // summary: 
    63                 //              Resets the menu for whatever reason - making it "populatable"  
    64                 //              again on the next click. 
     59        _resetButtonState: function(){ 
     60                // summary:  
     61                //              Resets the menu and the length attribute of the button - and 
     62                //              ensures that the label is appropriately set. 
     63                var len = this.options.length; 
     64                 
     65                // reset the menu to make it "populatable on the next click 
    6566                var dropDown = this.dropDown; 
    6667                dojo.forEach(dropDown.getChildren(), function(i){ 
     
    6869                }); 
    6970                this._isPopulated = false; 
    70         }, 
    71  
    72         _initializeDropdown: function(/* item or string, optional */ selected){ 
    73                 // summary: 
    74                 //              Initializes the dropdown after a store has been set (or on  
    75                 //              postcreate) 
    76                 this._setValue(selected); 
    77                 this.store.fetch({onComplete: function(items){ 
    78                                                         var len = items.length; 
    79                                                         this.setAttribute("readOnly", (len === 1)); 
    80                                                         this.setAttribute("disabled", (len === 0)); 
    81                                                 }, 
    82                                                 scope: this}); 
    83         }, 
    84  
     71                 
     72                // Set our length attribute and our value 
     73                this.setAttribute("readOnly", (len === 1)); 
     74                this.setAttribute("disabled", (len === 0));      
     75                this.setAttribute("value", this.value); 
     76        }, 
     77         
    8578        _updateSelectedState: function(){ 
    8679                // summary: 
     
    9689        }, 
    9790         
    98         addOption: function(/* string? */ value, /* string? */ label){ 
    99                 // summary: 
    100                 //              Adds an option to the end of the select.  If either value or  
    101                 //              label are empty or missing, a separator is created instead. 
    102                 var store = this.store; 
    103                 if(!store.getFeatures()["dojo.data.api.Write"]){ 
    104                         throw new Error("Cannot add or remove options from a non-writeable store"); 
    105                 } 
    106                 if(!value || !label){ 
    107                         // create a separator 
    108                         this._numSeps++; 
    109                         store.newItem({value: ("_separator-" + this._numSeps), name: ""}); 
    110                 }else{ 
    111                         store.newItem({value: value, name: label}); 
    112                 } 
    113         }, 
    114          
    115         removeOption: function(/* string or number */ valueOrIdx){ 
    116                 // summary: 
    117                 //              Removes an option at the given value (if valueOrIndex is a  
    118                 //              string) or at the given index in the menu (if valueOrIndex is 
    119                 //              a number) 
    120                 var store = this.store; 
    121                 if(!store.getFeatures()["dojo.data.api.Write"]){ 
    122                         throw new Error("Cannot add or remove options from a non-writeable store"); 
    123                 } 
    124                 var allItems = function(items){ 
    125                         if(items[valueOrIdx]){ 
    126                                 store.deleteItem(items[valueOrIdx]); 
    127                         } 
    128                 }; 
    129                 if(typeof valueOrIdx === "number"){ 
    130                         // Remove by index 
    131                         store.fetch({onComplete: allItems}); 
    132                 }else if(typeof valueOrIdx === "string") { 
    133                         // Remove by id 
    134                         store.fetchItemByIdentity({identity: valueOrIdx,  
    135                                                                         onItem: store.deleteItem, 
    136                                                                         scope: store}); 
    137                 }else{ 
    138                         // Try to just remove it (in case we happened to be passed an  
    139                         // item 
    140                         store.deleteItem(valueOrIdx); 
    141                 }                
    142         }, 
    143  
    144         _setValue: function(/* item or string, optional */ value){ 
    145                 // summary: 
    146                 var store = this.store; 
    147                  
    148                 // If a string is passed, then we set our value from looking it up 
    149                 // as the identity 
    150                 if(typeof value == "string" && value){ 
    151                         store.fetchItemByIdentity({identity: value,  
    152                                                                         onItem: this._setValue,  
    153                                                                         scope: this}); 
    154                         return; 
    155                 } 
    156                  
    157                 // If we don't have a value, try to show the first item 
    158                 if(!value){ 
    159                         var cb = function(items){ 
    160                                 if(items.length){ 
    161                                         this._setValue(items[0]); 
    162                                 }else{ 
    163                                         this.value = ""; 
    164                                         this._handleOnChange(this.value); 
    165                                         this.setLabel(this.emptyLabel || " "); 
    166                                 } 
    167                         }; 
    168                         store.fetch({onComplete: cb, scope: this}); 
    169                         return; 
    170                 } 
    171                  
    172                 // We have a value, and we're an item - so load it and set from 
    173                 //  that. 
    174                 var onItem = dojo.hitch(this, function(i){ 
    175                         this.value = store.getIdentity(i); 
    176                         this._handleOnChange(this.value); 
    177                         this.setLabel(store.getLabel(i)); 
     91        addOption: function(/* dojox.form.__SelectOption or string, optional */ value, /* string? */ label){ 
     92                // summary: 
     93                //              Adds an option to the end of the select.  If value is empty or  
     94                //              missing, a separator is created instead. 
     95                 
     96                this.options.push(value.value ? value : {value:value, label:label}); 
     97        }, 
     98         
     99        removeOption: function(/* string, dojox.form.__SelectOption or number */ valueOrIdx){ 
     100                // summary: 
     101                //              Removes the given option 
     102                this.options = dojo.filter(this.options, function(i, idx){ 
     103                        return !((typeof valueOrIdx === "number" && idx === valueOrIdx) || 
     104                                        (typeof valueOrIdx === "string" && i.value === valueOrIdx) || 
     105                                        (valueOrIdx.value && i.value === valueOrIdx.value)); 
    178106                }); 
    179                 if(store.isItem(value) && store.isItemLoaded(value)){ 
    180                         onItem(value); 
    181                 }else if(store.isItem(value)) { 
    182                         store.loadItem({item: value, onItem: onItem}); 
    183                 } 
    184         }, 
    185          
    186         setAttribute: function(attr, value){ 
     107        }, 
     108 
     109        destroy: function(){ 
     110                // summary: 
     111                //              Clear out an outstanding hack handle 
     112                if(this._labelHackHandle){ 
     113                        clearTimeout(this._labelHackHandle); 
     114                } 
     115                this.inherited(arguments); 
     116        }, 
     117         
     118        setLabel: function(/* string */ content){ 
     119                // summary: 
     120                //              Wraps our label in a div - that way, our rich text can work 
     121                //              correctly. 
     122 
     123                content = '<div class=" ' + this.baseClass + 'Label">' + 
     124                                        content + 
     125                                        '</div>'; 
     126                //              Because FF2 has a problem with layout, we need to delay this 
     127                //              call for it. 
     128                if(this._labelHackHandle){ 
     129                        clearTimeout(this._labelHackHandle); 
     130                } 
     131                if(dojo.isFF === 2){ 
     132                        this._labelHackHandle = setTimeout(dojo.hitch(this, function(){ 
     133                                this._labelHackHandle = null; 
     134                                dijit.form.DropDownButton.prototype.setLabel.call(this, content); 
     135                        }), 0); 
     136                }else{ 
     137                        this.inherited(arguments); 
     138                } 
     139        }, 
     140 
     141        setAttribute: function(/*string*/ attr, /* anything */ value){ 
    187142                // summary: sometime we get called to set our value - we need to  
    188143                //                      make sure and route those requests through _setValue() 
    189144                //                      instead. 
    190145                if(attr === "value"){ 
    191                         this._setValue(value); 
    192                         return; 
    193                 } 
    194                 this.inherited(arguments); 
    195         }, 
    196          
    197         postMixInProperties: function(){ 
    198                 // summary:  Loads our store and sets up our dropdown correctly 
    199  
    200                 if(!this.store){ 
    201                         var items = this.srcNodeRef ? dojo.query(">",  
     146                        // If a string is passed, then we set our value from looking it up. 
     147                        if(typeof value === "string"){ 
     148                                value = dojo.filter(this.options, function(i){ 
     149                                        return i.value === value; 
     150                                })[0]; 
     151                        } 
     152                         
     153                        // If we don't have a value, try to show the first item 
     154                        if(!value){ 
     155                                value = this.options[0] || {value: "", label: ""}; 
     156                        } 
     157                        this.value = value.value; 
     158                        if(this._started) 
     159                                this.setLabel(value.label||this.emptyLabel||"&nbsp;"); 
     160                        this._handleOnChange(value.value); 
     161                        value = this.value; 
     162                }else{ 
     163                        this.inherited(arguments); 
     164                } 
     165        }, 
     166         
     167        _fillContent: function(){ 
     168                // summary:   
     169                //              Loads our options and sets up our dropdown correctly.  We  
     170                //              don't want any content, so we don't call any inherit chain 
     171                //              function. 
     172                var opts = this.options; 
     173                if(!opts){ 
     174                        opts = this.options = this.srcNodeRef ? dojo.query(">",  
    202175                                                this.srcNodeRef).map(function(i){ 
    203176                                                        if(i.getAttribute("type") === "separator"){ 
    204                                                                 this._numSeps++; 
    205                                                                 return { value: ("_separator-" + this._numSeps), 
    206                                                                                 name: "" }; 
     177                                                                return { value: "", label: "" }; 
    207178                                                        } 
    208179                                                        return { value: i.getAttribute("value"), 
    209                                                                         name: String(i.innerHTML) }; 
     180                                                                                label: String(i.innerHTML) }; 
    210181                                                }, this) : []; 
    211                         this.store = new dojo.data.ItemFileWriteStore({data: { 
    212                                                                                                                         identifier: "value", 
    213                                                                                                                         label: "name", 
    214                                                                                                                         items: items}}); 
    215  
    216                         // Set the value to be the first, or the selected index 
    217                         if(items && items.length && !this.value){ 
    218                                 var si = this.srcNodeRef.selectedIndex; 
    219                                 this.value = items[si != -1 ? si : 0].value; 
    220                         } 
    221                 } 
    222                 this.inherited(arguments); 
    223                 dojo.place(dojo.doc.createElement("span"), this.srcNodeRef, "first"); 
     182                } 
     183                 
     184                // Set the value to be the first, or the selected index 
     185                if(opts.length && !this.value){ 
     186                        var si = this.srcNodeRef.selectedIndex; 
     187                        this.value = opts[si != -1 ? si : 0].value; 
     188                } 
     189                 
     190                // Create the dropDown widget 
     191                this.dropDown = new dijit.Menu(); 
    224192        }, 
    225193 
    226194        postCreate: function(){ 
    227195                // summary: sets up our event handling that we need for functioning 
    228                 //              as a select 
    229  
    230                 this._initializeDropdown(this.value); 
     196                //                      as a select 
     197 
    231198                this.inherited(arguments); 
    232199 
    233                 // Validate our store and make our notification connections 
    234                 var store = this.store; 
    235                 if(!store.getFeatures()["dojo.data.api.Identity"]){ 
    236                         throw new Error ("dojox.form.DropDownSelect requires an Identity store"); 
    237                 } 
    238                 if(store.getFeatures()["dojo.data.api.Notification"]){ 
    239                         this.connect(store, "onNew", "_onNewItem"); 
    240                         this.connect(store, "onDelete", "_onDeleteItem"); 
    241                         this.connect(store, "onSet", "_onSetItem"); 
    242                 } 
     200                // Make our event connections for updating state 
    243201                var fx = function(){ 
    244202                        dojo[this._opened ? "addClass" : "removeClass"](this.focusNode, 
     
    248206                this.connect(this, "_closeDropDown", fx); 
    249207                this.connect(this, "onChange", "_updateSelectedState"); 
    250                  
    251                 // Create the dropDown widget 
    252                 this.dropDown = new dijit.Menu(); 
    253         }, 
    254  
     208                this.connect(this, "addOption", "_resetButtonState"); 
     209                this.connect(this, "removeOption", "_resetButtonState"); 
     210        }, 
     211 
     212        startup: function(){ 
     213                // summary:  
     214                //              FF2 has layout problems if the reset call isn't done on a 
     215                //              slight delay 
     216                this.inherited(arguments); 
     217                if(dojo.isFF === 2){ 
     218                        setTimeout(dojo.hitch(this, this._resetButtonState), 0); 
     219                }else{ 
     220                        this._resetButtonState(); 
     221                } 
     222        }, 
     223         
    255224        _populate: function(/* function */ callback){ 
    256                 // summary: populates the menu (and does the callback, if passed) 
     225                // summary:  
     226                //                      populates the menu (and does the callback, if passed) 
    257227                 
    258228                var dropDown = this.dropDown; 
    259229                 
    260                 // Re-fetch (or fetch) our items, and create MenuItems for them 
    261                 this.store.fetch({ 
    262                         onItem: this._addMenuItem,  
    263                         onComplete: function(){ 
    264                                 // Flag as populated and call the callback (if needed) 
    265                                 this._updateSelectedState(); 
    266                                 dojo.addClass(this.dropDown.domNode, this.baseClass + "Menu"); 
    267                                 this._isPopulated = true; 
    268                                 if(callback){ callback.call(this); } 
    269                         }, 
    270                         scope: this 
    271                 }); 
    272         }, 
    273          
    274         _onNewItem: function(/* item */ item, /* Object? */ parentInfo){ 
    275                 // summary: Called when a new item is added to our store 
    276                 this._resetMenu(); 
    277                 this._initializeDropdown(this.value); 
    278         }, 
    279          
    280         _onSetItem: function(/* item */ item){ 
    281                 // summary: Called when an item has been set 
    282                 this._resetMenu(); 
    283                 if(this.store.getIdentity(item) === this.value){ 
    284                         this.setLabel(this.store.getLabel(item)); 
    285                 } 
    286         }, 
    287          
    288         _onDeleteItem: function(/* item */ item){ 
    289                 // summary: Called when an item has been deleted from our store 
    290                 this._resetMenu(); 
    291                 if(this.store.getIdentity(item) === this.value){ 
    292                         this._initializeDropdown(""); 
    293                 } 
     230                // Add each menu item 
     231                dojo.forEach(this.options, this._addMenuItem, this); 
     232                 
     233                // Update states 
     234                this._updateSelectedState(); 
     235                dojo.addClass(this.dropDown.domNode, this.baseClass + "Menu"); 
     236                this._isPopulated = true; 
     237                if(callback){ callback.call(this); } 
    294238        }, 
    295239         
     
    297241                // summary: Overrides DropDownButton's toggle function to make sure  
    298242                //                      that the values are correctly populated. 
    299                 var dropDown = this.dropDown, store = this.store; 
     243                var dropDown = this.dropDown; 
    300244                if(dropDown && !dropDown.isShowingNow && !this._isPopulated){ 
    301245                        this._populate(dojox.form.DropDownSelect.superclass._toggleDropDown); 
  • dojox/trunk/form/resources/DropDownSelect.css

    r13085 r13095  
    1919} 
    2020 
    21 /* Make vertical-align be auto */ 
    22 .dojoxDropDownSelect .dijitButtonNode .dijitButtonText * { 
     21/* Fix the baseline of our label (for multi-size font elements) */ 
     22.dijitButtonNode .dojoxDropDownSelectLabel * 
     23{ 
    2324        vertical-align: baseline; 
    2425} 
  • dojox/trunk/form/tests/test_DropDownSelect.html

    r13085 r13095  
    1313                         
    1414                        var numOptions = 0; 
     15                        var numChanges = 0; 
    1516                         
    1617                        dojo.addOnLoad(function(){ 
     18                                dojo.connect(s1, "onChange", function(val){ 
     19                                        console.log("First Select Changed to " + val); 
     20                                        numChanges++; 
     21                                }); 
    1722                                doh.register("tests", 
    1823                                        [ 
     
    2328                                                        s1.setAttribute("value", "UT"); 
    2429                                                        t.is("TN", s1.value); 
     30                                                        t.is(2, numChanges); 
    2531                                                } 
    2632                                        ] 
    2733                                ); 
    2834                                doh.run(); 
    29                                 dojo.connect(s1, "onChange", function(val){ 
    30                                         console.log("First Select Changed to " + val); 
    31                                 }); 
    3235                        }); 
    3336                </script> 
     
    8184                        </div> 
    8285                        <div jsId="s5" name="s5" value="move" dojoType="dojox.form.DropDownSelect"> 
    83                                 <span value="copy"><img style="vertical-align: middle; margin: 0px" src="../../../dijit/themes/tundra/images/dndCopy.png" /> Copy</span> 
    84                                 <span value="move"><img  style="vertical-align: middle; margin: 0px" src="../../../dijit/themes/tundra/images/dndMove.png" /> Move</span> 
    85                                 <span value="nocopy"><img  style="vertical-align: middle; margin: 0px" src="../../../dijit/themes/tundra/images/dndNoCopy.png" /> No Copy</span> 
    86                                 <span value="nomove"><img  style="vertical-align: middle; margin: 0px" src="../../../dijit/themes/tundra/images/dndNoMove.png" /> No Move</span> 
     86                                <span value="copy"><img style="vertical-align: middle;margin-top: 1px;margin-bottom:1px;" src="../../../dijit/themes/tundra/images/dndCopy.png" /> Copy</span> 
     87                                <span value="move"><img  style="vertical-align: middle;margin-top: 1px;margin-bottom:1px;" src="../../../dijit/themes/tundra/images/dndMove.png" /> Move</span> 
     88                                <span value="nocopy"><img  style="vertical-align: middle;margin-top: 1px;margin-bottom:1px;" src="../../../dijit/themes/tundra/images/dndNoCopy.png" /> No Copy</span> 
     89                                <span value="nomove"><img  style="vertical-align: middle;margin-top: 1px;margin-bottom:1px;" src="../../../dijit/themes/tundra/images/dndNoMove.png" /> No Move</span> 
    8790                        </div> 
    8891                <hr>