From 28b2ddaec38611ca0ee1fd4092b4a9af020a8cbf Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Thu, 19 Feb 2026 15:52:27 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20impl=C3=A9menter=20vue=20compacte=20tab?= =?UTF-8?q?leau=20avec=20grille=20multi-colonnes=20(titre/tags/meta/action?= =?UTF-8?q?s),=20en-t=C3=AAtes=20de=20sections=20pinned/normal=20avec=20ba?= =?UTF-8?q?dges=20visuels,=20mode=20responsive=20single-column=20mobile,?= =?UTF-8?q?=20masquage=20s=C3=A9lectif=20thumbnail/description/permalink,?= =?UTF-8?q?=20actions=20hover=20avec=20opacity,=20checkbox/badge=20reposit?= =?UTF-8?q?ionn=C3=A9s,=20calendrier=20daily=20en=20modal=20fixe=20plein?= =?UTF-8?q?=20=C3=A9cran,=20am=C3=A9lioration=20indentation=20CSS=20page-e?= =?UTF-8?q?dit,=20et=20ajout=20styles=20key-value-data/bookmarklet-actions?= =?UTF-8?q?/third-party-links=20avec=20support=20complet=20th=C3=A8me=20cl?= =?UTF-8?q?air/sombre?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shaarli-pro/css/style.css | 453 ++++++++++++++++++++++++++++----- shaarli-pro/js/custom_views.js | 92 ++++++- shaarli-pro/linklist.html | 9 + 3 files changed, 489 insertions(+), 65 deletions(-) diff --git a/shaarli-pro/css/style.css b/shaarli-pro/css/style.css index 1503758..90628dc 100644 --- a/shaarli-pro/css/style.css +++ b/shaarli-pro/css/style.css @@ -1255,6 +1255,18 @@ body.view-notes .paging { grid-template-columns: minmax(0, 1fr); } +.links-list.view-compact { + display: block; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 0.8rem; + overflow: hidden; +} + +.compact-table-head { + display: none; +} + /* ===== Link Card ===== */ .link-outer { background: var(--bg-card); @@ -2209,25 +2221,6 @@ select:focus { box-shadow: 0 0 0 3px var(--primary-light); } - - - -.nav-link { - display: flex; - align-items: center; - gap: 0.375rem; - padding: 0.5rem 0.75rem; - color: var(--text-secondary); - border-radius: 0.375rem; - font-weight: 500; - transition: all 0.15s ease; -} - -.nav-link:hover { - background: var(--primary-light); - color: var(--primary); -} - /* ===== Alerts ===== */ .alert { padding: 1rem 1.25rem; @@ -3336,72 +3329,71 @@ select:focus { } .page-edit .bookmark-editor-footer { -background: var(--bookmark-panel-soft); -border-top: 1px solid var(--bookmark-panel-border); -padding: 1rem 1.5rem; + background: var(--bookmark-panel-soft); + border-top: 1px solid var(--bookmark-panel-border); + padding: 1rem 1.5rem; } .page-edit .bookmark-editor-footer .btn { -min-height: 42px; -border-radius: 10px; -padding-inline: 1rem; -font-weight: 600; + min-height: 42px; + border-radius: 10px; + padding-inline: 1rem; + font-weight: 600; } .page-edit .bookmark-editor-footer .btn-primary { -background: var(--bookmark-save-bg); -border-color: transparent; -box-shadow: 0 10px 22px rgba(77, 130, 240, 0.35); + background: var(--bookmark-save-bg); + border-color: transparent; + box-shadow: 0 10px 22px rgba(77, 130, 240, 0.35); } .page-edit .bookmark-editor-footer .btn-primary:hover { -filter: brightness(1.06); + filter: brightness(1.06); } .page-edit .bookmark-editor-footer .btn-danger { -background: #c84e4e; -border-color: transparent; + background: #c84e4e; + border-color: transparent; } .page-edit .bookmark-editor-footer .btn-danger:hover { -background: #dd5a5a; + background: #dd5a5a; } @media (max-width: 991px) { -.page-edit .editlinkform-col { -max-width: 100%; -padding-inline: 0; -} + .page-edit .editlinkform-col { + max-width: 100%; + padding-inline: 0; + } -.page-edit .bookmark-editor-header, -.page-edit .bookmark-editor-card .card-body, -.page-edit .bookmark-editor-footer { -padding: 1rem; -} + .page-edit .bookmark-editor-header, + .page-edit .bookmark-editor-card .card-body, + .page-edit .bookmark-editor-footer { + padding: 1rem; + } -.page-edit .bookmark-toggle-grid { -grid-template-columns: 1fr; -} + .page-edit .bookmark-toggle-grid { + grid-template-columns: 1fr; + } -.page-edit .bookmark-tags-input { -gap: 0.4rem; -} + .page-edit .bookmark-tags-input { + gap: 0.4rem; + } -.page-edit .bookmark-tag-pill { -height: 32px; -font-size: 0.9rem; -} + .page-edit .bookmark-tag-pill { + height: 32px; + font-size: 0.9rem; + } -.page-edit .bookmark-editor-footer { -flex-wrap: wrap; -justify-content: stretch; -} - -.page-edit .bookmark-editor-footer .btn, -.page-edit .bookmark-editor-footer a.btn { -flex: 1 1 calc(50% - 0.4rem); -} + .page-edit .bookmark-editor-footer { + flex-wrap: wrap; + justify-content: stretch; + } + .page-edit .bookmark-editor-footer .btn, + .page-edit .bookmark-editor-footer a.btn { + flex: 1 1 calc(50% - 0.4rem); + } } @media (max-width: 640px) { @@ -4039,7 +4031,342 @@ flex: 1 1 calc(50% - 0.4rem); font-size: 0.7rem; } - .daily-item-header { - font-size: 0.94rem; +} + +.key-value-data { + text-align: left; +} + +.bookmarklet-actions, +.third-party-links { + display: flex; + flex-direction: column; + gap: 0.65rem; +} + +.bookmarklet-actions .btn, +.third-party-links .btn { + width: 100%; +} + +.daily { + padding-top: 1rem; +} + +.daily-nav-unified { + gap: 0.9rem; + border-radius: 14px; + padding: 0.85rem; +} + +.daily-nav-center { + width: 100%; +} + +.daily-nav-btn, +.daily-date-pill, +.daily-calendar-btn { + min-height: 38px; +} + +.daily-calendar-panel { + position: fixed; + inset: 0; + z-index: 600; + padding: 0.9rem; + background: rgba(15, 23, 42, 0.58); + display: none; + align-items: center; + justify-content: center; +} + +.daily-calendar-panel.is-open { + display: flex; +} + +.daily-calendar-shell { + width: 100%; + max-width: 560px; + max-height: 90vh; + overflow: auto; + border-radius: 14px; +} + +.daily-calendar-sidebar, +.daily-calendar-main { + width: 100%; + padding: 1rem; +} + +.daily-calendar-sidebar { + border-right: 0; + border-bottom: 1px solid var(--border-light); +} + +.daily-calendar-day { + width: 36px; + height: 36px; + font-size: 0.85rem; +} + +.media-player-inner { + grid-template-columns: auto minmax(0, 1fr) auto; +} + +.media-player-title { + font-size: 0.8rem; +} + +.media-player-time { + min-width: auto; + font-size: 0.74rem; +} + +table { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; +} + +.pinned-section-head, +.normal-section-head { + display: none; + grid-column: 1 / -1; + align-items: center; + padding: 0.55rem 0.9rem; + margin: 0; + font-size: 0.74rem; + font-weight: 700; + letter-spacing: 0.06em; + text-transform: uppercase; +} + +.links-list.has-pinned .pinned-section-head { + display: flex; + color: #fff; + background: var(--primary); + border-radius: 0.7rem; + margin-bottom: 0.45rem; +} + +.links-list.has-normal .normal-section-head { + display: flex; + color: var(--text-muted); + background: var(--bg-body); + border-top: 1px solid var(--border); + border-bottom: 1px solid var(--border); + margin-top: 0.65rem; +} + +.links-list:not(.has-pinned) .pinned-section-head, +.links-list:not(.has-normal) .normal-section-head, +.links-list:not(.has-normal) .compact-table-head { + display: none !important; +} + +.view-compact .compact-table-head { + display: grid; + grid-column: 1 / -1; + grid-template-columns: minmax(0, 2fr) minmax(0, 1.35fr) minmax(120px, 0.75fr) auto; + gap: 0.85rem; + align-items: center; + padding: 0.8rem 1rem; + background: var(--bg-body); + border-bottom: 1px solid var(--border); + color: var(--text-muted); + font-size: 0.79rem; + font-weight: 700; + letter-spacing: 0.05em; + text-transform: uppercase; +} + +.view-compact .link-outer { + margin: 0; + border: 0; + border-radius: 0; + border-bottom: 1px solid var(--border-light); + padding: 0; + box-shadow: none; + background: transparent; +} + +.view-compact .link-outer:last-child { + border-bottom: 0; +} + +.view-compact .link-outer:hover { + border-color: var(--border-light); + box-shadow: none; + background: var(--bg-card-hover); +} + +.view-compact .link-actions { + opacity: 0; + transition: opacity 0.15s ease; +} + +.view-compact .link-outer:hover .link-actions { + opacity: 1; +} + +.view-compact .link-thumbnail, +.view-compact .link-description, +.view-compact .link-readlater-badge { + display: none !important; +} + +.view-compact .link-content { + display: grid; + grid-template-columns: minmax(0, 2fr) minmax(0, 1.35fr) minmax(120px, 0.75fr) auto; + gap: 0.85rem; + align-items: center; + padding: 0.85rem 1rem 0.85rem 2.8rem; + padding-right: 3.2rem; +} + +.view-compact .link-header { + display: contents; + margin: 0; +} + +.view-compact .link-title { + grid-column: 1; + grid-row: 1; + padding-right: 0; + margin: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.view-compact .link-url { + grid-column: 1; + grid-row: 2; +} + +.view-compact .link-meta { + grid-column: 3; + grid-row: 1 / span 2; + margin: 0; + gap: 0.5rem; + align-self: center; +} + +.view-compact .link-date { + font-size: 0.95rem; +} + +.view-compact .link-permalink { + display: none; +} + +.view-compact .link-footer { + display: contents; + margin: 0; + padding: 0; + border: 0; +} + +.view-compact .link-tag-list { + grid-column: 2; + grid-row: 1 / span 2; + gap: 0.35rem; + align-self: center; + overflow: visible; +} + +.view-compact .link-tag, +.view-compact .link-tag.is-tech-tag { + display: inline-flex; +} + +.view-compact .link-actions { + grid-column: 4; + grid-row: 1 / span 2; + margin-left: 0; + justify-content: flex-start; + align-self: center; + flex-wrap: nowrap; +} + +.view-compact .link-actions a, +.view-compact .link-actions button, +.view-compact .link-actions .readitlater-toggle-btn { + width: 32px; + height: 32px; +} + +.view-compact .link-select-checkbox { + top: 50%; + right: 0.75rem; + transform: translateY(-50%); + width: 28px; + height: 28px; + border-radius: 0.45rem; +} + +.view-compact .link-visibility-badge { + top: 50%; + right: auto; + left: 0.85rem; + transform: translateY(-50%); + width: 24px; + height: 24px; +} + +@media (max-width: 768px) { + .view-compact .compact-table-head { + display: none; + } + + .links-list.view-compact { + border-radius: 0.7rem; + } + + .pinned-section-head, + .normal-section-head { + padding: 0.5rem 0.75rem; + font-size: 0.7rem; + } + + .view-compact .link-content { + grid-template-columns: minmax(0, 1fr); + gap: 0.55rem; + padding: 0.8rem 0.85rem 0.8rem 2.65rem; + } + + .view-compact .link-title, + .view-compact .link-url, + .view-compact .link-meta, + .view-compact .link-tag-list, + .view-compact .link-actions { + grid-column: 1; + grid-row: auto; + } + + .view-compact .link-meta { + justify-content: flex-start; + } + + .view-compact .link-actions { + width: 100%; + flex-wrap: wrap; + row-gap: 0.3rem; + opacity: 1; + } + + .view-compact .link-select-checkbox, + .view-compact .link-visibility-badge { + top: 0.7rem; + transform: none; + } + + .view-compact .link-visibility-badge { + left: 0.7rem; + } + + .view-compact .link-select-checkbox { + right: 0.7rem; } } \ No newline at end of file diff --git a/shaarli-pro/js/custom_views.js b/shaarli-pro/js/custom_views.js index 2794a69..58b8a97 100644 --- a/shaarli-pro/js/custom_views.js +++ b/shaarli-pro/js/custom_views.js @@ -11,6 +11,10 @@ document.addEventListener("DOMContentLoaded", function () { initBookmarkPaletteButtons(); } + if (typeof organizePinnedBookmarks === "function") { + window.requestAnimationFrame(organizePinnedBookmarks); + } + // Always init Pinned Items logic (sorting and listeners) // This function is defined at the end of the file if (typeof initPinnedItems === "function") { @@ -265,7 +269,7 @@ function initTagDisplayAndRemoval() { return; } if (isTechnicalTag(tag)) { - el.remove(); + el.classList.add("is-tech-tag"); } }); @@ -385,6 +389,79 @@ if (typeof console !== "undefined" && console && typeof console.info === "functi console.info("[custom_views] loaded", window.__shaarliCustomViewsVersion); } +function organizePinnedBookmarks() { + const linksList = document.getElementById("links-list"); + if (!linksList) return; + + const pinnedHead = linksList.querySelector(".pinned-section-head"); + const normalHead = linksList.querySelector(".normal-section-head"); + const compactHead = linksList.querySelector(".compact-table-head"); + if (!pinnedHead || !normalHead || !compactHead) return; + + const allBookmarks = Array.from(linksList.getElementsByClassName("link-outer")); + if (!allBookmarks.length) return; + + const isPinnedBookmark = (bookmark) => { + if (bookmark.classList.contains("is-sticky") || bookmark.classList.contains("is-pinned-tag")) { + return true; + } + + const tags = bookmark.querySelectorAll(".link-tag-list a, .link-tag-list .link-tag-link, .note-tag"); + for (const tag of tags) { + const value = (tag.textContent || "").trim().toLowerCase(); + if (value === "shaarli-pin") { + return true; + } + } + + return false; + }; + + const pinnedBookmarks = allBookmarks.filter((bookmark) => isPinnedBookmark(bookmark)); + const normalBookmarks = allBookmarks.filter((bookmark) => !isPinnedBookmark(bookmark)); + + linksList.classList.toggle("has-pinned", pinnedBookmarks.length > 0); + linksList.classList.toggle("has-normal", normalBookmarks.length > 0); + + const fragment = document.createDocumentFragment(); + + fragment.appendChild(pinnedHead); + pinnedBookmarks.forEach((bookmark) => fragment.appendChild(bookmark)); + + fragment.appendChild(normalHead); + fragment.appendChild(compactHead); + normalBookmarks.forEach((bookmark) => fragment.appendChild(bookmark)); + + linksList.replaceChildren(fragment); +} + +function initPinnedBookmarksSeparation() { + const linksList = document.getElementById("links-list"); + if (!linksList) return; + + organizePinnedBookmarks(); + + const viewButtons = ["view-grid-btn", "view-list-btn", "view-compact-btn"]; + viewButtons.forEach((id) => { + const button = document.getElementById(id); + if (!button) return; + button.addEventListener("click", () => { + window.requestAnimationFrame(organizePinnedBookmarks); + }); + }); + + linksList.addEventListener("click", (event) => { + const pinLink = event.target.closest('a[href*="/pin?token="]'); + if (!pinLink) return; + + window.setTimeout(() => { + window.location.reload(); + }, 500); + }); +} + +document.addEventListener("DOMContentLoaded", initPinnedBookmarksSeparation); + const NOTE_BACKGROUND_MANIFEST_INLINE = [ { key: "cafe", label: "Café", files: { light: "bg-light-cafe.jpg", dark: "bg-dark-cafe.jpg" } }, { key: "codes", label: "Codes", files: { light: "bg-light-codes.jpg", dark: "bg-dark-codes.jpg" } }, @@ -2660,6 +2737,10 @@ function initPinnedItems() { } } + if (typeof organizePinnedBookmarks === "function") { + window.requestAnimationFrame(organizePinnedBookmarks); + } + // 4. Click Listener for all Pin Buttons (Event Delegation) // Avoid adding multiple listeners if init called multiple times? // We'll rely on one global listener on document, but here we add it inside this function which is called once on load. @@ -2717,6 +2798,8 @@ function togglePinTag(id, editUrl, btn) { // Update Title Icon (The one "devant le titre") const card = btn.closest(".link-outer, .note-card"); if (card) { + card.classList.toggle("is-pinned-tag", isPinning); + // Update Title Icon const titleArea = card.querySelector(".link-title, .note-title"); if (titleArea) { @@ -2847,7 +2930,12 @@ function togglePinTag(id, editUrl, btn) { }); }) .then((res) => { - if (res.ok) console.log("Pin toggled successfully"); + if (res.ok) { + console.log("Pin toggled successfully"); + if (typeof organizePinnedBookmarks === "function") { + window.requestAnimationFrame(organizePinnedBookmarks); + } + } }) .catch((err) => console.error(err)); } diff --git a/shaarli-pro/linklist.html b/shaarli-pro/linklist.html index 5d62c38..bc796a0 100644 --- a/shaarli-pro/linklist.html +++ b/shaarli-pro/linklist.html @@ -52,6 +52,15 @@ {else}