Resize height of swimlane by dragging. Font Awesome to Unicode icons.

Thanks to xet7 !
This commit is contained in:
Lauri Ojansivu 2025-10-17 05:58:53 +03:00
parent 2947238a02
commit 09631d6b0c
16 changed files with 832 additions and 146 deletions

View file

@ -192,7 +192,7 @@ and adds the following new features:
- [Mobile one board per row. Board zoom size percent. Board toggle mobile/desktop mode. In Progress](https://github.com/wekan/wekan/commit/752699d1c2fb8ea9ff0f3ec9ae0b2b776443d826).
Thanks to xet7.
- [Drag any files from file manager to minicard or opened card.
- Drag any files from file manager to minicard or opened card.
[Part 1](https://github.com/wekan/wekan/commit/3e9481c5bd2c02ba501bd0a6ef1d1e6ce82bb1d9),
[Part 2](https://github.com/wekan/wekan/commit/cdd7d69c660d0b6ac06b7b75d4f59985b8a9322a).
Thanks to xet7.

View file

@ -5,11 +5,11 @@ template(name="minicard")
class="{{#if colorClass}}minicard-{{colorClass}}{{/if}}")
if canModifyCard
if isTouchScreenOrShowDesktopDragHandles
a.fa.fa-navicon.minicard-details-menu-with-handle.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
a.minicard-details-menu-with-handle.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}") | ☰
.handle
.fa.fa-arrows
| ↔️
else
a.fa.fa-navicon.minicard-details-menu.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
a.minicard-details-menu.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}") | ☰
.dates
if getReceived
unless getStart
@ -36,7 +36,7 @@ template(name="minicard")
if hasActiveUploads
.minicard-upload-progress
.upload-progress-header
i.fa.fa-upload
| 📤
span {{_ 'uploading-files'}} ({{uploadCount}})
each uploads
.upload-progress-item(class="{{#if $eq status 'error'}}upload-error{{/if}}")
@ -45,11 +45,11 @@ template(name="minicard")
.upload-progress-fill(style="width: {{progress}}%")
if $eq status 'error'
.upload-progress-error
i.fa.fa-exclamation-triangle
| ⚠️
span {{_ 'upload-failed'}}
else if $eq status 'completed'
.upload-progress-success
i.fa.fa-check
| ✅
span {{_ 'upload-completed'}}
.minicard-title
@ -61,12 +61,12 @@ template(name="minicard")
| {{ parentCardName }}
if isLinkedBoard
a.js-linked-link
span.linked-icon.fa.fa-folder
span.linked-icon | 📁
else if isLinkedCard
a.js-linked-link
span.linked-icon.fa.fa-id-card
span.linked-icon | 🃏
if getArchived
span.linked-icon.linked-archived.fa.fa-archive
span.linked-icon.linked-archived | 📦
+viewer
if currentBoard.allowsCardNumber
span.card-number
@ -147,7 +147,7 @@ template(name="minicard")
if canModifyCard
if comments.length
.badge(title="{{_ 'card-comments-title' comments.length }}")
span.badge-icon.fa.fa-comment-o.badge-comment.badge-text
span.badge-icon.badge-comment.badge-text | 💬
= ' '
= comments.length
//span.badge-comment.badge-text
@ -155,36 +155,36 @@ template(name="minicard")
if getDescription
unless currentBoard.allowsDescriptionTextOnMinicard
.badge.badge-state-image-only(title=getDescription)
span.badge-icon.fa.fa-align-left
span.badge-icon | 📝
if getVoteQuestion
.badge.badge-state-image-only(title=getVoteQuestion)
span.badge-icon.fa.fa-thumbs-up(class="{{#if voteState}}text-green{{/if}}")
span.badge-icon(class="{{#if voteState}}text-green{{/if}}") | 👍
span.badge-text {{ voteCountPositive }}
span.badge-icon.fa.fa-thumbs-down(class="{{#if $eq voteState false}}text-red{{/if}}")
span.badge-icon(class="{{#if $eq voteState false}}text-red{{/if}}") | 👎
span.badge-text {{ voteCountNegative }}
if getPokerQuestion
.badge.badge-state-image-only(title=getPokerQuestion)
span.badge-icon.fa.fa-check(class="{{#if pokerState}}text-green{{/if}}")
span.badge-icon(class="{{#if pokerState}}text-green{{/if}}") | ✅
if expiredPoker
span.badge-text {{ getPokerEstimation }}
if attachments.length
if currentBoard.allowsBadgeAttachmentOnMinicard
.badge
span.badge-icon.fa.fa-paperclip
span.badge-icon | 📎
span.badge-text= attachments.length
if checklists.length
.badge(class="{{#if checklistFinished}}is-finished{{/if}}")
span.badge-icon.fa.fa-check-square-o
span.badge-icon | ☑️
span.badge-text.check-list-text {{checklistFinishedCount}}/{{checklistItemCount}}
if allSubtasks.count
.badge
span.badge-icon.fa.fa-sitemap
span.badge-icon | 🌐
span.badge-text.check-list-text {{subtasksFinishedCount}}/{{allSubtasksCount}}
//{{subtasksFinishedCount}}/{{subtasksCount}} does not work because when a subtaks is archived, the count goes down
if currentBoard.allowsCardSortingByNumber
if currentBoard.allowsCardSortingByNumberOnMinicard
.badge
span.badge-icon.fa.fa-sort
span.badge-icon | 🔢
span.badge-text.check-list-sort {{ sort }}
if currentBoard.allowsDescriptionTextOnMinicard
if getDescription
@ -193,7 +193,7 @@ template(name="minicard")
| {{ getDescription }}
if shouldShowListOnMinicard
.minicard-list-name
i.fa.fa-list
| 📋
| {{ listName }}
if $eq 'subtext-with-full-path' currentBoard.presentParentTask
.parent-subtext
@ -212,50 +212,50 @@ template(name="minicardDetailsActionsPopup")
if canModifyCard
li
a.js-move-card
i.fa.fa-arrow-right
| ➡️
| {{_ 'moveCardPopup-title'}}
li
a.js-copy-card
i.fa.fa-copy
| 📋
| {{_ 'copyCardPopup-title'}}
hr
li
a.js-archive
i.fa.fa-arrow-right
i.fa.fa-archive
| ➡️
| 📦
| {{_ 'archive-card'}}
hr
li
a.js-move-card-to-top
i.fa.fa-arrow-up
| ⬆️
| {{_ 'moveCardToTop-title'}}
li
a.js-move-card-to-bottom
i.fa.fa-arrow-down
| ⬇️
| {{_ 'moveCardToBottom-title'}}
hr
li
a.js-add-labels
i.fa.fa-tags
| 🏷️
| {{_ 'card-edit-labels'}}
li
a.js-due-date
i.fa.fa-sign-in
| 📥
| {{_ 'editCardDueDatePopup-title'}}
li
a.js-set-card-color
i.fa.fa-paint-brush
| 🎨
| {{_ 'setCardColorPopup-title'}}
li
a.js-link
i.fa.fa-link
| 🔗
| {{_ 'link-card'}}
li
a.js-toggle-watch-card
if isWatching
i.fa.fa-eye
| 👁️
| {{_ 'unwatch'}}
else
i.fa.fa-eye-slash
| 👁️-slash
| {{_ 'watch'}}

View file

@ -21,6 +21,8 @@
background: transparent;
transition: background-color 0.2s ease;
border-radius: 2px;
/* Ensure the handle is clickable */
pointer-events: auto;
}
.list-resize-handle:hover {
@ -90,6 +92,8 @@
width: var(--list-width, auto) !important;
min-width: var(--list-width, auto) !important;
max-width: var(--list-width, auto) !important;
/* Ensure the width is applied immediately */
overflow: visible !important;
}
body.list-resizing-active {
@ -250,7 +254,70 @@ body.list-resizing-active * {
}
.list.list-collapsed {
flex: none;
min-width: 60px;
max-width: 80px;
width: 60px;
min-height: 60vh;
height: 60vh;
overflow: visible;
position: relative;
}
.list.list-collapsed .list-header {
padding: 1vh 1.5vw 0.5vh;
min-height: 2.5vh !important;
height: auto !important;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
position: relative;
overflow: visible !important;
width: 100%;
max-width: 60px;
margin: 0 auto;
}
.list.list-collapsed .list-header .js-collapse {
margin: 0 auto 20px auto;
z-index: 10;
padding: 8px 12px;
font-size: 12px;
white-space: nowrap;
display: block;
width: fit-content;
}
.list.list-collapsed .list-header .list-rotated {
width: auto !important;
height: auto !important;
margin: 20px 0 0 0 !important;
position: relative !important;
overflow: visible !important;
}
.list.list-collapsed .list-header .list-rotated h2.list-header-name {
text-align: left;
overflow: visible;
white-space: nowrap;
display: block !important;
font-size: 12px;
line-height: 1.2;
color: #333;
background-color: rgba(255, 255, 255, 0.95);
border: 1px solid #ddd;
padding: 8px 4px;
border-radius: 4px;
margin: 0 auto;
width: 25vh;
height: 60vh;
position: absolute;
left: 50%;
top: 50%;
transform: translate(calc(-50% + 50px), -50%) rotate(0deg);
z-index: 10;
visibility: visible !important;
opacity: 1 !important;
pointer-events: none;
}
.list.list-composer .open-list-composer,
.list .list-composer .open-list-composer {
color: #8c8c8c;
@ -334,11 +401,152 @@ body.list-resizing-active * {
color: #a6a6a6;
margin-right: 15px;
}
.list-header .list-header-uncollapse-left {
.list-header .js-collapse {
color: #a6a6a6;
margin-right: 15px;
display: inline-block;
vertical-align: middle;
padding: 5px 8px;
border: 1px solid #ccc;
border-radius: 4px;
background-color: #f5f5f5;
cursor: pointer;
font-size: 14px;
}
.list-header .js-collapse:hover {
background-color: #e0e0e0;
color: #333;
}
.list.list-collapsed .list-header .js-collapse {
display: inline-block !important;
visibility: visible !important;
opacity: 1 !important;
}
/* Responsive adjustments for collapsed lists */
@media (min-width: 768px) {
.list.list-collapsed {
min-width: 60px;
max-width: 80px;
width: 60px;
min-height: 60vh;
height: 60vh;
}
.list.list-collapsed .list-header {
max-width: 60px;
margin: 0 auto;
min-height: 2.5vh !important;
height: auto !important;
}
.list.list-collapsed .list-header .list-rotated {
width: auto !important;
height: auto !important;
margin: 20px 0 0 0 !important;
position: relative !important;
}
.list.list-collapsed .list-header .list-rotated h2.list-header-name {
width: 15vh;
font-size: 12px;
height: 30px;
line-height: 1.2;
padding: 8px 4px;
margin: 0 auto;
position: absolute;
left: 50%;
top: 50%;
transform: translate(calc(-50% + 50px), -50%) rotate(0deg);
text-align: left;
visibility: visible !important;
opacity: 1 !important;
display: block !important;
background-color: rgba(255, 255, 255, 0.95);
border: 1px solid #ddd;
color: #333;
z-index: 10;
}
.list.list-collapsed .list-header .js-collapse {
margin: 0 auto 20px auto;
}
}
@media (min-width: 1024px) {
.list.list-collapsed {
min-height: 60vh;
height: 60vh;
}
.list.list-collapsed .list-header {
min-height: 2.5vh !important;
height: auto !important;
}
.list.list-collapsed .list-header .list-rotated {
width: auto !important;
height: auto !important;
margin: 20px 0 0 0 !important;
position: relative !important;
}
.list.list-collapsed .list-header .list-rotated h2.list-header-name {
width: 15vh;
font-size: 12px;
height: 30px;
line-height: 1.2;
padding: 8px 4px;
margin: 0 auto;
position: absolute;
left: 50%;
top: 50%;
transform: translate(calc(-50% + 50px), -50%) rotate(0deg);
text-align: left;
visibility: visible !important;
opacity: 1 !important;
display: block !important;
background-color: rgba(255, 255, 255, 0.95);
border: 1px solid #ddd;
color: #333;
z-index: 10;
}
.list.list-collapsed .list-header .js-collapse {
margin: 0 auto 20px auto;
}
}
@media (min-width: 1200px) {
.list.list-collapsed {
min-height: 60vh;
height: 60vh;
}
.list.list-collapsed .list-header {
min-height: 2.5vh !important;
height: auto !important;
}
.list.list-collapsed .list-header .list-rotated {
width: auto !important;
height: auto !important;
margin: 20px 0 0 0 !important;
position: relative !important;
}
.list.list-collapsed .list-header .list-rotated h2.list-header-name {
width: 15vh;
font-size: 12px;
height: 30px;
line-height: 1.2;
padding: 8px 4px;
margin: 0 auto;
position: absolute;
left: 50%;
top: 50%;
transform: translate(calc(-50% + 50px), -50%) rotate(0deg);
text-align: left;
visibility: visible !important;
opacity: 1 !important;
display: block !important;
background-color: rgba(255, 255, 255, 0.95);
border: 1px solid #ddd;
color: #333;
z-index: 10;
}
.list.list-collapsed .list-header .js-collapse {
margin: 0 auto 20px auto;
}
.list-header .list-header-uncollapse-right {
color: #a6a6a6;
}
.list-header .list-header-collapse {
color: #a6a6a6;

View file

@ -4,7 +4,7 @@ template(name='list')
class="{{#if collapsed}}list-collapsed{{/if}} {{#if autoWidth}}list-auto-width{{/if}} {{#if isMiniScreen}}mobile-view{{/if}}")
+listHeader
+listBody
.list-resize-handle.js-list-resize-handle
.list-resize-handle.js-list-resize-handle.nodragscroll
template(name='miniList')
a.mini-list.js-select-list.js-list(id="js-list-{{_id}}" class="{{#if isMiniScreen}}mobile-view{{/if}}")

View file

@ -24,7 +24,7 @@ BlazeComponent.extendComponent({
onRendered() {
const boardComponent = this.parentComponent().parentComponent();
// Initialize list resize functionality
// Initialize list resize functionality immediately
this.initializeListResize();
const itemsSelector = '.js-minicard:not(.placeholder, .js-card-composer)';
@ -201,13 +201,15 @@ BlazeComponent.extendComponent({
listWidth() {
const user = ReactiveCache.getCurrentUser();
const list = Template.currentData();
return user.getListWidth(list.boardId, list._id);
if (!user || !list) return 270; // Return default width if user or list is not available
return user.getListWidthFromStorage(list.boardId, list._id);
},
listConstraint() {
const user = ReactiveCache.getCurrentUser();
const list = Template.currentData();
return user.getListConstraint(list.boardId, list._id);
if (!user || !list) return 550; // Return default constraint if user or list is not available
return user.getListConstraintFromStorage(list.boardId, list._id);
},
autoWidth() {
@ -217,12 +219,31 @@ BlazeComponent.extendComponent({
},
initializeListResize() {
// Check if we're still in a valid template context
if (!Template.currentData()) {
console.warn('No current template data available for list resize initialization');
return;
}
const list = Template.currentData();
const $list = this.$('.js-list');
const $resizeHandle = this.$('.js-list-resize-handle');
// Check if elements exist
if (!$list.length || !$resizeHandle.length) {
console.warn('List or resize handle not found, retrying in 100ms');
Meteor.setTimeout(() => {
if (!this.isDestroyed) {
this.initializeListResize();
}
}, 100);
return;
}
// Only enable resize for non-collapsed, non-auto-width lists
if (list.collapsed || this.autoWidth()) {
const isAutoWidth = this.autoWidth();
if (list.collapsed || isAutoWidth) {
$resizeHandle.hide();
return;
}
@ -240,41 +261,41 @@ BlazeComponent.extendComponent({
startX = e.pageX || e.originalEvent.touches[0].pageX;
startWidth = $list.outerWidth();
// Add visual feedback
$list.addClass('list-resizing');
$('body').addClass('list-resizing-active');
// Prevent text selection during resize
$('body').css('user-select', 'none');
e.preventDefault();
e.stopPropagation();
};
const doResize = (e) => {
if (!isResizing) return;
if (!isResizing) {
return;
}
const currentX = e.pageX || e.originalEvent.touches[0].pageX;
const deltaX = currentX - startX;
const newWidth = Math.max(minWidth, Math.min(maxWidth, startWidth + deltaX));
// Apply the new width immediately for real-time feedback using CSS custom properties
// Apply the new width immediately for real-time feedback
$list[0].style.setProperty('--list-width', `${newWidth}px`);
$list.css({
'width': `${newWidth}px`,
'min-width': `${newWidth}px`,
'max-width': `${newWidth}px`,
'flex': 'none',
'flex-basis': 'auto',
'flex-grow': '0',
'flex-shrink': '0'
});
$list[0].style.setProperty('width', `${newWidth}px`);
$list[0].style.setProperty('min-width', `${newWidth}px`);
$list[0].style.setProperty('max-width', `${newWidth}px`);
$list[0].style.setProperty('flex', 'none');
$list[0].style.setProperty('flex-basis', 'auto');
$list[0].style.setProperty('flex-grow', '0');
$list[0].style.setProperty('flex-shrink', '0');
// Debug: log the width change
if (process.env.DEBUG === 'true') {
console.log(`Resizing list to ${newWidth}px`);
}
e.preventDefault();
e.stopPropagation();
};
const stopResize = (e) => {
@ -287,17 +308,15 @@ BlazeComponent.extendComponent({
const deltaX = currentX - startX;
const finalWidth = Math.max(minWidth, Math.min(maxWidth, startWidth + deltaX));
// Ensure the final width is applied using CSS custom properties
// Ensure the final width is applied
$list[0].style.setProperty('--list-width', `${finalWidth}px`);
$list.css({
'width': `${finalWidth}px`,
'min-width': `${finalWidth}px`,
'max-width': `${finalWidth}px`,
'flex': 'none',
'flex-basis': 'auto',
'flex-grow': '0',
'flex-shrink': '0'
});
$list[0].style.setProperty('width', `${finalWidth}px`);
$list[0].style.setProperty('min-width', `${finalWidth}px`);
$list[0].style.setProperty('max-width', `${finalWidth}px`);
$list[0].style.setProperty('flex', 'none');
$list[0].style.setProperty('flex-basis', 'auto');
$list[0].style.setProperty('flex-grow', '0');
$list[0].style.setProperty('flex-shrink', '0');
// Remove visual feedback but keep the width
$list.removeClass('list-resizing');
@ -311,16 +330,14 @@ BlazeComponent.extendComponent({
const boardId = list.boardId;
const listId = list._id;
// Use the same method as the hamburger menu
// Use the new storage method that handles both logged-in and non-logged-in users
if (process.env.DEBUG === 'true') {
console.log(`Saving list width: ${finalWidth}px for list ${listId}`);
}
Meteor.call('applyListWidth', boardId, listId, finalWidth, listConstraint, (error, result) => {
Meteor.call('applyListWidthToStorage', boardId, listId, finalWidth, listConstraint, (error, result) => {
if (error) {
console.error('Error saving list width:', error);
} else {
if (process.env.DEBUG === 'true') {
console.log('List width saved successfully:', result);
}
}
});
@ -334,9 +351,16 @@ BlazeComponent.extendComponent({
$(document).on('mouseup', stopResize);
// Touch events for mobile
$resizeHandle.on('touchstart', startResize);
$(document).on('touchmove', doResize);
$(document).on('touchend', stopResize);
$resizeHandle.on('touchstart', startResize, { passive: false });
$(document).on('touchmove', doResize, { passive: false });
$(document).on('touchend', stopResize, { passive: false });
// Prevent dragscroll interference
$resizeHandle.on('mousedown', (e) => {
e.stopPropagation();
});
// Reactively update resize handle visibility when auto-width changes
component.autorun(() => {

View file

@ -32,7 +32,7 @@ template(name="listBody")
+addCardForm(listId=_id position="bottom")
else
a.open-minicard-composer.js-card-composer.js-open-inlined-form(title="{{_ 'add-card-to-bottom-of-list'}}")
i.fa.fa-plus
|
template(name="spinnerList")
.sk-spinner.sk-spinner-list(
@ -54,7 +54,7 @@ template(name="addCardForm")
.add-controls.clearfix
button.primary.confirm(type="submit") {{_ 'add'}}
a.fa.fa-times-thin.js-close-inlined-form
a.js-close-inlined-form | ❌
.add-controls.clearfix
unless currentBoard.isTemplatesBoard
unless currentBoard.isTemplateBoard

View file

@ -10,9 +10,6 @@ template(name="listHeader")
a.list-header-left-icon.fa.fa-angle-left.js-unselect-list
else
if collapsed
a.js-collapse(title="{{_ 'uncollapse'}}")
i.fa.fa-arrow-left.list-header-uncollapse-left
i.fa.fa-arrow-right.list-header-uncollapse-right
if showCardsCountForList cards.length
br
span.cardCount {{cardsCount}}
@ -29,6 +26,10 @@ template(name="listHeader")
if showCardsCountForList cards.length
span.cardCount {{cardsCount}} {{cardsCountForListIsOne cards.length}}
else
if collapsed
a.js-collapse(title="{{_ 'uncollapse'}}")
| ⬅️
| ➡️
div(class="{{#if collapsed}}list-rotated{{/if}}")
h2.list-header-name(
title="{{ moment modifiedAt 'LLL' }}"
@ -45,32 +46,32 @@ template(name="listHeader")
if isMiniScreen
if currentList
if isWatching
i.list-header-watch-icon.fa.fa-eye
i.list-header-watch-icon | 👁️
div.list-header-menu
unless currentUser.isCommentOnly
if canSeeAddCard
a.js-add-card.fa.fa-plus.list-header-plus-top(title="{{_ 'add-card-to-top-of-list'}}")
a.fa.fa-navicon.js-open-list-menu(title="{{_ 'listActionPopup-title'}}")
a.js-add-card.list-header-plus-top(title="{{_ 'add-card-to-top-of-list'}}") |
a.js-open-list-menu(title="{{_ 'listActionPopup-title'}}") | ☰
else
a.list-header-menu-icon.fa.fa-angle-right.js-select-list
a.list-header-handle.handle.fa.fa-arrows.js-list-handle
a.list-header-menu-icon.js-select-list | ▶️
a.list-header-handle.handle.js-list-handle | ↔️
else if currentUser.isBoardMember
if isWatching
i.list-header-watch-icon.fa.fa-eye
i.list-header-watch-icon | 👁️
unless collapsed
div.list-header-menu
unless currentUser.isCommentOnly
//if isBoardAdmin
// a.fa.js-list-star.list-header-plus-top(class="fa-star{{#unless starred}}-o{{/unless}}")
if canSeeAddCard
a.js-add-card.fa.fa-plus.list-header-plus-top(title="{{_ 'add-card-to-top-of-list'}}")
a.js-add-card.list-header-plus-top(title="{{_ 'add-card-to-top-of-list'}}") |
a.js-collapse(title="{{_ 'collapse'}}")
i.fa.fa-arrow-right.list-header-collapse-right
i.fa.fa-arrow-left.list-header-collapse-left
a.fa.fa-navicon.js-open-list-menu(title="{{_ 'listActionPopup-title'}}")
| ⬅️
| ➡️
a.js-open-list-menu(title="{{_ 'listActionPopup-title'}}") | ☰
if currentUser.isBoardAdmin
if isTouchScreenOrShowDesktopDragHandles
a.list-header-handle.handle.fa.fa-arrows.js-list-handle
a.list-header-handle.handle.js-list-handle | ↔️
template(name="editListTitleForm")
.list-composer

View file

@ -7,14 +7,14 @@ template(name="sidebar")
.sidebar-actions
.sidebar-shortcuts
a.sidebar-btn.js-shortcuts(title="{{_ 'keyboard-shortcuts' }}")
i.fa.fa-keyboard-o
| ⌨️
span {{_ 'keyboard-shortcuts' }}
a.sidebar-btn.js-keyboard-shortcuts-toggle(
title="{{#if isKeyboardShortcuts}}{{_ 'keyboard-shortcuts-enabled'}}{{else}}{{_ 'keyboard-shortcuts-disabled'}}{{/if}}")
i.fa(class="fa-solid fa-{{#if isKeyboardShortcuts}}check-square-o{{else}}ban{{/if}}")
| {{#if isKeyboardShortcuts}}✅{{else}}🚫{{/if}}
if isAccessibilityEnabled
a.sidebar-accessibility
i.fa.fa-universal-access
| ♿
span {{_ 'accessibility'}}
a.sidebar-xmark.js-close-sidebar ✕
.sidebar-content.js-board-sidebar-content
@ -22,7 +22,7 @@ template(name="sidebar")
// i.fa.fa-navicon
unless isDefaultView
h2
a.fa.fa-chevron-left.js-back-home
a.js-back-home | ⬅️
= getViewTitle
if isOpen
+Template.dynamic(template=getViewTemplate)
@ -51,7 +51,7 @@ template(name='homeSidebar')
hr
unless currentUser.isNoComments
h3.activity-title
i.fa.fa-comments-o
| 💬
| {{_ 'activities'}}
.material-toggle-switch(title="{{_ 'show-activities'}}")
@ -67,11 +67,11 @@ template(name="membersWidget")
unless currentUser.isWorker
h3
a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}")
i.board-header-btn-icon.fa.fa-cog
| ⚙️
| {{_ 'boardMenuPopup-title'}}
hr
h3
i.fa.fa-users
| 👥
| {{_ 'members'}}
+basicTabs(tabs=tabs)
+tabContent(slug="people")
@ -83,15 +83,15 @@ template(name="membersWidget")
if isSandstorm
if currentUser.isBoardMember
a.member.add-member.sandstorm-powerbox-request-identity(title="{{_ 'add-members'}}")
i.fa.fa-plus
|
else if currentUser.isBoardAdmin
a.member.add-member.js-manage-board-members(title="{{_ 'add-members'}}")
i.fa.fa-plus
|
.clearfix
if isInvited
hr
p
i.fa.fa-exclamation-circle
| ⚠️
| {{_ 'just-invited'}}
button.js-member-invite-accept.primary {{_ 'accept'}}
button.js-member-invite-decline {{_ 'decline'}}
@ -127,7 +127,7 @@ template(name="boardOrgGeneral")
th
if currentUser.isBoardAdmin
a.member.orgOrTeamMember.add-member.js-manage-board-addOrg(title="{{_ 'add-members'}}")
i.addTeamFaPlus.fa.fa-plus
|
.divaddfaplusminus
| {{_ 'add'}}
each org in currentBoard.activeOrgs
@ -148,7 +148,7 @@ template(name="boardTeamGeneral")
th
if currentUser.isBoardAdmin
a.member.orgOrTeamMember.add-member.js-manage-board-addTeam(title="{{_ 'add-members'}}")
i.addTeamFaPlus.fa.fa-plus
|
.divaddfaplusminus
| {{_ 'add'}}
each currentBoard.activeTeams
@ -161,7 +161,7 @@ template(name="boardChangeColorPopup")
span.background-box(class="board-color-{{this}}")
span {{this}}
if isSelected
i.fa.fa-check
| ✅
template(name="boardChangeBackgroundImagePopup")
form
@ -423,7 +423,7 @@ template(name="chooseBoardSource")
template(name="archiveBoardPopup")
p {{_ 'close-board-pop'}}
button.js-confirm.negate.full(type="submit")
i.fa.fa-archive
| 📦
| {{_ 'archive'}}
template(name="outgoingWebhooksPopup")
@ -459,25 +459,25 @@ template(name="boardMenuPopup")
if currentUser.isBoardAdmin
li
a.js-open-rules-view(title="{{_ 'rules'}}")
i.fa.fa-magic
| ✨
| {{_ 'rules'}}
if currentUser.isBoardAdmin
li
a.js-custom-fields
i.fa.fa-list-alt
| 📝
| {{_ 'custom-fields'}}
li
a.js-open-archives
i.fa.fa-archive
| 📦
| {{_ 'archived-items'}}
if currentUser.isBoardAdmin
li
a.js-change-board-color
i.fa.fa-paint-brush
| 🎨
| {{_ 'board-change-color'}}
li
a.js-change-background-image
i.fa.fa-picture-o
| 🖼️
| {{_ 'board-change-background-image'}}
//Bug Board icons random dance https://github.com/wekan/wekan/issues/4214
//if currentUser.isBoardAdmin
@ -492,20 +492,20 @@ template(name="boardMenuPopup")
if withApi
li
a.js-export-board
i.fa.fa-share-alt
| 📤
| {{_ 'export-board'}}
if currentUser.isBoardAdmin
li
a.js-outgoing-webhooks
i.fa.fa-globe
| 🌐
| {{_ 'outgoing-webhooks'}}
li
a.js-card-settings
i.fa.fa-id-card-o
| 🃏
| {{_ 'card-settings'}}
li
a.js-subtask-settings
i.fa.fa-sitemap
| 🌐
| {{_ 'subtask-settings'}}
unless currentBoard.isTemplatesBoard
if currentUser.isBoardAdmin
@ -513,41 +513,40 @@ template(name="boardMenuPopup")
ul.pop-over-list
li
a.js-archive-board
i.fa.fa-arrow-right
i.fa.fa-archive
| ➡️📦
| {{_ 'archive-board'}}
template(name="exportBoard")
ul.pop-over-list
li
a.download-json-link(href="{{exportUrl}}", download="{{exportJsonFilename}}")
i.fa.fa-share-alt
| 📤
| {{_ 'export-board-json'}}
li
a(href="{{exportUrlExcel}}", download="{{exportFilenameExcel}}")
i.fa.fa-share-alt
| 📤
| {{_ 'export-board-excel'}}
li
a(href="{{exportCsvUrl}}", download="{{exportCsvFilename}}")
i.fa.fa-share-alt
| 📤
| {{_ 'export-board-csv'}} ,
li
a(href="{{exportScsvUrl}}", download="{{exportCsvFilename}}")
i.fa.fa-share-alt
| 📤
| {{_ 'export-board-csv'}} ;
li
a(href="{{exportTsvUrl}}", download="{{exportTsvFilename}}")
i.fa.fa-share-alt
| 📤
| {{_ 'export-board-tsv'}}
li
a.html-export-board
i.fa.fa-archive
| 📦
| {{_ 'export-board-html'}}
template(name="labelsWidget")
.board-widget.board-widget-labels
h3
i.fa.fa-tags
| 🏷️
| {{_ 'labels'}}
.board-widget-content
each currentBoard.labels
@ -558,7 +557,7 @@ template(name="labelsWidget")
= name
if currentUser.isBoardAdmin
a.card-label.add-label.js-add-label(title="{{_ 'label-create'}}")
i.fa.fa-plus
|
template(name="memberPopup")
.board-member-menu
@ -570,7 +569,7 @@ template(name="memberPopup")
p.quiet @#{user.username}
if isInvited
p
i.fa.fa-exclamation-circle
| ⚠️
| {{_ 'not-accepted-yet'}}
ul.pop-over-list
@ -665,31 +664,31 @@ template(name="changePermissionsPopup")
a(class="{{#if isLastAdmin}}disabled{{else}}js-set-admin{{/if}}")
| {{_ 'admin'}}
if isAdmin
i.fa.fa-check
| ✅
span.sub-name {{_ 'admin-desc'}}
li
a(class="{{#if isLastAdmin}}disabled{{else}}js-set-normal{{/if}}")
| {{_ 'normal'}}
if isNormal
i.fa.fa-check
| ✅
span.sub-name {{_ 'normal-desc'}}
li
a(class="{{#if isLastAdmin}}disabled{{else}}js-set-no-comments{{/if}}")
| {{_ 'no-comments'}}
if isNoComments
i.fa.fa-check
| ✅
span.sub-name {{_ 'no-comments-desc'}}
li
a(class="{{#if isLastAdmin}}disabled{{else}}js-set-comment-only{{/if}}")
| {{_ 'comment-only'}}
if isCommentOnly
i.fa.fa-check
| ✅
span.sub-name {{_ 'comment-only-desc'}}
li
a(class="{{#if isLastAdmin}}disabled{{else}}js-set-worker{{/if}}")
| {{_ 'worker'}}
if isWorker
i.fa.fa-check
| ✅
span.sub-name {{_ 'worker-desc'}}
if isLastAdmin
hr

View file

@ -24,10 +24,10 @@ template(name="swimlaneFixedHeader")
| {{isTitleDefault title}}
.swimlane-header-menu
unless currentUser.isCommentOnly
a.js-open-add-swimlane-menu.swimlane-header-plus-icon
| (title="{{_ 'add-swimlane'}}")
a.js-open-swimlane-menu
| ☰(title="{{_ 'swimlaneActionPopup-title'}}")
a.js-open-add-swimlane-menu.swimlane-header-plus-icon(title="{{_ 'add-swimlane'}}")
|
a.js-open-swimlane-menu(title="{{_ 'swimlaneActionPopup-title'}}")
| ☰
//// TODO: Collapse Swimlane: make button working, etc.
//unless collapsed
// a.js-collapse-swimlane(title="{{_ 'collapse'}}")
@ -99,7 +99,7 @@ template(name="setSwimlaneColorPopup")
each colors
span.card-label.palette-color.js-palette-color(class="card-details-{{color}}")
if(isSelected color)
i.fa.fa-check
| ✅
// Buttons aligned left too
.flush-left
button.primary.confirm.js-submit(style="margin-left:0") {{_ 'save'}}

View file

@ -8,6 +8,7 @@
flex-direction: row;
overflow: auto;
max-height: 100%;
position: relative;
}
.swimlane-header-menu .swimlane-header-collapse-down {
font-size: 50%;
@ -234,3 +235,106 @@
background: #4b0082 !important;
color: #fff !important;
}
/* Swimlane resize handle */
.swimlane-resize-handle {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 8px;
background: transparent;
cursor: row-resize;
z-index: 20;
border-top: 2px solid transparent;
transition: all 0.2s ease;
border-radius: 2px;
/* Ensure the handle is clickable */
pointer-events: auto;
}
/* Show resize handle only on hover */
.swimlane:hover .swimlane-resize-handle {
background: rgba(0, 0, 0, 0.1);
border-top-color: rgba(0, 0, 0, 0.2);
}
/* Add a subtle resize indicator line at the bottom of swimlane on hover */
.swimlane:hover .swimlane-resize-handle::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2px;
background: rgba(0, 123, 255, 0.3);
z-index: 21;
transition: all 0.2s ease;
border-radius: 1px;
}
/* Make the indicator line more prominent when hovering over the resize handle */
.swimlane-resize-handle:hover::after {
background: rgba(0, 123, 255, 0.6) !important;
height: 3px !important;
box-shadow: 0 0 4px rgba(0, 123, 255, 0.2);
}
.swimlane-resize-handle:hover {
background: rgba(0, 123, 255, 0.4) !important;
border-top-color: #0079bf !important;
box-shadow: 0 0 4px rgba(0, 123, 255, 0.3);
}
.swimlane-resize-handle:active {
background: rgba(0, 123, 255, 0.6) !important;
border-top-color: #0079bf !important;
box-shadow: 0 0 6px rgba(0, 123, 255, 0.4);
}
/* Add a subtle indicator line */
.swimlane-resize-handle::before {
content: '';
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 20px;
height: 2px;
background: rgba(0, 123, 255, 0.6);
border-radius: 1px;
opacity: 0;
transition: opacity 0.2s ease;
}
.swimlane-resize-handle:hover::before {
opacity: 1;
}
/* Visual feedback during resize */
.swimlane.swimlane-resizing {
transition: none !important;
box-shadow: 0 0 10px rgba(0, 123, 255, 0.3);
/* Ensure the swimlane maintains its new height during resize */
flex: none !important;
flex-basis: auto !important;
flex-grow: 0 !important;
flex-shrink: 0 !important;
/* Override any conflicting layout properties */
display: flex !important;
position: relative !important;
/* Force height to be respected */
height: var(--swimlane-height, auto) !important;
min-height: var(--swimlane-height, auto) !important;
max-height: var(--swimlane-height, auto) !important;
/* Ensure the height is applied immediately */
overflow: visible !important;
}
body.swimlane-resizing-active {
cursor: row-resize !important;
}
body.swimlane-resizing-active * {
cursor: row-resize !important;
}

View file

@ -4,6 +4,7 @@ template(name="swimlane")
unless collapseSwimlane
.swimlane.js-lists.js-swimlane.dragscroll(id="swimlane-{{_id}}"
style="height:{{swimlaneHeight}};")
.swimlane-resize-handle.js-swimlane-resize-handle.nodragscroll
if isMiniScreen
if currentListIsInThisSwimlane _id
+list(currentList)
@ -67,7 +68,7 @@ template(name="addListForm")
a.js-list-template {{_ 'template'}}
else
a.open-list-composer.js-open-inlined-form(title="{{_ 'add-list'}}")
i.fa.fa-plus
|
template(name="moveSwimlanePopup")
unless currentUser.isWorker

View file

@ -247,10 +247,21 @@ BlazeComponent.extendComponent({
Utils.isTouchScreenOrShowDesktopDragHandles()
? ['.js-list-handle', '.js-swimlane-header-handle']
: ['.js-list-header'],
);
).concat([
'.js-list-resize-handle',
'.js-swimlane-resize-handle'
]);
const isResizeHandle = $(evt.target).closest('.js-list-resize-handle, .js-swimlane-resize-handle').length > 0;
const isInNoDragArea = $(evt.target).closest(noDragInside.join(',')).length > 0;
if (isResizeHandle) {
console.log('Board drag prevented - resize handle clicked');
return;
}
if (
$(evt.target).closest(noDragInside.join(',')).length === 0 &&
!isInNoDragArea &&
this.$('.swimlane').prop('clientHeight') > evt.offsetY
) {
this._isDragging = true;
@ -283,9 +294,150 @@ BlazeComponent.extendComponent({
swimlaneHeight() {
const user = ReactiveCache.getCurrentUser();
const swimlane = Template.currentData();
const height = user.getSwimlaneHeight(swimlane.boardId, swimlane._id);
const height = user.getSwimlaneHeightFromStorage(swimlane.boardId, swimlane._id);
return height == -1 ? "auto" : (height + 5 + "px");
},
onRendered() {
// Initialize swimlane resize functionality immediately
this.initializeSwimlaneResize();
},
initializeSwimlaneResize() {
// Check if we're still in a valid template context
if (!Template.currentData()) {
console.warn('No current template data available for swimlane resize initialization');
return;
}
const swimlane = Template.currentData();
const $swimlane = $(`#swimlane-${swimlane._id}`);
const $resizeHandle = $swimlane.find('.js-swimlane-resize-handle');
// Check if elements exist
if (!$swimlane.length || !$resizeHandle.length) {
console.warn('Swimlane or resize handle not found, retrying in 100ms');
Meteor.setTimeout(() => {
if (!this.isDestroyed) {
this.initializeSwimlaneResize();
}
}, 100);
return;
}
if ($resizeHandle.length === 0) {
return;
}
let isResizing = false;
let startY = 0;
let startHeight = 0;
const minHeight = 100;
const maxHeight = 2000;
const startResize = (e) => {
isResizing = true;
startY = e.pageY || e.originalEvent.touches[0].pageY;
startHeight = parseInt($swimlane.css('height')) || 300;
$swimlane.addClass('swimlane-resizing');
$('body').addClass('swimlane-resizing-active');
$('body').css('user-select', 'none');
e.preventDefault();
e.stopPropagation();
};
const doResize = (e) => {
if (!isResizing) {
return;
}
const currentY = e.pageY || e.originalEvent.touches[0].pageY;
const deltaY = currentY - startY;
const newHeight = Math.max(minHeight, Math.min(maxHeight, startHeight + deltaY));
// Apply the new height immediately for real-time feedback
$swimlane[0].style.setProperty('--swimlane-height', `${newHeight}px`);
$swimlane[0].style.setProperty('height', `${newHeight}px`);
$swimlane[0].style.setProperty('min-height', `${newHeight}px`);
$swimlane[0].style.setProperty('max-height', `${newHeight}px`);
$swimlane[0].style.setProperty('flex', 'none');
$swimlane[0].style.setProperty('flex-basis', 'auto');
$swimlane[0].style.setProperty('flex-grow', '0');
$swimlane[0].style.setProperty('flex-shrink', '0');
e.preventDefault();
e.stopPropagation();
};
const stopResize = (e) => {
if (!isResizing) return;
isResizing = false;
// Calculate final height
const currentY = e.pageY || e.originalEvent.touches[0].pageY;
const deltaY = currentY - startY;
const finalHeight = Math.max(minHeight, Math.min(maxHeight, startHeight + deltaY));
// Ensure the final height is applied
$swimlane[0].style.setProperty('--swimlane-height', `${finalHeight}px`);
$swimlane[0].style.setProperty('height', `${finalHeight}px`);
$swimlane[0].style.setProperty('min-height', `${finalHeight}px`);
$swimlane[0].style.setProperty('max-height', `${finalHeight}px`);
$swimlane[0].style.setProperty('flex', 'none');
$swimlane[0].style.setProperty('flex-basis', 'auto');
$swimlane[0].style.setProperty('flex-grow', '0');
$swimlane[0].style.setProperty('flex-shrink', '0');
// Remove visual feedback but keep the height
$swimlane.removeClass('swimlane-resizing');
$('body').removeClass('swimlane-resizing-active');
$('body').css('user-select', '');
// Save the new height using the existing system
const boardId = swimlane.boardId;
const swimlaneId = swimlane._id;
if (process.env.DEBUG === 'true') {
}
// Use the new storage method that handles both logged-in and non-logged-in users
Meteor.call('applySwimlaneHeightToStorage', boardId, swimlaneId, finalHeight, (error, result) => {
if (error) {
console.error('Error saving swimlane height:', error);
} else {
if (process.env.DEBUG === 'true') {
}
}
});
e.preventDefault();
};
// Mouse events
$resizeHandle.on('mousedown', startResize);
$(document).on('mousemove', doResize);
$(document).on('mouseup', stopResize);
// Touch events for mobile
$resizeHandle.on('touchstart', startResize, { passive: false });
$(document).on('touchmove', doResize, { passive: false });
$(document).on('touchend', stopResize, { passive: false });
// Prevent dragscroll interference
$resizeHandle.on('mousedown', (e) => {
e.stopPropagation();
});
},
}).register('swimlane');
BlazeComponent.extendComponent({

View file

@ -173,7 +173,7 @@ template(name="changeLanguagePopup")
a.js-set-language
= name
if isCurrentLanguage
i.fa.fa-check
| ✅
template(name="changeSettingsPopup")
ul.pop-over-list
@ -186,7 +186,7 @@ template(name="changeSettingsPopup")
unless currentUser.isWorker
li
label.bold.clear
i.fa.fa-sort-numeric-asc
| 🔢
| {{_ 'show-cards-minimum-count'}}
input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="-1")
label.bold.clear

View file

@ -65,8 +65,8 @@ Blaze.registerHelper('isTouchScreenOrShowDesktopDragHandles', () =>
Blaze.registerHelper('moment', (...args) => {
args.pop(); // hash
const [date, format] = args;
return format(new Date(date), format ?? 'LLLL');
const [date, formatStr] = args;
return format(new Date(date), formatStr ?? 'LLLL');
});
Blaze.registerHelper('canModifyCard', () =>

View file

@ -875,6 +875,52 @@ Users.helpers({
}
},
getSwimlaneHeightFromStorage(boardId, swimlaneId) {
// For logged-in users, get from profile
if (this._id) {
return this.getSwimlaneHeight(boardId, swimlaneId);
}
// For non-logged-in users, get from localStorage
try {
const stored = localStorage.getItem('wekan-swimlane-heights');
if (stored) {
const heights = JSON.parse(stored);
if (heights[boardId] && heights[boardId][swimlaneId]) {
return heights[boardId][swimlaneId];
}
}
} catch (e) {
console.warn('Error reading swimlane heights from localStorage:', e);
}
return -1;
},
setSwimlaneHeightToStorage(boardId, swimlaneId, height) {
// For logged-in users, save to profile
if (this._id) {
return this.setSwimlaneHeight(boardId, swimlaneId, height);
}
// For non-logged-in users, save to localStorage
try {
const stored = localStorage.getItem('wekan-swimlane-heights');
let heights = stored ? JSON.parse(stored) : {};
if (!heights[boardId]) {
heights[boardId] = {};
}
heights[boardId][swimlaneId] = height;
localStorage.setItem('wekan-swimlane-heights', JSON.stringify(heights));
return true;
} catch (e) {
console.warn('Error saving swimlane height to localStorage:', e);
return false;
}
},
/** returns all confirmed move and copy dialog field values
* <li> the board, swimlane and list id is stored for each board
*/
@ -1032,6 +1078,144 @@ Users.helpers({
_id: this._id,
});
},
getListWidthFromStorage(boardId, listId) {
// For logged-in users, get from profile
if (this._id) {
return this.getListWidth(boardId, listId);
}
// For non-logged-in users, get from localStorage
try {
const stored = localStorage.getItem('wekan-list-widths');
if (stored) {
const widths = JSON.parse(stored);
if (widths[boardId] && widths[boardId][listId]) {
return widths[boardId][listId];
}
}
} catch (e) {
console.warn('Error reading list widths from localStorage:', e);
}
return 270; // Return default width instead of -1
},
setListWidthToStorage(boardId, listId, width) {
// For logged-in users, save to profile
if (this._id) {
return this.setListWidth(boardId, listId, width);
}
// For non-logged-in users, save to localStorage
try {
const stored = localStorage.getItem('wekan-list-widths');
let widths = stored ? JSON.parse(stored) : {};
if (!widths[boardId]) {
widths[boardId] = {};
}
widths[boardId][listId] = width;
localStorage.setItem('wekan-list-widths', JSON.stringify(widths));
return true;
} catch (e) {
console.warn('Error saving list width to localStorage:', e);
return false;
}
},
getListConstraintFromStorage(boardId, listId) {
// For logged-in users, get from profile
if (this._id) {
return this.getListConstraint(boardId, listId);
}
// For non-logged-in users, get from localStorage
try {
const stored = localStorage.getItem('wekan-list-constraints');
if (stored) {
const constraints = JSON.parse(stored);
if (constraints[boardId] && constraints[boardId][listId]) {
return constraints[boardId][listId];
}
}
} catch (e) {
console.warn('Error reading list constraints from localStorage:', e);
}
return 550; // Return default constraint instead of -1
},
setListConstraintToStorage(boardId, listId, constraint) {
// For logged-in users, save to profile
if (this._id) {
return this.setListConstraint(boardId, listId, constraint);
}
// For non-logged-in users, save to localStorage
try {
const stored = localStorage.getItem('wekan-list-constraints');
let constraints = stored ? JSON.parse(stored) : {};
if (!constraints[boardId]) {
constraints[boardId] = {};
}
constraints[boardId][listId] = constraint;
localStorage.setItem('wekan-list-constraints', JSON.stringify(constraints));
return true;
} catch (e) {
console.warn('Error saving list constraint to localStorage:', e);
return false;
}
},
getSwimlaneHeightFromStorage(boardId, swimlaneId) {
// For logged-in users, get from profile
if (this._id) {
return this.getSwimlaneHeight(boardId, swimlaneId);
}
// For non-logged-in users, get from localStorage
try {
const stored = localStorage.getItem('wekan-swimlane-heights');
if (stored) {
const heights = JSON.parse(stored);
if (heights[boardId] && heights[boardId][swimlaneId]) {
return heights[boardId][swimlaneId];
}
}
} catch (e) {
console.warn('Error reading swimlane heights from localStorage:', e);
}
return -1; // Return -1 if not found
},
setSwimlaneHeightToStorage(boardId, swimlaneId, height) {
// For logged-in users, save to profile
if (this._id) {
return this.setSwimlaneHeight(boardId, swimlaneId, height);
}
// For non-logged-in users, save to localStorage
try {
const stored = localStorage.getItem('wekan-swimlane-heights');
let heights = stored ? JSON.parse(stored) : {};
if (!heights[boardId]) {
heights[boardId] = {};
}
heights[boardId][swimlaneId] = height;
localStorage.setItem('wekan-swimlane-heights', JSON.stringify(heights));
return true;
} catch (e) {
console.warn('Error saving swimlane height to localStorage:', e);
return false;
}
},
});
Users.mutations({
@ -1429,6 +1613,24 @@ Meteor.methods({
const user = ReactiveCache.getCurrentUser();
user.setSwimlaneHeight(boardId, swimlaneId, height);
},
applySwimlaneHeightToStorage(boardId, swimlaneId, height) {
check(boardId, String);
check(swimlaneId, String);
check(height, Number);
const user = ReactiveCache.getCurrentUser();
user.setSwimlaneHeightToStorage(boardId, swimlaneId, height);
},
applyListWidthToStorage(boardId, listId, width, constraint) {
check(boardId, String);
check(listId, String);
check(width, Number);
check(constraint, Number);
const user = ReactiveCache.getCurrentUser();
user.setListWidthToStorage(boardId, listId, width);
user.setListConstraintToStorage(boardId, listId, constraint);
},
setZoomLevel(level) {
check(level, Number);
const user = ReactiveCache.getCurrentUser();

7
package-lock.json generated
View file

@ -137,7 +137,7 @@
"from": "git+https://github.com/wekan/dragscroll.git"
},
"@wekanteam/exceljs": {
"version": "git+https://github.com/wekan/exceljs.git#7d182abf83ddfb1a8f5a9592a0fdf60ef72f6686",
"version": "git+https://github.com/wekan/exceljs.git#e0229907e7a81bc3fe6daf4e42b1fdfbecdcb7cb",
"from": "git+https://github.com/wekan/exceljs.git",
"requires": {
"archiver": "^5.0.0",
@ -2517,11 +2517,6 @@
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
},
"moment": {
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="
},
"mongo-object": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mongo-object/-/mongo-object-3.0.1.tgz",