feat: refactorer et améliorer le système de palette de couleurs avec nouveau Background Studio, sections organisées (couleurs/backgrounds), scrolling horizontal optimisé, galerie de miniatures avec filtres et recherche, swatches de couleurs interactifs, et améliorations visuelles incluant hover states, transitions fluides, et support complet du thème sombre
This commit is contained in:
parent
7e0ff872df
commit
97f3bbca45
@ -774,11 +774,16 @@ body.view-notes .content-container {
|
||||
background: var(--background-secondary, #ffffff);
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
border-radius: 12px;
|
||||
padding: 10px;
|
||||
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.18);
|
||||
padding: 12px;
|
||||
box-shadow: 0 14px 34px rgba(0, 0, 0, 0.22);
|
||||
z-index: 50;
|
||||
width: max-content;
|
||||
max-width: min(340px, calc(100vw - 32px));
|
||||
max-height: min(320px, calc(100vh - 140px));
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .palette-popup {
|
||||
@ -790,25 +795,84 @@ body.view-notes .content-container {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.palette-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.palette-section-title {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
color: rgba(0, 0, 0, 0.62);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .palette-section-title {
|
||||
color: rgba(255, 255, 255, 0.72);
|
||||
}
|
||||
|
||||
.palette-divider {
|
||||
height: 1px;
|
||||
background: rgba(0, 0, 0, 0.10);
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .palette-divider {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.palette-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.palette-row-colors {
|
||||
flex-wrap: nowrap;
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
padding-bottom: 4px;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.palette-row-colors::-webkit-scrollbar {
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.palette-row-colors::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 0, 0, 0.18);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .palette-row-colors::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
.palette-row-backgrounds {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 28px);
|
||||
gap: 10px;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.palette-row + .palette-row {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.palette-btn {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(0, 0, 0, 0.18);
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
background-position: center bottom;
|
||||
background-size: cover;
|
||||
flex: 0 0 auto;
|
||||
transition: transform 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .palette-btn {
|
||||
@ -820,6 +884,20 @@ body.view-notes .content-container {
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.palette-btn:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 14px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .palette-btn:hover {
|
||||
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
.palette-btn:focus-visible {
|
||||
outline: 2px solid var(--primary-color, #2563eb);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.palette-btn-bg-none {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -835,6 +913,385 @@ body.view-notes .content-container {
|
||||
top: calc(100% + 8px);
|
||||
}
|
||||
|
||||
.bg-studio-panel {
|
||||
position: fixed;
|
||||
z-index: 1200;
|
||||
width: min(420px, calc(100vw - 24px));
|
||||
border-radius: 20px;
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
border: 1px solid var(--border, #d1d5db);
|
||||
box-shadow: 0 20px 50px rgba(15, 23, 42, 0.22);
|
||||
backdrop-filter: blur(16px);
|
||||
-webkit-backdrop-filter: blur(16px);
|
||||
color: var(--text-main, #0f172a);
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bg-studio-panel {
|
||||
background: rgba(31, 37, 41, 0.92);
|
||||
border-color: #2A3238;
|
||||
box-shadow: 0 28px 70px rgba(0, 0, 0, 0.55);
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
}
|
||||
|
||||
.bg-studio-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.bg-studio-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.bg-studio-close {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(15, 23, 42, 0.16);
|
||||
background: rgba(15, 23, 42, 0.05);
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.bg-studio-close:hover {
|
||||
background: rgba(15, 23, 42, 0.10);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bg-studio-close {
|
||||
border-color: rgba(255, 255, 255, 0.12);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bg-studio-close:hover {
|
||||
background: rgba(255, 255, 255, 0.10);
|
||||
}
|
||||
|
||||
.bg-studio-gallery {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-template-rows: repeat(2, 102px);
|
||||
grid-auto-columns: 84px;
|
||||
gap: 12px;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
padding: 2px 2px 10px;
|
||||
margin-bottom: 12px;
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.bg-studio-gallery::-webkit-scrollbar {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.bg-studio-gallery::-webkit-scrollbar-thumb {
|
||||
background: rgba(15, 23, 42, 0.20);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bg-studio-gallery::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
.bg-studio-thumb {
|
||||
width: 84px;
|
||||
height: 72px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(15, 23, 42, 0.16);
|
||||
background: rgba(15, 23, 42, 0.05);
|
||||
background-position: center bottom;
|
||||
background-size: cover;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
|
||||
flex: 0 0 auto;
|
||||
overflow: hidden;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.bg-studio-thumb:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.28);
|
||||
}
|
||||
|
||||
.bg-studio-thumb.is-active {
|
||||
border: 3px solid #3b82f6;
|
||||
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.22);
|
||||
}
|
||||
|
||||
.bg-studio-thumb:focus-visible {
|
||||
outline: 3px solid rgba(59, 130, 246, 0.9);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.bg-studio-thumb-color,
|
||||
.bg-studio-thumb-solid {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.bg-studio-thumb-solid {
|
||||
background: linear-gradient(135deg, rgba(255,255,255,0.15), rgba(255,255,255,0.02));
|
||||
}
|
||||
|
||||
.bg-studio-tile {
|
||||
display: grid;
|
||||
grid-template-rows: 72px auto;
|
||||
gap: 6px;
|
||||
width: 84px;
|
||||
}
|
||||
|
||||
.bg-studio-thumb-label {
|
||||
font-size: 0.72rem;
|
||||
line-height: 1.1;
|
||||
text-align: center;
|
||||
color: var(--text-muted, rgba(15, 23, 42, 0.62));
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bg-studio-thumb-label {
|
||||
color: rgba(255, 255, 255, 0.72);
|
||||
}
|
||||
|
||||
.bg-studio-rows {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 10px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.bg-studio-row {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.bg-studio-row-heading {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.bg-studio-row-label {
|
||||
min-width: 108px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.bg-studio-swatches {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 26px);
|
||||
grid-auto-rows: 26px;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bg-studio-swatch {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(15, 23, 42, 0.22);
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
transition: transform 0.12s ease, box-shadow 0.12s ease;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.bg-studio-swatch:hover {
|
||||
transform: scale(1.15);
|
||||
box-shadow: 0 10px 20px rgba(0,0,0,0.35);
|
||||
}
|
||||
|
||||
.bg-studio-swatch.is-active {
|
||||
border: 2px solid rgba(255, 255, 255, 0.95);
|
||||
box-shadow: 0 8px 16px rgba(0,0,0,0.35);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bg-studio-swatch {
|
||||
border-color: rgba(255, 255, 255, 0.28);
|
||||
}
|
||||
|
||||
.bg-studio-filters {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.bg-studio-filter-btn {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(15, 23, 42, 0.16);
|
||||
background: rgba(15, 23, 42, 0.05);
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.bg-studio-filter-btn.is-active {
|
||||
border-color: rgba(59, 130, 246, 0.9);
|
||||
color: #60a5fa;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bg-studio-filter-btn {
|
||||
border-color: rgba(255, 255, 255, 0.16);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.bg-studio-reset {
|
||||
height: 28px;
|
||||
padding: 0 12px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(15, 23, 42, 0.18);
|
||||
background: linear-gradient(180deg, #ffffff, #f3f4f6);
|
||||
color: #111827;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.01em;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.16);
|
||||
transition: background 0.16s ease, border-color 0.16s ease, box-shadow 0.16s ease, transform 0.16s ease;
|
||||
}
|
||||
|
||||
.bg-studio-reset:hover {
|
||||
border-color: rgba(59, 130, 246, 0.5);
|
||||
background: linear-gradient(180deg, #ffffff, #e9eefb);
|
||||
box-shadow: 0 6px 14px rgba(59, 130, 246, 0.2);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.bg-studio-reset:focus-visible {
|
||||
outline: 2px solid rgba(59, 130, 246, 0.9);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bg-studio-reset {
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.08));
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bg-studio-reset:hover {
|
||||
border-color: rgba(96, 165, 250, 0.65);
|
||||
background: linear-gradient(180deg, rgba(96, 165, 250, 0.26), rgba(96, 165, 250, 0.16));
|
||||
box-shadow: 0 8px 16px rgba(2, 6, 23, 0.45);
|
||||
}
|
||||
|
||||
.bg-studio-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid var(--border, rgba(15, 23, 42, 0.16));
|
||||
background: rgba(15, 23, 42, 0.05);
|
||||
margin-bottom: 12px;
|
||||
transition: border-color 0.16s ease, box-shadow 0.16s ease, background-color 0.16s ease;
|
||||
}
|
||||
|
||||
.bg-studio-search:focus-within {
|
||||
border-color: rgba(59, 130, 246, 0.55);
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.18);
|
||||
}
|
||||
|
||||
.bg-studio-search i {
|
||||
color: var(--text-muted, rgba(15, 23, 42, 0.62));
|
||||
}
|
||||
|
||||
.bg-studio-search-input {
|
||||
flex: 1;
|
||||
border: 0 !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
border-radius: 0;
|
||||
color: inherit;
|
||||
outline: none;
|
||||
font-size: 0.95rem;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
caret-color: currentColor;
|
||||
}
|
||||
|
||||
.bg-studio-search-input:focus,
|
||||
.bg-studio-search-input:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.bg-studio-search-input::-webkit-search-cancel-button {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.bg-studio-search-input::placeholder {
|
||||
color: var(--text-muted, rgba(15, 23, 42, 0.52));
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bg-studio-search {
|
||||
border-color: rgba(255, 255, 255, 0.18);
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bg-studio-search:focus-within {
|
||||
border-color: rgba(96, 165, 250, 0.75);
|
||||
background: rgba(15, 23, 42, 0.34);
|
||||
box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.2);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bg-studio-search i {
|
||||
color: rgba(255, 255, 255, 0.82);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bg-studio-search-input {
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bg-studio-search-input::placeholder {
|
||||
color: rgba(255, 255, 255, 0.62);
|
||||
}
|
||||
|
||||
.bg-studio-clear {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(15, 23, 42, 0.16);
|
||||
background: rgba(15, 23, 42, 0.05);
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bg-studio-clear {
|
||||
border-color: rgba(255, 255, 255, 0.16);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
BIN
shaarli-pro/img/note-bg-dark/bg-dark-radio.jpg
Normal file
BIN
shaarli-pro/img/note-bg-dark/bg-dark-radio.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 103 KiB |
BIN
shaarli-pro/img/note-bg-light/bg-light-radio.jpg
Normal file
BIN
shaarli-pro/img/note-bg-light/bg-light-radio.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 124 KiB |
@ -103,6 +103,14 @@ window.SHAARLI_NOTE_BACKGROUNDS_MANIFEST = [
|
||||
dark: "bg-dark-ocean.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
"key": "radio",
|
||||
"label": "Radio",
|
||||
"files": {
|
||||
"light": "bg-light-radio.jpg",
|
||||
"dark": "bg-dark-radio.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "sports",
|
||||
label: "Sports",
|
||||
|
||||
@ -103,6 +103,14 @@
|
||||
"dark": "bg-dark-ocean.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "radio",
|
||||
"label": "Radio",
|
||||
"files": {
|
||||
"light": "bg-light-radio.jpg",
|
||||
"dark": "bg-dark-radio.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "sports",
|
||||
"label": "Sports",
|
||||
@ -143,4 +151,5 @@
|
||||
"dark": "bg-dark-voyage.jpg"
|
||||
}
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
@ -173,6 +173,7 @@ const NOTE_BACKGROUND_MANIFEST_INLINE = [
|
||||
{ key: "legumes", label: "Légumes", files: { light: "bg-light-legumes.jpg", dark: "bg-dark-legumes.jpg" } },
|
||||
{ key: "montagnes", label: "Montagnes", files: { light: "bg-light-montagnes.jpg", dark: "bg-dark-montagnes.jpg" } },
|
||||
{ key: "ocean", label: "Océan", files: { light: "bg-light-ocean.jpg", dark: "bg-dark-ocean.jpg" } },
|
||||
{ key: "radio", label: "Radio", files: { light: "bg-light-radio.jpg", dark: "bg-dark-radio.jpg" } },
|
||||
{ key: "sports", label: "Sports", files: { light: "bg-light-sports.jpg", dark: "bg-dark-sports.jpg" } },
|
||||
{ key: "vague1", label: "Vague 1", files: { light: "bg-light-vague1.jpg", dark: "bg-dark-vague1.jpg" } },
|
||||
{ key: "vague2", label: "Vague 2", files: { light: "bg-light-vague2.jpg", dark: "bg-dark-vague2.jpg" } },
|
||||
@ -499,6 +500,428 @@ function positionPalettePopup(popup) {
|
||||
}
|
||||
}
|
||||
|
||||
let backgroundStudioPanelInitialized = false;
|
||||
|
||||
function ensureBackgroundStudioPanel() {
|
||||
if (backgroundStudioPanelInitialized) return;
|
||||
if (document.getElementById("shaarli-bg-studio")) {
|
||||
backgroundStudioPanelInitialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const panel = document.createElement("div");
|
||||
panel.id = "shaarli-bg-studio";
|
||||
panel.className = "bg-studio-panel";
|
||||
panel.setAttribute("role", "dialog");
|
||||
panel.setAttribute("aria-modal", "false");
|
||||
panel.setAttribute("aria-hidden", "true");
|
||||
panel.style.display = "none";
|
||||
document.body.appendChild(panel);
|
||||
|
||||
document.addEventListener("click", (e) => {
|
||||
const p = document.getElementById("shaarli-bg-studio");
|
||||
if (!p || !p.classList.contains("open")) return;
|
||||
if (e.target.closest("#shaarli-bg-studio")) return;
|
||||
const anchor = p.__anchorEl;
|
||||
if (anchor && (e.target === anchor || e.target.closest(`#${anchor.id}`))) return;
|
||||
closeBackgroundStudioPanel();
|
||||
});
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key !== "Escape") return;
|
||||
const p = document.getElementById("shaarli-bg-studio");
|
||||
if (!p || !p.classList.contains("open")) return;
|
||||
closeBackgroundStudioPanel();
|
||||
});
|
||||
|
||||
panel.addEventListener("click", (e) => {
|
||||
const actionBtn = e.target.closest("button[data-bg-studio-action]");
|
||||
if (!actionBtn) return;
|
||||
|
||||
const panelEl = document.getElementById("shaarli-bg-studio");
|
||||
if (!panelEl) return;
|
||||
|
||||
const mode = panelEl.dataset.mode || "entity";
|
||||
const entityId = panelEl.dataset.entityId || "";
|
||||
const editUrl = panelEl.dataset.editUrl || "";
|
||||
|
||||
const action = actionBtn.dataset.bgStudioAction;
|
||||
if (action === "close") {
|
||||
closeBackgroundStudioPanel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === "set-color") {
|
||||
const key = actionBtn.dataset.colorKey || "default";
|
||||
if (mode === "modal") setModalNoteColor(key);
|
||||
else setNoteColor(entityId, key, editUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === "set-background") {
|
||||
const key = actionBtn.dataset.bgKey || "none";
|
||||
if (mode === "modal") setModalNoteBackground(key);
|
||||
else setNoteBackground(entityId, key, editUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === "set-filter") {
|
||||
panelEl.dataset.filter = actionBtn.dataset.filter || "all";
|
||||
renderBackgroundStudioPanel(panelEl);
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === "set-query") {
|
||||
panelEl.dataset.query = "";
|
||||
renderBackgroundStudioPanel(panelEl);
|
||||
const input = panelEl.querySelector(".bg-studio-search-input");
|
||||
if (input) input.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === "set-defaults") {
|
||||
if (mode === "modal") {
|
||||
setModalNoteColor("default");
|
||||
setModalNoteBackground("none");
|
||||
} else {
|
||||
setNoteColor(entityId, "default", editUrl);
|
||||
setNoteBackground(entityId, "none", editUrl);
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
panel.addEventListener("keydown", (e) => {
|
||||
const targetBtn = e.target && e.target.closest ? e.target.closest("button") : null;
|
||||
if (!targetBtn) return;
|
||||
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
if (targetBtn.hasAttribute("data-bg-studio-action") || targetBtn.classList.contains("bg-studio-thumb") || targetBtn.classList.contains("bg-studio-swatch")) {
|
||||
e.preventDefault();
|
||||
targetBtn.click();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const moveFocus = (buttons, nextIndex) => {
|
||||
if (!buttons || buttons.length === 0) return;
|
||||
const idx = Math.max(0, Math.min(buttons.length - 1, nextIndex));
|
||||
const el = buttons[idx];
|
||||
if (el && typeof el.focus === "function") el.focus();
|
||||
};
|
||||
|
||||
if (targetBtn.classList.contains("bg-studio-thumb")) {
|
||||
const buttons = Array.from(panel.querySelectorAll(".bg-studio-gallery .bg-studio-thumb"));
|
||||
const currentIndex = buttons.indexOf(targetBtn);
|
||||
if (currentIndex === -1) return;
|
||||
|
||||
const colSize = 2;
|
||||
if (e.key === "ArrowRight") {
|
||||
e.preventDefault();
|
||||
moveFocus(buttons, currentIndex + colSize);
|
||||
} else if (e.key === "ArrowLeft") {
|
||||
e.preventDefault();
|
||||
moveFocus(buttons, currentIndex - colSize);
|
||||
} else if (e.key === "ArrowDown") {
|
||||
e.preventDefault();
|
||||
moveFocus(buttons, currentIndex + 1);
|
||||
} else if (e.key === "ArrowUp") {
|
||||
e.preventDefault();
|
||||
moveFocus(buttons, currentIndex - 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetBtn.classList.contains("bg-studio-swatch")) {
|
||||
const buttons = Array.from(panel.querySelectorAll(".bg-studio-swatches .bg-studio-swatch"));
|
||||
const currentIndex = buttons.indexOf(targetBtn);
|
||||
if (currentIndex === -1) return;
|
||||
|
||||
if (e.key === "ArrowRight") {
|
||||
e.preventDefault();
|
||||
moveFocus(buttons, currentIndex + 1);
|
||||
} else if (e.key === "ArrowLeft") {
|
||||
e.preventDefault();
|
||||
moveFocus(buttons, currentIndex - 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
panel.addEventListener("input", (e) => {
|
||||
const input = e.target && e.target.matches(".bg-studio-search-input") ? e.target : null;
|
||||
if (!input) return;
|
||||
|
||||
const panelEl = document.getElementById("shaarli-bg-studio");
|
||||
if (!panelEl) return;
|
||||
panelEl.dataset.query = input.value || "";
|
||||
|
||||
window.clearTimeout(panelEl.__searchTimer);
|
||||
panelEl.__searchTimer = window.setTimeout(() => {
|
||||
renderBackgroundStudioPanel(panelEl);
|
||||
}, 150);
|
||||
});
|
||||
|
||||
backgroundStudioPanelInitialized = true;
|
||||
}
|
||||
|
||||
function closeBackgroundStudioPanel() {
|
||||
const panel = document.getElementById("shaarli-bg-studio");
|
||||
if (!panel) return;
|
||||
panel.classList.remove("open");
|
||||
panel.style.display = "none";
|
||||
panel.setAttribute("aria-hidden", "true");
|
||||
panel.__anchorEl = null;
|
||||
}
|
||||
|
||||
function openBackgroundStudioPanel({
|
||||
anchorEl,
|
||||
mode,
|
||||
entityId,
|
||||
editUrl,
|
||||
currentColor,
|
||||
currentBackground,
|
||||
title,
|
||||
}) {
|
||||
ensureBackgroundStudioPanel();
|
||||
const panel = document.getElementById("shaarli-bg-studio");
|
||||
if (!panel) return;
|
||||
|
||||
panel.dataset.mode = mode || "entity";
|
||||
panel.dataset.entityId = entityId || "";
|
||||
panel.dataset.editUrl = editUrl || "";
|
||||
panel.dataset.color = currentColor || "default";
|
||||
panel.dataset.background = normalizeBackgroundKey(currentBackground || "") || "none";
|
||||
panel.dataset.filter = panel.dataset.filter || "all";
|
||||
panel.dataset.query = panel.dataset.query || "";
|
||||
panel.dataset.title = title || "Mes images & couleurs";
|
||||
panel.__anchorEl = anchorEl || null;
|
||||
|
||||
renderBackgroundStudioPanel(panel);
|
||||
|
||||
panel.style.display = "block";
|
||||
panel.classList.add("open");
|
||||
panel.setAttribute("aria-hidden", "false");
|
||||
|
||||
positionBackgroundStudioPanel(panel, anchorEl);
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
const input = panel.querySelector(".bg-studio-search-input");
|
||||
if (input && typeof input.focus === "function") {
|
||||
input.focus();
|
||||
input.select();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function positionBackgroundStudioPanel(panel, anchorEl) {
|
||||
if (!panel) return;
|
||||
|
||||
const viewportPadding = 12;
|
||||
const rect = anchorEl && anchorEl.getBoundingClientRect ? anchorEl.getBoundingClientRect() : null;
|
||||
|
||||
panel.style.left = "";
|
||||
panel.style.top = "";
|
||||
panel.style.right = "";
|
||||
panel.style.bottom = "";
|
||||
|
||||
const panelRect = panel.getBoundingClientRect();
|
||||
const preferredTop = rect ? rect.top - panelRect.height - 10 : viewportPadding;
|
||||
const preferredLeft = rect ? rect.left : viewportPadding;
|
||||
|
||||
let top = preferredTop;
|
||||
if (top < viewportPadding) {
|
||||
top = rect ? rect.bottom + 10 : viewportPadding;
|
||||
}
|
||||
|
||||
let left = preferredLeft;
|
||||
if (left + panelRect.width > window.innerWidth - viewportPadding) {
|
||||
left = window.innerWidth - viewportPadding - panelRect.width;
|
||||
}
|
||||
if (left < viewportPadding) left = viewportPadding;
|
||||
|
||||
if (top + panelRect.height > window.innerHeight - viewportPadding) {
|
||||
top = window.innerHeight - viewportPadding - panelRect.height;
|
||||
}
|
||||
if (top < viewportPadding) top = viewportPadding;
|
||||
|
||||
panel.style.left = `${Math.round(left)}px`;
|
||||
panel.style.top = `${Math.round(top)}px`;
|
||||
}
|
||||
|
||||
function normalizeSearchText(value) {
|
||||
return String(value || "")
|
||||
.toLowerCase()
|
||||
.normalize("NFD")
|
||||
.replace(/[\u0300-\u036f]/g, "")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function categorizeBackgroundKey(key, label) {
|
||||
const hay = normalizeSearchText(`${key || ""} ${label || ""}`);
|
||||
|
||||
if (!key || key === "none") return "solid";
|
||||
if (hay.includes("degrade") || hay.includes("gradient")) return "gradient";
|
||||
if (hay.includes("grid") || hay.includes("quadrill") || hay.includes("lign") || hay.includes("feuille")) return "grid";
|
||||
if (hay.includes("damier") || hay.includes("checker") || hay.includes("texture")) return "texture";
|
||||
|
||||
return "image";
|
||||
}
|
||||
|
||||
function getBackgroundStudioItems() {
|
||||
const colors = NOTE_COLOR_OPTIONS.map((opt) => ({
|
||||
type: "color",
|
||||
key: opt.key,
|
||||
label: opt.label,
|
||||
color: getThemeColorValue(opt),
|
||||
keywords: [opt.key, opt.label],
|
||||
}));
|
||||
|
||||
const backgrounds = [
|
||||
{
|
||||
type: "background",
|
||||
category: "solid",
|
||||
key: "none",
|
||||
label: "Couleur unie",
|
||||
keywords: ["none", "sans image", "solid", "uni"],
|
||||
},
|
||||
...getAvailableBackgroundOptionsForMode().map((bg) => ({
|
||||
type: "background",
|
||||
category: categorizeBackgroundKey(bg.key, bg.label),
|
||||
key: bg.key,
|
||||
label: bg.label,
|
||||
url: getNoteBackgroundUrl(bg.key),
|
||||
keywords: [bg.key, bg.label, "image", "background"],
|
||||
})),
|
||||
];
|
||||
|
||||
return { colors, backgrounds };
|
||||
}
|
||||
|
||||
function renderBackgroundStudioPanel(panel) {
|
||||
if (!panel) return;
|
||||
|
||||
const activeEl = document.activeElement;
|
||||
const wasSearchFocused = !!(
|
||||
activeEl &&
|
||||
panel.contains(activeEl) &&
|
||||
activeEl.classList &&
|
||||
activeEl.classList.contains("bg-studio-search-input")
|
||||
);
|
||||
const caretStart = wasSearchFocused && typeof activeEl.selectionStart === "number" ? activeEl.selectionStart : null;
|
||||
const caretEnd = wasSearchFocused && typeof activeEl.selectionEnd === "number" ? activeEl.selectionEnd : null;
|
||||
|
||||
const title = panel.dataset.title || "Mes images & couleurs";
|
||||
const color = panel.dataset.color || "default";
|
||||
const background = normalizeBackgroundKey(panel.dataset.background || "") || "none";
|
||||
const filter = panel.dataset.filter || "all";
|
||||
const query = normalizeSearchText(panel.dataset.query || "");
|
||||
|
||||
const { colors, backgrounds } = getBackgroundStudioItems();
|
||||
|
||||
const filterMatch = (item) => {
|
||||
if (filter === "all") return true;
|
||||
if (filter === "color") return item.type === "color";
|
||||
if (item.type !== "background") return false;
|
||||
if (filter === "solid") return item.category === "solid";
|
||||
if (filter === "gradient") return item.category === "gradient";
|
||||
if (filter === "grid") return item.category === "grid";
|
||||
if (filter === "image") return item.category === "image";
|
||||
if (filter === "texture") return item.category === "texture";
|
||||
return true;
|
||||
};
|
||||
|
||||
const queryMatch = (item) => {
|
||||
if (!query) return true;
|
||||
const hay = normalizeSearchText([item.label, ...(item.keywords || [])].join(" "));
|
||||
return hay.includes(query);
|
||||
};
|
||||
|
||||
const galleryItems = backgrounds
|
||||
.filter((b) => filterMatch(b) && queryMatch(b))
|
||||
.map((b) => ({ ...b, kind: "background" }));
|
||||
|
||||
const galleryHtml = galleryItems
|
||||
.map((item) => {
|
||||
const isActive = background === item.key;
|
||||
const common = `class="bg-studio-thumb ${isActive ? "is-active" : ""}" type="button"`;
|
||||
const thumb =
|
||||
item.key === "none"
|
||||
? `<button ${common} data-bg-studio-action="set-background" data-bg-key="none" title="${item.label}" aria-label="${item.label}" aria-pressed="${isActive}"><span class="bg-studio-thumb-solid"></span></button>`
|
||||
: `<button ${common} data-bg-studio-action="set-background" data-bg-key="${item.key}" title="${item.label}" aria-label="${item.label}" aria-pressed="${isActive}" style="background-image:url('${item.url || ""}')"></button>`;
|
||||
|
||||
return `<div class="bg-studio-tile">${thumb}<div class="bg-studio-thumb-label">${item.label}</div></div>`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
const colorsRowHtml = colors
|
||||
.filter((c) => c.key !== "default")
|
||||
.map((c) => {
|
||||
const isActive = color === c.key;
|
||||
const hex = c.color || "";
|
||||
return `<button class="bg-studio-swatch ${isActive ? "is-active" : ""}" type="button" data-bg-studio-action="set-color" data-color-key="${c.key}" title="${hex}" aria-label="${c.label}" aria-pressed="${isActive}" style="background-color:${hex}"></button>`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
const filterBtn = (key, icon, label) => {
|
||||
const active = filter === key;
|
||||
return `<button class="bg-studio-filter-btn ${active ? "is-active" : ""}" type="button" data-bg-studio-action="set-filter" data-filter="${key}" title="${label}" aria-label="${label}" aria-pressed="${active}"><i class="mdi ${icon}" aria-hidden="true"></i></button>`;
|
||||
};
|
||||
|
||||
const filtersHtml = [
|
||||
filterBtn("solid", "mdi-checkbox-blank-outline", "Couleur unie"),
|
||||
filterBtn("gradient", "mdi-gradient-horizontal", "Dégradé"),
|
||||
filterBtn("grid", "mdi-grid", "Motif grille"),
|
||||
filterBtn("image", "mdi-image-outline", "Image personnalisée"),
|
||||
filterBtn("texture", "mdi-checkerboard", "Texture"),
|
||||
].join("");
|
||||
|
||||
const showClear = !!(panel.dataset.query || "").trim();
|
||||
|
||||
panel.innerHTML = `
|
||||
<div class="bg-studio-header">
|
||||
<div class="bg-studio-title">${title}</div>
|
||||
<button class="bg-studio-close" type="button" data-bg-studio-action="close" title="Fermer" aria-label="Fermer"><i class="mdi mdi-close" aria-hidden="true"></i></button>
|
||||
</div>
|
||||
<div class="bg-studio-gallery" role="list">${galleryHtml}</div>
|
||||
<div class="bg-studio-rows">
|
||||
<div class="bg-studio-row">
|
||||
<div class="bg-studio-row-heading">
|
||||
<div class="bg-studio-row-label">Colors:</div>
|
||||
<button type="button" class="bg-studio-reset" data-bg-studio-action="set-defaults" title="Réinitialiser" aria-label="Réinitialiser">Reset</button>
|
||||
</div>
|
||||
<div class="bg-studio-swatches" role="list">${colorsRowHtml}</div>
|
||||
</div>
|
||||
<div class="bg-studio-row">
|
||||
<div class="bg-studio-row-label">Backgrounds:</div>
|
||||
<div class="bg-studio-filters">${filtersHtml}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-studio-search">
|
||||
<i class="mdi mdi-magnify" aria-hidden="true"></i>
|
||||
<input class="bg-studio-search-input" type="search" placeholder="Rechercher mes images… (filtrer par couleur ou background)" value="${panel.dataset.query || ""}" />
|
||||
<button class="bg-studio-clear" type="button" data-bg-studio-action="set-query" title="Effacer" aria-label="Effacer" style="${showClear ? "" : "display:none"}"><i class="mdi mdi-close-circle-outline" aria-hidden="true"></i></button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (wasSearchFocused) {
|
||||
const input = panel.querySelector(".bg-studio-search-input");
|
||||
if (input && typeof input.focus === "function") {
|
||||
input.focus({ preventScroll: true });
|
||||
if (typeof input.setSelectionRange === "function") {
|
||||
const len = (input.value || "").length;
|
||||
const start = typeof caretStart === "number" ? Math.max(0, Math.min(len, caretStart)) : len;
|
||||
const end = typeof caretEnd === "number" ? Math.max(0, Math.min(len, caretEnd)) : len;
|
||||
try {
|
||||
input.setSelectionRange(start, end);
|
||||
} catch (e) {
|
||||
// Ignore selection errors for non-text inputs or unsupported browsers.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function applyNoteVisualState(element, note) {
|
||||
if (!element || !note) return;
|
||||
|
||||
@ -595,9 +1018,6 @@ function initBookmarkPaletteButtons() {
|
||||
wrapper.style.position = "relative";
|
||||
wrapper.innerHTML = `
|
||||
<button type="button" title="Couleur" aria-label="Couleur" id="${paletteBtnId}"><i class="mdi mdi-palette-outline" aria-hidden="true"></i></button>
|
||||
<div class="palette-popup" id="popup-${paletteBtnId}">
|
||||
${generateBookmarkPaletteButtons(cardId, editUrl, color, background)}
|
||||
</div>
|
||||
`;
|
||||
|
||||
const pinLink = Array.from(actions.querySelectorAll('a[href*="/pin"], a[aria-label*="Épingler"], a[title*="Épingler"]'))[0];
|
||||
@ -610,59 +1030,81 @@ function initBookmarkPaletteButtons() {
|
||||
}
|
||||
|
||||
const paletteBtn = wrapper.querySelector(`#${paletteBtnId}`);
|
||||
const palettePopup = wrapper.querySelector(`#popup-${paletteBtnId}`);
|
||||
|
||||
if (!paletteBtn || !palettePopup) return;
|
||||
if (!paletteBtn) return;
|
||||
|
||||
paletteBtn.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
document.querySelectorAll(".palette-popup.open").forEach((p) => {
|
||||
if (p !== palettePopup) {
|
||||
p.classList.remove("open");
|
||||
const parentCard = p.closest(".link-outer");
|
||||
if (parentCard) parentCard.classList.remove("palette-open");
|
||||
}
|
||||
openBackgroundStudioPanel({
|
||||
anchorEl: paletteBtn,
|
||||
mode: "entity",
|
||||
entityId: cardId,
|
||||
editUrl,
|
||||
currentColor: getElementVisualColor(card),
|
||||
currentBackground: getElementVisualBackground(card),
|
||||
title: "Mes images & couleurs",
|
||||
});
|
||||
const nextOpenState = !palettePopup.classList.contains("open");
|
||||
palettePopup.classList.toggle("open");
|
||||
card.classList.toggle("palette-open", nextOpenState);
|
||||
positionPalettePopup(palettePopup);
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener("click", (e) => {
|
||||
if (e.target.closest(".bookmark-palette")) return;
|
||||
document.querySelectorAll(".palette-popup.open").forEach((p) => p.classList.remove("open"));
|
||||
document.querySelectorAll(".link-outer.palette-open").forEach((el) => el.classList.remove("palette-open"));
|
||||
if (e.target.closest("#shaarli-bg-studio")) return;
|
||||
closeBackgroundStudioPanel();
|
||||
});
|
||||
}
|
||||
|
||||
function generateBookmarkPaletteButtons(bookmarkId, editUrl, currentColor, currentBackground) {
|
||||
return generateUnifiedPaletteMenu({
|
||||
entityId: bookmarkId,
|
||||
editUrl,
|
||||
currentColor,
|
||||
currentBackground,
|
||||
mode: "entity",
|
||||
});
|
||||
}
|
||||
|
||||
function generateUnifiedPaletteMenu({ entityId, editUrl, currentColor, currentBackground, mode }) {
|
||||
const color = currentColor || "default";
|
||||
const background = normalizeBackgroundKey(currentBackground || "") || "none";
|
||||
|
||||
const onColorClick =
|
||||
mode === "modal"
|
||||
? (c) => `setModalNoteColor('${c}')`
|
||||
: (c) => `setNoteColor('${entityId}', '${c}', '${editUrl}')`;
|
||||
|
||||
const onBackgroundClick =
|
||||
mode === "modal"
|
||||
? (k) => `setModalNoteBackground('${k}')`
|
||||
: (k) => `setNoteBackground('${entityId}', '${k}', '${editUrl}')`;
|
||||
|
||||
const colorButtons = [
|
||||
`<button class="palette-btn palette-btn-default ${color === "default" ? "is-active" : ""}" title="Par défaut" onclick="setNoteColor('${bookmarkId}', 'default', '${editUrl}')"><i class="mdi mdi-format-color-reset"></i></button>`,
|
||||
...NOTE_COLOR_OPTIONS.filter((opt) => opt.key !== "default").map(
|
||||
(opt) => {
|
||||
const swatchColor = getThemeColorValue(opt);
|
||||
return `<button class="palette-btn note-color-${opt.key} ${color === opt.key ? "is-active" : ""}" title="${opt.label}" onclick="setNoteColor('${bookmarkId}', '${opt.key}', '${editUrl}')" style="background-color:${swatchColor}"></button>`;
|
||||
},
|
||||
),
|
||||
`<button class="palette-btn palette-btn-default ${color === "default" ? "is-active" : ""}" type="button" title="Par défaut" aria-label="Par défaut" aria-pressed="${color === "default"}" onclick="${onColorClick("default")}"><i class="mdi mdi-format-color-reset" aria-hidden="true"></i></button>`,
|
||||
...NOTE_COLOR_OPTIONS.filter((opt) => opt.key !== "default").map((opt) => {
|
||||
const swatchColor = getThemeColorValue(opt);
|
||||
return `<button class="palette-btn note-color-${opt.key} ${color === opt.key ? "is-active" : ""}" type="button" title="${opt.label}" aria-label="${opt.label}" aria-pressed="${color === opt.key}" onclick="${onColorClick(opt.key)}" style="background-color:${swatchColor}"></button>`;
|
||||
}),
|
||||
].join("");
|
||||
|
||||
const backgroundButtons = [
|
||||
`<button class="palette-btn palette-btn-bg-none ${background === "none" ? "is-active" : ""}" title="Sans image" onclick="setNoteBackground('${bookmarkId}', 'none', '${editUrl}')"><i class="mdi mdi-image-off-outline"></i></button>`,
|
||||
`<button class="palette-btn palette-btn-bg-none ${background === "none" ? "is-active" : ""}" type="button" title="Sans image" aria-label="Sans image" aria-pressed="${background === "none"}" onclick="${onBackgroundClick("none")}"><i class="mdi mdi-image-off-outline" aria-hidden="true"></i></button>`,
|
||||
...getAvailableBackgroundOptionsForMode().map((bg) => {
|
||||
const bgUrl = getNoteBackgroundUrl(bg.key);
|
||||
return `<button class="palette-btn palette-btn-bg ${background === bg.key ? "is-active" : ""}" title="${bg.label}" onclick="setNoteBackground('${bookmarkId}', '${bg.key}', '${editUrl}')" style="background-image:url('${bgUrl}')"></button>`;
|
||||
return `<button class="palette-btn palette-btn-bg ${background === bg.key ? "is-active" : ""}" type="button" title="${bg.label}" aria-label="${bg.label}" aria-pressed="${background === bg.key}" onclick="${onBackgroundClick(bg.key)}" style="background-image:url('${bgUrl}')"></button>`;
|
||||
}),
|
||||
].join("");
|
||||
|
||||
return `
|
||||
<div class="palette-row palette-row-colors">${colorButtons}</div>
|
||||
<div class="palette-row palette-row-backgrounds">${backgroundButtons}</div>
|
||||
<div class="palette-section">
|
||||
<div class="palette-section-title">Couleurs</div>
|
||||
<div class="palette-row palette-row-colors">${colorButtons}</div>
|
||||
</div>
|
||||
<div class="palette-divider"></div>
|
||||
<div class="palette-section">
|
||||
<div class="palette-section-title">Images</div>
|
||||
<div class="palette-row palette-row-backgrounds">${backgroundButtons}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -1021,8 +1463,16 @@ function initNoteView(linkList, container) {
|
||||
modalColorBtn.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
modalColorPopup.classList.toggle("open");
|
||||
positionPalettePopup(modalColorPopup);
|
||||
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",
|
||||
currentBackground: modalCard ? getElementVisualBackground(modalCard) : "none",
|
||||
title: "Mes images & couleurs",
|
||||
});
|
||||
});
|
||||
|
||||
modalPinBtn.addEventListener("click", (e) => {
|
||||
@ -1255,9 +1705,6 @@ function renderNotes(container, notes, viewMode) {
|
||||
<button title="Collaborateur"><i class="mdi mdi-account-plus-outline"></i></button>
|
||||
<div style="position:relative;">
|
||||
<button title="Couleur" id="${paletteBtnId}"><i class="mdi mdi-palette-outline"></i></button>
|
||||
<div class="palette-popup" id="popup-${paletteBtnId}">
|
||||
${generatePaletteButtons(note)}
|
||||
</div>
|
||||
</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>
|
||||
@ -1270,17 +1717,18 @@ function renderNotes(container, notes, viewMode) {
|
||||
|
||||
// Palette Toggle
|
||||
const paletteBtn = actions.querySelector(`#${paletteBtnId}`);
|
||||
const palettePopup = actions.querySelector(`#popup-${paletteBtnId}`);
|
||||
|
||||
paletteBtn.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// Close others?
|
||||
document.querySelectorAll(".palette-popup.open").forEach((p) => {
|
||||
if (p !== palettePopup) p.classList.remove("open");
|
||||
openBackgroundStudioPanel({
|
||||
anchorEl: paletteBtn,
|
||||
mode: "entity",
|
||||
entityId: note.id,
|
||||
editUrl: note.editUrl,
|
||||
currentColor: getElementVisualColor(card),
|
||||
currentBackground: getElementVisualBackground(card),
|
||||
title: "Mes images & couleurs",
|
||||
});
|
||||
palettePopup.classList.toggle("open");
|
||||
positionPalettePopup(palettePopup);
|
||||
});
|
||||
|
||||
inner.appendChild(actions);
|
||||
@ -1365,31 +1813,13 @@ function openNoteModal(note) {
|
||||
}
|
||||
|
||||
function generateModalPaletteButtons(note) {
|
||||
const currentColor = note.color || "default";
|
||||
const currentBackground = normalizeBackgroundKey(note.background || "") || "none";
|
||||
|
||||
const colorButtons = [
|
||||
`<button class="palette-btn palette-btn-default ${currentColor === "default" ? "is-active" : ""}" title="Par défaut" onclick="setModalNoteColor('default')"><i class="mdi mdi-format-color-reset"></i></button>`,
|
||||
...NOTE_COLOR_OPTIONS.filter((opt) => opt.key !== "default").map(
|
||||
(opt) => {
|
||||
const swatchColor = getThemeColorValue(opt);
|
||||
return `<button class="palette-btn note-color-${opt.key} ${currentColor === opt.key ? "is-active" : ""}" title="${opt.label}" onclick="setModalNoteColor('${opt.key}')" style="background-color:${swatchColor}"></button>`;
|
||||
},
|
||||
),
|
||||
].join("");
|
||||
|
||||
const backgroundButtons = [
|
||||
`<button class="palette-btn palette-btn-bg-none ${currentBackground === "none" ? "is-active" : ""}" title="Sans image" onclick="setModalNoteBackground('none')"><i class="mdi mdi-image-off-outline"></i></button>`,
|
||||
...getAvailableBackgroundOptionsForMode().map((bg) => {
|
||||
const bgUrl = getNoteBackgroundUrl(bg.key);
|
||||
return `<button class="palette-btn palette-btn-bg ${currentBackground === bg.key ? "is-active" : ""}" title="${bg.label}" onclick="setModalNoteBackground('${bg.key}')" style="background-image:url('${bgUrl}')"></button>`;
|
||||
}),
|
||||
].join("");
|
||||
|
||||
return `
|
||||
<div class="palette-row palette-row-colors">${colorButtons}</div>
|
||||
<div class="palette-row palette-row-backgrounds">${backgroundButtons}</div>
|
||||
`;
|
||||
return generateUnifiedPaletteMenu({
|
||||
entityId: note && note.id ? note.id : "",
|
||||
editUrl: note && note.editUrl ? note.editUrl : "",
|
||||
currentColor: note && note.color ? note.color : "default",
|
||||
currentBackground: note && note.background ? note.background : "none",
|
||||
mode: "modal",
|
||||
});
|
||||
}
|
||||
|
||||
window.setModalNoteColor = function (color) {
|
||||
@ -1436,31 +1866,13 @@ window.setModalNoteBackground = function (backgroundKey) {
|
||||
};
|
||||
|
||||
function generatePaletteButtons(note) {
|
||||
const currentColor = note.color || "default";
|
||||
const currentBackground = normalizeBackgroundKey(note.background || "") || "none";
|
||||
|
||||
const colorButtons = [
|
||||
`<button class="palette-btn palette-btn-default ${currentColor === "default" ? "is-active" : ""}" title="Par défaut" onclick="setNoteColor('${note.id}', 'default', '${note.editUrl}')"><i class="mdi mdi-format-color-reset"></i></button>`,
|
||||
...NOTE_COLOR_OPTIONS.filter((opt) => opt.key !== "default").map(
|
||||
(opt) => {
|
||||
const swatchColor = getThemeColorValue(opt);
|
||||
return `<button class="palette-btn note-color-${opt.key} ${currentColor === opt.key ? "is-active" : ""}" title="${opt.label}" onclick="setNoteColor('${note.id}', '${opt.key}', '${note.editUrl}')" style="background-color:${swatchColor}"></button>`;
|
||||
},
|
||||
),
|
||||
].join("");
|
||||
|
||||
const backgroundButtons = [
|
||||
`<button class="palette-btn palette-btn-bg-none ${currentBackground === "none" ? "is-active" : ""}" title="Sans image" onclick="setNoteBackground('${note.id}', 'none', '${note.editUrl}')"><i class="mdi mdi-image-off-outline"></i></button>`,
|
||||
...getAvailableBackgroundOptionsForMode().map((bg) => {
|
||||
const bgUrl = getNoteBackgroundUrl(bg.key);
|
||||
return `<button class="palette-btn palette-btn-bg ${currentBackground === bg.key ? "is-active" : ""}" title="${bg.label}" onclick="setNoteBackground('${note.id}', '${bg.key}', '${note.editUrl}')" style="background-image:url('${bgUrl}')"></button>`;
|
||||
}),
|
||||
].join("");
|
||||
|
||||
return `
|
||||
<div class="palette-row palette-row-colors">${colorButtons}</div>
|
||||
<div class="palette-row palette-row-backgrounds">${backgroundButtons}</div>
|
||||
`;
|
||||
return generateUnifiedPaletteMenu({
|
||||
entityId: note && note.id ? note.id : "",
|
||||
editUrl: note && note.editUrl ? note.editUrl : "",
|
||||
currentColor: note && note.color ? note.color : "default",
|
||||
currentBackground: note && note.background ? note.background : "none",
|
||||
mode: "entity",
|
||||
});
|
||||
}
|
||||
|
||||
window.setNoteColor = function (noteId, color, editUrl) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user