Number of cards per list and sum of custom number field in list head.

Thanks to xet7 !

Fixes #3796
This commit is contained in:
Lauri Ojansivu 2025-12-22 21:06:44 +02:00
parent 4292302c3c
commit e569c2957e
29 changed files with 239 additions and 142 deletions

View file

@ -108,15 +108,12 @@
text-decoration: none;
height: 24px;
}
.comments .comment .comment-desc .reactions .open-comment-reaction-popup i.fa.fa-smile-o {
font-size: 17px;
.comments .comment .comment-desc .reactions .open-comment-reaction-popup span {
display: inline-block;
font-size: clamp(14px, 2vw, 18px);
font-weight: 500;
margin-left: 2px;
}
.comments .comment .comment-desc .reactions .open-comment-reaction-popup i.fa.fa-plus {
font-size: 8px;
margin-top: -7px;
margin-left: 1px;
line-height: 1;
margin-left: 4px;
}
.comments .comment .comment-desc .reactions .reaction {
cursor: pointer;

View file

@ -25,7 +25,7 @@ template(name="comment")
= text
.edit-controls
button.primary(type="submit") {{_ 'edit'}}
.fa.fa-times-thin.js-close-inlined-form
a.js-close-inlined-form(title="{{_ 'close' }}") ❌
else
.comment-text
+viewer
@ -55,8 +55,8 @@ template(name="commentReactions")
span.reaction-count #{reaction.userIds.length}
if (currentUser.isBoardMember)
a.open-comment-reaction-popup(title="{{_ 'addReactionPopup-title'}}")
i.fa.fa-smile-o
i.fa.fa-plus
span(title="{{_ 'reaction' }}") 😀
span(title="{{_ 'add' }}")
template(name="addReactionPopup")
.reactions-popup

View file

@ -1,6 +1,6 @@
template(name="archivedBoards")
h2
i.fa.fa-archive
span(title="{{_ 'archived-boards'}}") 📦
| {{_ 'archived-boards'}}
ul.archived-lists
@ -8,10 +8,10 @@ template(name="archivedBoards")
li.archived-lists-item
div.board-header-btns
button.board-header-btn.js-delete-board
i.fa.fa-trash-o
| 🗑️
| {{_ 'delete-board'}}
button.board-header-btn.js-restore-board
i.fa.fa-undo
| ↩️
| {{_ 'restore-board'}}
= title
span {{ moment archivedAt 'LLL' }}

View file

@ -3,6 +3,6 @@ template(name="miniboard")
class="minicard-{{colorClass}}")
.minicard-title
.handle
.fa.fa-arrows
span.drag-handle(title="{{_ 'dragBoard'}}") ↕️
+viewer
= title

View file

@ -31,8 +31,8 @@
display: block;
position: relative;
float: left;
height: 30px;
width: 30px;
height: clamp(24px, 3.5vw, 36px);
width: clamp(24px, 3.5vw, 36px);
margin: .3vh;
cursor: pointer;
user-select: none;

View file

@ -26,10 +26,10 @@ template(name="cardDetails")
| ☰
a.card-copy-button.js-copy-link(
id="cardURL_copy"
class="fa-link"
title="{{_ 'copy-card-link-to-clipboard'}}"
href="{{ originRelativeUrl }}"
)
| 🔗
span.copied-tooltip {{_ 'copied'}}
else
unless isPopup
@ -40,10 +40,10 @@ template(name="cardDetails")
| ☰
a.card-copy-mobile-button.js-copy-link(
id="cardURL_copy"
class="fa-link"
title="{{_ 'copy-card-link-to-clipboard'}}"
href="{{ originRelativeUrl }}"
)
| 🔗
span.copied-tooltip {{_ 'copied'}}
h2.card-details-title.js-card-title(
class="{{#if canModifyCard}}js-open-inlined-form is-editable{{/if}}")
@ -304,7 +304,7 @@ template(name="cardDetails")
hr
.card-details-item.card-details-item-customfield
h3.card-details-item-title
| 📋-alt
| 📋
= definition.name
+cardCustomField
@ -678,7 +678,7 @@ template(name="cardDetailsActionsPopup")
| 👁️
| {{_ 'unwatch'}}
else
| 👁️-slash
| 👁️
| {{_ 'watch'}}
hr
if canModifyCard
@ -698,7 +698,7 @@ template(name="cardDetailsActionsPopup")
if currentUser.isBoardAdmin
li
a.js-custom-fields
| 📋-alt
| 📋
| {{_ 'card-edit-custom-fields'}}
//li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}}
//li: a.js-start-date {{_ 'editCardStartDatePopup-title'}}
@ -718,7 +718,7 @@ template(name="cardDetailsActionsPopup")
| 👁️
| {{_ 'hide-list-on-minicard'}}
else
| 👁️-slash
| 👁️
| {{_ 'show-list-on-minicard'}}
hr
ul.pop-over-list

View file

@ -67,14 +67,14 @@ textarea.js-edit-checklist-item {
.checklist-title .checklist-stat.is-finished {
color: #3cb500;
}
.checklist-title span.fa.checklist-handle {
.checklist-title span.checklist-handle {
padding-right: 20px;
padding-top: 3px;
float: left;
}
.checklist-title span.fa.checklist-handle.fa-arrows::before {
content: "↕️" !important;
font-family: inherit !important;
display: inline-block;
width: 1.2em;
text-align: center;
color: #999;
}
#card-details-overlay {
top: 0;
@ -148,13 +148,13 @@ textarea.js-edit-checklist-item {
word-wrap: break-word;
max-width: 420px;
}
.checklist-item span.fa.checklistitem-handle {
.checklist-item span.checklistitem-handle {
padding-top: 2px;
padding-right: 10px;
}
.checklist-item span.fa.checklistitem-handle.fa-arrows::before {
content: "↕️" !important;
font-family: inherit !important;
display: inline-block;
width: 1.2em;
text-align: center;
color: #999;
}
.js-delete-checklist-item,
.js-convert-checklist-item-to-card {

View file

@ -43,7 +43,7 @@ template(name="checklistDetail")
if canModifyCard
h4.title.js-open-inlined-form.is-editable
if isTouchScreenOrShowDesktopDragHandles
span.fa.checklist-handle(class="fa-arrows" title="{{_ 'dragChecklist'}}")
span.checklist-handle(title="{{_ 'dragChecklist'}}") ↕️
+viewer
= checklist.title
else
@ -127,7 +127,7 @@ template(name='checklistItemDetail')
if canModifyCard
.check-box-container
.check-box.materialCheckBox(class="{{#if item.isFinished }}is-checked{{/if}}")
span.fa.checklistitem-handle(class="fa-arrows" title="{{_ 'dragChecklistItem'}}")
span.checklistitem-handle(title="{{_ 'dragChecklistItem'}}") ↕️
.item-title.js-open-inlined-form.is-editable(class="{{#if item.isFinished }}is-checked{{/if}}")
+viewer
= item.title

View file

@ -223,9 +223,13 @@
.card-label-edit-button:hover {
background: #dbdbdb;
}
ul.edit-labels-pop-over span.fa.label-handle {
ul.edit-labels-pop-over span.label-handle {
padding-right: 10px;
display: inline-block;
width: 1.2em;
text-align: center;
color: #999;
}
ul.edit-labels-pop-over span.fa.label-handle + .card-label {
ul.edit-labels-pop-over span.label-handle + .card-label {
max-width: 180px;
}

View file

@ -31,7 +31,7 @@ template(name="cardLabelsPopup")
a.card-label-edit-button.js-edit-label
| ✏️
if isTouchScreenOrShowDesktopDragHandles
span.fa.label-handle(class="fa-arrows" title="{{_ 'dragLabel'}}")
span.label-handle(title="{{_ 'dragLabel'}}") ↕️
span.card-label.card-label-selectable.js-select-label.card-label-wrapper(class="card-label-{{color}}"
class="{{# if isLabelSelected ../_id }}active{{/if}}")
+viewer

View file

@ -142,9 +142,12 @@
display: block;
}
}
.minicard .handle .fa-arrows {
.minicard .handle .drag-handle {
font-size: clamp(16px, 3vw, 20px);
color: #ccc;
display: inline-block;
width: 1.4em;
text-align: center;
}
.minicard .minicard-title .card-number {
color: #b3b3b3;
@ -297,19 +300,6 @@
background-color: #1976d2 !important;
}
/* Font Awesome icons in minicard dates */
.minicard .card-date i.fa {
margin-right: 0.3vw;
font-size: 0.9em;
vertical-align: middle;
}
/* Font Awesome icons in minicard spent time */
.minicard .card-time i.fa {
margin-right: 0.3vw;
font-size: 0.9em;
vertical-align: middle;
}
.minicard .badges {
float: left;
margin-top: 1vh;
@ -740,8 +730,3 @@
align-items: center;
gap: 0.3vw;
}
.minicard-list-name i.fa {
font-size: 0.8em;
opacity: 0.7;
}

View file

@ -251,6 +251,6 @@ template(name="minicardDetailsActionsPopup")
| 👁️
| {{_ 'unwatch'}}
else
| 👁️-slash
| 👁️
| {{_ 'watch'}}

View file

@ -2,19 +2,17 @@
<div class="original-position-info">
{{#if isLoading}}
<div class="original-position-loading">
<i class="fa fa-spinner fa-spin"></i> Loading original position...
Loading original position...
</div>
{{else if showOriginalPosition}}
<div class="original-position-details">
{{#if hasMovedFromOriginal}}
<div class="original-position-moved">
<i class="fa fa-info-circle"></i>
<span class="original-position-text">{{getOriginalPositionDescription}}</span>
<span class="original-position-text"> {{getOriginalPositionDescription}}</span>
</div>
{{else}}
<div class="original-position-unchanged">
<i class="fa fa-check-circle"></i>
<span class="original-position-text">In original position</span>
<span class="original-position-text">✅ In original position</span>
</div>
{{/if}}

View file

@ -1,7 +1,7 @@
template(name="importHeaderBar")
h1
a.back-btn(href="{{pathFor 'home'}}")
i.fa.fa-chevron-left
| ⬅️
| {{_ title}}
template(name="import")
@ -36,7 +36,7 @@ template(name="importMapMembers")
+userAvatar(userId=wekanId)
else
a.member.add-member
i.fa.fa-plus
|
//-
Due to the way the flewbox layout is working, we need to set some
invisible items so that the last row items have a consistent width.

View file

@ -368,6 +368,18 @@ body.list-resizing-active * {
text-overflow: ellipsis;
word-wrap: break-word;
}
/* Sum badge shown before list title */
.list-header .list-sum-badge {
display: inline-block;
margin-right: 8px;
padding: 0;
border-radius: 0;
background: transparent;
color: #8c8c8c;
font-weight: bold;
font-size: 12px;
vertical-align: middle;
}
.list-rotated {
width: 1.3vw;
height: 35vh;
@ -750,6 +762,9 @@ body.list-resizing-active * {
grid-row: 2;
grid-column: 2;
align-self: start;
text-align: left;
padding-left: 0;
margin-left: 0;
font-size: 16px !important;
line-height: 1.2;
}
@ -964,6 +979,9 @@ body.list-resizing-active * {
grid-row: 2 !important;
grid-column: 2 !important;
align-self: start !important;
text-align: left !important;
padding-left: 0 !important;
margin-left: 0 !important;
font-size: 16px !important;
line-height: 1.2 !important;
}

View file

@ -16,11 +16,50 @@ BlazeComponent.extendComponent({
},
customFieldsSum() {
const ret = ReactiveCache.getCustomFields({
boardIds: { $in: [Session.get('currentBoard')] },
const list = Template.currentData();
if (!list) return [];
const boardId = Session.get('currentBoard');
const fields = ReactiveCache.getCustomFields({
boardIds: { $in: [boardId] },
showSumAtTopOfList: true,
});
return ret;
if (!fields || !fields.length) return [];
const cards = ReactiveCache.getCards({
listId: list._id,
archived: false,
});
const result = fields.map(field => {
let sum = 0;
if (cards && cards.length) {
cards.forEach(card => {
const cfs = (card.customFields || []);
const cf = cfs.find(f => f && f._id === field._id);
if (!cf || cf.value === null || cf.value === undefined) return;
let v = cf.value;
if (typeof v === 'string') {
// try to parse string numbers, accept comma decimal
const parsed = parseFloat(v.replace(',', '.'));
if (isNaN(parsed)) return;
v = parsed;
}
if (typeof v === 'number' && isFinite(v)) {
sum += v;
}
});
}
return {
_id: field._id,
name: field.name,
type: field.type,
settings: field.settings || {},
value: sum,
};
});
return result;
},
openForm(options) {
@ -254,6 +293,22 @@ BlazeComponent.extendComponent({
},
}).register('listBody');
// Helpers for listBody template context
Template.listBody.helpers({
formattedCurrencyCustomFieldValue(val) {
// `this` is the custom field sum object from customFieldsSum each-iteration
const field = this || {};
const code = (field.settings && field.settings.currencyCode) || 'USD';
try {
const n = typeof val === 'number' ? val : parseFloat(val);
if (!isFinite(n)) return val;
return new Intl.NumberFormat(undefined, { style: 'currency', currency: code }).format(n);
} catch (e) {
return `${code} ${val}`;
}
},
});
function toggleValueInReactiveArray(reactiveValue, value) {
const array = reactiveValue.get();
const valueIndex = array.indexOf(value);

View file

@ -26,6 +26,9 @@ template(name="listHeader")
|/#{wipLimit.value})
if showCardsCountForList cards.length
span.cardCount {{cardsCount}} {{cardsCountForListIsOne cards.length}}
if hasNumberFieldsSum
| &nbsp;
span.list-sum-badge(title="{{_ 'sum-of-number-fields'}}") ∑ {{numberFieldsSum}}
else
if collapsed
a.js-collapse(title="{{_ 'uncollapse'}}")
@ -44,6 +47,9 @@ template(name="listHeader")
unless collapsed
if showCardsCountForList cards.length
span.cardCount {{cardsCount}} {{cardsCountForListIsOne cards.length}}
if hasNumberFieldsSum
| &nbsp;
span.list-sum-badge(title="{{_ 'sum-of-number-fields'}}") ∑ {{numberFieldsSum}}
if isMiniScreen
if currentList
if isWatching

View file

@ -142,7 +142,48 @@ BlazeComponent.extendComponent({
Template.listHeader.helpers({
isBoardAdmin() {
return ReactiveCache.getCurrentUser().isBoardAdmin();
}
},
numberFieldsSum() {
const list = Template.currentData();
if (!list) return 0;
const boardId = Session.get('currentBoard');
const fields = ReactiveCache.getCustomFields({
boardIds: { $in: [boardId] },
showSumAtTopOfList: true,
type: 'number',
});
if (!fields || !fields.length) return 0;
const cards = ReactiveCache.getCards({ listId: list._id, archived: false });
let total = 0;
if (cards && cards.length) {
cards.forEach(card => {
const cfs = (card.customFields || []);
fields.forEach(field => {
const cf = cfs.find(f => f && f._id === field._id);
if (!cf || cf.value === null || cf.value === undefined) return;
let v = cf.value;
if (typeof v === 'string') {
const parsed = parseFloat(v.replace(',', '.'));
if (isNaN(parsed)) return;
v = parsed;
}
if (typeof v === 'number' && isFinite(v)) {
total += v;
}
});
});
}
return total;
},
hasNumberFieldsSum() {
const boardId = Session.get('currentBoard');
const fields = ReactiveCache.getCustomFields({
boardIds: { $in: [boardId] },
showSumAtTopOfList: true,
type: 'number',
});
return !!(fields && fields.length);
},
});
Template.listActionPopup.helpers({

View file

@ -3,6 +3,6 @@ template(name="minilist")
class="minicard-{{colorClass}}")
.minicard-title
.handle
.fa.fa-arrows
span.drag-handle(title="{{_ 'dragList'}}") ↕️
+viewer
= title

View file

@ -527,7 +527,7 @@ a:not(.disabled).is-active i.fa {
/* Board canvas */
.board-canvas {
padding: 8px;
padding: 0 8px 8px 0;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
@ -675,7 +675,7 @@ a:not(.disabled).is-active i.fa {
}
.board-canvas {
padding: 12px;
padding: 0 12px 12px 0;
}
#header {
@ -756,7 +756,7 @@ a:not(.disabled).is-active i.fa {
.inline-input {
height: 37px;
margin: 8px 10px 0 0;
width: 50px;
width: 100px;
}
.select-authentication {
width: 100%;

View file

@ -20,10 +20,10 @@
height: 3vw;
}
#notifications-drawer .notification .read-status .activity-type {
margin: 2vh 0 0;
width: 2.2vw;
height: 2.2vw;
font-size: clamp(14px, 2.5vw, 17px);
margin: 8px 0 0;
width: 1.2em;
height: 1.2em;
font-size: clamp(14px, 2vw, 17px);
display: block;
color: #bbb;
}

View file

@ -7,4 +7,4 @@ template(name='notification')
+activity(activity=activityData mode='none')
if read
.remove
a.fa.fa-trash
a(title="{{_ 'delete'}}") 🗑️

View file

@ -1,8 +1,8 @@
template(name='notificationIcon')
if($in activityType 'deleteAttachment' 'addAttachment')
i.fa.fa-paperclip.activity-type(title="attachment")
span.activity-type(title="attachment") 📎
else if($in activityType 'createBoard' 'importBoard')
i.fa.fa-chalkboard.activity-type(title="board")
span.activity-type(title="board") 🗂️
else if($in activityType 'createCard' 'importCard' 'moveCard')
+cardNotificationIcon
@ -19,17 +19,17 @@ template(name='notificationIcon')
//- DRY and consistant
else if($in activityType 'checkedItem' 'uncheckedItem' 'addChecklistItem' 'removedChecklistItem')
i.fa.fa-check-square.activity-type(title="checklist item")
span.activity-type(title="checklist item") ☑️
else if($in activityType 'addComment')
i.fa.fa-comment-o.activity-type(title="comment")
span.activity-type(title="comment") 💬
else if($in activityType 'createCustomField' 'setCustomField' 'unsetCustomField')
i.fa.fa-code.activity-type(title="custom field")
span.activity-type(title="custom field") 🧩
else if($in activityType 'addedLabel' 'removedLabel')
i.fa.fa-tag.activity-type(title="label")
span.activity-type(title="label") 🏷️
else if($in activityType 'a-startAt' 'a-receivedAt')
i.fa.fa-clock-o.activity-type(title="date")
span.activity-type(title="date") ⏰
else if($in activityType 'a-dueAt' 'a-endAt')
i.fa.fa-clock-o.activity-type(title="date")
span.activity-type(title="date") ⏰
else if($in activityType 'createList' 'removeList' 'archivedList')
+listNotificationIcon
@ -41,17 +41,17 @@ template(name='notificationIcon')
//- elswhere in the app we use fa-trello to indicate lists...
//- i personally like fa-columns a bit better
else if($in activityType 'unjoinMember' 'addBoardMember' 'joinMember' 'removeBoardMember')
i.fa.fa-user.activity-type(title="member")
span.activity-type(title="member") 👤
else if($in activityType 'createSwimlane' 'archivedSwimlane')
i.fa.fa-th-large.activity-type(title="swimlane")
span.activity-type(title="swimlane") 🧭
else
i.fa.fa-bug.activity-type(title="can't find icon for #{activityType}")
span.activity-type(title="can't find icon for #{activityType}") 🐞
template(name='cardNotificationIcon')
i.fa.fa-clone.activity-type(title="card")
span.activity-type(title="card") 🗒️
template(name='checklistNotificationIcon')
i.fa.fa-list.activity-type(title="checklist")
span.activity-type(title="checklist") 📝
template(name='listNotificationIcon')
i.fa.fa-columns.activity-type(title="list")
span.activity-type(title="list") 📋

View file

@ -55,9 +55,6 @@ section#notifications-drawer .remove-read {
section#notifications-drawer .remove-read:hover {
color: #eb4646 !important;
}
section#notifications-drawer .remove-read:hover i.fa {
color: inherit;
}
section#notifications-drawer ul.notifications {
display: block;
padding: 0px 16px 0px 16px;

View file

@ -8,7 +8,7 @@ template(name='notificationsDrawer')
h5 {{_ 'notifications'}}
if($gt unreadNotifications 0)
|(#{unreadNotifications})
a.fa.fa-times-thin.close
a.close
ul.notifications
each transformedProfile.notifications
+notification(activityData=activityObj index=dbIndex read=read)
@ -16,5 +16,5 @@ template(name='notificationsDrawer')
a.all-read {{_ 'mark-all-as-read'}}
if ($and ($.Session.get 'showReadNotifications') ($gt readNotifications 0))
a.remove-read
i.fa.fa-trash
| 🗑️
| {{_ 'remove-all-read'}}

View file

@ -36,7 +36,7 @@ template(name="filterSidebar")
else
span.quiet {{_ "label-default" (_ (concat "color-" color))}}
if Filter.labelIds.isSelected _id
i.fa.fa-check
| ✅
hr
h3
| 👥
@ -68,7 +68,7 @@ template(name="filterSidebar")
span.sidebar-list-item-description
| {{_ 'filter-no-assignee'}}
if Filter.assignees.isSelected undefined
i.fa.fa-check
| ✅
each currentBoard.activeMembers
with getUser userId
li(class="{{#if Filter.assignees.isSelected _id}}active{{/if}}")
@ -90,37 +90,37 @@ template(name="filterSidebar")
span.sidebar-list-item-description
| {{_ 'filter-no-due-date' }}
if Filter.dueAt.isSelected 'noDate'
i.fa.fa-check
| ✅
li(class="{{#if Filter.dueAt.isSelected 'past'}}active{{/if}}")
a.name.js-toggle-overdue-filter
span.sidebar-list-item-description
| {{_ 'filter-overdue' }}
if Filter.dueAt.isSelected 'past'
i.fa.fa-check
| ✅
li(class="{{#if Filter.dueAt.isSelected 'today'}}active{{/if}}")
a.name.js-toggle-due-today-filter
span.sidebar-list-item-description
| {{_ 'filter-due-today' }}
if Filter.dueAt.isSelected 'today'
i.fa.fa-check
| ✅
li(class="{{#if Filter.dueAt.isSelected 'tomorrow'}}active{{/if}}")
a.name.js-toggle-due-tomorrow-filter
span.sidebar-list-item-description
| {{_ 'filter-due-tomorrow' }}
if Filter.dueAt.isSelected 'tomorrow'
i.fa.fa-check
| ✅
li(class="{{#if Filter.dueAt.isSelected 'thisweek'}}active{{/if}}")
a.name.js-toggle-due-this-week-filter
span.sidebar-list-item-description
| {{_ 'filter-due-this-week' }}
if Filter.dueAt.isSelected 'thisweek'
i.fa.fa-check
| ✅
li(class="{{#if Filter.dueAt.isSelected 'nextweek'}}active{{/if}}")
a.name.js-toggle-due-next-week-filter
span.sidebar-list-item-description
| {{_ 'filter-due-next-week' }}
if Filter.dueAt.isSelected 'nextweek'
i.fa.fa-check
| ✅
hr
h3
| 📋
@ -138,7 +138,7 @@ template(name="filterSidebar")
span.sidebar-list-item-description
| {{ name }}
if Filter.customFields.isSelected _id
i.fa.fa-check
| ✅
hr
h3
| {{_ 'other-filters-label'}}
@ -148,14 +148,14 @@ template(name="filterSidebar")
span.sidebar-list-item-description
| {{_ 'filter-show-archive'}}
if Filter.archive.isSelected _id
i.fa.fa-check
| ✅
ul.sidebar-list
li(class="{{#if Filter.hideEmpty.isSelected _id}}active{{/if}}")
a.name.js-toggle-hideEmpty-filter
span.sidebar-list-item-description
| {{_ 'filter-hide-empty'}}
if Filter.hideEmpty.isSelected _id
i.fa.fa-check
| ✅
hr
h3 {{_ 'advanced-filter-label'}}
input.js-field-advanced-filter(type="text")

View file

@ -28,23 +28,14 @@ template(name="swimlaneFixedHeader")
unless currentUser.isWorker
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'}}")
// i.fa.fa-arrow-down.swimlane-header-collapse-down
// ⬆️.swimlane-header-collapse-up
//if collapsed
// a.js-collapse-swimlane(title="{{_ 'uncollapse'}}")
// ⬆️.swimlane-header-collapse-up
// i.fa.fa-arrow-down.swimlane-header-collapse-down
unless isTouchScreen
a.swimlane-header-handle.handle.js-swimlane-header-handle
| ↕️
if isTouchScreen
a.swimlane-header-miniscreen-handle.handle.js-swimlane-header-handle
| ↕️
a.js-open-swimlane-menu(title="{{_ 'swimlaneActionPopup-title'}}")
| ☰
template(name="editSwimlaneTitleForm")
.list-composer

View file

@ -54,12 +54,14 @@
width: 100%;
min-width: 100%;
position: relative;
overflow: hidden;
overflow: visible;
min-height: 33px;
padding: 0;
margin: 0;
}
.swimlane .swimlane-header-wrap .swimlane-header {
font-size: 14px;
padding: 5px 5px;
padding: 0;
font-weight: bold;
min-height: 33px;
width: 100%;
@ -74,30 +76,39 @@
}
.swimlane .swimlane-header-wrap .swimlane-header-menu {
position: absolute;
padding: 5px 5px;
top: 0;
left: 0;
padding: 0;
margin: 0;
font-size: 22px;
line-height: 1;
z-index: 20;
pointer-events: auto;
}
.swimlane .swimlane-header-wrap .swimlane-header-menu .js-open-swimlane-menu {
top: calc(50% + 6px);
padding: 5px;
display: inline-block;
margin-left: 74px;
}
@media print {
.swimlane .swimlane-header-wrap .swimlane-header-menu {
display: none;
}
}
.swimlane .swimlane-header-wrap .swimlane-header-plus-icon {
margin-left: 5px;
padding-right: 20px;
top: calc(50% + 6px);
padding: 5px;
font-size: 22px;
}
.swimlane .swimlane-header-wrap .swimlane-header-menu-icon {
padding-right: 20px;
top: calc(50% + 6px);
padding: 5px;
font-size: 22px;
}
.swimlane .swimlane-header-wrap .swimlane-header-handle {
position: absolute;
padding: 7px;
top: 50%;
left: 230px;
top: calc(50% + 2px);
padding: 2px;
font-size: clamp(16px, 3vw, 20px);
transform: translateY(-50%);
display: flex;
@ -109,22 +120,16 @@
}
.swimlane .swimlane-header-wrap .swimlane-header-miniscreen-handle {
position: absolute;
padding: 7px;
top: 50%;
padding: 2px;
top: calc(50% + 2px);
transform: translateY(-50%);
right: 10px;
right: 60px;
font-size: 24px;
cursor: move;
z-index: 15;
pointer-events: auto;
}
/* Safety: ensure wrapper is interactive and above list content */
.swimlane .swimlane-header-wrap {
position: relative;
z-index: 9;
pointer-events: auto;
}
#js-swimlane-height-edit .swimlane-height-error {
display: none;
}

View file

@ -3,8 +3,8 @@
display: block;
position: relative;
float: left;
height: 30px;
width: 30px;
height: clamp(24px, 3.5vw, 36px);
width: clamp(24px, 3.5vw, 36px);
margin: .3vh;
cursor: pointer;
user-select: none;
@ -111,7 +111,7 @@
padding-top: 0;
}
.mini-profile-info .member {
width: 50px;
height: 50px;
width: clamp(40px, 5vw, 60px);
height: clamp(40px, 5vw, 60px);
margin-right: 10px;
}