mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-10-06 11:57:16 +08:00
Add pagination to projects list [SCI-6655]
This commit is contained in:
parent
a85b78291d
commit
d96103d714
6 changed files with 249 additions and 19 deletions
|
@ -7,7 +7,7 @@
|
|||
// - refresh project users tab after manage user modal is closed
|
||||
// - refactor view handling using library, ex. backbone.js
|
||||
|
||||
/* global HelperModule dropdownSelector Sidebar Turbolinks filterDropdown */
|
||||
/* global HelperModule dropdownSelector Sidebar Turbolinks filterDropdown InfiniteScroll */
|
||||
|
||||
var ProjectsIndex = (function() {
|
||||
const PERMISSIONS = ['editable', 'archivable', 'restorable', 'moveable', 'deletable'];
|
||||
|
@ -16,6 +16,7 @@ var ProjectsIndex = (function() {
|
|||
var cardsWrapper = '#cardsWrapper';
|
||||
var editProjectModal = '#edit-modal';
|
||||
var moveToModal = '#move-to-modal';
|
||||
var pageSize = 20;
|
||||
|
||||
var exportProjectsModal = null;
|
||||
var exportProjectsModalHeader = null;
|
||||
|
@ -443,23 +444,35 @@ var ProjectsIndex = (function() {
|
|||
});
|
||||
}
|
||||
|
||||
function loadPlaceHolder() {
|
||||
let palceholder = '';
|
||||
$.each(Array(pageSize), function() {
|
||||
palceholder += $('#projectPlaceholder').html();
|
||||
});
|
||||
$(palceholder).insertAfter($(cardsWrapper).find('.table-header'));
|
||||
}
|
||||
|
||||
function loadCardsView() {
|
||||
var requestParams = {
|
||||
view_mode: $('.projects-index').data('view-mode'),
|
||||
sort: projectsCurrentSort,
|
||||
search: projectsViewSearch,
|
||||
members: membersFilter,
|
||||
created_on_from: createdOnFromFilter,
|
||||
created_on_to: createdOnToFilter,
|
||||
folders_search: lookInsideFolders,
|
||||
archived_on_from: archivedOnFromFilter,
|
||||
archived_on_to: archivedOnToFilter
|
||||
};
|
||||
var viewContainer = $(cardsWrapper);
|
||||
var cardsUrl = viewContainer.data('projects-cards-url');
|
||||
|
||||
loadPlaceHolder();
|
||||
$.ajax({
|
||||
url: viewContainer.data('projects-cards-url'),
|
||||
url: cardsUrl,
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
view_mode: $('.projects-index').data('view-mode'),
|
||||
sort: projectsCurrentSort,
|
||||
search: projectsViewSearch,
|
||||
members: membersFilter,
|
||||
created_on_from: createdOnFromFilter,
|
||||
created_on_to: createdOnToFilter,
|
||||
folders_search: lookInsideFolders,
|
||||
archived_on_from: archivedOnFromFilter,
|
||||
archived_on_to: archivedOnToFilter
|
||||
},
|
||||
data: { ...requestParams, ...{ page: 1 } },
|
||||
success: function(data) {
|
||||
$('#breadcrumbsWrapper').html(data.breadcrumbs_html);
|
||||
$(projectsWrapper).find('.projects-title').html(data.title);
|
||||
|
@ -474,6 +487,23 @@ var ProjectsIndex = (function() {
|
|||
selectedProjects.length = 0;
|
||||
selectedProjectFolders.length = 0;
|
||||
updateProjectsToolbar();
|
||||
if (data.filtered) {
|
||||
InfiniteScroll.removeScroll(cardsWrapper);
|
||||
} else {
|
||||
InfiniteScroll.init(cardsWrapper, {
|
||||
url: cardsUrl,
|
||||
eventTarget: window,
|
||||
placeholderTemplate: '#projectPlaceholder',
|
||||
endOfListTemplate: '#projectEndOfList',
|
||||
pageSize: pageSize,
|
||||
customResponse: (response) => {
|
||||
$(response.cards_html).appendTo(cardsWrapper);
|
||||
},
|
||||
customParams: (params) => {
|
||||
return { ...params, ...requestParams };
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
viewContainer.html('Error loading project list');
|
||||
|
@ -490,6 +520,7 @@ var ProjectsIndex = (function() {
|
|||
$(projectsPageSelector).find('.cards-switch .button-to').removeClass('selected');
|
||||
$(ev.target).find('.button-to').addClass('selected');
|
||||
$(ev.target).parents('.dropdown.view-switch').removeClass('open');
|
||||
InfiniteScroll.loadMore(cardsWrapper);
|
||||
})
|
||||
.on('ajax:error', '.change-projects-view-type-form', function(ev, data) {
|
||||
HelperModule.flashAlertMsg(data.responseJSON.flash, 'danger');
|
||||
|
|
|
@ -6,7 +6,8 @@ var InfiniteScroll = (function() {
|
|||
}
|
||||
|
||||
function scrollNotVisible($container) {
|
||||
return (getScrollHeight($container) - $container.height() - 150 <= 0);
|
||||
let eventTarget = $($container.data('config').eventTarget || $container);
|
||||
return scrollHitBottom(eventTarget[0]);
|
||||
}
|
||||
|
||||
function loadData($container, page = 1) {
|
||||
|
@ -15,8 +16,9 @@ var InfiniteScroll = (function() {
|
|||
|
||||
if ($container.hasClass('loading') || $container.hasClass('last-page')) return;
|
||||
$container.addClass('loading');
|
||||
|
||||
renderPlaceholder($container);
|
||||
$.get($container.data('config').url, params, function(result) {
|
||||
$container.find('.placeholder-block').remove();
|
||||
if ($container.data('config').customResponse) {
|
||||
$container.data('config').customResponse(result, $container);
|
||||
} else {
|
||||
|
@ -27,6 +29,9 @@ var InfiniteScroll = (function() {
|
|||
$container.data('next-page', result.next_page);
|
||||
} else {
|
||||
$container.addClass('last-page');
|
||||
if ($container.data('config').endOfListTemplate) {
|
||||
$($($container.data('config').endOfListTemplate).html()).appendTo($container);
|
||||
}
|
||||
}
|
||||
$container.removeClass('loading');
|
||||
|
||||
|
@ -44,20 +49,73 @@ var InfiniteScroll = (function() {
|
|||
var $container = $(object);
|
||||
$container.data('next-page', 2);
|
||||
$container.data('config', config);
|
||||
|
||||
if (config.loadFirstPage) {
|
||||
loadData($container, 1);
|
||||
}
|
||||
|
||||
$container.on('scroll', () => {
|
||||
if ($container.scrollTop() + $container.height() > getScrollHeight($container) - 150 && !$container.hasClass('last-page')) {
|
||||
let eventTarget = $($container.data('config').eventTarget || $container);
|
||||
eventTarget.on('scroll', () => {
|
||||
if (scrollHitBottom(eventTarget[0]) && !$container.hasClass('last-page')) {
|
||||
loadData($container, $container.data('next-page'));
|
||||
}
|
||||
});
|
||||
|
||||
if (scrollNotVisible($container)) {
|
||||
loadData($container, $container.data('next-page'));
|
||||
}
|
||||
}
|
||||
|
||||
// support functions
|
||||
|
||||
// Full scroll height
|
||||
function scrollHeight(con) {
|
||||
return con.scrollHeight || document.documentElement.scrollHeight;
|
||||
}
|
||||
|
||||
// Top scroll position
|
||||
function scrollTop(con) {
|
||||
return con.scrollTop || con.scrollY || 0;
|
||||
}
|
||||
|
||||
// Get container size
|
||||
function containerSize(con) {
|
||||
return con.innerHeight || con.offsetHeight;
|
||||
}
|
||||
|
||||
// Container position
|
||||
function containerPosition(con) {
|
||||
return scrollTop(con) + containerSize(con);
|
||||
}
|
||||
|
||||
// Check when load next page
|
||||
function scrollHitBottom(con) {
|
||||
return scrollHeight(con) - containerPosition(con) <= 0;
|
||||
}
|
||||
|
||||
function removeScroll(con) {
|
||||
let $container = $(con);
|
||||
|
||||
if (!$container.data('config')) {
|
||||
return;
|
||||
}
|
||||
|
||||
let eventTarget = $($container.data('config').eventTarget) || $container;
|
||||
$container.data('config', null);
|
||||
$container.data('next-page', null);
|
||||
eventTarget.off('scroll');
|
||||
}
|
||||
|
||||
function renderPlaceholder($container) {
|
||||
let palceholder = '';
|
||||
$.each(Array($container.data('config').pageSize || 10), function() {
|
||||
palceholder += $($container.data('config').placeholderTemplate).html();
|
||||
});
|
||||
$(palceholder).addClass('placeholder-block').appendTo($container);
|
||||
}
|
||||
|
||||
return {
|
||||
init: (object, config) => {
|
||||
removeScroll(object);
|
||||
initScroll(object, config);
|
||||
},
|
||||
resetScroll: (object) => {
|
||||
|
@ -65,6 +123,15 @@ var InfiniteScroll = (function() {
|
|||
if (scrollNotVisible($(object))) {
|
||||
loadData($(object), $(object).data('next-page'));
|
||||
}
|
||||
},
|
||||
removeScroll: (object) => {
|
||||
removeScroll(object);
|
||||
},
|
||||
loadMore: (object) => {
|
||||
let $container = $(object);
|
||||
if (scrollNotVisible($container)) {
|
||||
loadData($container, $container.data('next-page'));
|
||||
}
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
|
|
@ -623,6 +623,11 @@ li.module-hover {
|
|||
--card-min-width: 291px;
|
||||
--list-columns-number: 6;
|
||||
|
||||
&.last-page {
|
||||
position: relative;
|
||||
padding-bottom: 5em;
|
||||
}
|
||||
|
||||
.projects-group {
|
||||
grid-column: 1/-1;
|
||||
margin: 0;
|
||||
|
@ -784,6 +789,58 @@ li.module-hover {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.project-placeholder {
|
||||
align-items: center;
|
||||
background-color: $color-white;
|
||||
border-radius: $border-radius-default;
|
||||
box-shadow: $flyout-shadow;
|
||||
display: flex;
|
||||
|
||||
.placeholder-element {
|
||||
animation-name: placeholder-pulsing;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
background-color: $color-alto;
|
||||
border-radius: $border-radius-default;
|
||||
height: 18px;
|
||||
|
||||
&.line-0 {
|
||||
flex-basis: 80%;
|
||||
}
|
||||
|
||||
&.line-1 {
|
||||
flex-basis: 65%;
|
||||
}
|
||||
|
||||
&.line-2 {
|
||||
flex-basis: 85%;
|
||||
}
|
||||
|
||||
&.line-3 {
|
||||
flex-basis: 25%;
|
||||
}
|
||||
|
||||
&.circle {
|
||||
border-radius: 50%;
|
||||
height: 27px;
|
||||
margin-left: .5em;
|
||||
width: 27px;
|
||||
}
|
||||
|
||||
@keyframes placeholder-pulsing {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: .5;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.list {
|
||||
|
@ -884,9 +941,59 @@ li.module-hover {
|
|||
position: initial;
|
||||
}
|
||||
}
|
||||
|
||||
&.project-placeholder {
|
||||
display: contents;
|
||||
|
||||
.placeholder-element {
|
||||
display: none;
|
||||
padding: .5em 0;
|
||||
}
|
||||
|
||||
.line-0 {
|
||||
display: block;
|
||||
grid-column: 2;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.line-1 {
|
||||
display: block;
|
||||
grid-column: 3;
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.line-2 {
|
||||
display: block;
|
||||
grid-column: 4;
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.circle-0 {
|
||||
display: block;
|
||||
grid-column: 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.project-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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
|
|
|
@ -39,6 +39,7 @@ class ProjectsController < ApplicationController
|
|||
if filters_included?
|
||||
render json: {
|
||||
toolbar_html: render_to_string(partial: 'projects/index/toolbar.html.erb'),
|
||||
filtered: true,
|
||||
cards_html: render_to_string(
|
||||
partial: 'projects/index/team_projects_grouped_by_folder.html.erb',
|
||||
locals: { projects_by_folder: overview_service.grouped_by_folder_project_cards }
|
||||
|
@ -63,14 +64,18 @@ class ProjectsController < ApplicationController
|
|||
projects_cards_url = cards_projects_url
|
||||
end
|
||||
|
||||
cards = Kaminari.paginate_array(overview_service.project_and_folder_cards)
|
||||
.page(params[:page] || 1).per(20)
|
||||
|
||||
render json: {
|
||||
projects_cards_url: projects_cards_url,
|
||||
breadcrumbs_html: breadcrumbs_html,
|
||||
title: title,
|
||||
next_page: cards.next_page,
|
||||
toolbar_html: render_to_string(partial: 'projects/index/toolbar.html.erb'),
|
||||
cards_html: render_to_string(
|
||||
partial: 'projects/index/team_projects.html.erb',
|
||||
locals: { cards: overview_service.project_and_folder_cards }
|
||||
locals: { cards: cards }
|
||||
)
|
||||
}
|
||||
end
|
||||
|
|
|
@ -38,4 +38,23 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<template id="projectPlaceholder">
|
||||
<div class="project-placeholder card">
|
||||
<% 4.times do |i| %>
|
||||
<div class="placeholder-element line-<%= i %>"></div>
|
||||
<% end %>
|
||||
<% 3.times do |i| %>
|
||||
<div class="placeholder-element circle circle-<%= i %>"></div>
|
||||
<% end %>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="projectEndOfList">
|
||||
<div class="project-list-end-placeholder">
|
||||
<i class="fas fa-flag-checkered"></i>
|
||||
<span><%= t('.end_of_list_placeholder') %></span>
|
||||
<i class="fas fa-flag-checkered"></i>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<%= javascript_include_tag "projects/index" %>
|
||||
|
|
|
@ -419,6 +419,7 @@ en:
|
|||
users: "Members"
|
||||
name: "Project name"
|
||||
archived_date: "Archived"
|
||||
end_of_list_placeholder: 'You’ve reached the end of the list'
|
||||
folder:
|
||||
description: "%{projects_count} projects | %{folders_count} folders"
|
||||
modal_new_project_folder:
|
||||
|
|
Loading…
Add table
Reference in a new issue