Ver código fonte

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

Liming Xie 9 anos atrás
pai
commit
354eff9f7b

+ 4 - 1
client/components/boards/boardBody.jade

@@ -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")

+ 4 - 0
client/components/boards/boardBody.js

@@ -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

+ 10 - 1
client/components/boards/boardColors.styl

@@ -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)
 

+ 5 - 3
client/components/boards/boardHeader.jade

@@ -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

+ 0 - 1
client/components/boards/boardsList.jade

@@ -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

+ 22 - 3
client/components/boards/boardsList.styl

@@ -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%

+ 1 - 1
client/components/cards/cardDetails.jade

@@ -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

+ 1 - 1
client/components/cards/cardDetails.js

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

+ 17 - 0
client/components/cards/cardDetails.styl

@@ -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

+ 9 - 0
client/components/cards/minicard.styl

@@ -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)

+ 6 - 0
client/components/forms/forms.styl

@@ -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

+ 1 - 1
client/components/lists/listHeader.jade

@@ -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

+ 35 - 34
client/components/main/header.jade

@@ -1,32 +1,32 @@
 template(name="header")
-  #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'}}
+  //-
+    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")
+        a#header-new-board-icon.js-create-board
+          i.fa.fa-plus(title="Create a new board")
 
-          +headerUserBar
+        +headerUserBar
 
+  #header(class=currentBoard.colorClass)
     //-
       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")

+ 109 - 68
client/components/main/header.styl

@@ -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

+ 12 - 0
client/components/main/layouts.styl

@@ -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

+ 86 - 1
client/components/main/popup.styl

@@ -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%)

+ 2 - 0
client/components/sidebar/sidebar.jade

@@ -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

+ 3 - 1
client/components/sidebar/sidebar.js

@@ -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,
     }];

+ 48 - 0
client/components/sidebar/sidebar.styl

@@ -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

+ 32 - 0
client/components/users/userForm.styl

@@ -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

+ 2 - 3
client/components/users/userHeader.jade

@@ -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

+ 7 - 9
client/lib/popup.js

@@ -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;
     };
   }
 };

+ 17 - 0
client/lib/utils.js

@@ -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());