feat: implémenter système Todo complet avec bouton segmenté sidebar (bookmark/note/todo), toggle Todo dans formulaire édition avec gestion émoji ✅ automatique, masquage champ URL pour notes/todos, tag shaarli-todo unifié (remplacement todo), déduplication tags case-insensitive, correction tag archive (shaarli-archiver→shaarli-archive), ajout shaarli-todo aux tags cachés par défaut, styles bouton segmenté avec gradient bleu et hover states, et support responsive mobile avec masquage tex
This commit is contained in:
@@ -91,11 +91,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const DEFAULT_HIDDEN_TAGS = [
|
||||
'note',
|
||||
'shaarli-pin',
|
||||
'shaarli-todo',
|
||||
'note-color-*',
|
||||
'notebg-*',
|
||||
'notefilter-*',
|
||||
'readitlater',
|
||||
'shaarli-archiver'
|
||||
'shaarli-archive'
|
||||
];
|
||||
|
||||
// Get hidden tags from localStorage
|
||||
@@ -1602,6 +1603,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const tagsTextInput = form.querySelector('.bookmark-tags-text-input');
|
||||
const readLaterCheckbox = form.querySelector('.bookmark-toggle-readlater');
|
||||
const noteCheckbox = form.querySelector('.bookmark-toggle-note');
|
||||
const todoCheckbox = form.querySelector('.bookmark-toggle-todo');
|
||||
const titleInput = form.querySelector('input[name="lf_title"]');
|
||||
const isDarkTheme = document.documentElement.getAttribute('data-theme') === 'dark';
|
||||
|
||||
@@ -1677,6 +1679,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
.map((tag) => normalizeTag(tag))
|
||||
.filter(Boolean);
|
||||
|
||||
// Remove duplicate tags (case-insensitive)
|
||||
tags = tags.filter((tag, index, self) =>
|
||||
index === self.findIndex((t) => t.toLowerCase() === tag.toLowerCase())
|
||||
);
|
||||
|
||||
// Update return URL based on bookmark type (note vs regular)
|
||||
const returnUrlInput = form.querySelector('input[name="returnurl"]');
|
||||
if (returnUrlInput) {
|
||||
@@ -1702,19 +1709,35 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
noteCheckbox.checked = tags.some((tag) => /^note$/i.test(tag));
|
||||
};
|
||||
|
||||
const syncTodoCheckbox = () => {
|
||||
if (!todoCheckbox) return;
|
||||
todoCheckbox.checked = tags.some((tag) => /^shaarli-todo$/i.test(tag));
|
||||
};
|
||||
|
||||
// Helper functions to manage note emoji in title
|
||||
const NOTE_EMOJI = '📝';
|
||||
const TODO_EMOJI = '✅';
|
||||
|
||||
const hasNoteEmoji = (title) => {
|
||||
return title && title.startsWith(NOTE_EMOJI);
|
||||
};
|
||||
|
||||
const hasTodoEmoji = (title) => {
|
||||
return title && title.startsWith(TODO_EMOJI);
|
||||
};
|
||||
|
||||
const addNoteEmoji = (title) => {
|
||||
if (!title) return NOTE_EMOJI + ' ';
|
||||
if (hasNoteEmoji(title)) return title;
|
||||
return NOTE_EMOJI + ' ' + title;
|
||||
};
|
||||
|
||||
const addTodoEmoji = (title) => {
|
||||
if (!title) return TODO_EMOJI + ' ';
|
||||
if (hasTodoEmoji(title)) return title;
|
||||
return TODO_EMOJI + ' ' + title;
|
||||
};
|
||||
|
||||
const removeNoteEmoji = (title) => {
|
||||
if (!title) return '';
|
||||
if (title.startsWith(NOTE_EMOJI + ' ')) {
|
||||
@@ -1726,6 +1749,17 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
return title;
|
||||
};
|
||||
|
||||
const removeTodoEmoji = (title) => {
|
||||
if (!title) return '';
|
||||
if (title.startsWith(TODO_EMOJI + ' ')) {
|
||||
return title.substring(TODO_EMOJI.length + 1);
|
||||
}
|
||||
if (title.startsWith(TODO_EMOJI)) {
|
||||
return title.substring(TODO_EMOJI.length);
|
||||
}
|
||||
return title;
|
||||
};
|
||||
|
||||
const updateNoteTitle = (isNote) => {
|
||||
if (!titleInput) return;
|
||||
const currentTitle = titleInput.value || '';
|
||||
@@ -1739,6 +1773,20 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const updateTodoTitle = (isTodo) => {
|
||||
if (!titleInput) return;
|
||||
const currentTitle = titleInput.value || '';
|
||||
if (isTodo) {
|
||||
if (!hasTodoEmoji(currentTitle)) {
|
||||
titleInput.value = addTodoEmoji(currentTitle);
|
||||
}
|
||||
} else {
|
||||
if (hasTodoEmoji(currentTitle)) {
|
||||
titleInput.value = removeTodoEmoji(currentTitle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addTag = (rawTag) => {
|
||||
const tag = normalizeTag(rawTag);
|
||||
@@ -1750,6 +1798,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
updateHiddenTags();
|
||||
syncReadLaterCheckbox();
|
||||
syncNoteCheckbox();
|
||||
syncTodoCheckbox();
|
||||
renderTags();
|
||||
refreshAutocomplete();
|
||||
}
|
||||
@@ -1760,6 +1809,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
updateHiddenTags();
|
||||
syncReadLaterCheckbox();
|
||||
syncNoteCheckbox();
|
||||
syncTodoCheckbox();
|
||||
renderTags();
|
||||
refreshAutocomplete();
|
||||
};
|
||||
@@ -1861,6 +1911,26 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
updateHiddenTags();
|
||||
syncReadLaterCheckbox();
|
||||
syncTodoCheckbox();
|
||||
renderTags();
|
||||
refreshAutocomplete();
|
||||
});
|
||||
}
|
||||
|
||||
if (todoCheckbox) {
|
||||
todoCheckbox.addEventListener('change', () => {
|
||||
tags = tags.filter((tag) => !/^shaarli-todo$/i.test(tag));
|
||||
|
||||
if (todoCheckbox.checked) {
|
||||
tags.push('shaarli-todo');
|
||||
updateTodoTitle(true);
|
||||
} else {
|
||||
updateTodoTitle(false);
|
||||
}
|
||||
|
||||
updateHiddenTags();
|
||||
syncReadLaterCheckbox();
|
||||
syncNoteCheckbox();
|
||||
renderTags();
|
||||
refreshAutocomplete();
|
||||
});
|
||||
@@ -1889,13 +1959,24 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
updateHiddenTags();
|
||||
syncReadLaterCheckbox();
|
||||
syncNoteCheckbox();
|
||||
syncTodoCheckbox();
|
||||
renderTags();
|
||||
refreshAutocomplete();
|
||||
|
||||
// Initial title update if note tag is present
|
||||
if (noteCheckbox && noteCheckbox.checked && titleInput) {
|
||||
// Initial title update if note or todo tag is present
|
||||
if (todoCheckbox && todoCheckbox.checked && titleInput) {
|
||||
const currentTitle = titleInput.value || '';
|
||||
if (!hasNoteEmoji(currentTitle) && !currentTitle.trim()) {
|
||||
if (!hasTodoEmoji(currentTitle) && !currentTitle.trim()) {
|
||||
titleInput.value = TODO_EMOJI + ' ';
|
||||
} else if (!hasTodoEmoji(currentTitle)) {
|
||||
titleInput.value = addTodoEmoji(currentTitle);
|
||||
}
|
||||
} else if (noteCheckbox && noteCheckbox.checked && titleInput) {
|
||||
const currentTitle = titleInput.value || '';
|
||||
// Replace default "Note:" title with just the emoji
|
||||
if (currentTitle === 'Note:' || currentTitle === 'Note') {
|
||||
titleInput.value = NOTE_EMOJI + ' ';
|
||||
} else if (!hasNoteEmoji(currentTitle) && !currentTitle.trim()) {
|
||||
titleInput.value = NOTE_EMOJI + ' ';
|
||||
} else if (!hasNoteEmoji(currentTitle)) {
|
||||
titleInput.value = addNoteEmoji(currentTitle);
|
||||
|
||||
Reference in New Issue
Block a user