diff --git a/app/assets/javascripts/projects/index.js b/app/assets/javascripts/projects/index.js index cc843cfed..ccf83aa2f 100644 --- a/app/assets/javascripts/projects/index.js +++ b/app/assets/javascripts/projects/index.js @@ -7,7 +7,8 @@ // - refresh project users tab after manage user modal is closed // - refactor view handling using library, ex. backbone.js -/* global HelperModule dropdownSelector Sidebar Turbolinks filterDropdown InfiniteScroll */ +/* global HelperModule dropdownSelector Sidebar Turbolinks filterDropdown InfiniteScroll GLOBAL_CONSTANTS */ +/* eslint-disable no-use-before-define */ var ProjectsIndex = (function() { const PERMISSIONS = ['editable', 'archivable', 'restorable', 'moveable', 'deletable']; @@ -16,7 +17,7 @@ var ProjectsIndex = (function() { var cardsWrapper = '#cardsWrapper'; var editProjectModal = '#edit-modal'; var moveToModal = '#move-to-modal'; - var pageSize = 20; + var pageSize = GLOBAL_CONSTANTS.DEFAULT_ELEMENTS_PER_PAGE; var exportProjectsModal = null; var exportProjectsModalHeader = null; diff --git a/app/assets/javascripts/projects/show.js b/app/assets/javascripts/projects/show.js index 59dd02284..e6ca04831 100644 --- a/app/assets/javascripts/projects/show.js +++ b/app/assets/javascripts/projects/show.js @@ -1,6 +1,8 @@ -/* global animateSpinner filterDropdown Sidebar Turbolinks HelperModule */ +/* global animateSpinner filterDropdown Sidebar Turbolinks HelperModule InfiniteScroll GLOBAL_CONSTANTS */ +/* eslint-disable no-use-before-define */ (function() { const PERMISSIONS = ['editable', 'archivable', 'restorable', 'moveable', 'duplicable']; + const pageSize = GLOBAL_CONSTANTS.DEFAULT_ELEMENTS_PER_PAGE; var cardsWrapper = '#cardsWrapper'; var experimentsPage = '#projectShowWrapper'; @@ -67,23 +69,35 @@ }); } + function loadPlaceHolder() { + let palceholder = ''; + $.each(Array(pageSize), function() { + palceholder += $('#experimentPlaceholder').html(); + }); + $(palceholder).insertAfter($(cardsWrapper).find('.table-header')); + } + function loadCardsView() { + var requestParams = { + view_mode: $(experimentsPage).data('view-mode'), + sort: experimentsCurrentSort, + search: experimentsViewSearch, + created_on_from: startedOnFromFilter, + created_on_to: startedOnToFilter, + updated_on_from: modifiedOnFromFilter, + updated_on_to: modifiedOnToFilter, + archived_on_from: archivedOnFromFilter, + archived_on_to: archivedOnToFilter + }; var viewContainer = $(cardsWrapper); + var cardsUrl = viewContainer.data('experiments-cards-url'); + + loadPlaceHolder(); $.ajax({ - url: viewContainer.data('experiments-cards-url'), + url: cardsUrl, type: 'GET', dataType: 'json', - data: { - view_mode: $(experimentsPage).data('view-mode'), - sort: experimentsCurrentSort, - search: experimentsViewSearch, - created_on_from: startedOnFromFilter, - created_on_to: startedOnToFilter, - updated_on_from: modifiedOnFromFilter, - updated_on_to: modifiedOnToFilter, - archived_on_from: archivedOnFromFilter, - archived_on_to: archivedOnToFilter - }, + data: requestParams, success: function(data) { viewContainer.find('.card, .no-results-container').remove(); viewContainer.removeClass('no-results'); @@ -94,6 +108,20 @@ selectedExperiments.length = 0; updateExperimentsToolbar(); loadExperimentWorkflowImages(); + + InfiniteScroll.init(cardsWrapper, { + url: cardsUrl, + eventTarget: window, + placeholderTemplate: '#experimentPlaceholder', + endOfListTemplate: '#experimentEndOfList', + pageSize: pageSize, + customResponse: (response) => { + $(response.cards_html).appendTo(cardsWrapper); + }, + customParams: (params) => { + return { ...params, ...requestParams }; + } + }); }, error: function() { viewContainer.html('Error loading project list'); diff --git a/app/assets/javascripts/sitewide/constants.js.erb b/app/assets/javascripts/sitewide/constants.js.erb index cdb0bb934..315b504ac 100644 --- a/app/assets/javascripts/sitewide/constants.js.erb +++ b/app/assets/javascripts/sitewide/constants.js.erb @@ -9,5 +9,6 @@ const GLOBAL_CONSTANTS = { REPOSITORY_LIST_ITEMS_PER_COLUMN: <%= Constants::REPOSITORY_LIST_ITEMS_PER_COLUMN %>, REPOSITORY_CHECKLIST_ITEMS_PER_COLUMN: <%= Constants::REPOSITORY_CHECKLIST_ITEMS_PER_COLUMN %>, REPOSITORY_STOCK_UNIT_ITEMS_PER_COLUMN: <%= Constants::REPOSITORY_STOCK_UNIT_ITEMS_PER_COLUMN %>, - HAS_UNSAVED_DATA_CLASS_NAME: 'has-unsaved-data' + HAS_UNSAVED_DATA_CLASS_NAME: 'has-unsaved-data', + DEFAULT_ELEMENTS_PER_PAGE: <%= Constants::DEFAULT_ELEMENTS_PER_PAGE %> }; diff --git a/app/assets/javascripts/sitewide/infinite_scroll.js b/app/assets/javascripts/sitewide/infinite_scroll.js index 94abdadab..82ff23a05 100644 --- a/app/assets/javascripts/sitewide/infinite_scroll.js +++ b/app/assets/javascripts/sitewide/infinite_scroll.js @@ -1,10 +1,7 @@ +/* eslint-disable no-use-before-define */ /* eslint-disable no-unused-vars */ var InfiniteScroll = (function() { - function getScrollHeight($container) { - return $container[0].scrollHeight; - } - function scrollNotVisible($container) { let eventTarget = $($container.data('config').eventTarget || $container); return scrollHitBottom(eventTarget[0]); diff --git a/app/assets/stylesheets/experiments.scss b/app/assets/stylesheets/experiments.scss index fb0982fc9..bbf5b2c73 100644 --- a/app/assets/stylesheets/experiments.scss +++ b/app/assets/stylesheets/experiments.scss @@ -173,6 +173,78 @@ } } } + + &.experiment-placeholder { + align-items: center; + background-color: $color-white; + border-radius: $border-radius-default; + box-shadow: $flyout-shadow; + display: flex; + + .placeholder-body { + display: flex; + flex-basis: 70%; + flex-wrap: wrap; + height: 80px; + } + + .image-container { + margin-left: auto; + + .image-text { + display: none; + } + } + + .placeholder-element { + animation-duration: 2s; + animation-iteration-count: infinite; + animation-name: placeholder-pulsing; + background-color: $color-alto; + border-radius: $border-radius-default; + height: 18px; + + &.header, + &.footer { + flex-basis: 100%; + } + + &.footer { + height: 36px; + } + + &.line-0 { + flex-basis: 100%; + } + + &.line-1 { + flex-basis: 90%; + } + + &.line-2 { + flex-basis: 80%; + } + + &.image { + height: 80px; + width: 80px; + } + + @keyframes placeholder-pulsing { + 0% { + opacity: 1; + } + + 50% { + opacity: .5; + } + + 100% { + opacity: 1; + } + } + } + } } &.list { @@ -281,6 +353,72 @@ background: linear-gradient(to right, $color-transparent, $color-concrete 50%); } } + + &.experiment-placeholder { + display: contents; + + .placeholder-body { + display: contents; + } + + .placeholder-element { + align-self: flex-start; + display: none; + margin-top: .5em; + padding: .5em 0; + } + + .footer { + display: block; + grid-column: 6; + height: 18px; + width: 80%; + + } + + .header { + display: block; + grid-column: 7; + width: 80%; + } + + .image-container { + display: flex; + grid-column: 2; + margin-left: 0; + + .image { + display: block; + flex-basis: 56px; + height: 56px; + margin: 0; + } + + .image-text { + display: block; + flex-basis: calc(100% - 80px); + margin: 0 10px 0 auto; + } + } + + .line-0 { + display: block; + grid-column: 3; + width: 80%; + } + + .line-1 { + display: block; + grid-column: 4; + width: 80%; + } + + .line-2 { + display: block; + grid-column: 5; + width: 80%; + } + } } } @@ -289,6 +427,29 @@ margin-left: 0 !important; } } + + &.last-page { + padding-bottom: 5em; + position: relative; + } + + .experiment-list-end-placeholder { + align-items: center; + background-color: $color-concrete; + bottom: 1em; + display: flex; + height: 3em; + left: calc(50% - 150px); + margin: 0 auto; + padding: 1em; + position: absolute; + width: 300px; + + > * { + flex-grow: 1; + text-align: center; + } + } } } diff --git a/app/assets/stylesheets/projects.scss b/app/assets/stylesheets/projects.scss index 9f06f1983..c379a691e 100644 --- a/app/assets/stylesheets/projects.scss +++ b/app/assets/stylesheets/projects.scss @@ -624,8 +624,8 @@ li.module-hover { --list-columns-number: 6; &.last-page { - position: relative; padding-bottom: 5em; + position: relative; } .projects-group { @@ -798,9 +798,9 @@ li.module-hover { display: flex; .placeholder-element { - animation-name: placeholder-pulsing; animation-duration: 2s; animation-iteration-count: infinite; + animation-name: placeholder-pulsing; background-color: $color-alto; border-radius: $border-radius-default; height: 18px; @@ -832,9 +832,11 @@ li.module-hover { 0% { opacity: 1; } + 50% { opacity: .5; } + 100% { opacity: 1; } diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index afd95fac1..2e4030f39 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -65,7 +65,7 @@ class ProjectsController < ApplicationController end cards = Kaminari.paginate_array(overview_service.project_and_folder_cards) - .page(params[:page] || 1).per(20) + .page(params[:page] || 1).per(Constants::DEFAULT_ELEMENTS_PER_PAGE) render json: { projects_cards_url: projects_cards_url, @@ -292,10 +292,12 @@ class ProjectsController < ApplicationController def experiments_cards overview_service = ExperimentsOverviewService.new(@project, current_user, params) + cards = overview_service.experiments.page(params[:page] || 1).per(Constants::DEFAULT_ELEMENTS_PER_PAGE) render json: { + next_page: cards.next_page, cards_html: render_to_string( partial: 'projects/show/experiments_list.html.erb', - locals: { cards: overview_service.experiments, + locals: { cards: cards, filters_included: filters_included? } ) } diff --git a/app/services/experiments_overview_service.rb b/app/services/experiments_overview_service.rb index dca191a6b..a11c84e0e 100644 --- a/app/services/experiments_overview_service.rb +++ b/app/services/experiments_overview_service.rb @@ -45,7 +45,7 @@ class ExperimentsOverviewService .select('experiments.*') .select('COUNT(DISTINCT active_tasks.id) AS task_count') .select('COUNT(DISTINCT active_completed_tasks.id) AS completed_task_count') - .group('experiments.id, user_assignments.id, user_roles.id') + .group('experiments.id') end def filter_records(records) diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb index 82437a18c..a92f6c491 100644 --- a/app/views/projects/show.html.erb +++ b/app/views/projects/show.html.erb @@ -37,5 +37,29 @@ + + + + + <% 3.times do |i| %> + + <% end %> + + + + + + + + + + + + + <%= t('projects.index.end_of_list_placeholder') %> + + + + <%= javascript_include_tag("projects/show") %> diff --git a/config/initializers/constants.rb b/config/initializers/constants.rb index c88760f87..5e443b6a2 100644 --- a/config/initializers/constants.rb +++ b/config/initializers/constants.rb @@ -71,6 +71,8 @@ class Constants RESULTS_PER_PAGE_LIMIT = 10 #Experiments more button appears EXPERIMENT_LONG_DESCRIPTION = 80 + # Infinite scroll default elements per page + DEFAULT_ELEMENTS_PER_PAGE = 20 #============================================================================= # File and data memory size