Ticket #47149: 47149.2.diff
File 47149.2.diff, 11.3 KB (added by , 6 years ago) |
---|
-
src/js/media/views/focus-manager.js
1 var $ = jQuery; 2 1 3 /** 2 4 * wp.media.view.FocusManager 3 5 * … … 11 13 var FocusManager = wp.media.View.extend(/** @lends wp.media.view.FocusManager.prototype */{ 12 14 13 15 events: { 14 'keydown': ' constrainTabbing'16 'keydown': 'focusManagementMode' 15 17 }, 16 18 19 initialize: function( options ) { 20 this.mode = options.mode || 'constrainTabbing'; 21 this.tabsAutomaticActivation = options.tabsAutomaticActivation || false; 22 }, 23 24 /** 25 * Determines which focus management mode to use. 26 * 27 * @since 5.3.0 28 * 29 * @param {object} event jQuery event object. 30 * 31 * @returns {void} 32 */ 33 focusManagementMode: function( event ) { 34 if ( this.mode === 'constrainTabbing' ) { 35 this.constrainTabbing( event ); 36 } 37 38 if ( this.mode === 'tabsNavigation' ) { 39 this.tabsNavigation( event ); 40 } 41 }, 42 17 43 /** 18 44 * Gets all the tabbable elements. 19 45 * … … 164 190 * 165 191 * @since 5.3.0 166 192 */ 167 ariaHiddenElements: [] 193 ariaHiddenElements: [], 194 195 /** 196 * Holds the jQuery collection of tabs. 197 * 198 * @since 5.3.0 199 */ 200 tabs: $(), 201 202 /** 203 * Initializes tabs in an ARIA tabbed interface. 204 * 205 * @since 5.3.0 206 * 207 * @param {object} event jQuery event object. 208 * 209 * @returns {void} 210 */ 211 initializeAriaTabs: function() { 212 this.tabs = this.$( '[role="tab"]' ); 213 214 // Set up initial attributes. 215 this.tabs.attr( { 216 'aria-selected': false, 217 tabIndex: '-1' 218 } ); 219 220 // Set up attributes on the initially active tab. 221 this.tabs.filter( '.active' ) 222 .removeAttr( 'tabindex' ) 223 .attr( 'aria-selected', 'true' ); 224 }, 225 226 /** 227 * Enables arrows navigation within the ARIA tabbed interface. 228 * 229 * @since 5.3.0 230 * 231 * @param {object} event jQuery event object. 232 * 233 * @returns {void} 234 */ 235 tabsNavigation: function( event ) { 236 var orientation = 'horizontal', 237 keys = [ 32, 35, 36, 37, 38, 39, 40 ]; 238 239 // Return if not Spacebar, End, Home, or Arrow keys. 240 if ( keys.indexOf( event.which ) === -1 ) { 241 return; 242 } 243 244 // Determine navigation direction. 245 if ( this.$el.attr( 'aria-orientation' ) === 'vertical' ) { 246 orientation = 'vertical'; 247 } 248 249 // Make Up and Down arrow keys do nothing with horizontal tabs. 250 if ( orientation === 'horizontal' && [ 38, 40 ].indexOf( event.which ) !== -1 ) { 251 return; 252 } 253 254 // Make Left and Right arrow keys do nothing with vertical tabs. 255 if ( orientation === 'vertical' && [ 37, 39 ].indexOf( event.which ) !== -1 ) { 256 return; 257 } 258 259 this.switchTabs( event, this.tabs ); 260 }, 261 262 /** 263 * Switch tabs in the ARIA tabbed interface. 264 * 265 * @since 5.3.0 266 * 267 * @param {object} event jQuery event object. 268 * 269 * @returns {void} 270 */ 271 switchTabs: function( event ) { 272 var key = event.which, 273 index = this.tabs.index( jQuery( event.target ) ), 274 newIndex; 275 276 switch ( key ) { 277 // Space bar: Activate current targeted tab. 278 case 32: { 279 this.activateTab( this.tabs[ index ] ); 280 break; 281 } 282 // End key: Activate last tab. 283 case 35: { 284 event.preventDefault(); 285 this.activateTab( this.tabs[ this.tabs.length - 1 ] ); 286 break; 287 } 288 // Home key: Activate first tab. 289 case 36: { 290 event.preventDefault(); 291 this.activateTab( this.tabs[ 0 ] ); 292 break; 293 } 294 // Left and up keys: Activate previous tab. 295 case 37: 296 case 38: { 297 event.preventDefault(); 298 newIndex = ( index - 1 ) < 0 ? this.tabs.length - 1 : index - 1; 299 this.activateTab( this.tabs[ newIndex ] ); 300 break; 301 } 302 // Right and down keys: Activate next tab. 303 case 39: 304 case 40: { 305 event.preventDefault(); 306 newIndex = ( index + 1 ) === this.tabs.length ? 0 : index + 1; 307 this.activateTab( this.tabs[ newIndex ] ); 308 break; 309 } 310 } 311 }, 312 313 /** 314 * Sets all the tabs to be not focusable and semantically not selected. 315 * 316 * @since 5.3.0 317 * 318 * @param {object} tabs The tabs jQuery collection. 319 * 320 * @returns {void} 321 */ 322 deactivateTabs: function( tabs ) { 323 tabs 324 .attr( { 325 'aria-selected': 'false', 326 tabIndex: '-1' 327 } ); 328 }, 329 330 /** 331 * Sets a single tab to be focusable and semantically selected. 332 * 333 * @since 5.3.0 334 * 335 * @param {object} tab The tab DOM element. 336 * 337 * @returns {void} 338 */ 339 activateTab: function( tab ) { 340 if ( ! tab ) { 341 return; 342 } 343 344 this.deactivateTabs( this.tabs ); 345 346 // The tab is a DOM element: no need for jQuery methods. 347 tab.focus(); 348 349 // Handle manual activation. 350 if ( ! this.tabsAutomaticActivation ) { 351 tab.removeAttribute( 'tabindex' ); 352 tab.setAttribute( 'aria-selected', 'true' ); 353 return; 354 } 355 356 // Handle automatic activation. 357 $( tab ).on( 'click', function() { 358 tab.removeAttribute( 'tabindex' ); 359 tab.setAttribute( 'aria-selected', 'true' ); 360 } ); 361 362 tab.click(); 363 } 168 364 }); 169 365 170 366 module.exports = FocusManager; -
src/js/media/views/frame/post.js
257 257 mainMenu: function( view ) { 258 258 view.set({ 259 259 'library-separator': new wp.media.View({ 260 className: 'separator', 261 priority: 100 260 className: 'separator', 261 priority: 100, 262 attributes: { 263 role: 'presentation' 264 } 262 265 }) 263 266 }); 264 267 }, -
src/js/media/views/media-frame.js
110 110 */ 111 111 createMenu: function( menu ) { 112 112 menu.view = new wp.media.view.Menu({ 113 controller: this 113 controller: this, 114 115 attributes: { 116 role: 'tablist', 117 'aria-orientation': 'vertical' 118 } 114 119 }); 115 120 }, 116 121 -
src/js/media/views/menu-item.js
11 11 * @augments Backbone.View 12 12 */ 13 13 MenuItem = wp.media.View.extend(/** @lends wp.media.view.MenuItem.prototype */{ 14 tagName: ' a',14 tagName: 'button', 15 15 className: 'media-menu-item', 16 16 17 17 attributes: { 18 href: '#' 18 type: 'button', 19 role: 'tab' 19 20 }, 20 21 21 22 events: { 22 23 'click': '_click' 23 24 }, 25 24 26 /** 25 * @param {Object} event27 * Allows to override the click event. 26 28 */ 27 _click: function( event) {29 _click: function() { 28 30 var clickOverride = this.options.click; 29 31 30 if ( event ) {31 event.preventDefault();32 }33 34 32 if ( clickOverride ) { 35 33 clickOverride.call( this ); 36 34 } else { … … 43 41 44 42 if ( state ) { 45 43 this.controller.setState( state ); 44 // Toggle the menu visibility in the responsive view. 46 45 this.views.parent.$el.removeClass( 'visible' ); // TODO: or hide on any click, see below 47 46 } 48 47 }, 48 49 49 /** 50 50 * @returns {wp.media.view.MenuItem} returns itself to allow chaining 51 51 */ -
src/js/media/views/menu.js
20 20 ItemView: MenuItem, 21 21 region: 'menu', 22 22 23 /* TODO: alternatively hide on any click anywhere24 events: {25 ' click': 'click'23 attributes: { 24 role: 'tablist', 25 'aria-orientation': 'horizontal' 26 26 }, 27 27 28 click: function() { 29 this.$el.removeClass( 'visible' ); 28 initialize: function() { 29 this._views = {}; 30 31 this.set( _.extend( {}, this._views, this.options.views ), { silent: true }); 32 delete this.options.views; 33 34 if ( ! this.options.silent ) { 35 this.render(); 36 } 37 38 // Initialize the Focus Manager. 39 this.focusManager = new wp.media.view.FocusManager( { 40 el: this.el, 41 mode: 'tabsNavigation' 42 } ); 30 43 }, 31 */32 44 33 45 /** 34 46 * @param {Object} options … … 47 59 */ 48 60 PriorityList.prototype.ready.apply( this, arguments ); 49 61 this.visibility(); 62 63 // Set up aria tabs initial attributes. 64 this.focusManager.initializeAriaTabs(); 50 65 }, 51 66 52 67 set: function() { … … 87 102 88 103 this.deselect(); 89 104 view.$el.addClass('active'); 105 106 // Set up again the aria tabs initial attributes after the menu updates. 107 this.focusManager.initializeAriaTabs(); 90 108 }, 91 109 92 110 deselect: function() { -
src/js/media/views/router.js
20 20 ItemView: wp.media.view.RouterItem, 21 21 region: 'router', 22 22 23 attributes: { 24 role: 'tablist', 25 'aria-orientation': 'horizontal' 26 }, 27 23 28 initialize: function() { 24 29 this.controller.on( 'content:render', this.update, this ); 25 30 // Call 'initialize' directly on the parent class. -
src/wp-includes/css/media-views.css
553 553 user-select: none; 554 554 } 555 555 556 .media-menu > a{556 .media-menu .media-menu-item { 557 557 display: block; 558 box-sizing: border-box; 559 width: 100%; 558 560 position: relative; 561 border: 0; 562 margin: 0; 559 563 padding: 8px 20px; 560 margin: 0;564 font-size: 14px; 561 565 line-height: 1.28571428; 562 font-size: 14px;566 background: transparent; 563 567 color: #0073aa; 568 text-align: left; 564 569 text-decoration: none; 570 cursor: pointer; 565 571 } 566 572 567 .media-menu > a:hover { 568 color: #0073aa; 573 .media-menu .media-menu-item:hover { 569 574 background: rgba(0, 0, 0, 0.04); 570 575 } 571 576 572 .media-menu > a:active { 577 .media-menu .media-menu-item:active { 578 color: #0073aa; 573 579 outline: none; 574 580 } 575 581 … … 579 585 font-weight: 600; 580 586 } 581 587 588 .media-menu .media-menu-item:focus { 589 box-shadow: 590 0 0 0 1px #5b9dd9, 591 0 0 2px 1px rgba(30, 140, 190, 0.8); 592 color: #124964; 593 /* Only visible in Windows High Contrast mode */ 594 outline: 1px solid transparent; 595 } 596 582 597 .media-menu .separator { 583 598 height: 0; 584 599 margin: 12px 20px; … … 594 609 padding: 0 6px; 595 610 margin: 0; 596 611 clear: both; 597 -webkit-user-select: none;598 -moz-user-select: none;599 -ms-user-select: none;600 user-select: none;601 612 } 602 613 603 .media-router a { 604 transition: none; 605 } 606 607 .media-router > a { 614 .media-router .media-menu-item { 608 615 position: relative; 609 616 float: left; 617 border: 0; 618 margin: 0; 610 619 padding: 8px 10px 9px; 611 margin: 0;612 620 height: 18px; 613 621 line-height: 1.28571428; 614 622 font-size: 14px; 615 623 text-decoration: none; 624 background: transparent; 625 cursor: pointer; 626 transition: none; 616 627 } 617 628 618 .media-router > a:last-child {629 .media-router .media-menu-item:last-child { 619 630 border-right: 0; 620 631 } 621 632 622 .media-router > a:active { 623 outline: none; 633 .media-router .media-menu-item:hover, 634 .media-router .media-menu-item:active { 635 color: #0073aa; 624 636 } 625 637 626 638 .media-router .active, 627 639 .media-router .active:hover { 628 color: # 32373c;640 color: #23282d; 629 641 } 630 642 643 .media-router .media-menu-item:focus { 644 box-shadow: 645 0 0 0 1px #5b9dd9, 646 0 0 2px 1px rgba(30, 140, 190, 0.8); 647 color: #124964; 648 /* Only visible in Windows High Contrast mode */ 649 outline: 1px solid transparent; 650 } 651 631 652 .media-router .active, 632 .media-router > a.active:last-child {653 .media-router .media-menu-item.active:last-child { 633 654 margin: -1px -1px 0; 634 655 background: #fff; 635 656 border: 1px solid #ddd;