1 /** 2 * @fileOverview manage a list of single-select options 3 * @author yiminghe@gmail.com 4 */ 5 KISSY.add("menubutton/select", function (S, Node, MenuButton, Menu, Option, undefined) { 6 7 function getSelectedItem(self) { 8 var menu = self.get("menu"), 9 cs = menu.children || menu.get && menu.get("children") || [], 10 value = self.get("value"), 11 c, 12 i; 13 for (i = 0; i < cs.length; i++) { 14 c = cs[i]; 15 if (getItemValue(c) == value) { 16 return c; 17 } 18 } 19 return null; 20 } 21 22 // c : Option 23 // c.get("value") 24 // c: Object 25 // c.value 26 function getItemValue(c) { 27 var v; 28 if (c) { 29 if (c.get) { 30 if ((v = c.get("value")) === undefined) { 31 v = c.get("textContent") || c.get("content"); 32 } 33 34 } else { 35 if ((v = c.value) === undefined) { 36 v = c.textContent || c.content; 37 } 38 } 39 } 40 return v; 41 } 42 43 function deSelectAllExcept(self) { 44 var menu = self.get("menu"), 45 value = self.get("value"), 46 cs = menu && menu.get && menu.get("children"); 47 S.each(cs, function (c) { 48 if (c && c.set) { 49 c.set("selected", getItemValue(c) == value) 50 } 51 }); 52 } 53 54 /** 55 * different from menubutton by highlighting the currently selected option 56 * on open menu. 57 */ 58 function _handleMenuShow() { 59 var self = this, 60 selectedItem = getSelectedItem(self), 61 m = self.get("menu"); 62 m.set("highlightedItem", selectedItem || m.getChildAt(0)); 63 // 初始化选中 64 if (selectedItem) { 65 selectedItem.set("selected", true); 66 } 67 } 68 69 function _updateCaption(self) { 70 var item = getSelectedItem(self), 71 textContent = item && ( item.textContent || item.get && item.get("textContent")), 72 content = item && (item.content || item.get && item.get('content')); 73 // 可能设置到 select content 的内容并不和 menuitem 的内容完全一致 74 self.set("content", textContent || content || self.get("defaultCaption")); 75 } 76 77 /** 78 * @class 79 * Select component which supports single selection from a drop down menu 80 * with semantics similar to native HTML select. 81 * xclass: 'select'. 82 * @name Select 83 * @memberOf MenuButton 84 * @extends MenuButton 85 */ 86 var Select = MenuButton.extend( 87 /** 88 * @lends MenuButton.Select.prototype 89 */ 90 { 91 92 /** 93 * Bind menu to current Select. When menu shows, set highlightedItem to current selectedItem. 94 * @protected 95 */ 96 bindMenu:function () { 97 var self = this; 98 Select.superclass.bindMenu.call(self); 99 self.get("menu").on("show", _handleMenuShow, self); 100 }, 101 102 /** 103 * Handle click on drop down menu. 104 * Set selected menu item as current selectedItem and hide drop down menu. 105 * Protected, should only be overridden by subclasses. 106 * @protected 107 * @override 108 * @param {Event.Object} e 109 */ 110 handleMenuClick:function (e) { 111 var self = this, 112 target = e.target, 113 prevTarget = getSelectedItem(self); 114 self.set("value", getItemValue(target)); 115 self.set("collapsed", true); 116 self.fire("click", { 117 target:target, 118 prevTarget:prevTarget 119 }); 120 }, 121 122 /** 123 * Removes all menu items from current select, and set selectedItem to null. 124 * @override 125 */ 126 removeItems:function () { 127 var self = this; 128 Select.superclass.removeItems.apply(self, arguments); 129 self.set("value", null); 130 }, 131 132 /** 133 * Remove specified item from current select. 134 * If specified item is selectedItem, then set selectedItem to null. 135 * @override 136 */ 137 removeItem:function (c) { 138 var self = this; 139 Select.superclass.removeItem.apply(self, arguments); 140 if (c.get("value") == self.get("value")) { 141 self.set("value", null); 142 } 143 }, 144 145 _uiSetValue:function () { 146 var self = this; 147 deSelectAllExcept(self); 148 _updateCaption(self); 149 }, 150 151 _uiSetDefaultCaption:function () { 152 _updateCaption(this); 153 } 154 }, 155 { 156 ATTRS:/** 157 * @lends MenuButton.Select.prototype 158 */ 159 { 160 161 /** 162 * Get current select 's value. 163 */ 164 value:{ 165 }, 166 167 /** 168 * Default caption to be shown when no option is selected. 169 * @type String 170 */ 171 defaultCaption:{ 172 value:"" 173 } 174 }, 175 176 /** 177 * Generate a select component from native select element. 178 * @param {HTMLElement} element Native html select element. 179 * @param {Object} cfg Extra configuration for current select component. 180 * @memberOf MenuButton.Select 181 */ 182 decorate:function (element, cfg) { 183 element = S.one(element); 184 cfg = cfg || {}; 185 cfg.elBefore = element; 186 187 var name, 188 allItems = [], 189 select, 190 selectedItem = null, 191 curValue = element.val(), 192 options = element.all("option"); 193 194 options.each(function (option) { 195 var item = { 196 content:option.text(), 197 elCls:option.attr("class"), 198 value:option.val(), 199 xclass:'option' 200 }; 201 if (curValue == option.val()) { 202 selectedItem = { 203 content:item.content, 204 value:item.value 205 }; 206 } 207 allItems.push(item); 208 }); 209 210 S.mix(cfg, { 211 menu:S.mix({ 212 xclass:'popupmenu', 213 children:allItems 214 }, cfg.menuCfg) 215 }); 216 217 delete cfg.menuCfg; 218 219 select = new Select(S.mix(cfg, selectedItem)).render(); 220 221 if (name = element.attr("name")) { 222 var input = new Node("<input" + 223 " type='hidden'" + 224 " name='" + name 225 + "' value='" + curValue + "'>") 226 .insertBefore(element, undefined); 227 228 select.on("afterValueChange", function (e) { 229 input.val(e.newVal || ""); 230 }); 231 } 232 233 element.remove(); 234 return select; 235 } 236 237 }, { 238 xclass:'select', 239 priority:30 240 }); 241 242 return Select; 243 244 }, { 245 requires:['node', './base', 'menu', './option'] 246 }); 247 248 /** 249 * TODO 250 * how to emulate multiple ? 251 **/