Quellcode durchsuchen

Some mobile fixes.

Thanks to xet7 !

Fixes #5899
Lauri Ojansivu vor 1 Monat
Ursprung
Commit
3fda90612d

+ 11 - 0
client/components/boards/boardBody.css

@@ -33,6 +33,17 @@
 .board-wrapper .board-canvas.is-dragging-active .minicard-wrapper.is-checked {
   display: none;
 }
+/* Mobile view styles - applied when isMiniScreen is true (iPhone, etc.) */
+.board-wrapper.mobile-view .board-canvas.mobile-view .swimlane {
+  border-bottom: 1px solid #ccc;
+  display: flex;
+  flex-direction: column;
+  margin: 0;
+  padding: 0 0px 0px 0;
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
 @media screen and (max-width: 800px) {
   .board-wrapper .board-canvas .swimlane {
     border-bottom: 1px solid #ccc;

+ 3 - 2
client/components/boards/boardBody.jade

@@ -16,13 +16,14 @@ template(name="boardBody")
   if notDisplayThisBoard
    | {{_ 'tableVisibilityMode-allowPrivateOnly'}}
   else
-    .board-wrapper(class=currentBoard.colorClass)
+    .board-wrapper(class=currentBoard.colorClass class="{{#if isMiniScreen}}mobile-view{{/if}}")
       .board-canvas.js-swimlanes(
         class="{{#if hasSwimlanes}}dragscroll{{/if}}"
         class="{{#if Sidebar.isOpen}}is-sibling-sidebar-open{{/if}}"
         class="{{#if MultiSelection.isActive}}is-multiselection-active{{/if}}"
         class="{{#if draggingActive.get}}is-dragging-active{{/if}}"
-        class="{{#unless isVerticalScrollbars}}no-scrollbars{{/unless}}")
+        class="{{#unless isVerticalScrollbars}}no-scrollbars{{/unless}}"
+        class="{{#if isMiniScreen}}mobile-view{{/if}}")
         if showOverlay.get
           .board-overlay
         if currentBoard.isTemplatesBoard

+ 0 - 12
client/components/boards/boardHeader.jade

@@ -62,15 +62,11 @@ template(name="boardHeaderBar")
           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
-                = currentBoard.stars
 
           a.board-header-btn(
             class="{{#if currentUser.isBoardAdmin}}js-change-visibility{{else}}is-disabled{{/if}}"
             title="{{_ currentBoard.permission}}")
             i.fa(class="{{#if currentBoard.isPublic}}fa-globe{{else}}fa-lock{{/if}}")
-            span {{_ currentBoard.permission}}
 
           a.board-header-btn.js-watch-board(
             title="{{_ watchLevel }}")
@@ -80,10 +76,8 @@ template(name="boardHeaderBar")
               i.fa.fa-bell
             if $eq watchLevel "muted"
               i.fa.fa-bell-slash
-            span {{_ watchLevel}}
           a.board-header-btn(title="{{_ 'sort-cards'}}" class="{{#if isSortActive }}emphasis{{else}} js-sort-cards {{/if}}")
             i.fa.fa-sort
-            span {{#if isSortActive }}{{_ 'sort-is-on'}}{{else}}{{_ 'sort-cards'}}{{/if}}
             if isSortActive
               a.board-header-btn-close.js-sort-reset(title="{{_ 'remove-sort'}}")
                 i.fa.fa-times-thin
@@ -92,13 +86,11 @@ template(name="boardHeaderBar")
           a.board-header-btn.js-log-in(
             title="{{_ 'log-in'}}")
             i.fa.fa-sign-in
-            span {{_ 'log-in'}}
 
       if isSandstorm
         if currentUser
           a.board-header-btn.js-open-archived-board
             i.fa.fa-archive
-            span {{_ 'archives'}}
 
       //if showSort
       //  a.board-header-btn.js-open-sort-view(title="{{_ 'sort-desc'}}")
@@ -109,14 +101,12 @@ template(name="boardHeaderBar")
           title="{{#if Filter.isActive}}{{_ 'filter-on-desc'}}{{else}}{{_ 'filter'}}{{/if}}"
           class="{{#if Filter.isActive}}emphasis{{/if}}")
         i.fa.fa-filter
-        span {{#if Filter.isActive}}{{_ 'filter-on'}}{{else}}{{_ 'filter'}}{{/if}}
         if Filter.isActive
           a.board-header-btn-close.js-filter-reset(title="{{_ 'filter-clear'}}")
             i.fa.fa-times-thin
 
       a.board-header-btn.js-open-search-view(title="{{_ 'search'}}")
         i.fa.fa-search
-        span {{_ 'search'}}
 
       unless currentBoard.isTemplatesBoard
         a.board-header-btn.js-toggle-board-view(
@@ -128,14 +118,12 @@ template(name="boardHeaderBar")
             i.fa.fa-trello
           if $eq boardView 'board-view-cal'
             i.fa.fa-calendar
-          span {{#if boardView}}{{_ boardView}}{{else}}{{_ 'board-view-swimlanes'}}{{/if}}
 
       if canModifyBoard
         a.board-header-btn.js-multiselection-activate(
             title="{{#if MultiSelection.isActive}}{{_ 'multi-selection-on'}}{{else}}{{_ 'multi-selection'}}{{/if}}"
             class="{{#if MultiSelection.isActive}}emphasis{{/if}}")
           i.fa.fa-check-square-o
-          span {{#if MultiSelection.isActive}}{{_ 'multi-selection-on'}}{{else}}{{_ 'multi-selection'}}{{/if}}
           if MultiSelection.isActive
             a.board-header-btn-close.js-multiselection-reset(title="{{_ 'filter-clear'}}")
               i.fa.fa-times-thin

+ 39 - 0
client/components/boards/boardsList.css

@@ -192,6 +192,32 @@
   font-size: 25px;
   color: #fff;
 }
+/* Mobile view styles - applied when isMiniScreen is true (iPhone, etc.) */
+.board-list.mobile-view {
+  height: 100%;
+  overflow: scroll;
+}
+.board-list.mobile-view li {
+  width: 50%;
+}
+.board-list.mobile-view .board-list-item {
+  overflow: hidden;
+  height: 8rem;
+}
+.board-list.mobile-view .board-list-item-sub-name {
+  position: relative;
+  top: -100px;
+  left: -100px;
+}
+.board-list.mobile-view .board-handle {
+  position: absolute;
+  padding: 7px;
+  top: 50%;
+  transform: translateY(-50%);
+  right: 10px;
+  font-size: 24px;
+}
+
 @media screen and (max-width: 800px) {
   .board-list {
     height: 100%;
@@ -218,6 +244,19 @@
     font-size: 24px;
   }
 }
+/* Mobile view styles for very small screens - applied when isMiniScreen is true */
+.board-list.mobile-view li {
+  width: 50%;
+}
+.board-list.mobile-view .board-handle {
+  position: absolute;
+  padding: 7px;
+  top: 50%;
+  transform: translateY(-50%);
+  right: 10px;
+  font-size: 24px;
+}
+
 @media screen and (max-width: 360px) {
   li {
     width: 100%;

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

@@ -29,7 +29,7 @@ template(name="boardList")
             input#filterBtn(type="button" value="{{_ 'filter'}}")
             input#resetBtn(type="button" value="{{_ 'filter-clear'}}")
 
-    ul.board-list.clearfix.js-boards
+    ul.board-list.clearfix.js-boards(class="{{#if isMiniScreen}}mobile-view{{/if}}")
       li.js-add-board
         a.board-list-item.label(title="{{_ 'add-board'}}")
           | {{_ 'add-board'}}

+ 145 - 7
client/components/lists/list.css

@@ -214,6 +214,125 @@
 #js-list-width-edit .list-width-error {
   display: none;
 }
+/* Mobile view styles - applied when isMiniScreen is true (iPhone, etc.) */
+.mini-list.mobile-view {
+  flex: 0 0 60px;
+  height: auto;
+  width: 100%;
+  border-left: 0px;
+  border-bottom: 1px solid #ccc;
+}
+.list.mobile-view {
+  display: contents;
+  flex-basis: auto;
+  width: 100%;
+  border-left: 0px;
+}
+.list.mobile-view:first-child {
+  margin-left: 0px;
+}
+.list.mobile-view.ui-sortable-helper {
+  flex: 0 0 60px;
+  height: 60px;
+  width: 100%;
+  border-left: 0px;
+  border-bottom: 1px solid #ccc;
+}
+.list.mobile-view.ui-sortable-helper .list-header.ui-sortable-handle {
+  cursor: grabbing;
+}
+.list.mobile-view.placeholder {
+  flex: 0 0 60px;
+  height: 60px;
+  width: 100%;
+  border-left: 0px;
+  border-bottom: 1px solid #ccc;
+}
+.list.mobile-view .list-body {
+  padding: 15px 19px;
+}
+.list.mobile-view .list-header {
+  /*Updated padding values for mobile devices, this should fix text grouping issue*/
+  padding: 20px 0px 20px 0px;
+  border-bottom: 0px solid #e4e4e4;
+  min-height: 30px;
+  margin-top: 10px;
+  align-items: center;
+  /* Force grid layout for iPhone */
+  display: grid !important;
+  grid-template-columns: 30px 1fr auto auto !important;
+  gap: 10px !important;
+}
+.list.mobile-view .list-header .list-header-left-icon {
+  padding: 7px;
+  padding-right: 27px;
+  margin-top: 1px;
+  top: -7px;
+  left: -7px;
+}
+.list.mobile-view .list-header .list-header-menu-icon {
+  padding: 14px;
+  font-size: 40px !important;
+  text-align: center;
+  /* Force positioning for iPhone */
+  position: absolute !important;
+  right: 60px !important;
+  top: 50% !important;
+  transform: translateY(-50%) !important;
+  z-index: 10;
+}
+.list.mobile-view .list-header .list-header-handle {
+  padding: 14px;
+  font-size: 48px !important;
+  text-align: center;
+  /* Force positioning for iPhone */
+  position: absolute !important;
+  right: 10px !important;
+  top: 50% !important;
+  transform: translateY(-50%) !important;
+  z-index: 10;
+}
+.list.mobile-view .list-header .list-header-left-icon {
+  display: grid;
+  grid-row: 1/3;
+  grid-column: 1;
+}
+.list.mobile-view .list-header .list-header-name {
+  grid-row: 1;
+  grid-column: 2;
+  align-self: end;
+  font-size: 20px !important;
+  font-weight: bold;
+  line-height: 1.2;
+  padding-bottom: 2px;
+}
+.list.mobile-view .list-header .cardCount {
+  grid-row: 2;
+  grid-column: 2;
+  align-self: start;
+  font-size: 16px !important;
+  line-height: 1.2;
+}
+.list.mobile-view .list-header .list-header-menu {
+  grid-row: 1/3;
+  grid-column: 3;
+}
+.list.mobile-view .list-header .list-header-menu-icon {
+  grid-row: 1/3;
+  grid-column: 3;
+}
+.list.mobile-view .list-header .list-header-handle {
+  grid-row: 1/3;
+  grid-column: 4;
+}
+.list.mobile-view .list-header .inlined-form {
+  grid-row: 1/3;
+  grid-column: 1/4;
+}
+.list.mobile-view .list-header .edit-controls {
+  align-items: initial;
+}
+
 @media screen and (max-width: 800px) {
   .mini-list {
     flex: 0 0 60px;
@@ -267,24 +386,29 @@
     left: -7px;
   }
   .list-header .list-header-menu-icon {
+    padding: 14px;
+    font-size: 40px;
+    text-align: center;
+    /* iOS Safari fallback positioning */
     position: absolute;
-    padding: 7px;
+    right: 60px;
     top: 50%;
     transform: translateY(-50%);
-    right: 47px;
-    font-size: 20px;
   }
   .list-header .list-header-handle {
+    padding: 14px;
+    font-size: 48px;
+    text-align: center;
+    /* iOS Safari fallback positioning */
     position: absolute;
-    padding: 7px;
+    right: 10px;
     top: 50%;
     transform: translateY(-50%);
-    right: 10px;
-    font-size: 24px;
   }
   .list-header {
     display: grid;
-    grid-template-columns: 30px 5fr 1fr;
+    grid-template-columns: 30px 1fr auto auto;
+    gap: 10px;
   }
   .list-header .list-header-left-icon {
     display: grid;
@@ -295,16 +419,30 @@
     grid-row: 1;
     grid-column: 2;
     align-self: end;
+    font-size: 20px;
+    font-weight: bold;
+    line-height: 1.2;
+    padding-bottom: 2px;
   }
   .list-header .cardCount {
     grid-row: 2;
     grid-column: 2;
     align-self: start;
+    font-size: 16px;
+    line-height: 1.2;
   }
   .list-header .list-header-menu {
     grid-row: 1/3;
     grid-column: 3;
   }
+  .list-header .list-header-menu-icon {
+    grid-row: 1/3;
+    grid-column: 3;
+  }
+  .list-header .list-header-handle {
+    grid-row: 1/3;
+    grid-column: 4;
+  }
   .list-header .inlined-form {
     grid-row: 1/3;
     grid-column: 1/4;

+ 2 - 2
client/components/lists/list.jade

@@ -1,10 +1,10 @@
 template(name='list')
   .list.js-list(id="js-list-{{_id}}"
                 style="{{#unless collapsed}}min-width:{{listWidth}}px;max-width:{{listConstraint}}px;{{/unless}}"
-                class="{{#if collapsed}}list-collapsed{{/if}} {{#if autoWidth}}list-auto-width{{/if}}")
+                class="{{#if collapsed}}list-collapsed{{/if}} {{#if autoWidth}}list-auto-width{{/if}} {{#if isMiniScreen}}mobile-view{{/if}}")
     +listHeader
     +listBody
 
 template(name='miniList')
-  a.mini-list.js-select-list.js-list(id="js-list-{{_id}}")
+  a.mini-list.js-select-list.js-list(id="js-list-{{_id}}" class="{{#if isMiniScreen}}mobile-view{{/if}}")
     +listHeader

+ 12 - 12
client/components/main/header.jade

@@ -23,12 +23,12 @@ template(name="header")
                 a(href="{{pathFor 'board' id=_id slug=slug}}")
                   +viewer
                     = title
-        a.js-toggle-desktop-drag-handles(title="{{_ 'show-desktop-drag-handles'}}" alt="{{_ 'show-desktop-drag-handles'}}")
-          i.fa.fa-arrows
-            if isShowDesktopDragHandles
-              i.fa.fa-check-square-o
-            unless isShowDesktopDragHandles
-              i.fa.fa-ban
+        //a.js-toggle-desktop-drag-handles(title="{{_ 'show-desktop-drag-handles'}}" alt="{{_ 'show-desktop-drag-handles'}}")
+        //  i.fa.fa-arrows
+        //    if isShowDesktopDragHandles
+        //      i.fa.fa-check-square-o
+        //    unless isShowDesktopDragHandles
+        //      i.fa.fa-ban
         #header-new-board-icon
       else
         //-
@@ -61,12 +61,12 @@ template(name="header")
                   = title
           else
             li.current.empty {{_ 'quick-access-description'}}
-        a.js-toggle-desktop-drag-handles(title="{{_ 'show-desktop-drag-handles'}}" alt="{{_ 'show-desktop-drag-handles'}}")
-          i.fa.fa-arrows
-            if isShowDesktopDragHandles
-              i.fa.fa-check-square-o
-            unless isShowDesktopDragHandles
-              i.fa.fa-ban
+        //a.js-toggle-desktop-drag-handles(title="{{_ 'show-desktop-drag-handles'}}" alt="{{_ 'show-desktop-drag-handles'}}")
+        //  i.fa.fa-arrows
+        //    if isShowDesktopDragHandles
+        //      i.fa.fa-check-square-o
+        //    unless isShowDesktopDragHandles
+        //      i.fa.fa-ban
       // Next line is used only for spacing at header,
       // there is no visible clickable icon.
       #header-new-board-icon

+ 46 - 10
client/lib/utils.js

@@ -287,22 +287,50 @@ Utils = {
   },
 
   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 browser. :-)
   isMiniScreen() {
-    // OLD WINDOW WIDTH DETECTION:
     this.windowResizeDep.depend();
-    return $(window).width() <= 800;
+    // Show mobile view when:
+    // 1. Screen width is 800px or less (matches CSS media queries)
+    // 2. Mobile phones in portrait mode
+    // 3. iPad in very small screens (≤ 600px)
+    const isSmallScreen = window.innerWidth <= 800;
+    const isVerySmallScreen = window.innerWidth <= 600;
+    const isPortrait = window.innerWidth < window.innerHeight || window.matchMedia("(orientation: portrait)").matches;
+    const isMobilePhone = /Mobile|Android|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) && !/iPad/i.test(navigator.userAgent);
+    const isIPhone = /iPhone|iPod/i.test(navigator.userAgent);
+    const isIPad = /iPad/i.test(navigator.userAgent);
+    const isUbuntuTouch = /Ubuntu/i.test(navigator.userAgent);
+
+    // For iPhone: always show mobile view regardless of orientation
+    // For other mobile phones: show mobile view in portrait, desktop view in landscape
+    // For iPad: show mobile view only in very small screens (≤ 600px)
+    // For Ubuntu Touch: smartphones behave like mobile phones, tablets like iPad
+    // For desktop: show mobile view when screen width <= 800px
+    if (isIPhone) {
+      return true; // iPhone: always mobile view
+    } else if (isMobilePhone) {
+      return isPortrait; // Other mobile phones: portrait = mobile, landscape = desktop
+    } else if (isIPad) {
+      return isVerySmallScreen; // iPad: only very small screens get mobile view
+    } else if (isUbuntuTouch) {
+      // Ubuntu Touch: smartphones (≤ 600px) behave like mobile phones, tablets (> 600px) like iPad
+      if (isVerySmallScreen) {
+        return isPortrait; // Ubuntu Touch smartphone: portrait = mobile, landscape = desktop
+      } else {
+        return isVerySmallScreen; // Ubuntu Touch tablet: only very small screens get mobile view
+      }
+    } else {
+      return isSmallScreen; // Desktop: based on 800px screen width
+    }
   },
 
   isTouchScreen() {
-
     // NEW TOUCH DEVICE DETECTION:
     // https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
-
     var hasTouchScreen = false;
     if ("maxTouchPoints" in navigator) {
       hasTouchScreen = navigator.maxTouchPoints > 0;
@@ -328,12 +356,19 @@ Utils = {
 
   // returns if desktop drag handles are enabled
   isShowDesktopDragHandles() {
-    //const currentUser = ReactiveCache.getCurrentUser();
-    //if (currentUser) {
-    //  return (currentUser.profile || {}).showDesktopDragHandles;
-    //} else if (window.localStorage.getItem('showDesktopDragHandles')) {
+    if (this.isTouchScreen()) {
+      return true;
+    /*
+    const currentUser = ReactiveCache.getCurrentUser();
+    if (currentUser) {
+      return (currentUser.profile || {}).showDesktopDragHandles;
+    } else if (window.localStorage.getItem('showDesktopDragHandles')) {
+    //
     if (window.localStorage.getItem('showDesktopDragHandles')) {
       return true;
+    } else {
+      return false;
+    */
     } else {
       return false;
     }
@@ -341,8 +376,9 @@ Utils = {
 
   // returns if mini screen or desktop drag handles
   isTouchScreenOrShowDesktopDragHandles() {
+    return this.isTouchScreen();
     //return this.isTouchScreen() || this.isShowDesktopDragHandles();
-    return this.isShowDesktopDragHandles();
+    //return this.isShowDesktopDragHandles();
   },
 
   calculateIndexData(prevData, nextData, nItems = 1) {