Improve scrolling

We now replace native scrollbar by custom ones on the list card (which
is required by the new ergonomics in the parent commit), but the
"scrolling engine", is still native, we just hide the scrollbar and
draw our own in HTML/CSS using the perfect-scrollbar package (from
bower).

This commit also implements component scrolling when certain actions
are performed, eg scroll to the bottom when the new card composer is
opened.
pull/188/head
Maxime Quandalle 10 years ago
parent 781577db04
commit 9a45f3752f
  1. 1
      .jshintrc
  2. 1
      .meteor/packages
  3. 1
      .meteor/versions
  4. 7
      bower.json
  5. 4
      client/components/boards/body.jade
  6. 22
      client/components/boards/body.js
  7. 16
      client/components/boards/body.styl
  8. 8
      client/components/cards/details.js
  9. 14
      client/components/cards/minicard.styl
  10. 79
      client/components/lists/body.jade
  11. 14
      client/components/lists/body.js
  12. 5
      client/components/lists/main.jade
  13. 7
      client/components/lists/main.js
  14. 59
      client/components/lists/main.styl
  15. 0
      client/components/mixins/infiniteScrolling.js
  16. 6
      client/components/mixins/perfectScrollbar.js
  17. 2
      client/components/mixins/perfectScrollbar.styl
  18. 2
      client/components/sidebar/helpers.js
  19. 21
      client/components/sidebar/rendered.js
  20. 4
      client/components/sidebar/sidebar.jade
  21. 26
      client/components/sidebar/sidebar.js
  22. 45
      client/styles/fancy-scrollbar.styl

@ -54,6 +54,7 @@
"SubsManager": false,
"Mousetrap": false,
"Avatar": true,
"Ps": true,
// Our collections
"Boards": true,

@ -32,6 +32,7 @@ audit-argument-checks
iron:router
meteorhacks:subs-manager
mquandalle:autofocus
mquandalle:bower
mquandalle:moment
ongoworks:speakingurl
raix:handlebar-helpers

@ -77,6 +77,7 @@ mongo@1.1.0
mongo-livedata@1.0.8
mousetrap:mousetrap@1.4.6_1
mquandalle:autofocus@1.0.0
mquandalle:bower@1.4.1
mquandalle:jade@0.4.3
mquandalle:jade-compiler@0.4.3
mquandalle:jquery-textcomplete@0.3.9_1

@ -0,0 +1,7 @@
{
"name": "LibreBoard",
"dependencies": {
"perfect-scrollbar": "0.6.2"
},
"private": true
}

@ -16,12 +16,12 @@ template(name="boardComponent")
+cardDetails(currentCard)
if currentUser.isBoardMember
+addListForm
+boardSidebar
+sidebar
else
+message(label="board-no-found")
template(name="addListForm")
.list.js-list.add-list.js-add-list
.list.js-list.list-composer.js-list-composer
+inlinedForm(autoclose=false)
input.list-name-input(type="text" placeholder="{{_ 'add-list'}}"
autocomplete="off" autofocus value=getCache)

@ -22,8 +22,20 @@ BlazeComponent.extendComponent({
});
},
scrollLeft: function() {
// TODO
scrollLeft: function(position) {
position = position || 0;
var $container = $(this.find('.js-lists'));
var containerWidth = $container.width();
var currentScrollPosition = $container.scrollLeft();
if (position < currentScrollPosition) {
$container.animate({
scrollLeft: position
});
} else if (position > currentScrollPosition + containerWidth) {
$container.animate({
scrollLeft: Math.max(0, position - containerWidth)
});
}
},
currentCardIsInThisList: function() {
@ -67,14 +79,14 @@ BlazeComponent.extendComponent({
tolerance: 'pointer',
appendTo: '.js-lists',
helper: 'clone',
items: '.js-list:not(.add-list)',
items: '.js-list:not(.js-list-composer)',
placeholder: 'list placeholder',
start: function(event, ui) {
$('.list.placeholder').height(ui.item.height());
Popup.close();
},
stop: function() {
self.$('.js-lists').find('.js-list:not(.add-list)').each(
self.$('.js-lists').find('.js-list:not(.js-list-composer)').each(
function(i, list) {
var data = Blaze.getData(list);
Lists.update(data._id, {
@ -95,7 +107,7 @@ BlazeComponent.extendComponent({
},
sidebarSize: function() {
var sidebar = this.componentChildren('boardSidebar')[0];
var sidebar = this.componentChildren('sidebar')[0];
if (sidebar && sidebar.isOpen())
return 'next-sidebar';
}

@ -32,19 +32,3 @@
right: 0
bottom: 0
left: 0
&::-webkit-scrollbar
height: 13px
width: 13px
&::-webkit-scrollbar-thumb:vertical,
&::-webkit-scrollbar-thumb:horizontal
background: rgba(255, 255, 255, .4)
&::-webkit-scrollbar-track-piece
background: rgba(0, 0, 0, .15)
&::-webkit-scrollbar-button
display: block
height: 5px
width: 5px

@ -17,6 +17,14 @@ BlazeComponent.extendComponent({
activitiesComponent.loadNextPage();
},
onRendered: function() {
var bodyBoardComponent = this.componentParent();
var additionalMargin = 550;
var $cardDetails = this.$(this.firstNode());
var scollLeft = $cardDetails.offset().left + additionalMargin;
bodyBoardComponent.scrollLeft(scollLeft);
},
events: function() {
return [{
'click .js-move-card': Popup.open('moveCard'),

@ -8,6 +8,9 @@
position: relative
z-index: 0
overflow: hidden
transition: transform 0.2s,
border-radius 0.2s,
border-left 0.2s
a
color: #4d4d4d
@ -39,19 +42,15 @@
.minicard-details
padding: 6px 8px 2px
position: relative
z-index: 10
// z-index: 1
&.is-selected
margin-left: -11px
transform: translateX(- @margin-left)
transform: translateX(11px)
border-bottom-right-radius: 0
border-top-right-radius: 0
z-index: 100
box-shadow: -2px 1px 2px rgba(0,0,0,.2)
.minicard-details
margin-right: 11px
a.minicard-details
text-decoration:none
@ -122,6 +121,9 @@
.minicard-members:empty
display: none
&.ui-sortable-helper
transform: rotate(4deg)
.badges
float: left

@ -1,43 +1,44 @@
template(name="listBody")
.minicards.clearfix.js-minicards
if cards.count
+inlinedForm(autoclose=false position="top")
+addCardForm(listId=_id position="top")
each cards
.minicard.card.js-minicard(
class="{{#if isSelected}}is-selected{{/if}}")
a.minicard-details.clearfix.show(href=absoluteUrl)
if cover
.minicard-cover.js-card-cover(style="background-image: url({{cover.url}});")
if labels
.minicard-labels
each labels
.minicard-label(class="card-label-{{color}}" title="{{name}}")
.minicard-title= title
if members
.minicard-members.js-minicard-members
each members
+userAvatar(userId=this size="small" cardId="{{../_id}}")
.badges
if comments.count
.badge(title="{{_ 'card-comments-title' comments.count }}")
span.badge-icon.icon-sm.fa.fa-comment-o
.badge-text= comments.count
if description
.badge.badge-state-image-only(title=description)
span.badge-icon.icon-sm.fa.fa-align-left
if attachments.count
.badge
span.badge-icon.icon-sm.fa.fa-paperclip
span.badge-text= attachments.count
if currentUser.isBoardMember
+inlinedForm(autoclose=false position="bottom")
+addCardForm(listId=_id position="bottom")
else
if newCardFormIsVisible.get
a.open-card-composer.js-open-inlined-form
i.fa.fa-plus
| {{_ 'add-card'}}
.list-body.js-perfect-scrollbar
.minicards.clearfix.js-minicards
if cards.count
+inlinedForm(autoclose=false position="top")
+addCardForm(listId=_id position="top")
each cards
.minicard.card.js-minicard(
class="{{#if isSelected}}is-selected{{/if}}")
a.minicard-details.clearfix.show(href=absoluteUrl)
if cover
.minicard-cover.js-card-cover(style="background-image: url({{cover.url}});")
if labels
.minicard-labels
each labels
.minicard-label(class="card-label-{{color}}" title="{{name}}")
.minicard-title= title
if members
.minicard-members.js-minicard-members
each members
+userAvatar(userId=this size="small" cardId="{{../_id}}")
.badges
if comments.count
.badge(title="{{_ 'card-comments-title' comments.count }}")
span.badge-icon.icon-sm.fa.fa-comment-o
.badge-text= comments.count
if description
.badge.badge-state-image-only(title=description)
span.badge-icon.icon-sm.fa.fa-align-left
if attachments.count
.badge
span.badge-icon.icon-sm.fa.fa-paperclip
span.badge-text= attachments.count
if currentUser.isBoardMember
+inlinedForm(autoclose=false position="bottom")
+addCardForm(listId=_id position="bottom")
else
if newCardFormIsVisible.get
a.open-card-composer.js-open-inlined-form
i.fa.fa-plus
| {{_ 'add-card'}}
template(name="addCardForm")
.minicard.js-composer

@ -3,6 +3,10 @@ BlazeComponent.extendComponent({
return 'listBody';
},
mixins: function() {
return [Mixins.PerfectScrollbar];
},
isSelected: function() {
return Session.equals('currentCard', this.currentData()._id);
},
@ -62,13 +66,21 @@ BlazeComponent.extendComponent({
this.newCardFormIsVisible.set(value);
},
scrollToBottom: function() {
var $container = $(this.firstNode());
$container.animate({
scrollTop: $container.height()
});
},
onCreated: function() {
this.newCardFormIsVisible = new ReactiveVar(true);
},
events: function() {
return [{
submit: this.addCard
submit: this.addCard,
'click .open-card-composer': this.scrollToBottom
}];
}
}).register('listBody');

@ -1,5 +1,4 @@
template(name='list')
.list.js-list(id="js-list-{{_id}}")
.list-wrapper
+listHeader
+listBody
+listHeader
+listBody

@ -19,9 +19,10 @@ BlazeComponent.extendComponent({
// XXX The jQuery UI sortable plugin is far from ideal here. First we include
// all jQuery components but only use one. Second, it modifies the DOM itself,
// resulting in Blaze abandoning reactive update of the nodes that have been
// moved which result in bugs if multiple users use the board in real time.
// I tried sortable:sortable but that was not better. Should we “simply” write
// the drag&drop code ourselves?
// moved which result in bugs if multiple users use the board in real time. I
// tried sortable:sortable but that was not better. And dragula is not
// powerful enough for our use casesShould we “simply” write the drag&drop
// code ourselves?
onRendered: function() {
if (Meteor.user().isBoardMember()) {
var boardComponent = this.componentParent();

@ -11,8 +11,7 @@
background: darken(white, 10%)
height: 100%
border-left: 1px solid darken(white, 20%)
padding: 12px 7px 5px
overflow-y: auto
padding: 0
&:first-child
margin-left: 5px
@ -21,15 +20,20 @@
.card-detail + &
border-left: none
&.editable
cursor: grab
&.ui-sortable-helper
cursor: grabbing
box-shadow: -2px 2px 8px rgba(0, 0, 0, .3),
0 0 1px rgba(0, 0, 0, .5)
transform: rotate(4deg)
.list-wrapper
cursor: default
&.placeholder
background-color: rgba(0, 0, 0, .2)
border-color: transparent
box-shadow: none
height: 100px
&.add-list
&.fade
opacity: 0
&.list-composer
padding: 17px
.list-name-input
background: rgba(0, 0, 0, .05)
@ -55,7 +59,7 @@
.list-header
flex: 0 0 auto
padding: 10px 26px 4px 6px
margin: 20px 15px 4px
position: relative
min-height: 20px
@ -74,24 +78,23 @@
.list-header-menu-icon
background-clip: content-box
background-origin: content-box
padding: 6px 8px
// padding: 6px 8px
position: absolute
top: 3px
right: -5px
top: 0
right: 0
color: #a6a6a6
.list-header-num-cards
color: #8c8c8c
margin: 0
.minicards
padding: 4px 4px 1px
z-index: 1
height: 100%
.list-body
flex: 1
overflow-y: auto
padding: 5px 11px
&::-webkit-scrollbar-button
display: block
height: 4px
.ps-scrollbar-y-rail
transform: translateX(2px)
.open-card-composer
border-radius: 2px
@ -100,6 +103,7 @@
padding: 7px 10px
position: relative
text-decoration: none
animation: fadeIn 0.3s
i.fa
margin-right: 7px
@ -117,18 +121,3 @@
opacity: 0
to
opacity: 1
.list.placeholder
background-color: rgba(0, 0, 0, .2)
border-color: transparent
box-shadow: none
height: 100px
.list.ui-sortable-helper
cursor: grabbing
box-shadow: -2px 2px 8px rgba(0, 0, 0, .3), 0 0 1px rgba(0, 0, 0, .5)
transform: rotate(4deg)
.list.ui-sortable-helper .list-header-menu-icon
display: none

@ -0,0 +1,6 @@
Mixins.PerfectScrollbar = BlazeComponent.extendComponent({
onRendered: function() {
var component = this.mixinParent();
Ps.initialize(component.find('.js-perfect-scrollbar'));
}
});

@ -0,0 +1,2 @@
.ps-container
position: relative

@ -3,7 +3,7 @@ var widgetTitles = {
background: 'change-background'
};
Template.boardSidebar.helpers({
Template.sidebar.helpers({
currentWidget: function() {
return Session.get('currentWidget') + 'Sidebar';
},

@ -1,21 +0,0 @@
Template.membersWidget.onRendered(function() {
var self = this;
if (! Meteor.user().isBoardMember())
return;
_.each(['.js-member', '.js-label'], function(className) {
$(document).on('mouseover', function() {
self.$(className).draggable({
appendTo: 'body',
helper: 'clone',
revert: 'invalid',
revertDuration: 150,
snap: false,
snapMode: 'both',
start: function() {
Popup.close();
}
});
});
});
});

@ -1,9 +1,9 @@
template(name="boardSidebar")
template(name="sidebar")
.board-sidebar.sidebar(class="{{#if isOpen}}is-open{{/if}}")
a.sidebar-tongue.js-toogle-sidebar(
class="{{#if isTongueHidden}}is-hidden{{/if}}")
i.fa.fa-chevron-left
.sidebar-content.js-board-sidebar-content
.sidebar-content.js-board-sidebar-content.js-perfect-scrollbar
//- XXX https://github.com/peerlibrary/meteor-blaze-components/issues/30
if Filter.isActive
+filterSidebar

@ -1,10 +1,10 @@
BlazeComponent.extendComponent({
template: function() {
return 'boardSidebar';
return 'sidebar';
},
mixins: function() {
return [Mixins.InfiniteScrolling];
return [Mixins.InfiniteScrolling, Mixins.PerfectScrollbar];
},
onCreated: function() {
@ -46,6 +46,26 @@ BlazeComponent.extendComponent({
return this.isOpen() && Filter.isActive();
},
onRendered: function() {
var self = this;
if (! Meteor.user().isBoardMember())
return;
$(document).on('mouseover', function() {
self.$('.js-member,.js-label').draggable({
appendTo: 'body',
helper: 'clone',
revert: 'invalid',
revertDuration: 150,
snap: false,
snapMode: 'both',
start: function() {
Popup.close();
}
});
});
},
events: function() {
// XXX Hacky, we need some kind of `super`
var mixinEvents = this.getMixin(Mixins.InfiniteScrolling).events();
@ -53,4 +73,4 @@ BlazeComponent.extendComponent({
'click .js-toogle-sidebar': this.toogle
}]);
}
}).register('boardSidebar');
}).register('sidebar');

@ -1,45 +0,0 @@
.fancy-scrollbar
-webkit-overflow-scrolling: touch
.fancy-scrollbar::-webkit-scrollbar
height: 9px
width: 9px
&::-webkit-scrollbar-button:start:decrement,
&::-webkit-scrollbar-button:end:increment
background: transparent
display: none
&::-webkit-scrollbar-track-piece
background: #dbdbdb
&:vertical:start
border-top-left-radius: 5px
border-top-right-radius: 5px
border-bottom-right-radius: 0
border-bottom-left-radius: 0
&:vertical:end
border-top-left-radius: 0
border-top-right-radius: 0
border-bottom-right-radius: 5px
border-bottom-left-radius: 5px
&:horizontal:start
border-top-left-radius: 5px
border-top-right-radius: 0
border-bottom-right-radius: 0
border-bottom-left-radius: 5px
&:horizontal:end
border-top-left-radius: 0
border-top-right-radius: 5px
border-bottom-right-radius: 5px
border-bottom-left-radius: 0
&::-webkit-scrollbar-thumb:vertical,
&::-webkit-scrollbar-thumb:horizontal
background: #c2c2c2
border-radius: 5px
display: block
height: 50px
Loading…
Cancel
Save