3131 * - The button's label is not automatically changed when an item in the list is clicked
3232 * - Its width is not the max of all the dropdown items' labels
3333 * - The button & dropdown's appearance can be customized far more
34+ * Events
35+ * - listRendered -- This event is dispatched after the entire list is rendered so that custom event handlers can be
36+ * set up for any custom UI in the list.
3437 *
3538 * TODO: merge DropdownEventHandler into this? Are there any other widgets that might want to use it separately?
39+ *
3640 */
3741define ( function ( require , exports , module ) {
3842 "use strict" ;
@@ -98,6 +102,17 @@ define(function (require, exports, module) {
98102 event . stopPropagation ( ) ;
99103 } ;
100104
105+ /**
106+ * Update the button label.
107+ * @param {string } label
108+ */
109+ DropdownButton . prototype . setButtonLabel = function ( label ) {
110+ if ( ! this . $button ) {
111+ return ;
112+ }
113+ $ ( this . $button ) . text ( label ) ;
114+ } ;
115+
101116 /**
102117 * Called for each item when rendering the dropdown.
103118 * @param {* } item from items array
@@ -108,17 +123,69 @@ define(function (require, exports, module) {
108123 return _ . escape ( String ( item ) ) ;
109124 } ;
110125
111- /** Converts the list of item objects into HTML list items in format required by DropdownEventHandler */
112- DropdownButton . prototype . _renderList = function ( ) {
126+ /**
127+ * Converts the list of item objects into HTML list items in format required by DropdownEventHandler
128+ * @param {!jQueryObject } parent The dropdown element
129+ * @return {!jQueryObject } The dropdown element with the rendered list items appended.
130+ */
131+ DropdownButton . prototype . _renderList = function ( parent ) {
132+ if ( ! parent ) {
133+ return null ;
134+ }
135+
113136 var html = "" ;
114137 this . items . forEach ( function ( item , i ) {
115- html += "<li><a class='stylesheet-link' data-index='" + i + "'>" ;
116- html += this . itemRenderer ( item , i ) ;
117- html += "</a></li>" ;
138+ if ( item === "---" ) {
139+ html += "<li class='divider'></li>" ;
140+ } else {
141+ html += "<li><a class='stylesheet-link' data-index='" + i + "'>" ;
142+ html += this . itemRenderer ( item , i ) ;
143+ html += "</a></li>" ;
144+ }
118145 } . bind ( this ) ) ;
119- return html ;
146+
147+ parent . append ( html ) ;
148+
149+ // Also trigger listRendered handler so that custom event handlers can be
150+ // set up for any custom UI in the list.
151+ $ ( this ) . triggerHandler ( "listRendered" , [ parent ] ) ;
152+
153+ return parent ;
120154 } ;
121155
156+ /**
157+ * Refresh the dropdown list by removing and re-creating all list items.
158+ * Call this after deleting/adding any item in the dropdown list.
159+ */
160+ DropdownButton . prototype . refresh = function ( ) {
161+ if ( ! this . $dropdown ) {
162+ return ;
163+ }
164+
165+ // Remove all list items and then re-create them from this.items.
166+ $ ( "li" , this . $dropdown ) . remove ( ) ;
167+ this . _renderList ( this . $dropdown ) ;
168+ } ;
169+
170+ /**
171+ * Check/Uncheck the list item of the given index.
172+ * @param {number } index The index of the list item to be checked or unchecked
173+ * @param {boolean } checked True if the list item is to be checked, false to get check
174+ * mark removed.
175+ */
176+ DropdownButton . prototype . setChecked = function ( index , checked ) {
177+ if ( ! this . $dropdown ) {
178+ return ;
179+ }
180+
181+ var listItems = $ ( "li" , this . $dropdown ) ,
182+ count = listItems . length ;
183+
184+ if ( index > - 1 && index < count ) {
185+ $ ( "a" , listItems [ index ] ) . toggleClass ( "checked" , checked ) ;
186+ }
187+ } ;
188+
122189 /** Pops open the dropdown if currently closed. Does nothing if items.length == 0 */
123190 DropdownButton . prototype . showDropdown = function ( ) {
124191 // Act like a plain old button if no items to show
@@ -133,20 +200,22 @@ define(function (require, exports, module) {
133200 Menus . closeAll ( ) ;
134201
135202 var $dropdown = $ ( "<ul class='dropdown-menu dropdownbutton-popup' tabindex='-1'>" )
136- . addClass ( this . dropdownExtraClasses ) // (no-op if unspecified)
137- . append ( this . _renderList ( ) )
203+ . addClass ( this . dropdownExtraClasses ) ; // (no-op if unspecified)
204+
205+ this . $dropdown = $dropdown ;
206+ this . _renderList ( this . $dropdown )
138207 . appendTo ( $ ( "body" ) )
139208 . data ( "attached-to" , this . $button [ 0 ] ) ; // keep ModalBar open while dropdown focused
140209
141210 // Calculate position of dropdown
142- var toggleOffset = this . $button . offset ( ) ,
143- posLeft = toggleOffset . left ,
144- posTop = toggleOffset . top + this . $button . outerHeight ( ) ,
145- elementRect = {
146- top : posTop ,
147- left : posLeft ,
148- height : $dropdown . height ( ) ,
149- width : $dropdown . width ( )
211+ var toggleOffset = this . $button . offset ( ) ,
212+ posLeft = toggleOffset . left ,
213+ posTop = toggleOffset . top + this . $button . outerHeight ( ) ,
214+ elementRect = {
215+ top : posTop ,
216+ left : posLeft ,
217+ height : $dropdown . height ( ) ,
218+ width : $dropdown . width ( )
150219 } ,
151220 clip = ViewUtils . getElementClipSize ( $ ( window ) , elementRect ) ;
152221
@@ -170,14 +239,12 @@ define(function (require, exports, module) {
170239 this . _dropdownEventHandler = new DropdownEventHandler ( $dropdown , this . _onSelect . bind ( this ) , this . _onDropdownClose . bind ( this ) ) ;
171240 this . _dropdownEventHandler . open ( ) ;
172241
173- window . document . body . addEventListener ( "click " , this . _onClickOutside , true ) ;
242+ window . document . body . addEventListener ( "mousedown " , this . _onClickOutside , true ) ;
174243 $ ( PanelManager ) . on ( "editorAreaResize" , this . closeDropdown ) ;
175244
176245 // Manage focus
177246 this . _lastFocus = window . document . activeElement ;
178247 $dropdown . focus ( ) ;
179-
180- this . $dropdown = $dropdown ;
181248 } ;
182249
183250 /**
@@ -186,14 +253,14 @@ define(function (require, exports, module) {
186253 * was closed.
187254 */
188255 DropdownButton . prototype . _onDropdownClose = function ( ) {
189- window . document . body . removeEventListener ( "click " , this . _onClickOutside , true ) ;
256+ window . document . body . removeEventListener ( "mousedown " , this . _onClickOutside , true ) ;
190257 $ ( PanelManager ) . off ( "editorAreaResize" , this . closeDropdown ) ;
191-
258+
192259 // Restore focus to old pos, unless "select" handler changed it
193260 if ( window . document . activeElement === this . $dropdown [ 0 ] ) {
194261 this . _lastFocus . focus ( ) ;
195262 }
196-
263+
197264 this . _dropdownEventHandler = null ;
198265 this . $dropdown = null ; // already remvoed from DOM automatically by PopUpManager
199266 } ;
@@ -209,9 +276,12 @@ define(function (require, exports, module) {
209276 DropdownButton . prototype . _onClickOutside = function ( event ) {
210277 var $container = $ ( event . target ) . closest ( ".dropdownbutton-popup" ) ;
211278
212- // If click is outside dropdown list, then close dropdown list
213- if ( $container . length === 0 || $container [ 0 ] !== this . $dropdown [ 0 ] ) {
279+ // If click is outside dropdown list or dropdown button, then close dropdown list
280+ if ( ! $ ( event . target ) . is ( this . $button ) &&
281+ ( $container . length === 0 || $container [ 0 ] !== this . $dropdown [ 0 ] ) ) {
214282 this . closeDropdown ( ) ;
283+ event . stopPropagation ( ) ;
284+ event . preventDefault ( ) ;
215285 }
216286 } ;
217287
0 commit comments