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") template(name="board")
if isBoardReady.get if isBoardReady.get
if currentBoard if currentBoard
+boardBody if onlyShowCurrentCard
+cardDetails(currentCard)
else
+boardBody
else else
//- XXX We need a better error message in case the board has been archived //- XXX We need a better error message in case the board has been archived
+message(label="board-not-found") +message(label="board-not-found")

View file

@ -57,6 +57,10 @@ BlazeComponent.extendComponent({
return currentCard && currentCard.listId === listId; return currentCard && currentCard.listId === listId;
}, },
onlyShowCurrentCard() {
return Utils.isMiniScreen() && Session.get('currentCard');
},
events() { events() {
return [{ return [{
// XXX The board-overlay div should probably be moved to the parent // 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 .sidebar-list li a:hover
background-color: lighten(color, 10%) 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%) border-bottom: 2px solid lighten(color, 10%)
&#header-quick-access
background: darken(color, 10%)
color: white
&#header #header-main-bar .board-header-btn.emphasis &#header #header-main-bar .board-header-btn.emphasis
background: complement(color) background: complement(color)
@ -47,6 +51,11 @@ setBoardColor(color)
&:not(.is-checked) + .minicard:hover:not(.minicard-composer) &:not(.is-checked) + .minicard:hover:not(.minicard-composer)
background: lighten(color, 97%) background: lighten(color, 97%)
@media screen and (max-width: 800px)
&.pop-over .header
background: color
color: white
.board-color-nephritis .board-color-nephritis
setBoardColor(#27AE60) setBoardColor(#27AE60)

View file

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

View file

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

View file

@ -1,7 +1,7 @@
$spaceBetweenTiles = 16px $spaceBetweenTiles = 16px
.board-list .board-list
margin: $spaceBetweenTiles ($spaceBetweenTiles/-2) 0 margin: 0 ($spaceBetweenTiles/2)
li li
float: left float: left
@ -24,8 +24,8 @@ $spaceBetweenTiles = 16px
display: block display: block
font-weight: 700 font-weight: 700
min-height: 18px min-height: 18px
padding: 8px 12px 8px 12px padding: 8px
margin: 0 ($spaceBetweenTiles/2) $spaceBetweenTiles margin: ($spaceBetweenTiles/2)
position: relative position: relative
text-decoration: none text-decoration: none
@ -128,3 +128,22 @@ $spaceBetweenTiles = 16px
font-size: 25px font-size: 25px
color: white 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 else
a.fa.fa-times-thin.close-card-details.js-close-card-details a.fa.fa-times-thin.close-card-details.js-close-card-details
if currentUser.isBoardMember 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( h2.card-details-title.js-card-title(
class="{{#if currentUser.isBoardMember}}js-open-inlined-form is-editable{{/if}}") class="{{#if currentUser.isBoardMember}}js-open-inlined-form is-editable{{/if}}")
= title = title

View file

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

View file

@ -94,3 +94,20 @@ input[type="submit"].attachment-add-link-submit
margin: 0 0 8px 4px margin: 0 0 8px 4px
padding: 6px 12px padding: 6px 12px
width: 18% 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 min-height: 36px
margin-bottom: 20px margin-bottom: 20px
overflow-y: auto 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 a, .quiet
color: white 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}}") class="{{#if currentUser.isBoardMember}}js-open-inlined-form is-editable{{/if}}")
= title = title
if currentUser.isBoardMember 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") template(name="editListTitleForm")
.list-composer .list-composer

View file

@ -1,32 +1,32 @@
template(name="header") 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) #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 The main bar is a colorful bar that provide all the meta-data for the
current page. This bar is contextual based. current page. This bar is contextual based.
@ -34,13 +34,14 @@ template(name="header")
#header-main-bar(class="{{#if wrappedHeader}}wrapper{{/if}}") #header-main-bar(class="{{#if wrappedHeader}}wrapper{{/if}}")
+Template.dynamic(template=headerBar) +Template.dynamic(template=headerBar)
//- 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 On sandstorm, the logo shouldn't be clickable, because we only have one
the list of all boards. page/document on it, and we don't want to see the home page containing
if isSandstorm the list of all boards.
.wekan-logo if isSandstorm
img(src="{{pathFor '/wekan-logo-header.png'}}" alt="Wekan") .wekan-logo
else img(src="{{pathFor '/wekan-logo-header.png'}}" alt="Wekan")
a.wekan-logo(href="{{pathFor 'home'}}" title="{{_ 'header-logo-title'}}") else
img(src="{{pathFor '/wekan-logo-header.png'}}" alt="Wekan") 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 transition: background-color 0.4s
background: #2980B9 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 #header-main-bar
height: 28px * 1.618034 - 6px height: 40px
padding: 7px 10px 0 padding: 7px 10px 0
h1 h1
@ -155,3 +88,111 @@
border-left: 1px solid rgba(255, 255, 255, .3) border-left: 1px solid rgba(255, 255, 255, .3)
height: 24px height: 24px
float: left 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 @keyframes flexGrowIn
from from
flex-basis: 0 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 transition: transform 0.2s
.content .content
width: 300 - 20px width: $popupWidth - 20px
padding: 0 10px 10px padding: 0 10px 10px
float: left float: left
@ -243,3 +243,88 @@ $popupWidth = 300px
&:hover &:hover
text-decoration: underline 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}}") class="{{#if isTongueHidden}}is-hidden{{/if}}")
i.fa.fa-angle-left i.fa.fa-angle-left
.sidebar-content.js-board-sidebar-content.js-perfect-scrollbar .sidebar-content.js-board-sidebar-content.js-perfect-scrollbar
a.hide-btn.js-hide-sidebar
i.fa.fa-angle-right
unless isDefaultView unless isDefaultView
h2 h2
a.fa.fa-chevron-left.js-back-home a.fa.fa-chevron-left.js-back-home

View file

@ -18,7 +18,8 @@ BlazeComponent.extendComponent({
}, },
onCreated() { 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); this._view = new ReactiveVar(defaultView);
Sidebar = this; Sidebar = this;
}, },
@ -96,6 +97,7 @@ BlazeComponent.extendComponent({
// XXX Hacky, we need some kind of `super` // XXX Hacky, we need some kind of `super`
const mixinEvents = this.getMixin(Mixins.InfiniteScrolling).events(); const mixinEvents = this.getMixin(Mixins.InfiniteScrolling).events();
return [...mixinEvents, { return [...mixinEvents, {
'click .js-hide-sidebar': this.hide,
'click .js-toggle-sidebar': this.toggle, 'click .js-toggle-sidebar': this.toggle,
'click .js-back-home': this.setView, 'click .js-back-home': this.setView,
}]; }];

View file

@ -19,6 +19,9 @@
overflow-x: hidden overflow-x: hidden
overflow-y: auto overflow-y: auto
.hide-btn
display: none
h3 h3
color: darken(white, 50%) color: darken(white, 50%)
font-size: 1em font-size: 1em
@ -130,3 +133,48 @@
margin: 0 margin: 0
margin-bottom: 5px margin-bottom: 5px
padding: 0 2px 0 10px 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 .select-lang
width: 275px width: 275px
font-size: 1.0em 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") template(name="headerUserBar")
#header-user-bar #header-user-bar
a.header-user-bar-name.js-open-header-member-menu 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 if currentUser.profile.fullname
= currentUser.profile.fullname = currentUser.profile.fullname
else else
= currentUser.username = currentUser.username
a.header-user-bar-avatar.js-change-avatar
+userAvatar(userId=currentUser._id)
template(name="memberMenuPopup") template(name="memberMenuPopup")
ul.pop-over-list 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 { window.Popup = new class {
constructor() { constructor() {
// The template we use to render popups // The template we use to render popups
@ -160,7 +153,10 @@ window.Popup = new class {
_getOffset(element) { _getOffset(element) {
const $element = $(element); const $element = $(element);
return () => { return () => {
windowResizeDep.depend(); Utils.windowResizeDep.depend();
if(Utils.isMiniScreen()) return { left:0, top:0 };
const offset = $element.offset(); const offset = $element.offset();
const popupWidth = 300 + 15; const popupWidth = 300 + 15;
return { return {
@ -183,7 +179,9 @@ window.Popup = new class {
// was available and returns `false`. There is a (small) risk a false // was available and returns `false`. There is a (small) risk a false
// positives. // positives.
const title = TAPi18n.__(translationKey); 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); 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 // Determine the new sort index
calculateIndex(prevCardDomElement, nextCardDomElement, nCards = 1) { calculateIndex(prevCardDomElement, nextCardDomElement, nCards = 1) {
let base, increment; 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());