wekan/client/components/lists/list.js

322 lines
10 KiB
JavaScript
Raw Normal View History

import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
require('/client/lib/jquery-ui.js')
const { calculateIndex } = Utils;
export const itemsSelector = '.js-minicard:not(.placeholder, .js-card-composer)';
BlazeComponent.extendComponent({
// Proxy
openForm(options) {
this.childComponents('listBody')[0].openForm(options);
Renaissance _,,ad8888888888bba,_ ,ad88888I888888888888888ba, ,88888888I88888888888888888888a, ,d888888888I8888888888888888888888b, d88888PP"""" ""YY88888888888888888888b, ,d88"'__,,--------,,,,.;ZZZY8888888888888, ,8IIl'" ;;l"ZZZIII8888888888, ,I88l;' ;lZZZZZ888III8888888, ,II88Zl;. ;llZZZZZ888888I888888, ,II888Zl;. .;;;;;lllZZZ888888I8888b ,II8888Z;; `;;;;;''llZZ8888888I8888, II88888Z;' .;lZZZ8888888I888b II88888Z; _,aaa, .,aaaaa,__.l;llZZZ88888888I888 II88888IZZZZZZZZZ, .ZZZZZZZZZZZZZZ;llZZ88888888I888, II88888IZZ<'(@@>Z| |ZZZ<'(@@>ZZZZ;;llZZ888888888I88I ,II88888; `""" ;| |ZZ; `""" ;;llZ8888888888I888 II888888l `;; .;llZZ8888888888I888, ,II888888Z; ;;; .;;llZZZ8888888888I888I III888888Zl; .., `;; ,;;lllZZZ88888888888I888 II88888888Z;;...;(_ _) ,;;;llZZZZ88888888888I888, II88888888Zl;;;;;' `--'Z;. .,;;;;llZZZZ88888888888I888b ]I888888888Z;;;;' ";llllll;..;;;lllZZZZ88888888888I8888, II888888888Zl.;;"Y88bd888P";;,..;lllZZZZZ88888888888I8888I II8888888888Zl;.; `"PPP";;;,..;lllZZZZZZZ88888888888I88888 II888888888888Zl;;. `;;;l;;;;lllZZZZZZZZW88888888888I88888 `II8888888888888Zl;. ,;;lllZZZZZZZZWMZ88888888888I88888 II8888888888888888ZbaalllZZZZZZZZZWWMZZZ8888888888I888888, `II88888888888888888b"WWZZZZZWWWMMZZZZZZI888888888I888888b `II88888888888888888;ZZMMMMMMZZZZZZZZllI888888888I8888888 `II8888888888888888 `;lZZZZZZZZZZZlllll888888888I8888888, II8888888888888888, `;lllZZZZllllll;;.Y88888888I8888888b, ,II8888888888888888b .;;lllllll;;;.;..88888888I88888888b, II888888888888888PZI;. .`;;;.;;;..; ...88888888I8888888888, II888888888888PZ;;';;. ;. .;. .;. .. Y8888888I88888888888b, ,II888888888PZ;;' `8888888I8888888888888b, II888888888' 888888I8888888888888888 ,II888888888 ,888888I8888888888888888 ,d88888888888 d888888I8888888888ZZZZZZ ,ad888888888888I 8888888I8888ZZZZZZZZZZZZ 888888888888888' 888888IZZZZZZZZZZZZZZZZZ 8888888888P'8P' Y888ZZZZZZZZZZZZZZZZZZZZ 888888888, " ,ZZZZZZZZZZZZZZZZZZZZZZZ 8888888888, ,ZZZZZZZZZZZZZZZZZZZZZZZZZZ 888888888888a, _ ,ZZZZZZZZZZZZZZZZZZZZ88888888 888888888888888ba,_d' ,ZZZZZZZZZZZZZZZZZ8888888888888 8888888888888888888888bbbaaa,,,______,ZZZZZZZZZZZZZZZ88888888888888888 88888888888888888888888888888888888ZZZZZZZZZZZZZZZ88888888888888888888 8888888888888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888888 888888888888888888888888888888888ZZZZZZZZZZZZZZ88888888888888888888888 8888888888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888888888 88888888888888888888888888888ZZZZZZZZZZZZZZ888888888888888888888888888 8888888888888888888888888888ZZZZZZZZZZZZZZ88888888888888888 Normand 8 88888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888 Veilleux 8 8888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888888888888888
2015-05-12 19:20:58 +02:00
},
onCreated() {
this.newCardFormIsVisible = new ReactiveVar(true);
this.collapse = new ReactiveVar(Utils.getListCollapseState(this.data()));
},
// The jquery UI sortable library is the best solution I've found so far. I
// tried sortable and dragula but they were not powerful enough four our use
// case. I also considered writing/forking a drag-and-drop + sortable library
// but it's probably too much work.
// By calling asking the sortable library to cancel its move on the `stop`
// callback, we basically solve all issues related to reactive updates. A
// comment below provides further details.
onRendered() {
this.list = this.firstNode();
this.resizeHandle = this.find('.js-list-resize-handle');
this.initializeListResize();
const ensureCollapseState = (collapsed) => {
if (this.collapse.get() === collapsed) return;
if (this.autoWidth() || collapsed) {
$(this.resizeHandle).hide();
} else {
$(this.resizeHandle).show();
}
this.collapse.set(collapsed);
this.initializeListResize();
}
// Reactively update collapse appearance and resize handle visibility when auto-width or collapse changes
this.autorun(() => {
ensureCollapseState(Utils.getListCollapseState(this.data()));
});
},
2023-06-13 12:54:18 -05:00
collapsed() {
return this.collapse.get();
},
2023-06-13 12:54:18 -05:00
listWidth() {
const user = ReactiveCache.getCurrentUser();
2023-06-13 12:54:18 -05:00
const list = Template.currentData();
if (!list) return 270; // Return default width if list is not available
if (user) {
// For logged-in users, get from user profile
return user.getListWidthFromStorage(list.boardId, list._id);
} else {
// For non-logged-in users, get from localStorage
try {
const stored = localStorage.getItem('wekan-list-widths');
if (stored) {
const widths = JSON.parse(stored);
if (widths[list.boardId] && widths[list.boardId][list._id]) {
return widths[list.boardId][list._id];
}
}
} catch (e) {
console.warn('Error reading list width from localStorage:', e);
}
return 270; // Return default width if not found
}
2023-06-13 12:54:18 -05:00
},
listConstraint() {
const user = ReactiveCache.getCurrentUser();
const list = Template.currentData();
if (!list) return 0;
if (user) {
// For logged-in users, get from user profile
return user.getListConstraintFromStorage(list.boardId, list._id);
} else {
// For non-logged-in users, get from localStorage
try {
const stored = localStorage.getItem('wekan-list-constraints');
if (stored) {
const constraints = JSON.parse(stored);
if (constraints[list.boardId] && constraints[list.boardId][list._id]) {
return constraints[list.boardId][list._id];
}
}
} catch (e) {
console.warn('Error reading list constraint from localStorage:', e);
}
return 0;
}
},
autoWidth() {
const user = ReactiveCache.getCurrentUser();
const list = Template.currentData();
if (!user) {
// For non-logged-in users, auto-width is disabled
return false;
}
return user.isAutoWidth(list.boardId);
},
initializeListResize() {
// Check if we're still in a valid template context
if (!this.data()) {
console.warn('No current template data available for list resize initialization');
return;
}
// Check if elements exist
if (!this.list || !this.resizeHandle) {
console.info('List or resize handle not found, retrying in 100ms');
Meteor.setTimeout(() => {
if (!this.isDestroyed) {
this.initializeListResize();
}
}, 100);
return;
}
let isResizing = false;
let previousLimit = false;
// seems reasonable; better let user shrink too much that too little
const minWidth = 280;
// stored width
const width = this.listWidth();
// min-width is initially min-content; a good start
let maxWidth = this.listConstraint() || parseInt(this.list.style.getProperty('--list-min-width', `${(minWidth)}px`), 10) || width + 100;
if (!width || width > maxWidth) {
width = (maxWidth + minWidth) / 2;
}
this.list.style.setProperty('--list-min-width', `${Math.round(minWidth)}px`);
// actual size before fitting (usually max-content equivalent)
this.list.style.setProperty('--list-max-width', `${Math.round(maxWidth)}px`);
// avoid jump effect and ensure width stays consistent
this.list.style.setProperty('--list-width', `${Math.round(width)}px`);
const component = this;
// wait for click to add other events
const startResize = (e) => {
// gain access to modern attributes e.g. isPrimary
e = e.originalEvent;
if (isResizing || Utils.shouldIgnorePointer(e)) {
return;
}
e.preventDefault();
e.stopPropagation();
$(document).on('pointermove', doResize);
// e.g. debugger can cancel event without pointerup being fired
$(document).on('pointercancel', stopResize);
$(document).on('pointerup', stopResize);
// --list-width can be either a stored size or "auto"; get actual computed size
component.currentWidth = component.list.offsetWidth;
component.list.classList.add('list-resizing');
document.body.classList.add('list-resizing-active');
isResizing = true;
};
const doResize = (e) => {
e = e.originalEvent;
e.preventDefault();
e.stopPropagation();
if (!isResizing || !e.isPrimary) {
return;
}
if (!previousLimit && component.collapsed()) {
previousLimit = true;
component.list.classList.add('cannot-resize');
return;
}
// relative to document, always >0 because pointer sticks to the right of list
const deltaX = e.clientX - component.list.getBoundingClientRect().right;
const candidateWidth = component.currentWidth + deltaX;
component.currentWidth = Math.max(minWidth, Math.min(maxWidth, candidateWidth));
const reachingMax = (maxWidth - component.currentWidth - 20) <= 0
const reachingMin = (component.currentWidth - 20 - minWidth) <= 0
// visual indicator to avoid trying too hard; try not to apply each tick
if (!previousLimit && (reachingMax && deltaX > 0 || reachingMin && deltaX < 0)) {
component.list.classList.add('cannot-resize');
previousLimit = true;
} else if (previousLimit && !reachingMax && !reachingMin) {
component.list.classList.remove('cannot-resize');
previousLimit = false;
}
// Apply the new width immediately for real-time feedback
component.list.style.setProperty('--list-width', `${component.currentWidth}px`);
};
const stopResize = (e) => {
e = e.originalEvent;
e.preventDefault();
e.stopPropagation();
if (!isResizing || !e.isPrimary) {
return;
}
// hopefully be gentler on cpu
$(document).off('pointermove', doResize);
$(document).off('pointercancel', stopResize);
$(document).off('pointerup', stopResize);
isResizing = false;
if (previousLimit) {
component.list.classList.remove('cannot-resize');
}
const finalWidth = parseInt(component.list.style.getPropertyValue('--list-width'), 10);
// Remove visual feedback but keep the height
component.list.classList.remove('list-resizing');
document.body.classList.remove('list-resizing-active');
if (component.collapse.get()) {
return;
}
// Save the new width using the existing system
const list = component.data();
const boardId = list.boardId;
const listId = list._id;
// Use the new storage method that handles both logged-in and non-logged-in users
if (process.env.DEBUG === 'true') {
}
const currentUser = ReactiveCache.getCurrentUser();
if (currentUser) {
// For logged-in users, use server method
Meteor.call('applyListWidthToStorage', boardId, listId, finalWidth, maxWidth, (error, result) => {
if (error) {
console.error('Error saving list width:', error);
} else {
if (process.env.DEBUG === 'true') {
}
}
});
} else {
// For non-logged-in users, save to localStorage directly
try {
// Save list width
const storedWidths = localStorage.getItem('wekan-list-widths');
let widths = storedWidths ? JSON.parse(storedWidths) : {};
if (!widths[boardId]) {
widths[boardId] = {};
}
widths[boardId][listId] = finalWidth;
localStorage.setItem('wekan-list-widths', JSON.stringify(widths));
// Save list constraint
const storedConstraints = localStorage.getItem('wekan-list-constraints');
let constraints = storedConstraints ? JSON.parse(storedConstraints) : {};
if (!constraints[boardId]) {
constraints[boardId] = {};
}
constraints[boardId][listId] = listConstraint;
localStorage.setItem('wekan-list-constraints', JSON.stringify(constraints));
if (process.env.DEBUG === 'true') {
}
} catch (e) {
console.warn('Error saving list width/constraint to localStorage:', e);
}
}
e.preventDefault();
};
// handle both pointer and touch
$(this.resizeHandle).on("pointerdown", startResize);
// Clean up on component destruction
component.onDestroyed(() => {
$(document).off('mousemove', doResize);
$(document).off('mouseup', stopResize);
$(document).off('touchmove', doResize);
$(document).off('touchend', stopResize);
});
},
Renaissance _,,ad8888888888bba,_ ,ad88888I888888888888888ba, ,88888888I88888888888888888888a, ,d888888888I8888888888888888888888b, d88888PP"""" ""YY88888888888888888888b, ,d88"'__,,--------,,,,.;ZZZY8888888888888, ,8IIl'" ;;l"ZZZIII8888888888, ,I88l;' ;lZZZZZ888III8888888, ,II88Zl;. ;llZZZZZ888888I888888, ,II888Zl;. .;;;;;lllZZZ888888I8888b ,II8888Z;; `;;;;;''llZZ8888888I8888, II88888Z;' .;lZZZ8888888I888b II88888Z; _,aaa, .,aaaaa,__.l;llZZZ88888888I888 II88888IZZZZZZZZZ, .ZZZZZZZZZZZZZZ;llZZ88888888I888, II88888IZZ<'(@@>Z| |ZZZ<'(@@>ZZZZ;;llZZ888888888I88I ,II88888; `""" ;| |ZZ; `""" ;;llZ8888888888I888 II888888l `;; .;llZZ8888888888I888, ,II888888Z; ;;; .;;llZZZ8888888888I888I III888888Zl; .., `;; ,;;lllZZZ88888888888I888 II88888888Z;;...;(_ _) ,;;;llZZZZ88888888888I888, II88888888Zl;;;;;' `--'Z;. .,;;;;llZZZZ88888888888I888b ]I888888888Z;;;;' ";llllll;..;;;lllZZZZ88888888888I8888, II888888888Zl.;;"Y88bd888P";;,..;lllZZZZZ88888888888I8888I II8888888888Zl;.; `"PPP";;;,..;lllZZZZZZZ88888888888I88888 II888888888888Zl;;. `;;;l;;;;lllZZZZZZZZW88888888888I88888 `II8888888888888Zl;. ,;;lllZZZZZZZZWMZ88888888888I88888 II8888888888888888ZbaalllZZZZZZZZZWWMZZZ8888888888I888888, `II88888888888888888b"WWZZZZZWWWMMZZZZZZI888888888I888888b `II88888888888888888;ZZMMMMMMZZZZZZZZllI888888888I8888888 `II8888888888888888 `;lZZZZZZZZZZZlllll888888888I8888888, II8888888888888888, `;lllZZZZllllll;;.Y88888888I8888888b, ,II8888888888888888b .;;lllllll;;;.;..88888888I88888888b, II888888888888888PZI;. .`;;;.;;;..; ...88888888I8888888888, II888888888888PZ;;';;. ;. .;. .;. .. Y8888888I88888888888b, ,II888888888PZ;;' `8888888I8888888888888b, II888888888' 888888I8888888888888888 ,II888888888 ,888888I8888888888888888 ,d88888888888 d888888I8888888888ZZZZZZ ,ad888888888888I 8888888I8888ZZZZZZZZZZZZ 888888888888888' 888888IZZZZZZZZZZZZZZZZZ 8888888888P'8P' Y888ZZZZZZZZZZZZZZZZZZZZ 888888888, " ,ZZZZZZZZZZZZZZZZZZZZZZZ 8888888888, ,ZZZZZZZZZZZZZZZZZZZZZZZZZZ 888888888888a, _ ,ZZZZZZZZZZZZZZZZZZZZ88888888 888888888888888ba,_d' ,ZZZZZZZZZZZZZZZZZ8888888888888 8888888888888888888888bbbaaa,,,______,ZZZZZZZZZZZZZZZ88888888888888888 88888888888888888888888888888888888ZZZZZZZZZZZZZZZ88888888888888888888 8888888888888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888888 888888888888888888888888888888888ZZZZZZZZZZZZZZ88888888888888888888888 8888888888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888888888 88888888888888888888888888888ZZZZZZZZZZZZZZ888888888888888888888888888 8888888888888888888888888888ZZZZZZZZZZZZZZ88888888888888888 Normand 8 88888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888 Veilleux 8 8888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888888888888888
2015-05-12 19:20:58 +02:00
}).register('list');
Template.miniList.events({
'click .js-select-list'() {
const listId = this._id;
Session.set('currentList', listId);
},
});
Template.miniList.helpers({
isCurrentList() {
const currentList = Utils.getCurrentList();
const list = Template.currentData();
return currentList && currentList._id == list._id;
},
});