add: support compact mode for mobile web, auto adapt to small screen/window

This commit is contained in:
Liming Xie 2015-12-17 14:23:35 +08:00
parent 0954cff5b4
commit 354eff9f7b
23 changed files with 436 additions and 131 deletions

View file

@ -1,7 +1,10 @@
template(name="board")
if isBoardReady.get
if currentBoard
+boardBody
if onlyShowCurrentCard
+cardDetails(currentCard)
else
+boardBody
else
//- XXX We need a better error message in case the board has been archived
+message(label="board-not-found")

View file

@ -57,6 +57,10 @@ BlazeComponent.extendComponent({
return currentCard && currentCard.listId === listId;
},
onlyShowCurrentCard() {
return Utils.isMiniScreen() && Session.get('currentCard');
},
events() {
return [{
// XXX The board-overlay div should probably be moved to the parent

View file

@ -23,9 +23,13 @@ setBoardColor(color)
.sidebar-list li a:hover
background-color: lighten(color, 10%)
&#header #header-quick-access ul li.current
&#header ul li.current, &#header-quick-access ul li.current
border-bottom: 2px solid lighten(color, 10%)
&#header-quick-access
background: darken(color, 10%)
color: white
&#header #header-main-bar .board-header-btn.emphasis
background: complement(color)
@ -47,6 +51,11 @@ setBoardColor(color)
&:not(.is-checked) + .minicard:hover:not(.minicard-composer)
background: lighten(color, 97%)
@media screen and (max-width: 800px)
&.pop-over .header
background: color
color: white
.board-color-nephritis
setBoardColor(#27AE60)

View file

@ -5,19 +5,21 @@ template(name="boardHeaderBar")
= title
.board-header-btns.left
.board-header-btns.right
unless isSandstorm
if currentUser
a.board-header-btn.js-star-board(class="{{#if isStarred}}is-active{{/if}}"
title="{{#if isStarred}}{{_ 'click-to-unstar'}}{{else}}{{_ 'click-to-star'}}{{/if}} {{_ 'starred-boards-description'}}")
i.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}")
if showStarCounter
span {{_ 'board-nb-stars' currentBoard.stars}}
span
= currentBoard.stars
a.board-header-btn(class="{{#if currentUser.isBoardAdmin}}js-change-visibility{{else}}is-disabled{{/if}}")
i.fa(class="{{#if currentBoard.isPublic}}fa-globe{{else}}fa-lock{{/if}}")
span {{_ currentBoard.permission}}
.board-header-btns.right
a.board-header-btn.js-open-filter-view(
title="{{#if Filter.isActive}}{{_ 'filter-on-desc'}}{{/if}}"
class="{{#if Filter.isActive}}emphasis{{/if}}")
@ -39,7 +41,7 @@ template(name="boardHeaderBar")
.separator
a.board-header-btn.js-open-board-menu
i.board-header-btn-icon.fa.fa-cog
i.board-header-btn-icon.fa.fa-navicon
template(name="boardMenuPopup")
ul.pop-over-list

View file

@ -27,7 +27,6 @@ template(name="boardList")
template(name="boardListHeaderBar")
h1 {{_ 'my-boards'}}
.board-header-btns.right
a.board-header-btn.js-open-archived-board
i.fa.fa-archive

View file

@ -1,7 +1,7 @@
$spaceBetweenTiles = 16px
.board-list
margin: $spaceBetweenTiles ($spaceBetweenTiles/-2) 0
margin: 0 ($spaceBetweenTiles/2)
li
float: left
@ -24,8 +24,8 @@ $spaceBetweenTiles = 16px
display: block
font-weight: 700
min-height: 18px
padding: 8px 12px 8px 12px
margin: 0 ($spaceBetweenTiles/2) $spaceBetweenTiles
padding: 8px
margin: ($spaceBetweenTiles/2)
position: relative
text-decoration: none
@ -128,3 +128,22 @@ $spaceBetweenTiles = 16px
font-size: 25px
color: white
@media screen and (max-width: 800px)
.board-list
height: 100%
overflow: scroll
li
width: 33.3%
.board-list-item
overflow: hidden
.board-list-item-sub-name
position: relative
top: -100px
left: -100px
@media screen and (max-width: 360px)
li
width: 50%

View file

@ -9,7 +9,7 @@ template(name="cardDetails")
else
a.fa.fa-times-thin.close-card-details.js-close-card-details
if currentUser.isBoardMember
a.fa.fa-ellipsis-v.card-details-menu.js-open-card-details-menu
a.fa.fa-navicon.card-details-menu.js-open-card-details-menu
h2.card-details-title.js-card-title(
class="{{#if currentUser.isBoardMember}}js-open-inlined-form is-editable{{/if}}")
= title

View file

@ -48,7 +48,7 @@ BlazeComponent.extendComponent({
},
onRendered() {
this.scrollParentContainer();
if (!Utils.isMiniScreen()) this.scrollParentContainer();
},
onDestroyed() {

View file

@ -94,3 +94,20 @@ input[type="submit"].attachment-add-link-submit
margin: 0 0 8px 4px
padding: 6px 12px
width: 18%
@media screen and (max-width: 800px)
.card-details
width: calc(100% - 40px)
padding: 0px 20px 0px 20px
margin: 0px
transition: none
.card-details-canvas
width: 100%
.card-details-header
.close-card-details
margin-right: 0px
.card-details-menu
margin-right: 10px

View file

@ -136,3 +136,12 @@
min-height: 36px
margin-bottom: 20px
overflow-y: auto
@media screen and (max-width: 800px)
.minicard
.is-selected &
transform: translateX(0px)
border-bottom-right-radius: 0
border-top-right-radius: 0
z-index: 15
box-shadow: 0 1px 2px rgba(0,0,0,.15)

View file

@ -629,3 +629,9 @@ button
a, .quiet
color: white
@media screen and (max-width: 800px)
.edit-controls,
.add-controls
.fa-times-thin
margin: 3px 20px

View file

@ -7,7 +7,7 @@ template(name="listHeader")
class="{{#if currentUser.isBoardMember}}js-open-inlined-form is-editable{{/if}}")
= title
if currentUser.isBoardMember
a.list-header-menu-icon.fa.fa-ellipsis-v.js-open-list-menu
a.list-header-menu-icon.fa.fa-navicon.js-open-list-menu
template(name="editListTitleForm")
.list-composer

View file

@ -1,32 +1,32 @@
template(name="header")
//-
If the user is connected we display a small "quick-access" top bar that
list all starred boards with a link to go there. This is inspired by the
Reddit "subreddit" bar.
The first link goes to the boards page.
unless isSandstorm
if currentUser
#header-quick-access(class=currentBoard.colorClass)
ul
li
a(href="{{pathFor 'home'}}")
span.fa.fa-home
| {{_ 'all-boards'}}
each currentUser.starredBoards
li.separator -
li(class="{{#if $.Session.equals 'currentBoard' _id}}current{{/if}}")
a(href="{{pathFor 'board' id=_id slug=slug}}")
= title
else
li.current {{_ 'quick-access-description'}}
a#header-new-board-icon.js-create-board
i.fa.fa-plus(title="Create a new board")
+headerUserBar
#header(class=currentBoard.colorClass)
//-
If the user is connected we display a small "quick-access" top bar that
list all starred boards with a link to go there. This is inspired by the
Reddit "subreddit" bar.
The first link goes to the boards page.
unless isSandstorm
if currentUser
#header-quick-access
ul
li
a(href="{{pathFor 'home'}}")
span.fa.fa-home
| {{_ 'all-boards'}}
each currentUser.starredBoards
li.separator -
li(class="{{#if $.Session.equals 'currentBoard' _id}}current{{/if}}")
a(href="{{pathFor 'board' id=_id slug=slug}}")
= title
else
li.current {{_ 'quick-access-description'}}
a#header-new-board-icon.js-create-board
i.fa.fa-plus(title="Create a new board")
+headerUserBar
//-
The main bar is a colorful bar that provide all the meta-data for the
current page. This bar is contextual based.
@ -34,13 +34,14 @@ template(name="header")
#header-main-bar(class="{{#if wrappedHeader}}wrapper{{/if}}")
+Template.dynamic(template=headerBar)
//-
On sandstorm, the logo shouldn't be clickable, because we only have one
page/document on it, and we don't want to see the home page containing
the list of all boards.
if isSandstorm
.wekan-logo
img(src="{{pathFor '/wekan-logo-header.png'}}" alt="Wekan")
else
a.wekan-logo(href="{{pathFor 'home'}}" title="{{_ 'header-logo-title'}}")
img(src="{{pathFor '/wekan-logo-header.png'}}" alt="Wekan")
if wrappedHeader
//-
On sandstorm, the logo shouldn't be clickable, because we only have one
page/document on it, and we don't want to see the home page containing
the list of all boards.
if isSandstorm
.wekan-logo
img(src="{{pathFor '/wekan-logo-header.png'}}" alt="Wekan")
else
a.wekan-logo(href="{{pathFor 'home'}}" title="{{_ 'header-logo-title'}}")
img(src="{{pathFor '/wekan-logo-header.png'}}" alt="Wekan")

View file

@ -5,75 +5,8 @@
transition: background-color 0.4s
background: #2980B9
#header-quick-access
background-color: rgba(0, 0, 0, 0.2)
padding: 0px 10px
height: 28px
font-size: 12px
display: flex
#header-user-bar,
#header-new-board-icon,
ul li
color: darken(white, 17%)
.fa
color: inherit
a:hover, a.is-active
color: white
ul
transition: opacity 0.2s
margin: 4px 0 0 5px
overflow: hidden
li
display: block
float: left
width: auto
color: darken(white, 15%)
padding: 2px 5px 0
&.current
color: darken(white, 5%)
&:first-child .fa-home
margin-right: 5px
a.js-create-board
margin-left: 5px
#header-user-bar,
#header-new-board-icon
flex-shrink: 0
#header-user-bar
margin: 2px 0
.header-user-bar-avatar
float: left
.member
width: 24px
height: @width
margin: 0
margin-top: 1px
.header-user-bar-name
margin: 4px 8px 0 0
float: left
i.fa-chevron-down
margin-right: 4px
#header-new-board-icon
flex-grow: 1
margin: 6px 5px 0
width: 12px
#header-main-bar
height: 28px * 1.618034 - 6px
height: 40px
padding: 7px 10px 0
h1
@ -155,3 +88,111 @@
border-left: 1px solid rgba(255, 255, 255, .3)
height: 24px
float: left
#header-quick-access
color: white
transition: background-color 0.4s
background: #2573a7
height: 28px
font-size: 12px
display: flex
#header-user-bar,
#header-new-board-icon,
ul li
color: darken(white, 17%)
.fa
color: inherit
a:hover, a.is-active
color: white
ul
transition: opacity 0.2s
margin: 4px 0 0 5px
overflow: hidden
li
display: block
float: left
width: auto
color: darken(white, 15%)
padding: 2px 5px 0
&.current
color: darken(white, 5%)
&:first-child .fa-home
margin-right: 5px
a.js-create-board
margin-left: 5px
#header-user-bar,
#header-new-board-icon
flex-shrink: 0
#header-user-bar
margin: 2px 0
.header-user-bar-avatar
float: left
position: relative
top: -5px
margin-right: 5px
.member
width: 24px
height: @width
margin: 0
margin-top: 1px
.header-user-bar-name
margin: 4px 8px 0 0
float: left
i.fa-chevron-down
margin-right: 4px
#header-new-board-icon
flex-grow: 1
margin: 6px 5px 0
width: 12px
@media screen and (max-width: 800px)
#header
#header-main-bar
height: 40px
h1
display: none
.board-header-btns
margin-top: 0px
.board-header-btn
height: 32px
line-height: @height
font-size: 16px
i.fa
line-height: 32px
+ span
display: none
#header-quick-access
transition: background-color 0.4s
width: 100%
padding: 10px 0px
z-index: 30
position: absolute
bottom: 0px
ul
width: calc(100% - 150px)
overflow: ellipsis
li
height: 28px

View file

@ -355,3 +355,15 @@ a
@keyframes flexGrowIn
from
flex-basis: 0
@media screen and (max-width: 800px)
#content
margin: 1px 0px 49px 0px
height: calc(100% - 96px)
> .wrapper
margin-top: 0px
.wrapper
height: 100%
margin: 0px

View file

@ -83,7 +83,7 @@ $popupWidth = 300px
transition: transform 0.2s
.content
width: 300 - 20px
width: $popupWidth - 20px
padding: 0 10px 10px
float: left
@ -243,3 +243,88 @@ $popupWidth = 300px
&:hover
text-decoration: underline
@media screen and (max-width: 800px)
.pop-over
width: 100%
height: 100%
overflow: hidden
margin-top: 0px
border: 0px solid #dbdbdb
.header
color: white
background: #2980B9
height: 48px
padding: 0px 0px
border: 0px
margin: 0px 0px
width: 100%
position: absolute
top: 0px
.header-title
font-size: 20px
font-weight: normal
padding-top: 8px
.back-btn
width: 30px
padding: 8px 12px 8px 12px
i.fa
color: white
.close-btn
padding: 10px 12px
i.fa
font-size: 24px
color: white
.content-wrapper
width: 100%
height: calc(100% - 48px)
overflow-y: scroll
overflow-x: hidden
margin: 48px 0px 0px 0px
.content-container
width: 1000%
height: 100%
max-height: 100%
.content
width: calc(10% - 20px)
height: calc(100% - 20px)
padding: 10px
form
margin: 10px 10px
width: calc(100% - 20px)
p,
textarea,
input[type="text"],
input[type="email"],
input[type="password"],
input[type="file"]
margin: 4px 0 12px
width: 100%
.pop-over-list
li > a
width: calc(100% - 20px)
padding: 10px 10px
margin: 0px 0px
border-bottom: 1px solid #eee
hr
width: 100%
height: 20px
margin: 0px 0px
color: #eee
for depth in (1..6)
.popup-container-depth-{depth}
transform: translateX(- depth * 10%)

View file

@ -4,6 +4,8 @@ template(name="sidebar")
class="{{#if isTongueHidden}}is-hidden{{/if}}")
i.fa.fa-angle-left
.sidebar-content.js-board-sidebar-content.js-perfect-scrollbar
a.hide-btn.js-hide-sidebar
i.fa.fa-angle-right
unless isDefaultView
h2
a.fa.fa-chevron-left.js-back-home

View file

@ -18,7 +18,8 @@ BlazeComponent.extendComponent({
},
onCreated() {
this._isOpen = new ReactiveVar(!Session.get('currentCard'));
const initOpen = Utils.isMiniScreen() ? false : (!Session.get('currentCard'));
this._isOpen = new ReactiveVar(initOpen);
this._view = new ReactiveVar(defaultView);
Sidebar = this;
},
@ -96,6 +97,7 @@ BlazeComponent.extendComponent({
// XXX Hacky, we need some kind of `super`
const mixinEvents = this.getMixin(Mixins.InfiniteScrolling).events();
return [...mixinEvents, {
'click .js-hide-sidebar': this.hide,
'click .js-toggle-sidebar': this.toggle,
'click .js-back-home': this.setView,
}];

View file

@ -19,6 +19,9 @@
overflow-x: hidden
overflow-y: auto
.hide-btn
display: none
h3
color: darken(white, 50%)
font-size: 1em
@ -130,3 +133,48 @@
margin: 0
margin-bottom: 5px
padding: 0 2px 0 10px
@media screen and (max-width: 800px)
.board-sidebar
width: 100%
right: -@width
.sidebar-content
.hide-btn
width: 40px
height: @width
position: absolute
right: 5px
top: 5px
display: block
z-index: 15
background: darken(white, 3%)
transition: left .1s
color: darken(white, 50%)
border-radius: 50%
border: 1px solid darken(white, 30%)
box-shadow: 0 1px 6px rgba(0, 0, 0, .3)
color: darken(white, 50%)
i.fa
padding: 8px 16px
font-size: 24px
font-weight: bold
.sidebar-tongue
width: 40px
height: @width
left: -@width - 7px
top: 5px
display: block
border-radius: 50%
border: 1px solid darken(white, 30%)
box-shadow: 0 1px 6px rgba(0, 0, 0, .3)
color: darken(white, 50%)
.board-sidebar.is-open &
display: none
i.fa
padding: 8px 0px 8px 16px
font-weight: bold

View file

@ -55,3 +55,35 @@
.select-lang
width: 275px
font-size: 1.0em
@media screen and (max-width: 800px)
.auth-layout
width: 100%
height: 100%
margin: 0px
padding: 0px
.at-form-landing-logo
margin-top: 20px
margin-bottom: 20px
.at-form
width: calc(100% - 50px)
height: calc(100% - 162px)
margin: 0px
padding: 25px
button
width: 100%
.at-form-lang
width: 100%
margin: 0px
padding: 0px
.select-lang
width: 200px
font-size: 1.2em
position: absolute
left: calc((100% - 200px)/2)
bottom: 25px

View file

@ -1,13 +1,12 @@
template(name="headerUserBar")
#header-user-bar
a.header-user-bar-name.js-open-header-member-menu
i.fa.fa-chevron-down
.header-user-bar-avatar
+userAvatar(userId=currentUser._id)
if currentUser.profile.fullname
= currentUser.profile.fullname
else
= currentUser.username
a.header-user-bar-avatar.js-change-avatar
+userAvatar(userId=currentUser._id)
template(name="memberMenuPopup")
ul.pop-over-list

View file

@ -1,10 +1,3 @@
// A simple tracker dependency that we invalidate every time the window is
// resized. This is used to reactively re-calculate the popup position in case
// of a window resize. This is the equivalent of a "Signal" in some other
// programming environments (eg, elm).
const windowResizeDep = new Tracker.Dependency();
$(window).on('resize', () => windowResizeDep.changed());
window.Popup = new class {
constructor() {
// The template we use to render popups
@ -160,7 +153,10 @@ window.Popup = new class {
_getOffset(element) {
const $element = $(element);
return () => {
windowResizeDep.depend();
Utils.windowResizeDep.depend();
if(Utils.isMiniScreen()) return { left:0, top:0 };
const offset = $element.offset();
const popupWidth = 300 + 15;
return {
@ -183,7 +179,9 @@ window.Popup = new class {
// was available and returns `false`. There is a (small) risk a false
// positives.
const title = TAPi18n.__(translationKey);
return title !== translationKey ? title : false;
// when popup showed as full of small screen, we need a default header to clearly see [X] button
const defaultTitle = Utils.isMiniScreen() ? 'Wekan' : false;
return title !== translationKey ? title : defaultTitle;
};
}
};

View file

@ -22,6 +22,17 @@ Utils = {
return string.charAt(0).toUpperCase() + string.slice(1);
},
windowResizeDep: new Tracker.Dependency(),
// in fact, what we really care is screen size
// large mobile device like iPad or android Pad has a big screen, it should also behave like a desktop
// in a small window (even on desktop), Wekan run in compact mode.
// we can easily debug with a small window of desktop broswer. :-)
isMiniScreen() {
this.windowResizeDep.depend();
return $(window).width() <= 800;
},
// Determine the new sort index
calculateIndex(prevCardDomElement, nextCardDomElement, nCards = 1) {
let base, increment;
@ -54,3 +65,9 @@ Utils = {
};
},
};
// A simple tracker dependency that we invalidate every time the window is
// resized. This is used to reactively re-calculate the popup position in case
// of a window resize. This is the equivalent of a "Signal" in some other
// programming environments (eg, elm).
$(window).on('resize', () => Utils.windowResizeDep.changed());