mirror of
https://github.com/wekan/wekan.git
synced 2026-03-02 20:00:16 +01:00
Resolve merge conflicts by accepting PR #6131 changes
Co-authored-by: xet7 <15545+xet7@users.noreply.github.com>
This commit is contained in:
parent
dc0b68ee80
commit
97dd5d2064
257 changed files with 9483 additions and 14103 deletions
|
|
@ -6,75 +6,80 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
.attachment-gallery {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
display: grid;
|
||||
grid-auto-flow: row;
|
||||
}
|
||||
.attachment-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
display: grid;
|
||||
grid-template-columns: 10ch auto;
|
||||
align-items: center;
|
||||
margin-top: 16px;
|
||||
grid-template-rows: repeat(auto-fit, minmax(1.5lh, auto));
|
||||
justify-content: stretch;
|
||||
gap: 2ch;
|
||||
padding: 2ch;
|
||||
border-radius: 0.6ch;
|
||||
}
|
||||
|
||||
.attachment-item:hover {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
.attachment-thumbnail-container {
|
||||
display: block;
|
||||
width: 150px;
|
||||
min-width: 150px;
|
||||
max-height: 150px;
|
||||
padding-right: 16px;
|
||||
|
||||
.attachment-details-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.attachment-thumbnail-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.attachment-thumbnail {
|
||||
max-width: 150px;
|
||||
max-height: 150px;
|
||||
min-height: 2em;
|
||||
/* more deterministic outcome */
|
||||
aspect-ratio: 1/1;
|
||||
object-fit: cover;
|
||||
max-width: 100%;
|
||||
cursor: pointer;
|
||||
border-radius: 0.4ch;
|
||||
}
|
||||
.attachment-thumbnail-text {
|
||||
min-height: 2em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 2em;
|
||||
cursor: pointer;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.attachment-details-container {
|
||||
display: block;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.attachment-details {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-right: 25px; /* Make sure the icons are not to far to the right */
|
||||
flex: 1;
|
||||
gap: 0.5ch;
|
||||
align-items: center;
|
||||
}
|
||||
.attachment-actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 1.5ch;
|
||||
}
|
||||
.attachment-actions a {
|
||||
margin-left: 16px;
|
||||
}
|
||||
.attachment-actions a:first-child {
|
||||
margin-left: 0;
|
||||
|
||||
body.mobile-mode .attachment-actions {
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.add-attachment {
|
||||
border: 1px dashed #555;
|
||||
border-radius: .5ch;
|
||||
cursor: pointer;
|
||||
aspect-ratio: 1/1;
|
||||
height: 1.5lh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px dashed #555;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
margin-top: 16px;
|
||||
}
|
||||
.icon {
|
||||
font-size: 1.5em;
|
||||
cursor: pointer;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.icon:hover {
|
||||
color: #666;
|
||||
|
|
@ -95,26 +100,25 @@
|
|||
height: 100%;
|
||||
}
|
||||
#viewer-top-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
justify-content: center;
|
||||
justify-items: center;
|
||||
font-size: 2rem;
|
||||
padding: 0.3lh 0.5ch;
|
||||
}
|
||||
#attachment-name {
|
||||
color: white;
|
||||
font-size: 1.5em;
|
||||
max-width: calc(
|
||||
100% - 50px
|
||||
); /* Make sure the name does not overlap the close button */
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
#viewer-close {
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 4em;
|
||||
position: absolute;
|
||||
right: 50px;
|
||||
top: 16px;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
/* Upload progress indicators for drag-and-drop uploads */
|
||||
|
|
@ -122,30 +126,24 @@
|
|||
.card-details-upload-progress {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
margin: 8px 0;
|
||||
font-size: 14px;
|
||||
border-radius: 0.4ch;
|
||||
|
||||
}
|
||||
|
||||
.upload-progress-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
font-weight: bold;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.upload-progress-header i {
|
||||
margin-right: 8px;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.upload-progress-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 8px;
|
||||
padding: 8px;
|
||||
background: white;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #dee2e6;
|
||||
|
|
@ -158,22 +156,17 @@
|
|||
|
||||
.upload-progress-filename {
|
||||
font-weight: 500;
|
||||
margin-bottom: 4px;
|
||||
color: #495057;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.upload-progress-bar {
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
background: #e9ecef;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.upload-progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #007bff, #0056b3);
|
||||
transition: width 0.3s ease;
|
||||
border-radius: 3px;
|
||||
|
|
@ -187,7 +180,6 @@
|
|||
.upload-progress-success {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
|
|
@ -199,47 +191,6 @@
|
|||
color: #28a745;
|
||||
}
|
||||
|
||||
.upload-progress-error i,
|
||||
.upload-progress-success i {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
/* Minicard specific styles */
|
||||
.minicard-upload-progress {
|
||||
margin: 4px 0;
|
||||
padding: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.minicard-upload-progress .upload-progress-item {
|
||||
padding: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.minicard-upload-progress .upload-progress-filename {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* Card details specific styles */
|
||||
.card-details-upload-progress {
|
||||
margin: 12px 0;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.card-details-upload-progress .upload-progress-header {
|
||||
font-size: 16px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.card-details-upload-progress .upload-progress-item {
|
||||
padding: 12px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.card-details-upload-progress .upload-progress-filename {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Drag over state for minicards */
|
||||
.minicard.is-dragging-over {
|
||||
border: 2px dashed #007bff !important;
|
||||
|
|
@ -256,7 +207,6 @@
|
|||
color: white;
|
||||
cursor: pointer;
|
||||
align-self: center;
|
||||
margin: 0 20px;
|
||||
}
|
||||
#prev-attachment {
|
||||
font-size: 4em;
|
||||
|
|
@ -322,7 +272,6 @@
|
|||
position: absolute;
|
||||
bottom: 2.2em;
|
||||
font-size: 1.6em;
|
||||
padding: 16px;
|
||||
}
|
||||
#prev-attachment {
|
||||
left: 0;
|
||||
|
|
@ -356,19 +305,10 @@
|
|||
margin-top: 20%;
|
||||
width: 100%;
|
||||
}
|
||||
.attachment-thumbnail-container {
|
||||
width: 100px;
|
||||
min-width: 100px;
|
||||
}
|
||||
.attachment-thumbnail {
|
||||
max-width: 100px;
|
||||
}
|
||||
.attachment-details {
|
||||
flex-direction: column;
|
||||
margin-right: 0px;
|
||||
}
|
||||
.attachment-actions {
|
||||
flex-direction: row;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,15 +49,11 @@ template(name="attachmentViewer")
|
|||
i.fa.fa-caret-right#next-attachment
|
||||
|
||||
template(name="attachmentGallery")
|
||||
|
||||
if canModifyCard
|
||||
a.add-attachment.js-add-attachment
|
||||
i.fa.fa-plus
|
||||
.attachment-gallery
|
||||
|
||||
if canModifyCard
|
||||
a.attachment-item.add-attachment.js-add-attachment
|
||||
i.fa.fa-plus
|
||||
|
||||
each attachments
|
||||
|
||||
.attachment-item(class="{{#if isAttachmentMigrating _id}}migrating{{/if}}")
|
||||
.attachment-thumbnail-container.open-preview(data-attachment-id="{{_id}}" data-card-id="{{ meta.cardId }}")
|
||||
if link
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
.card-date {
|
||||
display: block;
|
||||
border-radius: 4px;
|
||||
padding: 1px 3px;
|
||||
background-color: #dbdbdb;
|
||||
}
|
||||
|
|
@ -106,6 +105,10 @@
|
|||
background-color: #e6c200;
|
||||
}
|
||||
|
||||
.date a:has(time) {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.card-date.end-date {
|
||||
background-color: #ffb3b3; /* Light red for end */
|
||||
color: #000; /* Black text for end */
|
||||
|
|
@ -139,6 +142,6 @@
|
|||
}
|
||||
.customfield-date {
|
||||
display: block;
|
||||
border-radius: 4px;
|
||||
border-radius: 0.4ch;
|
||||
padding: 1px 3px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
import { TAPi18n } from '/imports/i18n';
|
||||
import { DatePicker } from '/client/lib/datepicker';
|
||||
import {
|
||||
formatDateTime,
|
||||
formatDate,
|
||||
import {
|
||||
formatDateTime,
|
||||
formatDate,
|
||||
formatDateByUserPreference,
|
||||
formatTime,
|
||||
getISOWeek,
|
||||
isValidDate,
|
||||
isBefore,
|
||||
isAfter,
|
||||
isSame,
|
||||
add,
|
||||
subtract,
|
||||
startOf,
|
||||
endOf,
|
||||
format,
|
||||
parseDate,
|
||||
now,
|
||||
createDate,
|
||||
fromNow,
|
||||
formatTime,
|
||||
getISOWeek,
|
||||
isValidDate,
|
||||
isBefore,
|
||||
isAfter,
|
||||
isSame,
|
||||
add,
|
||||
subtract,
|
||||
startOf,
|
||||
endOf,
|
||||
format,
|
||||
parseDate,
|
||||
now,
|
||||
createDate,
|
||||
fromNow,
|
||||
calendar,
|
||||
diff
|
||||
} from '/imports/lib/dateUtils';
|
||||
|
|
@ -143,7 +143,7 @@ class CardReceivedDate extends CardDate {
|
|||
const startAt = this.data().getStart();
|
||||
const theDate = this.date.get();
|
||||
const now = this.now.get();
|
||||
|
||||
|
||||
// Received date logic: if received date is after start, due, or end dates, it's overdue
|
||||
if (
|
||||
(startAt && isAfter(theDate, startAt)) ||
|
||||
|
|
@ -187,7 +187,7 @@ class CardStartDate extends CardDate {
|
|||
const endAt = this.data().getEnd();
|
||||
const theDate = this.date.get();
|
||||
const now = this.now.get();
|
||||
|
||||
|
||||
// Start date logic: if start date is after due or end dates, it's overdue
|
||||
if ((endAt && isAfter(theDate, endAt)) || (dueAt && isAfter(theDate, dueAt))) {
|
||||
classes += 'overdue';
|
||||
|
|
@ -230,7 +230,7 @@ class CardDueDate extends CardDate {
|
|||
const endAt = this.data().getEnd();
|
||||
const theDate = this.date.get();
|
||||
const now = this.now.get();
|
||||
|
||||
|
||||
// If there's an end date and it's before the due date, task is completed early
|
||||
if (endAt && isBefore(endAt, theDate)) {
|
||||
classes += 'completed-early';
|
||||
|
|
@ -242,7 +242,7 @@ class CardDueDate extends CardDate {
|
|||
// Due date logic based on current time
|
||||
else {
|
||||
const daysDiff = diff(theDate, now, 'days');
|
||||
|
||||
|
||||
if (daysDiff < 0) {
|
||||
// Due date is in the past - overdue
|
||||
classes += 'overdue';
|
||||
|
|
@ -254,7 +254,7 @@ class CardDueDate extends CardDate {
|
|||
classes += 'not-due';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
|
|
@ -286,7 +286,7 @@ class CardEndDate extends CardDate {
|
|||
let classes = 'end-date ';
|
||||
const dueAt = this.data().getDue();
|
||||
const theDate = this.date.get();
|
||||
|
||||
|
||||
if (!dueAt) {
|
||||
// No due date set - just show as completed
|
||||
classes += 'completed';
|
||||
|
|
@ -371,7 +371,7 @@ CardCustomFieldDate.register('cardCustomFieldDate');
|
|||
template() {
|
||||
return 'minicardReceivedDate';
|
||||
}
|
||||
|
||||
|
||||
showDate() {
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||
|
|
@ -383,7 +383,7 @@ CardCustomFieldDate.register('cardCustomFieldDate');
|
|||
template() {
|
||||
return 'minicardStartDate';
|
||||
}
|
||||
|
||||
|
||||
showDate() {
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||
|
|
@ -395,7 +395,7 @@ CardCustomFieldDate.register('cardCustomFieldDate');
|
|||
template() {
|
||||
return 'minicardDueDate';
|
||||
}
|
||||
|
||||
|
||||
showDate() {
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||
|
|
@ -407,7 +407,7 @@ CardCustomFieldDate.register('cardCustomFieldDate');
|
|||
template() {
|
||||
return 'minicardEndDate';
|
||||
}
|
||||
|
||||
|
||||
showDate() {
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||
|
|
@ -419,7 +419,7 @@ CardCustomFieldDate.register('cardCustomFieldDate');
|
|||
template() {
|
||||
return 'minicardCustomFieldDate';
|
||||
}
|
||||
|
||||
|
||||
showDate() {
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
.new-description {
|
||||
position: relative;
|
||||
margin: 0 0 20px 0;
|
||||
flex: 1;
|
||||
}
|
||||
.new-description.is-open .helper {
|
||||
display: inline-block;
|
||||
}
|
||||
.new-description.is-open textarea {
|
||||
min-height: 100px;
|
||||
.new-description textarea {
|
||||
min-height: 1lh;
|
||||
color: #4d4d4d;
|
||||
cursor: auto;
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
.new-description .too-long {
|
||||
margin-top: 8px;
|
||||
|
|
@ -19,9 +15,6 @@
|
|||
background-color: #fff;
|
||||
border: 0;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.23);
|
||||
height: 36px;
|
||||
margin: 4px 4px 6px 0;
|
||||
padding: 9px 11px;
|
||||
width: 100%;
|
||||
}
|
||||
.new-description textarea:hover,
|
||||
|
|
@ -39,16 +32,12 @@
|
|||
border: 0;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.23);
|
||||
color: #8c8c8c;
|
||||
height: 36px;
|
||||
margin: 4px 4px 6px 0;
|
||||
width: 92%;
|
||||
}
|
||||
.description-item:hover {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
.description-item.add-description {
|
||||
display: flex;
|
||||
margin: 5px;
|
||||
}
|
||||
.description-item.add-description a {
|
||||
display: block;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -2,25 +2,25 @@ import { ReactiveCache } from '/imports/reactiveCache';
|
|||
import { TAPi18n } from '/imports/i18n';
|
||||
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
|
||||
import { DatePicker } from '/client/lib/datepicker';
|
||||
import {
|
||||
formatDateTime,
|
||||
formatDate,
|
||||
formatTime,
|
||||
getISOWeek,
|
||||
isValidDate,
|
||||
isBefore,
|
||||
isAfter,
|
||||
isSame,
|
||||
add,
|
||||
subtract,
|
||||
startOf,
|
||||
endOf,
|
||||
format,
|
||||
parseDate,
|
||||
now,
|
||||
createDate,
|
||||
fromNow,
|
||||
calendar
|
||||
import {
|
||||
formatDateTime,
|
||||
formatDate,
|
||||
formatTime,
|
||||
getISOWeek,
|
||||
isValidDate,
|
||||
isBefore,
|
||||
isAfter,
|
||||
isSame,
|
||||
add,
|
||||
subtract,
|
||||
startOf,
|
||||
endOf,
|
||||
format,
|
||||
parseDate,
|
||||
now,
|
||||
createDate,
|
||||
fromNow,
|
||||
calendar
|
||||
} from '/imports/lib/dateUtils';
|
||||
import Cards from '/models/cards';
|
||||
import Boards from '/models/boards';
|
||||
|
|
@ -35,6 +35,7 @@ import { DialogWithBoardSwimlaneList } from '/client/lib/dialogWithBoardSwimlane
|
|||
import { DialogWithBoardSwimlaneListCard } from '/client/lib/dialogWithBoardSwimlaneListCard';
|
||||
import { handleFileUpload } from './attachments';
|
||||
import uploadProgressManager from '../../lib/uploadProgressManager';
|
||||
import PopupComponent from '../main/popup';
|
||||
|
||||
const subManager = new SubsManager();
|
||||
const { calculateIndexData } = Utils;
|
||||
|
|
@ -60,19 +61,8 @@ BlazeComponent.extendComponent({
|
|||
onCreated() {
|
||||
this.currentBoard = Utils.getCurrentBoard();
|
||||
this.isLoaded = new ReactiveVar(false);
|
||||
this.dep = new Tracker.Dependency();
|
||||
|
||||
if (this.parentComponent() && this.parentComponent().parentComponent()) {
|
||||
const boardBody = this.parentComponent().parentComponent();
|
||||
//in Miniview parent is Board, not BoardBody.
|
||||
if (boardBody !== null) {
|
||||
// Only show overlay in mobile mode, not in desktop mode
|
||||
const isMobile = Utils.getMobileMode();
|
||||
if (isMobile) {
|
||||
boardBody.showOverlay.set(true);
|
||||
}
|
||||
boardBody.mouseHasEnterCardDetails = false;
|
||||
}
|
||||
}
|
||||
this.calculateNextPeak();
|
||||
|
||||
Meteor.subscribe('unsaved-edits');
|
||||
|
|
@ -85,6 +75,18 @@ BlazeComponent.extendComponent({
|
|||
// });
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
const boardOverlay = document.getElementsByClassName('board-overlay')?.[0];
|
||||
this.boardBody = BlazeComponent.getComponentForElement(boardOverlay);
|
||||
if (this.boardBody) {
|
||||
this.boardBody.mouseHasEnterCardDetails = false;
|
||||
}
|
||||
const isMobile = Utils.getMobileMode();
|
||||
if (isMobile && Session.get('currentCard')) {
|
||||
//this.boardBody?.showOverlay.set(true);
|
||||
}
|
||||
},
|
||||
|
||||
isWatching() {
|
||||
const card = this.currentData();
|
||||
if (!card || typeof card.findWatcher !== 'function') return false;
|
||||
|
|
@ -95,8 +97,8 @@ BlazeComponent.extendComponent({
|
|||
return ReactiveCache.getCurrentUser().hasCustomFieldsGrid();
|
||||
},
|
||||
|
||||
|
||||
cardMaximized() {
|
||||
this.dep.depend();
|
||||
return !Utils.getPopupCardId() && ReactiveCache.getCurrentUser().hasCardMaximized();
|
||||
},
|
||||
|
||||
|
|
@ -175,6 +177,11 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
onRendered() {
|
||||
// #FIXME hackish; if accepted tweak static funcs
|
||||
if (this.cardMaximized()) {
|
||||
PopupComponent.maximize({target: this.firstNode()});
|
||||
}
|
||||
|
||||
if (Meteor.settings.public.CARD_OPENED_WEBHOOK_ENABLED) {
|
||||
// Send Webhook but not create Activities records ---
|
||||
const card = this.currentData();
|
||||
|
|
@ -209,11 +216,11 @@ BlazeComponent.extendComponent({
|
|||
}
|
||||
|
||||
const $checklistsDom = this.$('.card-checklist-items');
|
||||
|
||||
const sortableSelector = Utils.isMiniScreen() ? '.checklist-handle' : '.checklist-title';
|
||||
$checklistsDom.sortable({
|
||||
tolerance: 'pointer',
|
||||
helper: 'clone',
|
||||
handle: '.checklist-title',
|
||||
handle: sortableSelector,
|
||||
items: '.js-checklist',
|
||||
placeholder: 'checklist placeholder',
|
||||
distance: 7,
|
||||
|
|
@ -282,6 +289,8 @@ BlazeComponent.extendComponent({
|
|||
return ReactiveCache.getCurrentUser()?.isBoardMember();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Disable sorting if the current user is not a board member
|
||||
this.autorun(() => {
|
||||
const disabled = !userIsMember();
|
||||
|
|
@ -289,10 +298,7 @@ BlazeComponent.extendComponent({
|
|||
$checklistsDom.data('uiSortable') ||
|
||||
$checklistsDom.data('sortable')
|
||||
) {
|
||||
$checklistsDom.sortable('option', 'disabled', disabled);
|
||||
if (Utils.isTouchScreenOrShowDesktopDragHandles()) {
|
||||
$checklistsDom.sortable({ handle: '.checklist-handle' });
|
||||
}
|
||||
$checklistsDom.sortable('option', 'handle', sortableSelector);
|
||||
}
|
||||
if ($subtasksDom.data('uiSortable') || $subtasksDom.data('sortable')) {
|
||||
$subtasksDom.sortable('option', 'disabled', disabled);
|
||||
|
|
@ -301,11 +307,7 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
onDestroyed() {
|
||||
if (this.parentComponent() === null) return;
|
||||
const parentComponent = this.parentComponent().parentComponent();
|
||||
//on mobile view parent is Board, not board body.
|
||||
if (parentComponent === null) return;
|
||||
parentComponent.showOverlay.set(false);
|
||||
this.boardBody?.showOverlay.set(false);
|
||||
},
|
||||
|
||||
events() {
|
||||
|
|
@ -332,59 +334,11 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
'mousedown .js-card-drag-handle'(event) {
|
||||
event.preventDefault();
|
||||
const $card = $(event.target).closest('.card-details');
|
||||
const startX = event.clientX;
|
||||
const startY = event.clientY;
|
||||
const startLeft = $card.offset().left;
|
||||
const startTop = $card.offset().top;
|
||||
|
||||
const onMouseMove = (e) => {
|
||||
const deltaX = e.clientX - startX;
|
||||
const deltaY = e.clientY - startY;
|
||||
$card.css({
|
||||
left: startLeft + deltaX + 'px',
|
||||
top: startTop + deltaY + 'px'
|
||||
});
|
||||
};
|
||||
|
||||
const onMouseUp = () => {
|
||||
$(document).off('mousemove', onMouseMove);
|
||||
$(document).off('mouseup', onMouseUp);
|
||||
};
|
||||
|
||||
$(document).on('mousemove', onMouseMove);
|
||||
$(document).on('mouseup', onMouseUp);
|
||||
PopupComponent.toFront(event);
|
||||
},
|
||||
'mousedown .js-card-title-drag-handle'(event) {
|
||||
// Allow dragging from title for ReadOnly users
|
||||
// Don't interfere with text selection
|
||||
if (event.target.tagName === 'A' || $(event.target).closest('a').length > 0) {
|
||||
return; // Don't drag if clicking on links
|
||||
}
|
||||
|
||||
'click .js-card-send-to-back'(event) {
|
||||
event.preventDefault();
|
||||
const $card = $(event.target).closest('.card-details');
|
||||
const startX = event.clientX;
|
||||
const startY = event.clientY;
|
||||
const startLeft = $card.offset().left;
|
||||
const startTop = $card.offset().top;
|
||||
|
||||
const onMouseMove = (e) => {
|
||||
const deltaX = e.clientX - startX;
|
||||
const deltaY = e.clientY - startY;
|
||||
$card.css({
|
||||
left: startLeft + deltaX + 'px',
|
||||
top: startTop + deltaY + 'px'
|
||||
});
|
||||
};
|
||||
|
||||
const onMouseUp = () => {
|
||||
$(document).off('mousemove', onMouseMove);
|
||||
$(document).off('mouseup', onMouseUp);
|
||||
};
|
||||
|
||||
$(document).on('mousemove', onMouseMove);
|
||||
$(document).on('mouseup', onMouseUp);
|
||||
PopupComponent.toBack(event);
|
||||
},
|
||||
'click .js-close-card-details'() {
|
||||
// Get board ID from either the card data or current board in session
|
||||
|
|
@ -392,26 +346,21 @@ BlazeComponent.extendComponent({
|
|||
const boardId = (card && card.boardId) || Utils.getCurrentBoard()._id;
|
||||
const cardId = card && card._id;
|
||||
|
||||
if (boardId) {
|
||||
// In desktop mode, remove from openCards array
|
||||
const isMobile = Utils.getMobileMode();
|
||||
if (!isMobile && cardId) {
|
||||
const openCards = Session.get('openCards') || [];
|
||||
const filtered = openCards.filter(id => id !== cardId);
|
||||
Session.set('openCards', filtered);
|
||||
|
||||
// If this was the current card, clear it
|
||||
if (Session.get('currentCard') === cardId) {
|
||||
Session.set('currentCard', null);
|
||||
}
|
||||
// Don't navigate away in desktop mode - just close the card
|
||||
return;
|
||||
if (boardId && cardId) {
|
||||
const openCards = Session.get('openCards') || [];
|
||||
const filtered = openCards.filter(id => id !== cardId);
|
||||
// If this was the current card, clear it
|
||||
if (openCards.length === filtered.length) {
|
||||
Session.set('currentCard', null);
|
||||
}
|
||||
else {
|
||||
Session.set('currentCard', filtered[0]);
|
||||
}
|
||||
Session.set('openCards', filtered);
|
||||
|
||||
// Mobile mode: Clear the current card session to close the card
|
||||
Session.set('currentCard', null);
|
||||
|
||||
// Navigate back to board without card
|
||||
// Navigate back to board without card: must be done at the time of writing
|
||||
// otherwise the route for the card is disabled until another
|
||||
// card is opened
|
||||
const board = ReactiveCache.getBoard(boardId);
|
||||
if (board) {
|
||||
FlowRouter.go('board', {
|
||||
|
|
@ -434,34 +383,6 @@ BlazeComponent.extendComponent({
|
|||
Meteor.call('changeDateFormat', dateFormat);
|
||||
},
|
||||
'click .js-open-card-details-menu': Popup.open('cardDetailsActions'),
|
||||
// Mobile: switch to desktop popup view (maximize)
|
||||
'click .js-mobile-switch-to-desktop'(event) {
|
||||
event.preventDefault();
|
||||
// Switch global mode to desktop so the card appears as desktop popup
|
||||
Utils.setMobileMode(false);
|
||||
},
|
||||
'click .js-card-zoom-in'(event) {
|
||||
event.preventDefault();
|
||||
const current = Utils.getCardZoom();
|
||||
const newZoom = Math.min(3.0, current + 0.1);
|
||||
Utils.setCardZoom(newZoom);
|
||||
},
|
||||
'click .js-card-zoom-out'(event) {
|
||||
event.preventDefault();
|
||||
const current = Utils.getCardZoom();
|
||||
const newZoom = Math.max(0.5, current - 0.1);
|
||||
Utils.setCardZoom(newZoom);
|
||||
},
|
||||
'click .js-card-mobile-desktop-toggle'(event) {
|
||||
event.preventDefault();
|
||||
const currentMode = Utils.getMobileMode();
|
||||
Utils.setMobileMode(!currentMode);
|
||||
},
|
||||
'click .js-card-mobile-desktop-toggle'(event) {
|
||||
event.preventDefault();
|
||||
const currentMode = Utils.getMobileMode();
|
||||
Utils.setMobileMode(!currentMode);
|
||||
},
|
||||
async 'submit .js-card-description'(event) {
|
||||
event.preventDefault();
|
||||
const description = this.currentComponent().getValue();
|
||||
|
|
@ -525,7 +446,7 @@ BlazeComponent.extendComponent({
|
|||
'click .js-add-members': Popup.open('cardMembers'),
|
||||
'click .js-assignee': Popup.open('cardAssignee'),
|
||||
'click .js-add-assignees': Popup.open('cardAssignees'),
|
||||
'click .js-add-labels': Popup.open('cardLabels'),
|
||||
'click .js-add-labels'(event) {Popup.open('cardLabels')(event, { dataContextIfCurrentDataIsUndefined: this.currentData() })},
|
||||
'click .js-received-date': Popup.open('editCardReceivedDate'),
|
||||
'click .js-start-date': Popup.open('editCardStartDate'),
|
||||
'click .js-due-date': Popup.open('editCardDueDate'),
|
||||
|
|
@ -534,12 +455,10 @@ BlazeComponent.extendComponent({
|
|||
'click .js-show-negative-votes': Popup.open('negativeVoteMembers'),
|
||||
'click .js-custom-fields': Popup.open('cardCustomFields'),
|
||||
'mouseenter .js-card-details'() {
|
||||
if (this.parentComponent() === null) return;
|
||||
const parentComponent = this.parentComponent().parentComponent();
|
||||
//on mobile view parent is Board, not BoardBody.
|
||||
if (parentComponent === null) return;
|
||||
parentComponent.showOverlay.set(true);
|
||||
parentComponent.mouseHasEnterCardDetails = true;
|
||||
if (this.boardBody) {
|
||||
this.boardBody.showOverlay.set(true);
|
||||
this.boardBody.mouseHasEnterCardDetails = true;
|
||||
}
|
||||
},
|
||||
'mousedown .js-card-details'() {
|
||||
Session.set('cardDetailsIsDragging', false);
|
||||
|
|
@ -560,13 +479,13 @@ BlazeComponent.extendComponent({
|
|||
'click #toggleCustomFieldsGridButton'() {
|
||||
Meteor.call('toggleCustomFieldsGrid');
|
||||
},
|
||||
'click .js-maximize-card-details'() {
|
||||
'click .js-maximize-card-details'(e) {
|
||||
PopupComponent.maximize(e);
|
||||
Meteor.call('toggleCardMaximized');
|
||||
autosize($('.card-details'));
|
||||
},
|
||||
'click .js-minimize-card-details'() {
|
||||
'click .js-minimize-card-details'(e) {
|
||||
PopupComponent.minimize(e);
|
||||
Meteor.call('toggleCardMaximized');
|
||||
autosize($('.card-details'));
|
||||
},
|
||||
'click .js-vote'(e) {
|
||||
const forIt = $(e.target).hasClass('js-vote-positive');
|
||||
|
|
@ -737,16 +656,6 @@ Template.cardDetails.helpers({
|
|||
return uploadProgressManager.getUploadCountForCard(this._id);
|
||||
}
|
||||
});
|
||||
Template.cardDetailsPopup.onDestroyed(() => {
|
||||
Session.delete('popupCardId');
|
||||
Session.delete('popupCardBoardId');
|
||||
});
|
||||
Template.cardDetailsPopup.helpers({
|
||||
popupCard() {
|
||||
const ret = Utils.getPopupCard();
|
||||
return ret;
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
template() {
|
||||
|
|
@ -883,9 +792,7 @@ Template.cardDetailsActionsPopup.events({
|
|||
'click .js-toggle-watch-card'() {
|
||||
const currentCard = this;
|
||||
const level = currentCard.findWatcher(Meteor.userId()) ? null : 'watching';
|
||||
Meteor.call('watch', 'card', currentCard._id, level, (err, ret) => {
|
||||
if (!err && ret) Popup.close();
|
||||
});
|
||||
Meteor.call('watch', 'card', currentCard._id, level)
|
||||
},
|
||||
'click .js-toggle-show-list-on-minicard'() {
|
||||
const currentCard = this;
|
||||
|
|
@ -896,9 +803,6 @@ Template.cardDetailsActionsPopup.events({
|
|||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onRendered() {
|
||||
autosize(this.$('textarea.js-edit-card-title'));
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
|
|
@ -979,10 +883,6 @@ const filterMembers = (filterTerm) => {
|
|||
return members;
|
||||
}
|
||||
|
||||
Template.editCardRequesterForm.onRendered(function () {
|
||||
autosize(this.$('.js-edit-card-requester'));
|
||||
});
|
||||
|
||||
Template.editCardRequesterForm.events({
|
||||
'keydown .js-edit-card-requester'(event) {
|
||||
// If enter key was pressed, submit the data
|
||||
|
|
@ -992,10 +892,6 @@ Template.editCardRequesterForm.events({
|
|||
},
|
||||
});
|
||||
|
||||
Template.editCardAssignerForm.onRendered(function () {
|
||||
autosize(this.$('.js-edit-card-assigner'));
|
||||
});
|
||||
|
||||
Template.editCardAssignerForm.events({
|
||||
'keydown .js-edit-card-assigner'(event) {
|
||||
// If enter key was pressed, submit the data
|
||||
|
|
@ -1469,13 +1365,13 @@ BlazeComponent.extendComponent({
|
|||
'DD/MM/YYYY HH:mm',
|
||||
'DD-MM-YYYY HH:mm'
|
||||
];
|
||||
|
||||
|
||||
let parsedDate = null;
|
||||
for (const format of formats) {
|
||||
parsedDate = parseDate(dateString, [format], true);
|
||||
if (parsedDate) break;
|
||||
}
|
||||
|
||||
|
||||
// Fallback to native Date parsing
|
||||
if (!parsedDate) {
|
||||
parsedDate = new Date(dateString);
|
||||
|
|
@ -1721,13 +1617,13 @@ BlazeComponent.extendComponent({
|
|||
'DD/MM/YYYY HH:mm',
|
||||
'DD-MM-YYYY HH:mm'
|
||||
];
|
||||
|
||||
|
||||
let parsedDate = null;
|
||||
for (const format of formats) {
|
||||
parsedDate = parseDate(dateString, [format], true);
|
||||
if (parsedDate) break;
|
||||
}
|
||||
|
||||
|
||||
// Fallback to native Date parsing
|
||||
if (!parsedDate) {
|
||||
parsedDate = new Date(dateString);
|
||||
|
|
@ -1905,9 +1801,6 @@ EscapeActions.register(
|
|||
() => {
|
||||
return !Session.equals('currentCard', null);
|
||||
},
|
||||
{
|
||||
noClickEscapeOn: '.js-card-details,.board-sidebar,#header',
|
||||
},
|
||||
);
|
||||
|
||||
Template.cardAssigneesPopup.onCreated(function () {
|
||||
|
|
@ -1985,3 +1878,16 @@ Template.cardAssigneePopup.events({
|
|||
},
|
||||
'click .js-edit-profile': Popup.open('editProfile'),
|
||||
});
|
||||
|
||||
Template.cardDetailsPopup.helpers({
|
||||
popupArgs() {
|
||||
return {
|
||||
name: "cardDetails",
|
||||
showHeader: false,
|
||||
closeDOMs: ["click .js-close-card-details"],
|
||||
followDOM: ".card-details",
|
||||
handleDOM: ".card-header-middle",
|
||||
closeVar: "currentCard"
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
.card-time {
|
||||
display: block;
|
||||
border-radius: 4px;
|
||||
border-radius: 0.4ch;
|
||||
padding: 1px 3px;
|
||||
color: #fff;
|
||||
background-color: #dbdbdb;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
textarea.js-add-checklist-item,
|
||||
textarea.js-edit-checklist-item {
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
resize: none;
|
||||
height: 34px;
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ textarea.js-edit-checklist-item {
|
|||
.js-convert-checklist-item-to-card {
|
||||
color: #8c8c8c;
|
||||
text-decoration: underline;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
float: right;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ textarea.js-edit-checklist-item {
|
|||
.checklists-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.checklist-progress-bar-container {
|
||||
display: flex;
|
||||
|
|
@ -35,7 +36,7 @@ textarea.js-edit-checklist-item {
|
|||
margin-right: 10px;
|
||||
}
|
||||
.checklist-progress-bar-container .checklist-progress-bar {
|
||||
width: 80%;
|
||||
flex: 1;
|
||||
height: 10px;
|
||||
background-color: #e0e0e0;
|
||||
border-radius: 16px;
|
||||
|
|
@ -47,19 +48,29 @@ textarea.js-edit-checklist-item {
|
|||
border-radius: 16px;
|
||||
height: 100%;
|
||||
}
|
||||
.checklist-title {
|
||||
padding: 10px;
|
||||
|
||||
.checklist-controls {
|
||||
display: flex;
|
||||
gap: 0.25lh;
|
||||
}
|
||||
|
||||
.checklist-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.checklist-title .checkbox {
|
||||
float: left;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
font-size: 18px;
|
||||
|
||||
line-height: 30px;
|
||||
}
|
||||
.checklist-title .title {
|
||||
font-size: 18px;
|
||||
line-height: 25px;
|
||||
.checklist-title p, .title {
|
||||
font-size: 1em;
|
||||
line-height: 1;
|
||||
margin: 0;
|
||||
}
|
||||
.checklist-title .checklist-stat {
|
||||
margin: 0 0.5em;
|
||||
|
|
@ -79,29 +90,31 @@ textarea.js-edit-checklist-item {
|
|||
bottom: -600px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.checklist {
|
||||
background: #f7f7f7;
|
||||
padding: 0.5lh;
|
||||
margin: 0.5lh 0;
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
|
||||
.checklist.placeholder {
|
||||
background: #ccc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.checklist.ui-sortable-helper {
|
||||
box-shadow: -2px 2px 8px rgba(0,0,0,0.3), 0 0 1px rgba(0,0,0,0.5);
|
||||
transform: rotate(4deg);
|
||||
cursor: grabbing;
|
||||
}
|
||||
.checklist-item {
|
||||
margin: 0 0 0 0.1em;
|
||||
line-height: 18px;
|
||||
font-size: 1.1em;
|
||||
margin-top: 3px;
|
||||
display: flex;
|
||||
background: #f7f7f7;
|
||||
gap: 0.25lh;
|
||||
opacity: 1;
|
||||
transition: height 0ms 400ms, opacity 400ms 0ms;
|
||||
height: auto;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
min-height: 1.5lh;
|
||||
padding: 0 1ch;
|
||||
}
|
||||
.checklist-item.is-checked.invisible {
|
||||
opacity: 0;
|
||||
|
|
@ -114,26 +127,21 @@ textarea.js-edit-checklist-item {
|
|||
background: #ccc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.checklist-item.ui-sortable-helper {
|
||||
box-shadow: -2px 2px 8px rgba(0,0,0,0.3), 0 0 1px rgba(0,0,0,0.5);
|
||||
transform: rotate(4deg);
|
||||
cursor: grabbing;
|
||||
}
|
||||
.checklist-item:hover {
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
.checklist-item .check-box-container {
|
||||
padding-right: 10px;
|
||||
}
|
||||
.checklist-item .check-box {
|
||||
margin: 0.1em 0 0 0;
|
||||
}
|
||||
.checklist-item .check-box.is-checked {
|
||||
border-bottom: 2px solid #3cb500;
|
||||
border-right: 2px solid #3cb500;
|
||||
border-bottom: 0.2ch solid #3cb500;
|
||||
border-right: 0.2ch solid #3cb500;
|
||||
}
|
||||
.checklist-item .item-title {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
flex: 1;
|
||||
cursor: grab;
|
||||
}
|
||||
.checklist-item .item-title.is-checked {
|
||||
color: #8c8c8c;
|
||||
|
|
@ -141,27 +149,18 @@ textarea.js-edit-checklist-item {
|
|||
text-decoration: line-through;
|
||||
}
|
||||
.checklist-item .item-title .viewer p {
|
||||
margin-bottom: 2px;
|
||||
display: block;
|
||||
word-wrap: break-word;
|
||||
display: flex;
|
||||
overflow-wrap: break-word;
|
||||
max-width: 420px;
|
||||
}
|
||||
.checklist-item span.fa.checklistitem-handle {
|
||||
padding-top: 2px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.js-delete-checklist-item,
|
||||
.js-convert-checklist-item-to-card {
|
||||
margin: 0 0 0.5em 1.33em;
|
||||
padding: 12px 0 0 0;
|
||||
}
|
||||
.add-checklist-item {
|
||||
margin: 0.2em 0 0.5em 1.33em;
|
||||
}
|
||||
.add-checklist-item.js-open-inlined-form,
|
||||
.add-checklist.js-open-inlined-form {
|
||||
display: block;
|
||||
width: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
.add-checklist-item.js-open-inlined-form:hover,
|
||||
.add-checklist.js-open-inlined-form:hover {
|
||||
|
|
@ -169,25 +168,13 @@ textarea.js-edit-checklist-item {
|
|||
color: #222;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.2);
|
||||
}
|
||||
.add-checklist-top {
|
||||
/* more space to checklists title */
|
||||
padding-left: 20px;
|
||||
/* + is easier clickable */
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.add-checklist-top.js-open-inlined-form:hover {
|
||||
background: #dbdbdb;
|
||||
color: #222;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.2);
|
||||
}
|
||||
.card-details-item-title {
|
||||
/* max width for adding checklist at top */
|
||||
width: 100%;
|
||||
}
|
||||
.checklist-details-menu {
|
||||
float: right;
|
||||
padding: 6px 10px 6px 10px;
|
||||
}
|
||||
|
||||
.edit-controls label.toggle-label {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,26 +36,31 @@ template(name="checklistDetail")
|
|||
+editChecklistItemForm(checklist = checklist)
|
||||
else
|
||||
.checklist-title
|
||||
span
|
||||
if canModifyCard
|
||||
a.fa.fa-navicon.checklist-details-menu.js-open-checklist-details-menu(title="{{_ 'checklistActionsPopup-title'}}")
|
||||
|
||||
if canModifyCard
|
||||
h4.title.js-open-inlined-form.is-editable
|
||||
if isTouchScreenOrShowDesktopDragHandles
|
||||
span.fa.checklist-handle(class="fa-arrows" title="{{_ 'dragChecklist'}}")
|
||||
h4.title
|
||||
if canModifyCard
|
||||
a.js-open-inlined-form.is-editable(title="{{_ 'moveChecklistPopup-title'}}")
|
||||
+viewer
|
||||
= checklist.title
|
||||
else
|
||||
+viewer
|
||||
= checklist.title
|
||||
else
|
||||
h4.title
|
||||
+viewer
|
||||
.checklist-controls
|
||||
if canModifyCard
|
||||
a.fa.fa-navicon.checklist-details-menu.js-open-checklist-details-menu(title="{{_ 'checklistActionsPopup-title'}}")
|
||||
if isMiniScreen
|
||||
span.fa.checklist-handle(class="fa-arrows" title="{{_ 'dragChecklist'}}")
|
||||
+viewer
|
||||
= checklist.title
|
||||
|
||||
if $gt finishedPercent 0
|
||||
.checklist-progress-bar-container
|
||||
.checklist-progress-text {{finishedPercent}}%
|
||||
.checklist-progress-bar
|
||||
//- jumps where checking the first item is not comfortable;
|
||||
//- so try to show it anytime. also, it helps to separate the checklists.
|
||||
.checklist-progress-bar-container
|
||||
.checklist-progress-text {{finishedPercent}}%
|
||||
.checklist-progress-bar
|
||||
if $gt finishedPercent 0
|
||||
.checklist-progress(style="width:{{finishedPercent}}%")
|
||||
else
|
||||
.checklist-progress(style="visibility:hidden")
|
||||
+checklistItems(checklist = checklist card = card)
|
||||
|
||||
template(name="checklistDeletePopup")
|
||||
|
|
@ -64,7 +69,7 @@ template(name="checklistDeletePopup")
|
|||
|
||||
template(name="addChecklistItemForm")
|
||||
a.fa.fa-copy(title="{{_ 'copy-text-to-clipboard'}}")
|
||||
span.copied-tooltip {{_ 'copied'}}
|
||||
span.copied-tooltip.copied-tooltip-hidden {{_ 'copied'}}
|
||||
textarea.js-add-checklist-item(rows='1' autofocus)
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-add-checklist-item-form(type="submit") {{_ 'save'}}
|
||||
|
|
@ -73,16 +78,12 @@ template(name="addChecklistItemForm")
|
|||
.material-toggle-switch(title="{{_ 'newlineBecomesNewChecklistItem'}}")
|
||||
input.toggle-switch(type="checkbox" id="toggleNewlineBecomesNewChecklistItem")
|
||||
label.toggle-label(for="toggleNewlineBecomesNewChecklistItem")
|
||||
span.toggle-switch-desc
|
||||
| {{_ 'newLineNewItem'}}
|
||||
if $eq position 'top'
|
||||
.material-toggle-switch(title="{{_ 'newlineBecomesNewChecklistItemOriginOrder'}}")
|
||||
input.toggle-switch(type="checkbox" id="toggleNewlineBecomesNewChecklistItemOriginOrder")
|
||||
label.toggle-label(for="toggleNewlineBecomesNewChecklistItemOriginOrder")
|
||||
| {{_ 'originOrder'}}
|
||||
|
||||
template(name="editChecklistItemForm")
|
||||
a.fa.fa-copy(title="{{_ 'copy-text-to-clipboard'}}")
|
||||
span.copied-tooltip {{_ 'copied'}}
|
||||
span.copied-tooltip.copied-tooltip-hidden {{_ 'copied'}}
|
||||
textarea.js-edit-checklist-item(rows='1' autofocus dir="auto")
|
||||
if $eq type 'item'
|
||||
= item.title
|
||||
|
|
@ -99,13 +100,6 @@ template(name="editChecklistItemForm")
|
|||
| {{_ 'convertChecklistItemToCardPopup-title'}}
|
||||
|
||||
template(name="checklistItems")
|
||||
if checklist.items.length
|
||||
if canModifyCard
|
||||
+inlinedForm(autoclose=false classNames="js-add-checklist-item" checklist = checklist position="top")
|
||||
+addChecklistItemForm(checklist=checklist showNewlineBecomesNewChecklistItem=true position="top")
|
||||
else
|
||||
a.add-checklist-item.js-open-inlined-form(title="{{_ 'add-checklist-item'}}")
|
||||
i.fa.fa-plus
|
||||
.checklist-items.js-checklist-items
|
||||
each item in checklist.items
|
||||
+inlinedForm(classNames="js-edit-checklist-item" item = item checklist = checklist)
|
||||
|
|
@ -118,14 +112,15 @@ template(name="checklistItems")
|
|||
else
|
||||
a.add-checklist-item.js-open-inlined-form(title="{{_ 'add-checklist-item'}}")
|
||||
i.fa.fa-plus
|
||||
+inlinedForm(autoclose=false classNames="js-add-checklist-item" checklist = checklist)
|
||||
+addChecklistItemForm(checklist=checklist showNewlineBecomesNewChecklistItem=true position="top")
|
||||
|
||||
template(name='checklistItemDetail')
|
||||
.js-checklist-item.checklist-item(class="{{#if item.isFinished }}is-checked{{#if checklist.hideCheckedChecklistItems}} invisible{{/if}}{{/if}}{{#if checklist.hideAllChecklistItems}} is-checked invisible{{/if}}"
|
||||
role="checkbox" aria-checked="{{#if item.isFinished }}true{{else}}false{{/if}}" tabindex="0")
|
||||
.js-checklist-item.checklist-item(class="{{#if item.isFinished }}is-checked{{#if checklist.hideCheckedChecklistItems}} invisible{{/if}}{{/if}}{{#if checklist.hideAllChecklistItems}} is-checked invisible{{/if}}" role="checkbox" aria-checked="{{#if item.isFinished }}true{{else}}false{{/if}}" tabindex="0")
|
||||
if canModifyCard
|
||||
.check-box-container
|
||||
.check-box.materialCheckBox(class="{{#if item.isFinished }}is-checked{{/if}}")
|
||||
if isTouchScreenOrShowDesktopDragHandles
|
||||
if isMiniScreen
|
||||
span.fa.checklistitem-handle(class="fa-arrows" title="{{_ 'dragChecklistItem'}}")
|
||||
.item-title.js-open-inlined-form.is-editable(class="{{#if item.isFinished }}is-checked{{/if}}")
|
||||
+viewer
|
||||
|
|
@ -141,16 +136,16 @@ template(name="checklistActionsPopup")
|
|||
li
|
||||
a.js-delete-checklist.delete-checklist
|
||||
i.fa.fa-trash
|
||||
| {{_ "delete"}} ...
|
||||
| {{_ "delete"}}
|
||||
a.js-move-checklist.move-checklist
|
||||
i.fa.fa-arrow-right
|
||||
| {{_ "moveChecklist"}} ...
|
||||
| {{_ "moveChecklist"}}
|
||||
a.js-copy-checklist.copy-checklist
|
||||
i.fa.fa-copy
|
||||
| {{_ "copyChecklist"}} ...
|
||||
| {{_ "copyChecklist"}}
|
||||
a.js-hide-checked-checklist-items
|
||||
i.fa.fa-eye-slash
|
||||
| {{_ "hideCheckedChecklistItems"}} ...
|
||||
| {{_ "hideCheckedChecklistItems"}}
|
||||
.material-toggle-switch(title="{{_ 'hide-checked-items'}}")
|
||||
if checklist.hideCheckedChecklistItems
|
||||
input.toggle-switch(type="checkbox" id="toggleHideCheckedChecklistItems_{{checklist._id}}" checked="checked")
|
||||
|
|
@ -158,7 +153,7 @@ template(name="checklistActionsPopup")
|
|||
input.toggle-switch(type="checkbox" id="toggleHideCheckedChecklistItems_{{checklist._id}}")
|
||||
label.toggle-label(for="toggleHideCheckedChecklistItems_{{checklist._id}}")
|
||||
a.js-hide-all-checklist-items
|
||||
i.fa.fa-ban
|
||||
| 🚫
|
||||
| {{_ "hideAllChecklistItems"}} ...
|
||||
.material-toggle-switch(title="{{_ 'hideAllChecklistItems'}}")
|
||||
if checklist.hideAllChecklistItems
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { DialogWithBoardSwimlaneListCard } from '/client/lib/dialogWithBoardSwim
|
|||
const subManager = new SubsManager();
|
||||
const { calculateIndexData, capitalize } = Utils;
|
||||
|
||||
function initSorting(items) {
|
||||
function initSorting(items, handleSelector) {
|
||||
items.sortable({
|
||||
tolerance: 'pointer',
|
||||
helper: 'clone',
|
||||
|
|
@ -16,6 +16,7 @@ function initSorting(items) {
|
|||
appendTo: 'parent',
|
||||
distance: 7,
|
||||
placeholder: 'checklist-item placeholder',
|
||||
handle: handleSelector,
|
||||
scroll: true,
|
||||
start(evt, ui) {
|
||||
ui.placeholder.height(ui.helper.height());
|
||||
|
|
@ -48,8 +49,9 @@ function initSorting(items) {
|
|||
BlazeComponent.extendComponent({
|
||||
onRendered() {
|
||||
const self = this;
|
||||
this.handleSelector = Utils.isMiniScreen() ? 'span.fa.checklistitem-handle' : '.item-title';
|
||||
self.itemsDom = this.$('.js-checklist-items');
|
||||
initSorting(self.itemsDom);
|
||||
initSorting(self.itemsDom, this.handleSelector);
|
||||
self.itemsDom.mousedown(function (evt) {
|
||||
evt.stopPropagation();
|
||||
});
|
||||
|
|
@ -63,11 +65,9 @@ BlazeComponent.extendComponent({
|
|||
const $itemsDom = $(self.itemsDom);
|
||||
if ($itemsDom.data('uiSortable') || $itemsDom.data('sortable')) {
|
||||
$(self.itemsDom).sortable('option', 'disabled', !userIsMember());
|
||||
if (Utils.isTouchScreenOrShowDesktopDragHandles()) {
|
||||
$(self.itemsDom).sortable({
|
||||
handle: 'span.fa.checklistitem-handle',
|
||||
});
|
||||
}
|
||||
$(self.itemsDom).sortable({
|
||||
handle: this.handleSelector,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
.card-label {
|
||||
border: 1px solid #000;
|
||||
border-radius: 4px;
|
||||
border-radius: 0.4ch;
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
margin-right: 4px;
|
||||
margin-bottom: 5px;
|
||||
padding: 3px 8px;
|
||||
max-width: 210px;
|
||||
min-width: 8px;
|
||||
word-wrap: break-word;
|
||||
min-height: 18px;
|
||||
vertical-align: middle;
|
||||
white-space: initial;
|
||||
overflow: initial;
|
||||
font-size: 0.9em;
|
||||
display: flex;
|
||||
/* prefer not using padding/margin but let outer grids
|
||||
position/size labels (see e.g. minicards), otherwise we get
|
||||
inconsistencies */
|
||||
align-self: stretch;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 0 0.5ch;
|
||||
height: var(--label-height);
|
||||
min-width: 8ch;
|
||||
}
|
||||
.card-label:hover {
|
||||
color: #fff;
|
||||
|
|
@ -34,6 +34,7 @@
|
|||
}
|
||||
.card-label p {
|
||||
margin: 0px;
|
||||
--overflow-lines: 1;
|
||||
}
|
||||
.palette-colors {
|
||||
display: flex;
|
||||
|
|
@ -138,37 +139,22 @@
|
|||
.card-label-indigo {
|
||||
background-color: #4b0082;
|
||||
}
|
||||
.edit-label .card-label,
|
||||
.create-label .card-label {
|
||||
float: left;
|
||||
height: 25px;
|
||||
margin: 0px 3% 7px 0px;
|
||||
width: 10.5%;
|
||||
max-width: 10.5%;
|
||||
cursor: pointer;
|
||||
}
|
||||
.edit-labels input[type="text"] {
|
||||
margin: 4px 0 6px 38px;
|
||||
width: 243px;
|
||||
}
|
||||
.edit-labels .card-label {
|
||||
height: 30px;
|
||||
left: 0;
|
||||
padding: 1px 5px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 24px;
|
||||
}
|
||||
.edit-labels .labels-static .card-label {
|
||||
line-height: 30px;
|
||||
margin-bottom: 4px;
|
||||
position: relative;
|
||||
top: auto;
|
||||
left: 0;
|
||||
width: 260px;
|
||||
}
|
||||
.edit-labels-pop-over {
|
||||
margin-bottom: 8px;
|
||||
display: grid;
|
||||
/* so that inner elements, align nicely */
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.1lh;
|
||||
>li {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
gap: 1ch;
|
||||
align-items: center;
|
||||
}
|
||||
.card-label-selectable {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
gap: 1ch;
|
||||
}
|
||||
}
|
||||
.edit-labels-pop-over .card-label .viewer p {
|
||||
margin: 0;
|
||||
|
|
@ -176,34 +162,6 @@
|
|||
.edit-labels-pop-over .shortcut {
|
||||
display: inline-block;
|
||||
}
|
||||
.card-label-selectable {
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
margin-bottom: 3px;
|
||||
width: 190px;
|
||||
min-height: 18px;
|
||||
padding: 8px;
|
||||
position: relative;
|
||||
transition: margin-right 0.1s;
|
||||
}
|
||||
.card-label-selectable .card-label-selectable-icon {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: -20px;
|
||||
}
|
||||
.card-label-selectable.active:hover,
|
||||
.card-label-selectable.active,
|
||||
.card-label-selectable.active.selected:hover,
|
||||
.card-label-selectable.active.selected {
|
||||
padding-right: 32px;
|
||||
}
|
||||
.card-label-selectable.active:hover .card-label-selectable-icon,
|
||||
.card-label-selectable.active .card-label-selectable-icon,
|
||||
.card-label-selectable.active.selected:hover .card-label-selectable-icon,
|
||||
.card-label-selectable.active.selected .card-label-selectable-icon {
|
||||
right: 6px;
|
||||
}
|
||||
.card-label-selectable.selected,
|
||||
.card-label-selectable:hover {
|
||||
opacity: 0.8;
|
||||
|
|
@ -212,24 +170,6 @@
|
|||
.active .card-label-selectable:hover {
|
||||
margin-right: 0;
|
||||
}
|
||||
.active .card-label-selectable .card-label-selectable-icon {
|
||||
right: 8px;
|
||||
}
|
||||
.card-label-edit-button {
|
||||
border-radius: 3px;
|
||||
float: right;
|
||||
padding: 8px;
|
||||
}
|
||||
.card-label-edit-button:hover {
|
||||
background: #dbdbdb;
|
||||
}
|
||||
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.label-handle + .card-label {
|
||||
max-width: 180px;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,32 @@
|
|||
.minicard-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0 1ch 0.2lh 1ch;
|
||||
gap: 0.2lh;
|
||||
}
|
||||
|
||||
.minicard-wrapper {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1.2vh;
|
||||
height: min-content;
|
||||
}
|
||||
.minicard-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 1ch;
|
||||
gap: 1ch;
|
||||
}
|
||||
|
||||
.minicard > hr {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.minicard-add-form {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.minicard-wrapper.placeholder {
|
||||
background: #ccc;
|
||||
border-radius: 1.2vw;
|
||||
|
|
@ -28,32 +50,25 @@
|
|||
.minicard-wrapper .multi-selection-checkbox + .minicard {
|
||||
margin-left: 1vw;
|
||||
}
|
||||
@media only screen {
|
||||
.minicard {
|
||||
padding: 0.8vh 1vw 0.3vh;
|
||||
position: relative;
|
||||
flex: 1;
|
||||
flex-wrap: wrap;
|
||||
background-color: #fff;
|
||||
min-height: 2.5vh;
|
||||
box-shadow: 0 0.2vh 0.3vh rgba(0,0,0,0.15);
|
||||
border-radius: 0.3vw;
|
||||
color: #4d4d4d;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s, border-radius 0.2s;
|
||||
}
|
||||
.minicard {
|
||||
display: grid;
|
||||
grid-auto-flow: row;
|
||||
grid-template-rows: min-content 1fr auto auto;
|
||||
gap: 0.4lh;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0.2vh 0.3vh rgba(0,0,0,0.15);
|
||||
border-radius: 0.3vw;
|
||||
color: #4d4d4d;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s, border-radius 0.2s;
|
||||
flex: 1;
|
||||
}
|
||||
.minicard-details-menu-with-handle {
|
||||
float: right;
|
||||
padding-left: 0.7vw;
|
||||
font-size: clamp(14px, 3vw, 18px);
|
||||
padding: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
.minicard-details-menu {
|
||||
float: right;
|
||||
font-size: clamp(14px, 3vw, 18px);
|
||||
padding-left: 0.7vw;
|
||||
|
||||
.minicard-actions-right {
|
||||
justify-content: end;
|
||||
display: flex;
|
||||
align-items: end;
|
||||
gap: .5lh;
|
||||
}
|
||||
@media print {
|
||||
.minicard-details-menu,
|
||||
|
|
@ -76,7 +91,6 @@
|
|||
transform: translateX(1.5vw);
|
||||
border-bottom-right-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
z-index: 25;
|
||||
box-shadow: -0.3vw 0.2vh 0.3vh rgba(0,0,0,0.2);
|
||||
}
|
||||
.minicard:hover:not(.minicard-composer),
|
||||
|
|
@ -96,20 +110,30 @@
|
|||
margin: 0.8vh -1vw 0.8vh -1vw;
|
||||
border-radius: top 0.3vw;
|
||||
}
|
||||
.minicard .minicard-labels {
|
||||
float: none;
|
||||
margin-right: 6vw;
|
||||
}
|
||||
.minicard .minicard-labels .minicard-label {
|
||||
width: clamp(12px, 1.5vw, 16px);
|
||||
height: clamp(12px, 1.5vw, 16px);
|
||||
border-radius: 0.3vw;
|
||||
margin-right: 0.4vw;
|
||||
margin-bottom: 0.4vh;
|
||||
}
|
||||
.minicard .minicard-labels-no-text {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
.minicard {
|
||||
.minicard-labels, .dates {
|
||||
display: grid;
|
||||
grid-auto-rows: min-content;
|
||||
justify-content: stretch;
|
||||
font-size: 0.8em;
|
||||
grid-auto-rows: minmax(1.3lh, auto);
|
||||
}
|
||||
.minicard-labels {
|
||||
grid-template-columns: repeat(auto-fill, minmax(12ch, auto));
|
||||
gap: 0.2lh 0.5ch;
|
||||
}
|
||||
.minicard-labels-no-text {
|
||||
grid-template-columns: repeat(auto-fill, 4ch);
|
||||
grid-template-rows: 4ch;
|
||||
font-size: 0.4em;
|
||||
.minicard-label {
|
||||
border-radius: 1ch;
|
||||
}
|
||||
}
|
||||
.dates {
|
||||
height: min-content;
|
||||
grid-template-columns: repeat(auto-fit, minmax(15ch, auto));
|
||||
}
|
||||
}
|
||||
.minicard .minicard-custom-fields {
|
||||
display: block;
|
||||
|
|
@ -121,26 +145,22 @@
|
|||
.minicard .minicard-custom-field-item {
|
||||
flex-grow: 1;
|
||||
display: block;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
max-width: 13vw;
|
||||
margin-right: 0.5vw;
|
||||
}
|
||||
.minicard .minicard-custom-field-item-fullwidth {
|
||||
flex-grow: 1;
|
||||
display: block;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
max-width: 100%;
|
||||
margin-right: 0.5vw;
|
||||
}
|
||||
.minicard .handle {
|
||||
width: clamp(20px, 2.5vw, 28px);
|
||||
height: clamp(20px, 2.5vw, 28px);
|
||||
position: absolute;
|
||||
right: 0vw;
|
||||
top: 4vh;
|
||||
display: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@media only screen {
|
||||
.minicard .handle {
|
||||
display: block;
|
||||
|
|
@ -154,58 +174,34 @@
|
|||
text-align: center;
|
||||
}
|
||||
.minicard .minicard-title {
|
||||
margin-right: 1.5vw;
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
flex: 1;
|
||||
cursor: grab;
|
||||
.viewer {
|
||||
--overflow-lines: 2;
|
||||
}
|
||||
|
||||
}
|
||||
.minicard .minicard-title .card-number {
|
||||
color: #b3b3b3;
|
||||
display: inline-block;
|
||||
margin-right: 0.7vw;
|
||||
}
|
||||
@media only screen {
|
||||
.minicard .minicard-title p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.minicard .minicard-title .viewer {
|
||||
display: block;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
}
|
||||
.minicard .dates {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
margin-right: 6vw;
|
||||
clear: both;
|
||||
}
|
||||
.minicard .date {
|
||||
margin-right: 0.4vw;
|
||||
display: flex;
|
||||
&>a {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
align-self: stretch;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unicode icons for minicard dates - matching cardDate.css */
|
||||
.minicard .card-date.end-date time::before {
|
||||
content: "🏁"; /* Finish flag - represents end/completion */
|
||||
}
|
||||
.minicard .card-date.due-date time::before {
|
||||
content: "⏰"; /* Alarm clock - represents due/deadline */
|
||||
}
|
||||
.minicard .card-date.start-date time::before {
|
||||
content: "🚀"; /* Rocket - represents start/launch */
|
||||
}
|
||||
.minicard .card-date.received-date time::before {
|
||||
content: "📥"; /* Inbox tray - represents received/incoming */
|
||||
}
|
||||
|
||||
.minicard .card-date time::before {
|
||||
font-size: inherit;
|
||||
margin-right: 0.3em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Date type specific colors for minicards - matching cardDate.css */
|
||||
.minicard .card-date.received-date {
|
||||
background-color: #dbdbdb; /* Grey for received - same as base card-date */
|
||||
background-color: #d3d3d3; /* Grey for received - a bit darker than base card-date */
|
||||
}
|
||||
|
||||
.minicard .card-date.received-date:hover,
|
||||
|
|
@ -311,102 +307,134 @@
|
|||
background-color: #1976d2 !important;
|
||||
}
|
||||
|
||||
.minicard .minicard-badges-and-creator {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: end;
|
||||
gap: 0 0.5ch;;
|
||||
}
|
||||
|
||||
.minicard-people-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
grid-auto-rows: auto;
|
||||
}
|
||||
|
||||
.minicard-people-wrapper {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
gap: 0.1lh;
|
||||
}
|
||||
.minicard .badges {
|
||||
float: left;
|
||||
margin-top: 1vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5ch;
|
||||
color: #808080;
|
||||
/* this avoid padding-ish at the bottom of the card */
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.minicard .badges:empty {
|
||||
display: none;
|
||||
}
|
||||
.minicard .badges .badge {
|
||||
float: left;
|
||||
margin-right: 1.5vw;
|
||||
margin-bottom: 0.4vh;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.minicard .badges .badge.is-finished {
|
||||
background: #3cb500;
|
||||
padding: 0 0.4vw;
|
||||
padding: 0.3lh 0.8ch;
|
||||
border-radius: 0.4vw;
|
||||
color: #fff;
|
||||
&, .fa {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.minicard .badges .badge:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
.minicard .badges .badge .badge-icon,
|
||||
.minicard .badges .badge .badge-text {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.minicard .badges .badge .badge-icon.badge-comment,
|
||||
.minicard .badges .badge .badge-text.badge-comment {
|
||||
margin-bottom: 0.1rem;
|
||||
}
|
||||
.minicard .badges .badge .badge-text {
|
||||
font-size: 0.9em;
|
||||
padding-left: 0.3vw;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.minicard .badges .badge .check-list-text {
|
||||
padding-left: 0px;
|
||||
line-height: 1.1;
|
||||
}
|
||||
.minicard .minicard-members,
|
||||
.minicard .minicard-assignees,
|
||||
.minicard .minicard-creator {
|
||||
float: right;
|
||||
margin-left: 0.7vw;
|
||||
margin-bottom: 0.5vh;
|
||||
}
|
||||
.minicard .minicard-members .member,
|
||||
.minicard .minicard-assignees .member,
|
||||
.minicard .minicard-creator .member {
|
||||
float: right;
|
||||
display: flex;
|
||||
border-radius: 50%;
|
||||
height: clamp(24px, 3.5vw, 32px);
|
||||
width: clamp(24px, 3.5vw, 32px);
|
||||
margin-bottom: 0.5vh;
|
||||
font-size: 0.8em;
|
||||
margin-bottom: 0.2lh;
|
||||
}
|
||||
.minicard .minicard-members .assignee,
|
||||
.minicard .minicard-assignees .assignee,
|
||||
.minicard .minicard-creator .assignee {
|
||||
float: right;
|
||||
border-radius: 50%;
|
||||
height: clamp(24px, 3.5vw, 32px);
|
||||
width: clamp(24px, 3.5vw, 32px);
|
||||
|
||||
.minicard .minicard-assignees .member {
|
||||
border: 2px solid rgb(180, 87, 87);
|
||||
}
|
||||
.minicard .minicard-members + .badges,
|
||||
.minicard .minicard-assignees + .badges,
|
||||
.minicard .minicard-creator + .badges {
|
||||
margin-top: 0.7vh;
|
||||
|
||||
.minicard .minicard-creator .member {
|
||||
border: 2px solid #7fd67f;
|
||||
}
|
||||
|
||||
.minicard .minicard-members .member {
|
||||
border: 2px solid #5a5ac6;
|
||||
}
|
||||
.minicard .minicard-assignees {
|
||||
border-bottom: 1px solid #f00;
|
||||
}
|
||||
.minicard .minicard-creator {
|
||||
border-bottom: 1px solid #008000;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.minicard .minicard-members:empty,
|
||||
.minicard .minicard-assignees:empty {
|
||||
display: none;
|
||||
}
|
||||
.minicard .minicard-description {
|
||||
padding: 0.8vh 0 0 1vw;
|
||||
color: #000;
|
||||
background-color: #eee;
|
||||
width: 100%;
|
||||
margin-bottom: 0.3vh;
|
||||
margin-left: -0.5vw;
|
||||
border-radius: 0.4vw;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
padding: 0.5lh 1ch;
|
||||
--overflow-lines: 2;
|
||||
.viewer {
|
||||
font-size: 0.9em;
|
||||
ul {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.minicard .minicard-description p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.minicard-composer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.minicard-composer-icons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.2lh;
|
||||
}
|
||||
|
||||
.minicard-bottom {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
gap: 1ch;
|
||||
|
||||
.minicard-composer-icons {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.add-controls {
|
||||
display: flex;
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
textarea {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.minicard.minicard-composer {
|
||||
margin-bottom: 1.3vh;
|
||||
flex-wrap: wrap;
|
||||
flex: 1;
|
||||
align-self: stretch;
|
||||
gap: 0.3lh;
|
||||
padding: 0.5lh 1ch;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.minicard.minicard-composer textarea.minicard-composer-textarea,
|
||||
.minicard.minicard-composer textarea.minicard-composer-textarea:focus {
|
||||
resize: none;
|
||||
|
|
@ -415,11 +443,11 @@
|
|||
box-shadow: none;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
max-height: 22vh;
|
||||
min-height: 5vh;
|
||||
margin-bottom: 2.5vh;
|
||||
padding: 1ch;
|
||||
min-height: 5lh;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
.parent-prefix {
|
||||
color: #b3b3b3;
|
||||
|
|
@ -734,30 +762,12 @@
|
|||
|
||||
/* List name display on minicard */
|
||||
.minicard-list-name {
|
||||
font-size: 0.75em;
|
||||
font-size: inherit;
|
||||
color: #8c8c8c;
|
||||
margin-top: 0.2vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.3vw;
|
||||
}
|
||||
|
||||
/* Checklist display on minicard */
|
||||
.minicard-checklist {
|
||||
width: 100%;
|
||||
margin-top: 0.5vh;
|
||||
margin-bottom: 0.5vh;
|
||||
padding: 0.3vh 0.5vw;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
border-radius: 0.3vw;
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.minicard-checklist .checklist-header {
|
||||
padding: 0 0.5ch;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 0.3vh;
|
||||
gap: 0.5ch;
|
||||
}
|
||||
|
||||
.minicard-checklist .checklist-title {
|
||||
|
|
|
|||
|
|
@ -1,226 +1,236 @@
|
|||
template(name="minicard")
|
||||
if isSelected
|
||||
+cardDetailsPopup(this)
|
||||
.minicard.nodragscroll(
|
||||
class="{{#if isLinkedCard}}linked-card{{/if}}"
|
||||
class="{{#if isLinkedBoard}}linked-board{{/if}}"
|
||||
class="{{#if colorClass}}minicard-{{colorClass}}{{/if}}")
|
||||
if canMoveCard
|
||||
if isTouchScreenOrShowDesktopDragHandles
|
||||
.handle
|
||||
i.fa.fa-arrows
|
||||
if canModifyCard
|
||||
a.minicard-details-menu-with-handle.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
|
||||
i.fa.fa-bars
|
||||
.dates
|
||||
if getReceived
|
||||
.date
|
||||
+minicardReceivedDate
|
||||
if getStart
|
||||
.date
|
||||
+minicardStartDate
|
||||
if getDue
|
||||
.date
|
||||
+minicardDueDate
|
||||
if getEnd
|
||||
+minicardEndDate
|
||||
if allowsReceivedDate
|
||||
if getReceived
|
||||
.date.viewer
|
||||
+minicardReceivedDate
|
||||
if allowsStartDate
|
||||
if getStart
|
||||
.date.viewer
|
||||
+minicardStartDate
|
||||
if allowsDueDate
|
||||
if getDue
|
||||
.date.viewer
|
||||
+minicardDueDate
|
||||
if allowsEndDate
|
||||
if getEnd
|
||||
.date.viewer
|
||||
+minicardEndDate
|
||||
if getSpentTime
|
||||
.date
|
||||
.date.viewer
|
||||
+cardSpentTime
|
||||
if cover
|
||||
if currentBoard.allowsCoverAttachmentOnMinicard
|
||||
.minicard-cover(style="background-image: url('{{cover.link 'original'}}?dummyReloadAfterSessionEstablished={{sess}}');")
|
||||
.minicard-header
|
||||
.minicard-title
|
||||
if $eq 'prefix-with-full-path' currentBoard.presentParentTask
|
||||
.parent-prefix
|
||||
| {{ parentString ' > ' }}
|
||||
if $eq 'prefix-with-parent' currentBoard.presentParentTask
|
||||
.parent-prefix
|
||||
| {{ parentCardName }}
|
||||
if isLinkedBoard
|
||||
a.js-linked-link
|
||||
span.linked-icon
|
||||
i.fa.fa-folder
|
||||
else if isLinkedCard
|
||||
a.js-linked-link
|
||||
span.linked-icon
|
||||
i.fa.fa-id-card
|
||||
if getArchived
|
||||
span.linked-icon.linked-archived
|
||||
i.fa.fa-archive
|
||||
+viewer
|
||||
if allowsCardNumber
|
||||
span.card-number
|
||||
| ##{getCardNumber} 
|
||||
= getTitle
|
||||
div.minicard-actions-right
|
||||
if canModifyCard
|
||||
a.minicard-details-menu-with-handle.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
|
||||
i.fa.fa-bars
|
||||
if isMiniScreen
|
||||
if canMoveCard
|
||||
.handle
|
||||
i.fa.fa-arrows
|
||||
hr
|
||||
.minicard-body
|
||||
if cover
|
||||
if allowsCoverAttachmentOnMinicard
|
||||
.minicard-cover(style="background-image: url('{{cover.link 'original'}}?dummyReloadAfterSessionEstablished={{sess}}');")
|
||||
|
||||
// Upload progress indicator for drag-and-drop uploads
|
||||
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}}")
|
||||
.upload-progress-filename {{file.name}}
|
||||
.upload-progress-bar
|
||||
.upload-progress-fill(style="width: {{progress}}%")
|
||||
if $eq status 'error'
|
||||
.upload-progress-error
|
||||
i.fa.fa-warning
|
||||
span {{_ 'upload-failed'}}
|
||||
else if $eq status 'completed'
|
||||
.upload-progress-success
|
||||
i.fa.fa-check
|
||||
span {{_ 'upload-completed'}}
|
||||
//- Upload progress indicator for drag-and-drop uploads
|
||||
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}}")
|
||||
.upload-progress-filename {{file.name}}
|
||||
.upload-progress-bar
|
||||
.upload-progress-fill(style="width: {{progress}}%")
|
||||
if $eq status 'error'
|
||||
.upload-progress-error
|
||||
i.fa.fa-warning
|
||||
span {{_ 'upload-failed'}}
|
||||
else if $eq status 'completed'
|
||||
.upload-progress-success
|
||||
i.fa.fa-check
|
||||
span {{_ 'upload-completed'}}
|
||||
|
||||
.minicard-title
|
||||
if $eq 'prefix-with-full-path' currentBoard.presentParentTask
|
||||
.parent-prefix
|
||||
| {{ parentString ' > ' }}
|
||||
if $eq 'prefix-with-parent' currentBoard.presentParentTask
|
||||
.parent-prefix
|
||||
| {{ parentCardName }}
|
||||
if isLinkedBoard
|
||||
a.js-linked-link
|
||||
span.linked-icon
|
||||
i.fa.fa-folder
|
||||
else if isLinkedCard
|
||||
a.js-linked-link
|
||||
span.linked-icon
|
||||
i.fa.fa-id-card
|
||||
if getArchived
|
||||
span.linked-icon.linked-archived
|
||||
i.fa.fa-archive
|
||||
+viewer
|
||||
if currentBoard.allowsCardNumber
|
||||
span.card-number
|
||||
| ##{getCardNumber}
|
||||
= getTitle
|
||||
if labels
|
||||
.minicard-labels(class="{{#if hiddenMinicardLabelText}}minicard-labels-no-text{{/if}}")
|
||||
each labels
|
||||
unless hiddenMinicardLabelText
|
||||
span.js-card-label.card-label(class="card-label-{{color}}" title=name)
|
||||
+viewer
|
||||
= name
|
||||
if hiddenMinicardLabelText
|
||||
.minicard-label(class="card-label-{{color}}" title="{{name}}")
|
||||
if labels
|
||||
.minicard-labels(class="{{#if hiddenMinicardLabelText}}minicard-labels-no-text{{/if}}")
|
||||
each labels
|
||||
unless hiddenMinicardLabelText
|
||||
span.js-card-label.card-label(class="card-label-{{color}}" title=name)
|
||||
+viewer
|
||||
= name
|
||||
if hiddenMinicardLabelText
|
||||
.minicard-label(class="card-label-{{color}}" title="{{name}}")
|
||||
|
||||
.minicard-custom-fields
|
||||
each customFieldsWD
|
||||
if definition.showOnCard
|
||||
if trueValue
|
||||
.minicard-custom-field
|
||||
// If there is custom field label, show label at left,
|
||||
// and value at right
|
||||
if definition.showLabelOnMiniCard
|
||||
.minicard-custom-field-item
|
||||
+viewer
|
||||
= definition.name
|
||||
.minicard-custom-field-item
|
||||
if $eq definition.type "currency"
|
||||
.minicard-custom-fields
|
||||
each customFieldsWD
|
||||
if definition.showOnCard
|
||||
if trueValue
|
||||
.minicard-custom-field
|
||||
// If there is custom field label, show label at left,
|
||||
// and value at right
|
||||
if definition.showLabelOnMiniCard
|
||||
.minicard-custom-field-item
|
||||
+viewer
|
||||
= formattedCurrencyCustomFieldValue(definition)
|
||||
else if $eq definition.type "date"
|
||||
.date
|
||||
+minicardCustomFieldDate
|
||||
else if $eq definition.type "checkbox"
|
||||
.materialCheckBox(class="{{#if value }}is-checked{{/if}}")
|
||||
else if $eq definition.type "stringtemplate"
|
||||
+viewer
|
||||
= formattedStringtemplateCustomFieldValue(definition)
|
||||
else
|
||||
+viewer
|
||||
= trueValue
|
||||
else
|
||||
// If there is no custom field label,
|
||||
// show value full width
|
||||
.minicard-custom-field-item-fullwidth
|
||||
if $eq definition.type "currency"
|
||||
+viewer
|
||||
= formattedCurrencyCustomFieldValue(definition)
|
||||
else if $eq definition.type "date"
|
||||
.date
|
||||
+minicardCustomFieldDate
|
||||
else if $eq definition.type "checkbox"
|
||||
.materialCheckBox(class="{{#if value }}is-checked{{/if}}")
|
||||
else if $eq definition.type "stringtemplate"
|
||||
+viewer
|
||||
= formattedStringtemplateCustomFieldValue(definition)
|
||||
else
|
||||
+viewer
|
||||
= trueValue
|
||||
= definition.name
|
||||
.minicard-custom-field-item
|
||||
if $eq definition.type "currency"
|
||||
+viewer
|
||||
= formattedCurrencyCustomFieldValue(definition)
|
||||
else if $eq definition.type "date"
|
||||
.date
|
||||
+minicardCustomFieldDate
|
||||
else if $eq definition.type "checkbox"
|
||||
.materialCheckBox(class="{{#if value }}is-checked{{/if}}")
|
||||
else if $eq definition.type "stringtemplate"
|
||||
+viewer
|
||||
= formattedStringtemplateCustomFieldValue(definition)
|
||||
else
|
||||
+viewer
|
||||
= trueValue
|
||||
else
|
||||
// If there is no custom field label,
|
||||
// show value full width
|
||||
.minicard-custom-field-item-fullwidth
|
||||
if $eq definition.type "currency"
|
||||
+viewer
|
||||
= formattedCurrencyCustomFieldValue(definition)
|
||||
else if $eq definition.type "date"
|
||||
.date
|
||||
+minicardCustomFieldDate
|
||||
else if $eq definition.type "checkbox"
|
||||
.materialCheckBox(class="{{#if value }}is-checked{{/if}}")
|
||||
else if $eq definition.type "stringtemplate"
|
||||
+viewer
|
||||
= formattedStringtemplateCustomFieldValue(definition)
|
||||
else
|
||||
+viewer
|
||||
= trueValue
|
||||
.minicard-people-grid
|
||||
if allowsAssignee
|
||||
if getAssignees
|
||||
.minicard-people-wrapper
|
||||
.minicard-assignees.js-minicard-assignees
|
||||
each getAssignees
|
||||
+userAvatar(userId=this)
|
||||
|
||||
if showAssignee
|
||||
if getAssignees
|
||||
.minicard-assignees.js-minicard-assignees
|
||||
each getAssignees
|
||||
+userAvatar(userId=this)
|
||||
if allowsMembers
|
||||
if getMembers
|
||||
.minicard-people-wrapper
|
||||
.minicard-members.js-minicard-members
|
||||
each getMembers
|
||||
+userAvatar(userId=this)
|
||||
|
||||
if showMembers
|
||||
if getMembers
|
||||
.minicard-members.js-minicard-members
|
||||
each getMembers
|
||||
+userAvatar(userId=this)
|
||||
.minicard-badges-and-creator
|
||||
if allowsCreatorOnMinicard
|
||||
.minicard-creator
|
||||
+userAvatar(userId=this.userId noRemove=true)
|
||||
|
||||
if showCreatorOnMinicard
|
||||
.minicard-creator
|
||||
+userAvatar(userId=this.userId noRemove=true)
|
||||
|
||||
.badges
|
||||
if canModifyCard
|
||||
if comments.length
|
||||
.badge(title="{{_ 'card-comments-title' comments.length }}")
|
||||
span.badge-icon.badge-comment.badge-text
|
||||
i.fa.fa-comment-o
|
||||
= ' '
|
||||
= comments.length
|
||||
//span.badge-comment.badge-text
|
||||
//| {{_ 'comment'}}
|
||||
if getDescription
|
||||
unless currentBoard.allowsDescriptionTextOnMinicard
|
||||
.badge.badge-state-image-only(title=getDescription)
|
||||
span.badge-icon
|
||||
i.fa.fa-file-text-o
|
||||
if getVoteQuestion
|
||||
.badge.badge-state-image-only(title=getVoteQuestion)
|
||||
span.badge-icon(class="{{#if voteState}}text-green{{/if}}")
|
||||
i.fa.fa-thumbs-up
|
||||
span.badge-text {{ voteCountPositive }}
|
||||
span.badge-icon(class="{{#if $eq voteState false}}text-red{{/if}}")
|
||||
i.fa.fa-thumbs-down
|
||||
span.badge-text {{ voteCountNegative }}
|
||||
if getPokerQuestion
|
||||
.badge.badge-state-image-only(title=getPokerQuestion)
|
||||
span.badge-icon(class="{{#if pokerState}}text-green{{/if}}")
|
||||
i.fa.fa-check-square
|
||||
if expiredPoker
|
||||
span.badge-text {{ getPokerEstimation }}
|
||||
if attachments.length
|
||||
if currentBoard.allowsBadgeAttachmentOnMinicard
|
||||
.badge
|
||||
span.badge-icon
|
||||
i.fa.fa-paperclip
|
||||
span.badge-text= attachments.length
|
||||
if allSubtasks.count
|
||||
.badge
|
||||
span.badge-icon
|
||||
i.fa.fa-globe
|
||||
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
|
||||
i.fa.fa-sort-numeric-asc
|
||||
span.badge-text.check-list-sort {{ sort }}
|
||||
if shouldShowChecklistAtMinicard
|
||||
each shouldShowChecklistAtMinicard
|
||||
+minicardChecklist(checklist=. card=..)
|
||||
if currentBoard.allowsDescriptionTextOnMinicard
|
||||
.badges
|
||||
if canModifyCard
|
||||
if allowsComments
|
||||
if comments.length
|
||||
.badge(title="{{_ 'card-comments-title' comments.length }}")
|
||||
span.badge-icon.badge-comment.badge-text
|
||||
i.fa.fa-comment-o
|
||||
= ' '
|
||||
= comments.length
|
||||
//span.badge-comment.badge-text
|
||||
//| {{_ 'comment'}}
|
||||
if getDescription
|
||||
unless allowsDescriptionTextOnMinicard
|
||||
.badge.badge-state-image-only(title=getDescription)
|
||||
span.badge-icon
|
||||
i.fa.fa-file-text-o
|
||||
if getVoteQuestion
|
||||
.badge.badge-state-image-only(title=getVoteQuestion)
|
||||
span.badge-icon(class="{{#if voteState}}text-green{{/if}}")
|
||||
i.fa.fa-thumbs-up
|
||||
span.badge-text {{ voteCountPositive }}
|
||||
span.badge-icon(class="{{#if $eq voteState false}}text-red{{/if}}")
|
||||
i.fa.fa-thumbs-down
|
||||
span.badge-text {{ voteCountNegative }}
|
||||
if getPokerQuestion
|
||||
.badge.badge-state-image-only(title=getPokerQuestion)
|
||||
span.badge-icon(class="{{#if pokerState}}text-green{{/if}}")
|
||||
i.fa.fa-check-square
|
||||
if expiredPoker
|
||||
span.badge-text {{ getPokerEstimation }}
|
||||
if attachments.length
|
||||
if allowsBadgeAttachmentOnMinicard
|
||||
.badge
|
||||
span.badge-icon
|
||||
i.fa.fa-paperclip
|
||||
span.badge-text= attachments.length
|
||||
if checklists.length
|
||||
if allowsChecklists
|
||||
.badge(class="{{#if checklistFinished}}is-finished{{/if}}")
|
||||
span.badge-icon
|
||||
i.fa.fa-check
|
||||
span.badge-text.check-list-text {{checklistFinishedCount}}/{{checklistItemCount}}
|
||||
if allSubtasks.count
|
||||
if allowsSubtasks
|
||||
.badge
|
||||
span.badge-icon
|
||||
i.fa.fa-globe
|
||||
span.badge-text.check-list-text {{subtasksFinishedCount}}/{{allSubtasksCount}}
|
||||
//{{subtasksFinishedCount}}/{{subtasksCount}} does not work because when a subtaks is archived, the count goes down
|
||||
if allowsCardSortingByNumber
|
||||
if allowsCardSortingByNumberOnMinicard
|
||||
.badge
|
||||
span.badge-icon
|
||||
i.fa.fa-sort-numeric-asc
|
||||
span.badge-text.check-list-sort {{ sort }}
|
||||
if shouldShowListOnMinicard
|
||||
.minicard-list-name
|
||||
span
|
||||
i.fa.fa-list
|
||||
span
|
||||
| {{ listName }}
|
||||
if $eq 'subtext-with-full-path' presentParentTask
|
||||
.parent-subtext
|
||||
| {{ parentString ' > ' }}
|
||||
if $eq 'subtext-with-parent' presentParentTask
|
||||
.parent-subtext
|
||||
| {{ parentCardName }}
|
||||
if allowsDescriptionTextOnMinicard
|
||||
if getDescription
|
||||
.minicard-description
|
||||
+viewer
|
||||
| {{ getDescription }}
|
||||
if shouldShowListOnMinicard
|
||||
.minicard-list-name
|
||||
i.fa.fa-list
|
||||
| {{ listName }}
|
||||
if $eq 'subtext-with-full-path' currentBoard.presentParentTask
|
||||
.parent-subtext
|
||||
| {{ parentString ' > ' }}
|
||||
if $eq 'subtext-with-parent' currentBoard.presentParentTask
|
||||
.parent-subtext
|
||||
| {{ parentCardName }}
|
||||
|
||||
template(name="editCardSortOrderPopup")
|
||||
input.js-edit-card-sort-popup(type='text' autofocus value=sort dir="auto")
|
||||
input.js-edit-card-sort-popup(type='text' value=sort dir="auto")
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-edit-card-sort-popup(type="submit") {{_ 'save'}}
|
||||
|
||||
template(name="minicardChecklist")
|
||||
.minicard-checklist
|
||||
.checklist-header
|
||||
.checklist-title= checklist.title
|
||||
if canModifyCard
|
||||
a.checklist-menu.js-open-checklist-menu(title="{{_ 'checklistActionsPopup-title'}}")
|
||||
i.fa.fa-bars
|
||||
each visibleItems
|
||||
+checklistItemDetail(item = . checklist = checklist card = card)
|
||||
|
||||
button.primary.confirm.js-submit-edit-card-sort-popup(type="submit") {{_ 'save'}}
|
||||
|
|
@ -13,6 +13,24 @@ BlazeComponent.extendComponent({
|
|||
return 'minicard';
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
// cannot be done with CSS because newlines
|
||||
// rendered by the JADE engine count as non empty
|
||||
// and some "empty" divs are nested
|
||||
// this is not very robust and could probably be
|
||||
// done with a helper, but it could be in fact worse
|
||||
// because we would need to to if (allowsX() && X() && ...)
|
||||
const body = $(this.find('.minicard-body'));
|
||||
if (!body) {return}
|
||||
let emptyChildren;
|
||||
do {
|
||||
emptyChildren = body.find('*').filter((_, e) => !e.classList.contains('fa') && $(e).html().trim().length === 0).remove();
|
||||
} while (emptyChildren.length > 0)
|
||||
if (body.html().trim().length === 0) {
|
||||
body.parent().find('hr:has(+ .minicard-body)').remove();
|
||||
}
|
||||
},
|
||||
|
||||
formattedCurrencyCustomFieldValue(definition) {
|
||||
const customField = this.data()
|
||||
.customFieldsWD()
|
||||
|
|
@ -39,46 +57,14 @@ BlazeComponent.extendComponent({
|
|||
return ret;
|
||||
},
|
||||
|
||||
showCreatorOnMinicard() {
|
||||
// cache "board" to reduce the mini-mongodb access
|
||||
const board = this.data().board();
|
||||
let ret = false;
|
||||
if (board) {
|
||||
ret = board.allowsCreatorOnMinicard ?? false;
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
isWatching() {
|
||||
const card = this.currentData();
|
||||
return card.findWatcher(Meteor.userId());
|
||||
},
|
||||
|
||||
showMembers() {
|
||||
// cache "board" to reduce the mini-mongodb access
|
||||
const board = this.data().board();
|
||||
let ret = false;
|
||||
if (board) {
|
||||
ret =
|
||||
board.allowsMembers === null ||
|
||||
board.allowsMembers === undefined ||
|
||||
board.allowsMembers
|
||||
;
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
showAssignee() {
|
||||
// cache "board" to reduce the mini-mongodb access
|
||||
const board = this.data().board();
|
||||
let ret = false;
|
||||
if (board) {
|
||||
ret =
|
||||
board.allowsAssignee === null ||
|
||||
board.allowsAssignee === undefined ||
|
||||
board.allowsAssignee
|
||||
;
|
||||
}
|
||||
return ret;
|
||||
isSelected() {
|
||||
const card = this.currentData();
|
||||
return Session.get('currentCard') === card._id;
|
||||
},
|
||||
|
||||
/** opens the card label popup only if clicked onto a label
|
||||
|
|
@ -87,6 +73,8 @@ BlazeComponent.extendComponent({
|
|||
*/
|
||||
cardLabelsPopup(event) {
|
||||
if (this.find('.js-card-label:hover')) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
Popup.open("cardLabels")(event, {dataContextIfCurrentDataIsUndefined: this.currentData()});
|
||||
}
|
||||
},
|
||||
|
|
@ -203,7 +191,7 @@ BlazeComponent.extendComponent({
|
|||
visibleItems() {
|
||||
const checklist = this.currentData().checklist || this.currentData();
|
||||
const items = checklist.items();
|
||||
|
||||
|
||||
return items.filter(item => {
|
||||
// Hide finished items if hideCheckedChecklistItems is true
|
||||
if (item.isFinished && checklist.hideCheckedChecklistItems) {
|
||||
|
|
@ -254,33 +242,8 @@ Template.minicard.helpers({
|
|||
},
|
||||
|
||||
shouldShowListOnMinicard() {
|
||||
// Show list name if either:
|
||||
// 1. Board-wide setting is enabled, OR
|
||||
// 2. This specific card has the setting enabled
|
||||
const currentBoard = this.board();
|
||||
if (!currentBoard) return false;
|
||||
return currentBoard.allowsShowListsOnMinicard || this.showListOnMinicard;
|
||||
return Utils.allowsShowLists();
|
||||
},
|
||||
|
||||
shouldShowChecklistAtMinicard() {
|
||||
// Return checklists that should be shown on minicard
|
||||
const currentBoard = this.board();
|
||||
if (!currentBoard) return [];
|
||||
|
||||
const checklists = this.checklists();
|
||||
const visibleChecklists = [];
|
||||
|
||||
checklists.forEach(checklist => {
|
||||
// Show checklist if either:
|
||||
// 1. Board-wide setting is enabled, OR
|
||||
// 2. This specific checklist has the setting enabled
|
||||
if (currentBoard.allowsChecklistAtMinicard || checklist.showChecklistAtMinicard) {
|
||||
visibleChecklists.push(checklist);
|
||||
}
|
||||
});
|
||||
|
||||
return visibleChecklists;
|
||||
}
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
.result-card-context-list {
|
||||
margin-bottom: 0.7rem;
|
||||
display: flex;
|
||||
gap: 0.2ch;
|
||||
}
|
||||
.result-card-block-wrapper {
|
||||
display: inline-block;
|
||||
|
|
|
|||
|
|
@ -14,17 +14,10 @@ BlazeComponent.extendComponent({
|
|||
onReady() {
|
||||
Session.set('popupCardId', cardId);
|
||||
Session.set('popupCardBoardId', boardId);
|
||||
this_.cardDetailsPopup(evt);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
cardDetailsPopup(event) {
|
||||
if (!Popup.isOpen()) {
|
||||
Popup.open("cardDetails")(event);
|
||||
}
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,19 +4,14 @@
|
|||
textarea.js-add-subtask-item,
|
||||
textarea.js-edit-subtask-item {
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
resize: none;
|
||||
height: 34px;
|
||||
}
|
||||
.delete-text,
|
||||
.subtask-title .js-delete-subtask,
|
||||
.subtask-title .js-view-subtask,
|
||||
.js-delete-subtask-item {
|
||||
color: #8c8c8c;
|
||||
text-decoration: underline;
|
||||
word-wrap: break-word;
|
||||
float: right;
|
||||
padding-top: 6px;
|
||||
}
|
||||
.delete-text:hover,
|
||||
.subtask-title .js-delete-subtask:hover,
|
||||
|
|
@ -28,11 +23,11 @@ textarea.js-edit-subtask-item {
|
|||
float: left;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
font-size: 18px;
|
||||
|
||||
line-height: 30px;
|
||||
}
|
||||
.subtask-title .title {
|
||||
font-size: 18px;
|
||||
|
||||
line-height: 25px;
|
||||
}
|
||||
.subtask-title .subtasks-stat {
|
||||
|
|
@ -133,7 +128,7 @@ textarea.js-edit-subtask-item {
|
|||
margin: 0.1em 0 0 0;
|
||||
}
|
||||
.subtasks-item .check-box.is-checked {
|
||||
border-bottom: 2px solid #3cb500;
|
||||
border-bottom: 0.2ch solid #3cb500;
|
||||
border-right: 2px solid #3cb500;
|
||||
}
|
||||
/* Unicode checkbox icons styling */
|
||||
|
|
@ -165,16 +160,4 @@ body.grey-icons-enabled .subtasks-item .check-box-unicode {
|
|||
}
|
||||
.subtasks-item .item-title .viewer p {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.js-delete-subtask-item {
|
||||
margin: 0 0 0.5em 1.33em;
|
||||
padding: 12px 0 0 0;
|
||||
}
|
||||
.add-subtask-item {
|
||||
margin: 0.2em 0 0.5em 1.33em;
|
||||
display: inline-block;
|
||||
}
|
||||
.subtask-details-menu {
|
||||
float: right;
|
||||
padding: 6px 10px 6px 10px;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,12 +4,20 @@ template(name="subtasks")
|
|||
| {{_ 'subtasks'}}
|
||||
if currentUser.isBoardAdmin
|
||||
if toggleDeleteDialog.get
|
||||
.board-overlay#card-details-overlay
|
||||
+subtaskDeleteDialog(subtask = subtaskToDelete)
|
||||
|
||||
.card-subtasks-items
|
||||
each subtask in currentCard.subtasks
|
||||
+subtaskDetail(subtask = subtask)
|
||||
if currentCard.subtasks
|
||||
.card-subtasks-items
|
||||
each subtask in currentCard.subtasks
|
||||
.subtask-container
|
||||
+subtaskDetail(subtask = subtask)
|
||||
if canModifyCard
|
||||
a.subtask-details-menu.js-open-subtask-details-menu(title="{{_ 'subtaskActionsPopup-title'}}")
|
||||
| ☰
|
||||
if currentUser.isBoardAdmin
|
||||
a.js-delete-subtask-item
|
||||
| ❌
|
||||
|
||||
|
||||
if canModifyCard
|
||||
+inlinedForm(autoclose=false classNames="js-add-subtask" cardId = cardId)
|
||||
|
|
@ -24,9 +32,6 @@ template(name="subtaskDetail")
|
|||
+editSubtaskItemForm(subtask = subtask)
|
||||
else
|
||||
.subtask-title
|
||||
span
|
||||
if canModifyCard
|
||||
a.subtask-details-menu.js-open-subtask-details-menu(title="{{_ 'subtaskActionsPopup-title'}}")
|
||||
if canModifyCard
|
||||
h2.title.js-open-inlined-form.is-editable
|
||||
+viewer
|
||||
|
|
@ -37,13 +42,13 @@ template(name="subtaskDetail")
|
|||
= subtask.title
|
||||
|
||||
template(name="addSubtaskItemForm")
|
||||
textarea.js-add-subtask-item(rows='1' autofocus dir="auto")
|
||||
textarea.js-add-subtask-item(rows='1' dir="auto")
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-add-subtask-item-form(type="submit") {{_ 'save'}}
|
||||
a.js-close-inlined-form
|
||||
|
||||
template(name="editSubtaskItemForm")
|
||||
textarea.js-edit-subtask-item(rows='1' autofocus dir="auto")
|
||||
textarea.js-edit-subtask-item(rows='1' dir="auto")
|
||||
if $eq type 'item'
|
||||
= item.title
|
||||
else
|
||||
|
|
@ -52,9 +57,6 @@ template(name="editSubtaskItemForm")
|
|||
button.primary.confirm.js-submit-edit-subtask-item-form(type="submit") {{_ 'save'}}
|
||||
a.js-close-inlined-form
|
||||
span(title=createdAt) {{ moment createdAt }}
|
||||
if canModifyCard
|
||||
if currentUser.isBoardAdmin
|
||||
a.js-delete-subtask-item {{_ "delete"}}...
|
||||
|
||||
template(name="subtasksItems")
|
||||
.subtasks-items.js-subtasks-items
|
||||
|
|
@ -100,4 +102,3 @@ template(name="subtaskActionsPopup")
|
|||
a.js-delete-subtask.delete-subtask
|
||||
i.fa.fa-trash
|
||||
| {{_ "delete"}} ...
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue