feat: implémenter vue Archive complète avec navigation sidebar/header, filtrage notes archivées via tag shaarli-archive, bouton archivage/désarchivage dans modal et hover actions, styles dédiés avec titre centré et icône archive, layout responsive identique à Notes, et correction logique filtrage renderNotes pour exclusion mutuelle archive/notes normales
This commit is contained in:
parent
777fe93b7e
commit
4280ae171b
@ -2112,3 +2112,71 @@ body.view-notes .content-container {
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* --- ARCHIVE VIEW --- */
|
||||
body.view-archive .content-container {
|
||||
padding: 2rem;
|
||||
background-color: var(--bg-body);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
[data-theme="dark"] body.view-archive .content-container {
|
||||
background-color: var(--bg-body);
|
||||
}
|
||||
|
||||
/* Archive Title */
|
||||
.archive-title-container {
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.archive-title {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-color, #202124);
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.archive-title i {
|
||||
font-size: 1.5rem;
|
||||
color: var(--primary-color, #2563eb);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .archive-title {
|
||||
color: #e8eaed;
|
||||
}
|
||||
|
||||
.archive-subtitle {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-light, #5f6368);
|
||||
margin: 0.25rem 0 0 0;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .archive-subtitle {
|
||||
color: #9aa0a6;
|
||||
}
|
||||
|
||||
/* Archive wrapper */
|
||||
.archive-wrapper {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Archive top bar adjustments */
|
||||
.archive-top-bar {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-right: 0;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.archive-top-bar .notes-tools {
|
||||
position: relative;
|
||||
right: auto;
|
||||
top: auto;
|
||||
transform: none;
|
||||
}
|
||||
@ -33,6 +33,13 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
if (typeof initTagDisplayAndRemoval === "function") {
|
||||
initTagDisplayAndRemoval();
|
||||
}
|
||||
} else if (searchTags === "shaarli-archive") {
|
||||
// Vue Archive - similaire à Notes mais pour les notes archivées
|
||||
initArchiveView(linkList, container);
|
||||
// Puis supprimer les tags techniques de l'affichage
|
||||
if (typeof initTagDisplayAndRemoval === "function") {
|
||||
initTagDisplayAndRemoval();
|
||||
}
|
||||
} else {
|
||||
// Vue standard : supprimer les tags techniques
|
||||
if (typeof initTagDisplayAndRemoval === "function") {
|
||||
@ -1964,11 +1971,11 @@ function initNoteView(linkList, container) {
|
||||
const editUrl = modalCard.dataset.editUrl;
|
||||
if (!noteId || !editUrl) return;
|
||||
|
||||
addTagToNote(editUrl, "shaarli-archiver")
|
||||
addTagToNote(editUrl, "shaarli-archive")
|
||||
.then(() => {
|
||||
if (modalOverlay.currentNote) {
|
||||
if (!modalOverlay.currentNote.tags.includes("shaarli-archiver")) {
|
||||
modalOverlay.currentNote.tags.push("shaarli-archiver");
|
||||
if (!modalOverlay.currentNote.tags.includes("shaarli-archive")) {
|
||||
modalOverlay.currentNote.tags.push("shaarli-archive");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1993,7 +2000,231 @@ function initNoteView(linkList, container) {
|
||||
});
|
||||
|
||||
document.addEventListener("click", (e) => {
|
||||
if (e.target.closest(".note-hover-actions .palette-popup") || e.target.closest('.note-hover-actions [id^="palette-"]')) {
|
||||
if (!e.target.closest(".note-hover-actions .palette-popup") || e.target.closest('.note-hover-actions [id^="palette-"]')) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.querySelectorAll(".note-hover-actions .palette-popup.open").forEach((p) => {
|
||||
p.classList.remove("open");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the Archive view (similar to Notes but for archived notes)
|
||||
*/
|
||||
function initArchiveView(linkList, container) {
|
||||
document.body.classList.add("view-notes", "view-archive");
|
||||
|
||||
// Hide standard toolbar
|
||||
const toolbar = document.querySelector(".content-toolbar");
|
||||
if (toolbar) toolbar.style.display = "none";
|
||||
|
||||
// 1. Create Layout Wrapper
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.className = "notes-wrapper archive-wrapper";
|
||||
|
||||
// 2. Create Title Area (Top)
|
||||
const topBar = document.createElement("div");
|
||||
topBar.className = "notes-top-bar archive-top-bar";
|
||||
|
||||
// Title for archive view
|
||||
const titleContainer = document.createElement("div");
|
||||
titleContainer.className = "archive-title-container";
|
||||
titleContainer.innerHTML = `
|
||||
<h1 class="archive-title"><i class="mdi mdi-archive-arrow-down-outline"></i> Archive</h1>
|
||||
<p class="archive-subtitle">Notes archivées</p>
|
||||
`;
|
||||
topBar.appendChild(titleContainer);
|
||||
|
||||
// View Toggle and other tools
|
||||
const tools = document.createElement("div");
|
||||
tools.className = "notes-tools";
|
||||
tools.innerHTML = `
|
||||
<button class="icon-btn active" id="btn-view-grid" title="Vue grille"><i class="mdi mdi-view-dashboard-outline"></i></button>
|
||||
<button class="icon-btn" id="btn-view-list" title="Vue liste"><i class="mdi mdi-view-agenda-outline"></i></button>
|
||||
`;
|
||||
topBar.appendChild(tools);
|
||||
|
||||
wrapper.appendChild(topBar);
|
||||
|
||||
// 3. Content Area
|
||||
const contentArea = document.createElement("div");
|
||||
contentArea.className = "notes-content-area";
|
||||
|
||||
const links = Array.from(linkList.querySelectorAll(".link-outer"));
|
||||
const notes = links.map((link) => parseNoteFromLink(link));
|
||||
|
||||
// Filter only archived notes
|
||||
const archivedNotes = notes.filter((note) => (note.tags || []).includes("shaarli-archive"));
|
||||
|
||||
// Initial Render (Grid)
|
||||
renderNotes(contentArea, archivedNotes, "grid", true); // true = archive mode
|
||||
|
||||
wrapper.appendChild(contentArea);
|
||||
|
||||
// Replace original list
|
||||
linkList.style.display = "none";
|
||||
if (linkList.parentNode) {
|
||||
linkList.parentNode.insertBefore(wrapper, linkList);
|
||||
} else {
|
||||
container.appendChild(wrapper);
|
||||
}
|
||||
|
||||
// Modal Container (reuse the same modal as notes)
|
||||
const modalOverlay = document.createElement("div");
|
||||
modalOverlay.className = "note-modal-overlay";
|
||||
modalOverlay.innerHTML = `
|
||||
<div class="note-modal note-color-default">
|
||||
<div class="note-modal-header">
|
||||
<h2 class="note-title" id="note-modal-title"></h2>
|
||||
<button type="button" class="note-modal-pin-toggle" id="note-modal-pin" title="Épingler">
|
||||
<i class="mdi mdi-pin-outline"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="note-modal-content"></div>
|
||||
<div class="note-modal-tags is-empty" id="note-modal-tags"></div>
|
||||
<div class="note-modal-actions">
|
||||
<div class="note-modal-actions-left">
|
||||
<div class="note-modal-color-picker">
|
||||
<button type="button" id="note-modal-color-btn" title="Couleur"><i class="mdi mdi-palette-outline"></i></button>
|
||||
<div class="palette-popup note-modal-palette" id="note-modal-color-popup"></div>
|
||||
</div>
|
||||
<button type="button" title="Rappel"><i class="mdi mdi-bell-outline"></i></button>
|
||||
<button type="button" title="Collaborateur"><i class="mdi mdi-account-plus-outline"></i></button>
|
||||
<button type="button" title="Image"><i class="mdi mdi-image-outline"></i></button>
|
||||
<button type="button" id="note-modal-unarchive" title="Désarchiver"><i class="mdi mdi-archive-arrow-up-outline"></i></button>
|
||||
<a href="#" id="note-modal-edit" title="Modifier"><i class="mdi mdi-pencil-outline"></i></a>
|
||||
<button type="button" id="note-modal-delete" title="Supprimer"><i class="mdi mdi-dots-vertical"></i></button>
|
||||
</div>
|
||||
<button type="button" class="note-modal-close-btn" id="note-modal-close">Fermer</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(modalOverlay);
|
||||
|
||||
// Event Listeners for Toggles
|
||||
const btnGrid = wrapper.querySelector("#btn-view-grid");
|
||||
const btnList = wrapper.querySelector("#btn-view-list");
|
||||
|
||||
btnGrid.addEventListener("click", () => {
|
||||
btnGrid.classList.add("active");
|
||||
btnList.classList.remove("active");
|
||||
renderNotes(contentArea, archivedNotes, "grid", true);
|
||||
});
|
||||
|
||||
btnList.addEventListener("click", () => {
|
||||
btnList.classList.add("active");
|
||||
btnGrid.classList.remove("active");
|
||||
renderNotes(contentArea, archivedNotes, "list", true);
|
||||
});
|
||||
|
||||
// Close Modal
|
||||
modalOverlay.querySelector("#note-modal-close").addEventListener("click", () => {
|
||||
modalOverlay.classList.remove("open");
|
||||
});
|
||||
modalOverlay.addEventListener("click", (e) => {
|
||||
if (e.target === modalOverlay) modalOverlay.classList.remove("open");
|
||||
});
|
||||
|
||||
const modalPinBtn = modalOverlay.querySelector("#note-modal-pin");
|
||||
const modalColorBtn = modalOverlay.querySelector("#note-modal-color-btn");
|
||||
const modalColorPopup = modalOverlay.querySelector("#note-modal-color-popup");
|
||||
|
||||
modalColorBtn.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const modalCard = modalOverlay.querySelector(".note-modal");
|
||||
openBackgroundStudioPanel({
|
||||
anchorEl: modalColorBtn,
|
||||
mode: "modal",
|
||||
entityId: modalCard ? modalCard.dataset.noteId || "" : "",
|
||||
editUrl: modalCard ? modalCard.dataset.editUrl || "" : "",
|
||||
currentColor: modalCard ? getElementVisualColor(modalCard) : "default",
|
||||
currentFilter: modalCard ? getElementVisualFilter(modalCard) : "none",
|
||||
currentBackground: modalCard ? getElementVisualBackground(modalCard) : "none",
|
||||
currentFontColor: modalCard ? getElementVisualFontColor(modalCard) : "auto",
|
||||
title: "Mes images & couleurs",
|
||||
});
|
||||
});
|
||||
|
||||
modalPinBtn.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const modalCard = modalOverlay.querySelector(".note-modal");
|
||||
const noteId = modalCard.dataset.noteId;
|
||||
const editUrl = modalCard.dataset.editUrl;
|
||||
|
||||
if (!noteId || !editUrl) return;
|
||||
|
||||
togglePinTag(noteId, editUrl, modalPinBtn);
|
||||
|
||||
const isPinned = modalPinBtn.classList.contains("active");
|
||||
let tags = (modalCard.dataset.tags || "")
|
||||
.split("||")
|
||||
.filter((t) => t);
|
||||
|
||||
if (isPinned) {
|
||||
if (!tags.includes("shaarli-pin")) tags.push("shaarli-pin");
|
||||
} else {
|
||||
tags = tags.filter((t) => t !== "shaarli-pin");
|
||||
}
|
||||
|
||||
modalCard.dataset.tags = tags.join("||");
|
||||
renderModalTags(modalOverlay.querySelector("#note-modal-tags"), tags);
|
||||
|
||||
if (modalOverlay.currentNote) {
|
||||
modalOverlay.currentNote.isPinned = isPinned;
|
||||
modalOverlay.currentNote.tags = tags;
|
||||
}
|
||||
});
|
||||
|
||||
modalOverlay.querySelector("#note-modal-delete").addEventListener("click", () => {
|
||||
const modalCard = modalOverlay.querySelector(".note-modal");
|
||||
const deleteUrl = modalCard.dataset.deleteUrl;
|
||||
if (deleteUrl && deleteUrl !== "#") {
|
||||
window.location.href = deleteUrl;
|
||||
}
|
||||
});
|
||||
|
||||
// Unarchive button in modal
|
||||
modalOverlay.querySelector("#note-modal-unarchive").addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const modalCard = modalOverlay.querySelector(".note-modal");
|
||||
const noteId = modalCard.dataset.noteId;
|
||||
const editUrl = modalCard.dataset.editUrl;
|
||||
if (!noteId || !editUrl) return;
|
||||
|
||||
removeTagFromEntity(editUrl, "shaarli-archive")
|
||||
.then(() => {
|
||||
if (modalOverlay.currentNote) {
|
||||
modalOverlay.currentNote.tags = (modalOverlay.currentNote.tags || []).filter((t) => t !== "shaarli-archive");
|
||||
}
|
||||
|
||||
const noteCard = document.querySelector(`.note-card[data-id="${noteId}"]`);
|
||||
if (noteCard) noteCard.remove();
|
||||
|
||||
const index = archivedNotes.findIndex((n) => String(n.id) === String(noteId));
|
||||
if (index > -1) archivedNotes.splice(index, 1);
|
||||
|
||||
modalOverlay.classList.remove("open");
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Error unarchiving note:", err);
|
||||
alert("Erreur lors du désarchivage de la note.");
|
||||
});
|
||||
});
|
||||
|
||||
modalOverlay.addEventListener("click", (e) => {
|
||||
if (!e.target.closest(".note-modal-color-picker")) {
|
||||
modalColorPopup.classList.remove("open");
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("click", (e) => {
|
||||
if (!e.target.closest(".note-hover-actions .palette-popup") || e.target.closest('.note-hover-actions [id^="palette-"]')) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2064,11 +2295,19 @@ function parseNoteFromLink(linkEl) {
|
||||
return { id, title, descHtml, descText, coverImage, url, tags, color, filter, background, fontColor, editUrl, deleteUrl, pinUrl, isPinned };
|
||||
}
|
||||
|
||||
function renderNotes(container, notes, viewMode) {
|
||||
function renderNotes(container, notes, viewMode, isArchiveMode = false) {
|
||||
container.innerHTML = "";
|
||||
container.className = viewMode === "grid" ? "notes-masonry" : "notes-list-view";
|
||||
|
||||
const visibleNotes = notes.filter((note) => !(note.tags || []).includes("shaarli-archiver"));
|
||||
// Filter notes based on archive mode
|
||||
let visibleNotes;
|
||||
if (isArchiveMode) {
|
||||
// In archive mode: show only notes with shaarli-archive tag
|
||||
visibleNotes = notes.filter((note) => (note.tags || []).includes("shaarli-archive"));
|
||||
} else {
|
||||
// In normal notes mode: hide archived notes
|
||||
visibleNotes = notes.filter((note) => !(note.tags || []).includes("shaarli-archive"));
|
||||
}
|
||||
|
||||
// Sort: Pinned items first
|
||||
visibleNotes.sort((a, b) => {
|
||||
@ -2143,6 +2382,7 @@ function renderNotes(container, notes, viewMode) {
|
||||
|
||||
// Palette Button Logic
|
||||
const paletteBtnId = `palette-${note.id}`;
|
||||
const archiveBtnId = `archive-${note.id}`;
|
||||
|
||||
actions.innerHTML = `
|
||||
<button title="Rappel"><i class="mdi mdi-bell-outline"></i></button>
|
||||
@ -2151,7 +2391,7 @@ function renderNotes(container, notes, viewMode) {
|
||||
<button title="Couleur" id="${paletteBtnId}"><i class="mdi mdi-palette-outline"></i></button>
|
||||
</div>
|
||||
<button title="Image"><i class="mdi mdi-image-outline"></i></button>
|
||||
<button title="Archiver"><i class="mdi mdi-archive-arrow-down-outline"></i></button>
|
||||
<button title="Archiver" id="${archiveBtnId}"><i class="mdi mdi-archive-arrow-down-outline"></i></button>
|
||||
<div class="spacer"></div>
|
||||
<!-- Real Actions -->
|
||||
<a href="${note.pinUrl}" title="${note.isPinned ? "Unpin" : "Pin"}" class="${note.isPinned ? "active" : ""}"><i class="mdi mdi-pin${note.isPinned ? "" : "-outline"}"></i></a>
|
||||
@ -2176,6 +2416,26 @@ function renderNotes(container, notes, viewMode) {
|
||||
});
|
||||
});
|
||||
|
||||
// Archive button handler
|
||||
const archiveBtn = actions.querySelector(`#${archiveBtnId}`);
|
||||
archiveBtn.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (!note.editUrl || note.editUrl === "#") return;
|
||||
|
||||
addTagToNote(note.editUrl, "shaarli-archive")
|
||||
.then(() => {
|
||||
// Remove the card from the view
|
||||
card.remove();
|
||||
console.log(`Note ${note.id} archived`);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Error archiving note:", err);
|
||||
alert("Erreur lors de l'archivage de la note.");
|
||||
});
|
||||
});
|
||||
|
||||
inner.appendChild(actions);
|
||||
card.appendChild(inner);
|
||||
|
||||
|
||||
@ -57,6 +57,10 @@ Bookmarklet detection logic
|
||||
<i class="mdi mdi-note-text-outline" aria-hidden="true"></i>
|
||||
<span>Notes</span>
|
||||
</a>
|
||||
<a href="{$base_path}/?searchtags=shaarli-archive" class="sidebar-link{if="isset($search_tags) && preg_match('/(^|[\s,])shaarli-archive([\s,]|$)/i', (string) $search_tags)"} active{/if}" aria-label="Archive">
|
||||
<i class="mdi mdi-archive-arrow-down-outline" aria-hidden="true"></i>
|
||||
<span>Archive</span>
|
||||
</a>
|
||||
<a href="{$base_path}/?searchtags=readitlater" class="sidebar-link{if="isset($search_tags) && preg_match('/(^|[\s,])readitlater([\s,]|$)/i', (string) $search_tags)"} active{/if}" aria-label="Read It Later">
|
||||
<i class="mdi mdi-bookmark-outline" aria-hidden="true"></i>
|
||||
<span>Read It Later</span>
|
||||
@ -151,6 +155,10 @@ Bookmarklet detection logic
|
||||
<i class="mdi mdi-note-text-outline" aria-hidden="true"></i>
|
||||
<span>NOTES</span>
|
||||
</a>
|
||||
<a href="{$base_path}/?searchtags=shaarli-archive" class="header-nav-link{if="isset($search_tags) && preg_match('/(^|[\s,])shaarli-archive([\s,]|$)/i', (string) $search_tags)"} active{/if}" aria-label="Archive">
|
||||
<i class="mdi mdi-archive-arrow-down-outline" aria-hidden="true"></i>
|
||||
<span>ARCHIVE</span>
|
||||
</a>
|
||||
<a href="{$base_path}/?searchtags=readitlater" class="header-nav-link{if="isset($search_tags) && preg_match('/(^|[\s,])readitlater([\s,]|$)/i', (string) $search_tags)"} active{/if}" aria-label="Read It Later">
|
||||
<i class="mdi mdi-bookmark-outline" aria-hidden="true"></i>
|
||||
<span>READ IT LATER</span>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user