Changeset 50829
- Timestamp:
- 05/07/2021 11:17:33 PM (3 years ago)
- Location:
- trunk/src
- Files:
-
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/js/media/models/attachments.js
r50068 r50829 349 349 }, 350 350 /** 351 * Holds the total number of attachments. 352 * 353 * @since 5.7.0 354 */ 355 totalAttachments: 0, 356 357 /** 358 * Gets the total number of attachments. 359 * 360 * @since 5.7.0 361 * 362 * @return {number} The total number of attachments. 363 */ 364 getTotalAttachments: function() { 365 return this.mirroring ? this.mirroring.totalAttachments : 0; 366 }, 367 368 /** 351 369 * A custom Ajax-response parser. 352 370 * 353 371 * See trac ticket #24753 354 372 * 355 * @param {Object|Array} resp The raw response Object/Array. 373 * Called automatically by Backbone whenever a collection's models are returned 374 * by the server, in fetch. The default implementation is a no-op, simply 375 * passing through the JSON response. We override this to add attributes to 376 * the collection items. 377 * 378 * Since WordPress 5.5, the response returns the attachments under `response.attachments` 379 * and `response.totalAttachments` holds the total number of attachments found. 380 * 381 * @param {Object|Array} response The raw response Object/Array. 356 382 * @param {Object} xhr 357 383 * @return {Array} The array of model attributes to be added to the collection 358 384 */ 359 parse: function( resp, xhr ) { 360 if ( ! _.isArray( resp ) ) { 361 resp = [resp]; 362 } 363 364 return _.map( resp, function( attrs ) { 385 parse: function( response, xhr ) { 386 if ( ! _.isArray( response.attachments ) ) { 387 response = [response.attachments]; 388 } 389 390 this.totalAttachments = parseInt( response.totalAttachments, 10 ); 391 392 return _.map( response.attachments, function( attrs ) { 365 393 var id, attachment, newAttributes; 366 394 -
trunk/src/js/media/models/query.js
r50067 r50829 113 113 options.remove = false; 114 114 115 return this._more = this.fetch( options ).done( function( resp ) { 116 if ( _.isEmpty( resp ) || -1 === this.args.posts_per_page || resp.length < this.args.posts_per_page ) { 115 return this._more = this.fetch( options ).done( function( response ) { 116 // Since WordPress 5.5, the response returns the attachments under `response.attachments`. 117 var attachments = response.attachments; 118 119 if ( _.isEmpty( attachments ) || -1 === this.args.posts_per_page || attachments.length < this.args.posts_per_page ) { 117 120 query._hasMore = false; 118 121 } -
trunk/src/js/media/views/attachments.js
r47122 r50829 1 1 var View = wp.media.View, 2 2 $ = jQuery, 3 Attachments; 3 Attachments, 4 infiniteScrolling = wp.media.view.settings.infiniteScrolling; 4 5 5 6 Attachments = View.extend(/** @lends wp.media.view.Attachments.prototype */{ … … 36 37 37 38 /** 39 * @param infiniteScrolling Whether to enable infinite scrolling or use 40 * the default "load more" button. 38 41 * @param refreshSensitivity The time in milliseconds to throttle the scroll 39 42 * handler. … … 50 53 */ 51 54 _.defaults( this.options, { 55 infiniteScrolling: infiniteScrolling || false, 52 56 refreshSensitivity: wp.media.isTouchDevice ? 300 : 200, 53 57 refreshThreshold: 3, … … 85 89 this.controller.on( 'library:selection:add', this.attachmentFocus, this ); 86 90 87 // Throttle the scroll handler and bind this. 88 this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value(); 89 90 this.options.scrollElement = this.options.scrollElement || this.el; 91 $( this.options.scrollElement ).on( 'scroll', this.scroll ); 91 if ( this.options.infiniteScrolling ) { 92 // Throttle the scroll handler and bind this. 93 this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value(); 94 95 this.options.scrollElement = this.options.scrollElement || this.el; 96 $( this.options.scrollElement ).on( 'scroll', this.scroll ); 97 } 92 98 93 99 this.initSortable(); … … 388 394 } else { 389 395 this.views.unset(); 390 this.collection.more().done( this.scroll ); 396 if ( this.options.infiniteScrolling ) { 397 this.collection.more().done( this.scroll ); 398 } 391 399 } 392 400 }, … … 401 409 */ 402 410 ready: function() { 403 this.scroll(); 411 if ( this.options.infiniteScrolling ) { 412 this.scroll(); 413 } 404 414 }, 405 415 -
trunk/src/js/media/views/attachments/browser.js
r49539 r50829 3 3 l10n = wp.media.view.l10n, 4 4 $ = jQuery, 5 AttachmentsBrowser; 5 AttachmentsBrowser, 6 infiniteScrolling = wp.media.view.settings.infiniteScrolling, 7 __ = wp.i18n.__, 8 sprintf = wp.i18n.sprintf; 6 9 7 10 /** … … 69 72 } 70 73 71 72 74 // Add a heading before the attachments list. 73 75 this.createAttachmentsHeading(); 74 76 75 // Create the list of attachments. 76 this.createAttachments(); 77 // Create the attachments wrapper view. 78 this.createAttachmentsWrapperView(); 79 80 if ( ! infiniteScrolling ) { 81 this.$el.addClass( 'has-load-more' ); 82 this.createLoadMoreView(); 83 } 77 84 78 85 // For accessibility reasons, place the normal sidebar after the attachments, see ticket #36909. … … 92 99 93 100 this.collection.on( 'add remove reset', this.updateContent, this ); 101 102 if ( ! infiniteScrolling ) { 103 this.collection.on( 'add remove reset', this.updateLoadMoreView, this ); 104 } 94 105 95 106 // The non-cached or cached attachments query has completed. … … 107 118 */ 108 119 announceSearchResults: _.debounce( function() { 109 var count; 120 var count, 121 /* translators: Accessibility text. %d: Number of attachments found in a search. */ 122 mediaFoundHasMoreResultsMessage = __( 'Number of media items displayed: %d. Click load more for more results.' ); 123 124 if ( infiniteScrolling ) { 125 /* translators: Accessibility text. %d: Number of attachments found in a search. */ 126 mediaFoundHasMoreResultsMessage = __( 'Number of media items displayed: %d. Scroll the page for more results.' ); 127 } 110 128 111 129 if ( this.collection.mirroring.args.s ) { … … 118 136 119 137 if ( this.collection.hasMore() ) { 120 wp.a11y.speak( l10n.mediaFoundHasMoreResults.replace( '%d', count ) );138 wp.a11y.speak( mediaFoundHasMoreResultsMessage.replace( '%d', count ) ); 121 139 return; 122 140 } … … 393 411 394 412 if ( this.controller.isModeActive( 'grid' ) ) { 413 // Usually the media library. 395 414 noItemsView = view.attachmentsNoResults; 396 415 } else { 416 // Usually the media modal. 397 417 noItemsView = view.uploader; 398 418 } … … 434 454 }, 435 455 456 /** 457 * Creates the Attachments wrapper view. 458 * 459 * @since 5.7.0 460 * 461 * @return {void} 462 */ 463 createAttachmentsWrapperView: function() { 464 this.attachmentsWrapper = new wp.media.View( { 465 className: 'attachments-wrapper' 466 } ); 467 468 // Create the list of attachments. 469 this.views.add( this.attachmentsWrapper ); 470 this.createAttachments(); 471 }, 472 436 473 createAttachments: function() { 437 474 this.attachments = new wp.media.view.Attachments({ … … 452 489 this.controller.on( 'attachment:details:shift-tab', _.bind( this.attachments.restoreFocus, this.attachments ) ); 453 490 454 this.views.add( this.attachments ); 455 491 this.views.add( '.attachments-wrapper', this.attachments ); 456 492 457 493 if ( this.controller.isModeActive( 'grid' ) ) { … … 466 502 this.views.add( this.attachmentsNoResults ); 467 503 } 504 }, 505 506 /** 507 * Creates the load more button and attachments counter view. 508 * 509 * @since 5.7.0 510 * 511 * @return {void} 512 */ 513 createLoadMoreView: function() { 514 var view = this; 515 516 this.loadMoreWrapper = new View( { 517 controller: this.controller, 518 className: 'load-more-wrapper' 519 } ); 520 521 this.loadMoreCount = new View( { 522 controller: this.controller, 523 tagName: 'p', 524 className: 'load-more-count hidden' 525 } ); 526 527 this.loadMoreButton = new wp.media.view.Button( { 528 text: __( 'Load more' ), 529 className: 'load-more hidden', 530 style: 'primary', 531 size: '', 532 click: function() { 533 view.loadMoreAttachments(); 534 } 535 } ); 536 537 this.loadMoreSpinner = new wp.media.view.Spinner(); 538 539 this.loadMoreJumpToFirst = new wp.media.view.Button( { 540 text: __( 'Jump to first loaded item' ), 541 className: 'load-more-jump hidden', 542 size: '', 543 click: function() { 544 view.jumpToFirstAddedItem(); 545 } 546 } ); 547 548 this.views.add( '.attachments-wrapper', this.loadMoreWrapper ); 549 this.views.add( '.load-more-wrapper', this.loadMoreSpinner ); 550 this.views.add( '.load-more-wrapper', this.loadMoreCount ); 551 this.views.add( '.load-more-wrapper', this.loadMoreButton ); 552 this.views.add( '.load-more-wrapper', this.loadMoreJumpToFirst ); 553 }, 554 555 /** 556 * Updates the Load More view. This function is debounced because the 557 * collection updates multiple times at the add, remove, and reset events. 558 * We need it to run only once, after all attachments are added or removed. 559 * 560 * @since 5.7.0 561 * 562 * @return {void} 563 */ 564 updateLoadMoreView: _.debounce( function() { 565 // Ensure the load more view elements are initially hidden at each update. 566 this.loadMoreButton.$el.addClass( 'hidden' ); 567 this.loadMoreCount.$el.addClass( 'hidden' ); 568 this.loadMoreJumpToFirst.$el.addClass( 'hidden' ).prop( 'disabled', true ); 569 570 if ( ! this.collection.getTotalAttachments() ) { 571 return; 572 } 573 574 if ( this.collection.length ) { 575 this.loadMoreCount.$el.text( 576 /* translators: 1: Number of displayed attachments, 2: Number of total attachments. */ 577 sprintf( 578 __( 'Showing %1$s of %2$s media items' ), 579 this.collection.length, 580 this.collection.getTotalAttachments() 581 ) 582 ); 583 584 this.loadMoreCount.$el.removeClass( 'hidden' ); 585 } 586 587 /* 588 * Notice that while the collection updates multiple times hasMore() may 589 * return true when it's actually not true. 590 */ 591 if ( this.collection.hasMore() ) { 592 this.loadMoreButton.$el.removeClass( 'hidden' ); 593 } 594 595 // Find the media item to move focus to. The jQuery `eq()` index is zero-based. 596 this.firstAddedMediaItem = this.$el.find( '.attachment' ).eq( this.firstAddedMediaItemIndex ); 597 598 // If there's a media item to move focus to, make the "Jump to" button available. 599 if ( this.firstAddedMediaItem.length ) { 600 this.firstAddedMediaItem.addClass( 'new-media' ); 601 this.loadMoreJumpToFirst.$el.removeClass( 'hidden' ).prop( 'disabled', false ); 602 } 603 604 // If there are new items added, but no more to be added, move focus to Jump button. 605 if ( this.firstAddedMediaItem.length && ! this.collection.hasMore() ) { 606 this.loadMoreJumpToFirst.$el.trigger( 'focus' ); 607 } 608 }, 10 ), 609 610 /** 611 * Loads more attachments. 612 * 613 * @since 5.7.0 614 * 615 * @return {void} 616 */ 617 loadMoreAttachments: function() { 618 var view = this; 619 620 if ( ! this.collection.hasMore() ) { 621 return; 622 } 623 624 /* 625 * The collection index is zero-based while the length counts the actual 626 * amount of items. Thus the length is equivalent to the position of the 627 * first added item. 628 */ 629 this.firstAddedMediaItemIndex = this.collection.length; 630 631 this.$el.addClass( 'more-loaded' ); 632 this.collection.each( function( attachment ) { 633 var attach_id = attachment.attributes.id; 634 $( '[data-id="' + attach_id + '"]' ).addClass( 'found-media' ); 635 }); 636 637 view.loadMoreSpinner.show(); 638 639 this.collection.more().done( function() { 640 // Within done(), `this` is the returned collection. 641 view.loadMoreSpinner.hide(); 642 } ); 643 }, 644 645 /** 646 * Moves focus to the first new added item. . 647 * 648 * @since 5.7.0 649 * 650 * @return {void} 651 */ 652 jumpToFirstAddedItem: function() { 653 // Set focus on first added item. 654 this.firstAddedMediaItem.focus(); 468 655 }, 469 656 -
trunk/src/wp-admin/css/media.css
r50025 r50829 421 421 .media-frame.mode-grid, 422 422 .media-frame.mode-grid .media-frame-content, 423 .media-frame.mode-grid .attachments-browser .attachments, 423 .media-frame.mode-grid .attachments-browser:not(.has-load-more) .attachments, 424 .media-frame.mode-grid .attachments-browser.has-load-more .attachments-wrapper, 424 425 .media-frame.mode-grid .uploader-inline-content { 425 426 position: static; … … 499 500 } 500 501 501 .media-frame.mode-select .attachments-browser.fixed .attachments { 502 .media-frame.mode-select .attachments-browser.fixed:not(.has-load-more) .attachments, 503 .media-frame.mode-select .attachments-browser.has-load-more.fixed .attachments-wrapper { 502 504 position: relative; 503 505 top: 94px; /* prevent jumping up when the toolbar becomes fixed */ -
trunk/src/wp-admin/includes/ajax-actions.php
r50556 r50829 2994 2994 $posts = array_filter( $posts ); 2995 2995 2996 wp_send_json_success( $posts ); 2996 $result = array( 2997 'attachments' => $posts, 2998 'totalAttachments' => $query->found_posts, 2999 ); 3000 3001 wp_send_json_success( $result ); 2997 3002 } 2998 3003 -
trunk/src/wp-includes/css/media-views.css
r50784 r50829 1189 1189 } 1190 1190 1191 .attachments-browser .attachments, 1191 .attachments-browser:not(.has-load-more) .attachments, 1192 .attachments-browser.has-load-more .attachments-wrapper, 1192 1193 .attachments-browser .uploader-inline { 1193 1194 position: absolute; … … 1266 1267 .attachments-browser .no-media { 1267 1268 padding: 2em 0 0 2em; 1269 } 1270 1271 .more-loaded .attachment:not(.found-media) { 1272 background: #dcdcde; 1273 } 1274 1275 .load-more-wrapper { 1276 clear: both; 1277 display: flex; 1278 flex-wrap: wrap; 1279 align-items: center; 1280 justify-content: center; 1281 padding: 1em 0; 1282 } 1283 1284 .load-more-wrapper .load-more-count { 1285 min-width: 100%; 1286 margin: 0 0 1em; 1287 text-align: center; 1288 } 1289 1290 .load-more-wrapper .load-more { 1291 margin: 0; 1292 } 1293 1294 /* Needs high specificity. */ 1295 .media-frame .load-more-wrapper .load-more + .spinner { 1296 float: none; 1297 margin: 0 -30px 0 10px; 1298 } 1299 1300 /* Reset spinner margin when the button is hidden to avoid horizontal scrollbar. */ 1301 .media-frame .load-more-wrapper .load-more.hidden + .spinner { 1302 margin: 0; 1303 } 1304 1305 /* Force a new row within the flex container. */ 1306 .load-more-wrapper::after { 1307 content: ""; 1308 min-width: 100%; 1309 order: 1; 1310 } 1311 1312 .load-more-wrapper .load-more-jump { 1313 margin: 0 0 0 12px; 1314 } 1315 1316 .attachment.new-media { 1317 outline: 2px dotted #c3c4c7; 1318 } 1319 1320 .load-more-wrapper { 1321 clear: both; 1322 display: flex; 1323 flex-wrap: wrap; 1324 align-items: center; 1325 justify-content: center; 1326 padding: 1em 0; 1327 } 1328 1329 .load-more-wrapper .load-more-count { 1330 min-width: 100%; 1331 margin: 0 0 1em; 1332 text-align: center; 1333 } 1334 1335 .load-more-wrapper .load-more { 1336 margin: 0; 1337 } 1338 1339 /* Needs high specificity. */ 1340 .media-frame .load-more-wrapper .load-more + .spinner { 1341 float: none; 1342 margin: 0 -30px 0 10px; 1343 } 1344 1345 /* Reset spinner margin when the button is hidden to avoid horizontal scrollbar. */ 1346 .media-frame .load-more-wrapper .load-more.hidden + .spinner { 1347 margin: 0; 1348 } 1349 1350 /* Force a new row within the flex container. */ 1351 .load-more-wrapper::after { 1352 content: ""; 1353 min-width: 100%; 1354 order: 1; 1355 } 1356 1357 .load-more-wrapper .load-more-jump { 1358 margin: 0 0 0 12px; 1268 1359 } 1269 1360 … … 2819 2910 display: none; 2820 2911 } 2912 2913 /* Change margin direction on load more button in responsive views. */ 2914 .load-more-wrapper .load-more-jump { 2915 margin: 12px 0 0 0; 2916 } 2917 2821 2918 } 2822 2919 … … 2827 2924 padding-top: 44px; 2828 2925 } 2926 2927 /* Change margin direction on load more button in responsive views. */ 2928 .load-more-wrapper .load-more-jump { 2929 margin: 12px 0 0 0; 2930 } 2931 2829 2932 } 2830 2933 -
trunk/src/wp-includes/media.php
r50820 r50829 4307 4307 } 4308 4308 4309 /** 4310 * Filters whether the Media Library grid has infinite scrolling. Default `false`. 4311 * 4312 * @since 5.7.0 4313 * 4314 * @param bool $value The filtered value, defaults to `false`. 4315 */ 4316 $infinite_scrolling = apply_filters( 'media_library_infinite_scrolling', false ); 4317 4309 4318 $settings = array( 4310 'tabs' => $tabs,4311 'tabUrl' => add_query_arg( array( 'chromeless' => true ), admin_url( 'media-upload.php' ) ),4312 'mimeTypes' => wp_list_pluck( get_post_mime_types(), 0 ),4319 'tabs' => $tabs, 4320 'tabUrl' => add_query_arg( array( 'chromeless' => true ), admin_url( 'media-upload.php' ) ), 4321 'mimeTypes' => wp_list_pluck( get_post_mime_types(), 0 ), 4313 4322 /** This filter is documented in wp-admin/includes/media.php */ 4314 'captions' => ! apply_filters( 'disable_captions', '' ),4315 'nonce' => array(4323 'captions' => ! apply_filters( 'disable_captions', '' ), 4324 'nonce' => array( 4316 4325 'sendToEditor' => wp_create_nonce( 'media-send-to-editor' ), 4317 4326 ), 4318 'post' => array(4327 'post' => array( 4319 4328 'id' => 0, 4320 4329 ), 4321 'defaultProps' => $props,4322 'attachmentCounts' => array(4330 'defaultProps' => $props, 4331 'attachmentCounts' => array( 4323 4332 'audio' => ( $show_audio_playlist ) ? 1 : 0, 4324 4333 'video' => ( $show_video_playlist ) ? 1 : 0, 4325 4334 ), 4326 'oEmbedProxyUrl' => rest_url( 'oembed/1.0/proxy' ), 4327 'embedExts' => $exts, 4328 'embedMimes' => $ext_mimes, 4329 'contentWidth' => $content_width, 4330 'months' => $months, 4331 'mediaTrash' => MEDIA_TRASH ? 1 : 0, 4335 'oEmbedProxyUrl' => rest_url( 'oembed/1.0/proxy' ), 4336 'embedExts' => $exts, 4337 'embedMimes' => $ext_mimes, 4338 'contentWidth' => $content_width, 4339 'months' => $months, 4340 'mediaTrash' => MEDIA_TRASH ? 1 : 0, 4341 'infiniteScrolling' => ( $infinite_scrolling ) ? 1 : 0, 4332 4342 ); 4333 4343 … … 4413 4423 'searchMediaLabel' => __( 'Search media' ), // Backward compatibility pre-5.3. 4414 4424 'searchMediaPlaceholder' => __( 'Search media items...' ), // Placeholder (no ellipsis), backward compatibility pre-5.3. 4425 /* translators: %d: Number of attachments found in a search. */ 4415 4426 'mediaFound' => __( 'Number of media items found: %d' ), 4416 'mediaFoundHasMoreResults' => __( 'Number of media items displayed: %d. Scroll the page for more results.' ),4417 4427 'noMedia' => __( 'No media items found.' ), 4418 4428 'noMediaTryNewSearch' => __( 'No media items found. Try a different search.' ),
Note: See TracChangeset
for help on using the changeset viewer.