@@ -30,23 +30,18 @@ define(function (require, exports, module) {
3030
3131 // Load dependent modules
3232 var CSSUtils = require ( "language/CSSUtils" ) ,
33+ DropdownButton = require ( "widgets/DropdownButton" ) . DropdownButton ,
3334 CommandManager = require ( "command/CommandManager" ) ,
3435 Commands = require ( "command/Commands" ) ,
3536 DocumentManager = require ( "document/DocumentManager" ) ,
36- DropdownEventHandler = require ( "utils/DropdownEventHandler" ) . DropdownEventHandler ,
3737 EditorManager = require ( "editor/EditorManager" ) ,
3838 Editor = require ( "editor/Editor" ) . Editor ,
39- PanelManager = require ( "view/PanelManager" ) ,
4039 ProjectManager = require ( "project/ProjectManager" ) ,
4140 HTMLUtils = require ( "language/HTMLUtils" ) ,
42- Menus = require ( "command/Menus" ) ,
4341 MultiRangeInlineEditor = require ( "editor/MultiRangeInlineEditor" ) ,
44- PopUpManager = require ( "widgets/PopUpManager" ) ,
4542 Strings = require ( "strings" ) ,
4643 ViewUtils = require ( "utils/ViewUtils" ) ,
4744 _ = require ( "thirdparty/lodash" ) ;
48-
49- var StylesheetsMenuTemplate = require ( "text!htmlContent/stylesheets-menu.html" ) ;
5045
5146 var _newRuleCmd ,
5247 _newRuleHandlers = [ ] ;
@@ -102,19 +97,6 @@ define(function (require, exports, module) {
10297 return selectorName ;
10398 }
10499
105- /**
106- * @private
107- * Create the list of stylesheets in the dropdown menu.
108- * @return {string } The html content
109- */
110- function _renderList ( cssFileInfos ) {
111- var templateVars = {
112- styleSheetList : cssFileInfos
113- } ;
114-
115- return Mustache . render ( StylesheetsMenuTemplate , templateVars ) ;
116- }
117-
118100 /**
119101 * @private
120102 * Add a new rule for the given selector to the given stylesheet, then add the rule to the
@@ -147,6 +129,16 @@ define(function (require, exports, module) {
147129 }
148130 }
149131
132+ /** Item renderer for stylesheet-picker dropdown */
133+ function _stylesheetListRenderer ( item ) {
134+ var html = "<span class='stylesheet-name'>" + _ . escape ( item . name ) ;
135+ if ( item . subDirStr . length ) {
136+ html += "<span class='stylesheet-dir'> — " + _ . escape ( item . subDirStr ) + "</span>" ;
137+ }
138+ html += "</span>" ;
139+ return html ;
140+ }
141+
150142 /**
151143 * This function is registered with EditManager as an inline editor provider. It creates a CSSInlineEditor
152144 * when cursor is on an HTML tag name, class attribute, or id attribute, find associated
@@ -180,107 +172,14 @@ define(function (require, exports, module) {
180172 var result = new $ . Deferred ( ) ,
181173 cssInlineEditor ,
182174 cssFileInfos = [ ] ,
183- $newRuleButton ,
184- $dropdown ,
185- $dropdownItem ,
186- dropdownEventHandler ;
187-
188- /**
189- * @private
190- * Close the dropdown externally to dropdown, which ultimately calls the
191- * _cleanupDropdown callback.
192- */
193- function _closeDropdown ( ) {
194- if ( dropdownEventHandler ) {
195- dropdownEventHandler . close ( ) ;
196- }
197- }
198-
199- /**
200- * @private
201- * Handle click
202- */
203- function _onClickOutside ( event ) {
204- var $container = $ ( event . target ) . closest ( ".stylesheet-dropdown" ) ;
205-
206- // If click is outside dropdown list, then close dropdown list
207- if ( $container . length === 0 || $container [ 0 ] !== $dropdown [ 0 ] ) {
208- _closeDropdown ( ) ;
209- }
210- }
211-
212- /**
213- * @private
214- * Remove the various event handlers that close the dropdown. This is called by the
215- * PopUpManager when the dropdown is closed.
216- */
217- function _cleanupDropdown ( ) {
218- window . document . body . removeEventListener ( "click" , _onClickOutside , true ) ;
219- $ ( hostEditor ) . off ( "scroll" , _closeDropdown ) ;
220- $ ( PanelManager ) . off ( "editorAreaResize" , _closeDropdown ) ;
221- dropdownEventHandler = null ;
222- $dropdown = null ;
223-
224- EditorManager . focusEditor ( ) ;
225- }
175+ newRuleButton ;
226176
227177 /**
228178 * @private
229179 * Callback when item from dropdown list is selected
230- * @param {jQueryObject } $link The `a` element selected with mouse or keyboard
231180 */
232- function _onSelect ( $link ) {
233- var path = $link . data ( "path" ) ;
234-
235- if ( path ) {
236- _addRule ( selectorName , cssInlineEditor , path ) ;
237- }
238- }
239-
240- /**
241- * @private
242- * Show or hide the stylesheets dropdown.
243- */
244- function _showDropdown ( ) {
245- Menus . closeAll ( ) ;
246-
247- $dropdown = $ ( _renderList ( cssFileInfos ) )
248- . appendTo ( $ ( "body" ) ) ;
249-
250- var toggleOffset = $newRuleButton . offset ( ) ,
251- posLeft = toggleOffset . left ,
252- posTop = toggleOffset . top + $newRuleButton . outerHeight ( ) ,
253- elementRect = {
254- top : posTop ,
255- left : posLeft ,
256- height : $dropdown . height ( ) ,
257- width : $dropdown . width ( )
258- } ,
259- clip = ViewUtils . getElementClipSize ( $ ( window ) , elementRect ) ;
260-
261- if ( clip . bottom > 0 ) {
262- // Bottom is clipped, so move entire menu above button
263- posTop = Math . max ( 0 , toggleOffset . top - $dropdown . height ( ) - 4 ) ;
264- }
265-
266- if ( clip . right > 0 ) {
267- // Right is clipped, so adjust left to fit menu in editor
268- posLeft = Math . max ( 0 , posLeft - clip . right ) ;
269- }
270-
271- $dropdown . css ( {
272- left : posLeft ,
273- top : posTop
274- } ) ;
275-
276- dropdownEventHandler = new DropdownEventHandler ( $dropdown , _onSelect , _cleanupDropdown ) ;
277- dropdownEventHandler . open ( ) ;
278-
279- $dropdown . focus ( ) ;
280-
281- window . document . body . addEventListener ( "click" , _onClickOutside , true ) ;
282- $ ( hostEditor ) . on ( "scroll" , _closeDropdown ) ;
283- $ ( PanelManager ) . on ( "editorAreaResize" , _closeDropdown ) ;
181+ function _onDropdownSelect ( event , fileInfo ) {
182+ _addRule ( selectorName , cssInlineEditor , fileInfo . fullPath ) ;
284183 }
285184
286185 /**
@@ -302,27 +201,24 @@ define(function (require, exports, module) {
302201 * Update the enablement of associated menu commands.
303202 */
304203 function _updateCommands ( ) {
305- _newRuleCmd . setEnabled ( cssInlineEditor . hasFocus ( ) && ! $ newRuleButton. hasClass ( "disabled" ) ) ;
204+ _newRuleCmd . setEnabled ( cssInlineEditor . hasFocus ( ) && ! newRuleButton . $button . hasClass ( "disabled" ) ) ;
306205 }
307206
308207 /**
309208 * @private
310209 * Create a new rule on click.
311210 */
312211 function _handleNewRuleClick ( e ) {
313- if ( ! $ newRuleButton. hasClass ( "disabled" ) ) {
212+ if ( ! newRuleButton . $button . hasClass ( "disabled" ) ) {
314213 if ( cssFileInfos . length === 1 ) {
315214 // Just go ahead and create the rule.
316215 _addRule ( selectorName , cssInlineEditor , cssFileInfos [ 0 ] . fullPath ) ;
317- } else if ( $dropdown ) {
318- _closeDropdown ( ) ;
319216 } else {
320- _showDropdown ( ) ;
217+ // Although not attached to button click in 'dropdown mode', this handler can still be
218+ // invoked via the command shortcut. Just toggle dropdown open/closed in that case.
219+ newRuleButton . toggleDropdown ( ) ;
321220 }
322221 }
323- if ( e ) {
324- e . stopPropagation ( ) ;
325- }
326222 }
327223
328224 /**
@@ -388,6 +284,10 @@ define(function (require, exports, module) {
388284 return fileInfos ;
389285 }
390286
287+ function _onHostEditorScroll ( ) {
288+ newRuleButton . closeDropdown ( ) ;
289+ }
290+
391291 CSSUtils . findMatchingRules ( selectorName , hostEditor . document )
392292 . done ( function ( rules ) {
393293 var inlineEditorDeferred = new $ . Deferred ( ) ;
@@ -401,17 +301,21 @@ define(function (require, exports, module) {
401301 inlineEditorDeferred . resolve ( ) ;
402302 } ) ;
403303 $ ( cssInlineEditor ) . on ( "close" , function ( ) {
404- _closeDropdown ( ) ;
304+ newRuleButton . closeDropdown ( ) ;
305+ $ ( hostEditor ) . off ( "scroll" , _onHostEditorScroll ) ;
405306 } ) ;
406307
407308 var $header = $ ( ".inline-editor-header" , cssInlineEditor . $htmlContent ) ;
408- $ newRuleButton = $ ( "<button class='stylesheet-button btn btn-mini disabled'/>" )
409- . text ( Strings . BUTTON_NEW_RULE )
410- . on ( "click" , _handleNewRuleClick ) ;
411- $header . append ( $ newRuleButton) ;
309+ newRuleButton = new DropdownButton ( Strings . BUTTON_NEW_RULE , [ ] , _stylesheetListRenderer ) ; // actual item list populated later, below
310+ newRuleButton . $button . addClass ( "disabled" ) ; // disabled until list is known
311+ newRuleButton . $button . addClass ( "btn-mini stylesheet-button" ) ;
312+ $header . append ( newRuleButton . $button ) ;
412313 _newRuleHandlers . push ( { inlineEditor : cssInlineEditor , handler : _handleNewRuleClick } ) ;
413314
315+ $ ( hostEditor ) . on ( "scroll" , _onHostEditorScroll ) ;
316+
414317 result . resolve ( cssInlineEditor ) ;
318+
415319
416320 // Now that dialog has been built, collect list of stylesheets
417321 var stylesheetsPromise = _getCSSFilesInProject ( ) ;
@@ -428,14 +332,21 @@ define(function (require, exports, module) {
428332 // "New Rule" button is disabled by default and gets enabled
429333 // here if there are any stylesheets in project
430334 if ( cssFileInfos . length > 0 ) {
431- $ newRuleButton. removeClass ( "disabled" ) ;
335+ newRuleButton . $button . removeClass ( "disabled" ) ;
432336 if ( ! rules . length ) {
433337 // Force focus to the button so the user can create a new rule from the keyboard.
434- $newRuleButton . focus ( ) ;
338+ newRuleButton . $button . focus ( ) ;
339+ }
340+
341+ if ( cssFileInfos . length === 1 ) {
342+ // Make it look & feel like a plain button in this case
343+ newRuleButton . $button . removeClass ( "btn-dropdown" ) ;
344+ newRuleButton . $button . on ( "click" , _handleNewRuleClick ) ;
345+ } else {
346+ // Fill out remaining dropdown attributes otherwise
347+ newRuleButton . items = cssFileInfos ;
348+ $ ( newRuleButton ) . on ( "select" , _onDropdownSelect ) ;
435349 }
436- }
437- if ( cssFileInfos . length > 1 ) {
438- $newRuleButton . addClass ( "btn-dropdown" ) ;
439350 }
440351
441352 _updateCommands ( ) ;
0 commit comments