feat: optimiser l'interface des notes avec émoji 📝 automatique dans les titres, redirection intelligente vers vue Notes au clic sur titre, retour URL dynamique post-sauvegarde, auto-ouverture note via hash #open-note-{id}, réduction tailles boutons actions (36px→28px hover, 32px→30px), suppression gaps entre boutons (0.25rem→0), link-footer nowrap avec overflow hidden, et changement placeholder "Page title"→"Note title"
This commit is contained in:
parent
4280ae171b
commit
adb2564153
@ -1318,7 +1318,7 @@ body.view-notes .paging {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0.75rem;
|
top: 0.75rem;
|
||||||
left: auto;
|
left: auto;
|
||||||
right: 10.5rem;
|
right: 9.5rem;
|
||||||
transform: none;
|
transform: none;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
@ -1399,9 +1399,9 @@ body.view-notes .paging {
|
|||||||
.link-hover-actions {
|
.link-hover-actions {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0.75rem;
|
top: 0.75rem;
|
||||||
right: 3.5rem;
|
right: 2.9rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.25rem;
|
gap: 0;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.15s ease;
|
transition: opacity 0.15s ease;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
@ -1415,8 +1415,8 @@ body.view-notes .paging {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 32px;
|
width: 30px;
|
||||||
height: 32px;
|
height: 30px;
|
||||||
background: var(--bg-card);
|
background: var(--bg-card);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 0.375rem;
|
border-radius: 0.375rem;
|
||||||
@ -1514,7 +1514,7 @@ body.view-notes .paging {
|
|||||||
.link-readlater-badge {
|
.link-readlater-badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0.75rem;
|
top: 0.75rem;
|
||||||
right: 3.2rem;
|
right: 2.9rem;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -1582,13 +1582,14 @@ body.view-notes .paging {
|
|||||||
|
|
||||||
.link-footer {
|
.link-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: nowrap;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
padding-top: 0.75rem;
|
padding-top: 0.75rem;
|
||||||
border-top: 1px solid var(--border);
|
border-top: 1px solid var(--border);
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.view-grid .link-footer {
|
.view-grid .link-footer {
|
||||||
@ -1600,10 +1601,11 @@ body.view-notes .paging {
|
|||||||
/* Tags */
|
/* Tags */
|
||||||
.link-tag-list {
|
.link-tag-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: nowrap;
|
||||||
gap: 0.375rem;
|
gap: 0.375rem;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-tag {
|
.link-tag {
|
||||||
@ -1669,6 +1671,7 @@ body.view-notes .paging {
|
|||||||
order: 2;
|
order: 2;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.view-grid .link-actions {
|
.view-grid .link-actions {
|
||||||
@ -1678,10 +1681,10 @@ body.view-notes .paging {
|
|||||||
/* Actions */
|
/* Actions */
|
||||||
.link-actions {
|
.link-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.25rem;
|
gap: 0;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: nowrap;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
@ -1693,8 +1696,8 @@ body.view-notes .paging {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 36px;
|
width: 28px;
|
||||||
height: 36px;
|
height: 28px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 0.375rem;
|
border-radius: 0.375rem;
|
||||||
@ -1702,11 +1705,12 @@ body.view-notes .paging {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.15s ease;
|
transition: all 0.15s ease;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-actions a i,
|
.link-actions a i,
|
||||||
.link-actions button i {
|
.link-actions button i {
|
||||||
font-size: 1.15rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-actions a:hover,
|
.link-actions a:hover,
|
||||||
@ -1744,7 +1748,7 @@ body.view-notes .paging {
|
|||||||
|
|
||||||
.view-list .link-readlater-badge {
|
.view-list .link-readlater-badge {
|
||||||
top: 1.25rem;
|
top: 1.25rem;
|
||||||
right: 3.95rem;
|
right: 3.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* List view - selection */
|
/* List view - selection */
|
||||||
|
|||||||
@ -53,7 +53,7 @@
|
|||||||
<div class="form-group bookmark-field-group">
|
<div class="form-group bookmark-field-group">
|
||||||
<label class="form-label" for="lf_title{$index}">{'Title'|t}</label>
|
<label class="form-label" for="lf_title{$index}">{'Title'|t}</label>
|
||||||
<div class="{$asyncLoadClass}">
|
<div class="{$asyncLoadClass}">
|
||||||
<input type="text" class="form-control lf_input{if="empty($batch_mode) && $link.title==''"} autofocus{/if}" name="lf_title" id="lf_title{$index}" value="{$link.title}" placeholder="Page title">
|
<input type="text" class="form-control lf_input{if="empty($batch_mode) && $link.title==''"} autofocus{/if}" name="lf_title" id="lf_title{$index}" value="{$link.title}" placeholder="Note title">
|
||||||
<div class="icon-container"><i class="loader"></i></div>
|
<div class="icon-container"><i class="loader"></i></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1846,6 +1846,21 @@ function initNoteView(linkList, container) {
|
|||||||
container.appendChild(wrapper);
|
container.appendChild(wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for hash parameter to auto-open a specific note
|
||||||
|
const hash = window.location.hash;
|
||||||
|
if (hash && hash.startsWith('#open-note-')) {
|
||||||
|
const noteId = hash.replace('#open-note-', '');
|
||||||
|
const targetNote = notes.find(n => String(n.id) === String(noteId));
|
||||||
|
if (targetNote) {
|
||||||
|
// Clear the hash to avoid reopening on refresh
|
||||||
|
history.replaceState(null, null, window.location.pathname + window.location.search);
|
||||||
|
// Open the note modal after a short delay to ensure DOM is ready
|
||||||
|
setTimeout(() => {
|
||||||
|
openNoteModal(targetNote);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Modal Container
|
// Modal Container
|
||||||
const modalOverlay = document.createElement("div");
|
const modalOverlay = document.createElement("div");
|
||||||
modalOverlay.className = "note-modal-overlay";
|
modalOverlay.className = "note-modal-overlay";
|
||||||
|
|||||||
@ -1602,6 +1602,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const tagsTextInput = form.querySelector('.bookmark-tags-text-input');
|
const tagsTextInput = form.querySelector('.bookmark-tags-text-input');
|
||||||
const readLaterCheckbox = form.querySelector('.bookmark-toggle-readlater');
|
const readLaterCheckbox = form.querySelector('.bookmark-toggle-readlater');
|
||||||
const noteCheckbox = form.querySelector('.bookmark-toggle-note');
|
const noteCheckbox = form.querySelector('.bookmark-toggle-note');
|
||||||
|
const titleInput = form.querySelector('input[name="lf_title"]');
|
||||||
const isDarkTheme = document.documentElement.getAttribute('data-theme') === 'dark';
|
const isDarkTheme = document.documentElement.getAttribute('data-theme') === 'dark';
|
||||||
|
|
||||||
// Markdown editor
|
// Markdown editor
|
||||||
@ -1676,6 +1677,17 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
.map((tag) => normalizeTag(tag))
|
.map((tag) => normalizeTag(tag))
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
|
// Update return URL based on bookmark type (note vs regular)
|
||||||
|
const returnUrlInput = form.querySelector('input[name="returnurl"]');
|
||||||
|
if (returnUrlInput) {
|
||||||
|
form.addEventListener('submit', () => {
|
||||||
|
const isNote = noteCheckbox?.checked || tags.some((tag) => /^note$/i.test(tag));
|
||||||
|
if (isNote) {
|
||||||
|
returnUrlInput.value = `${shaarli.basePath}/?searchtags=note`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const updateHiddenTags = () => {
|
const updateHiddenTags = () => {
|
||||||
hiddenTagsInput.value = tags.join(' ');
|
hiddenTagsInput.value = tags.join(' ');
|
||||||
};
|
};
|
||||||
@ -1690,6 +1702,44 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
noteCheckbox.checked = tags.some((tag) => /^note$/i.test(tag));
|
noteCheckbox.checked = tags.some((tag) => /^note$/i.test(tag));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Helper functions to manage note emoji in title
|
||||||
|
const NOTE_EMOJI = '📝';
|
||||||
|
|
||||||
|
const hasNoteEmoji = (title) => {
|
||||||
|
return title && title.startsWith(NOTE_EMOJI);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addNoteEmoji = (title) => {
|
||||||
|
if (!title) return NOTE_EMOJI + ' ';
|
||||||
|
if (hasNoteEmoji(title)) return title;
|
||||||
|
return NOTE_EMOJI + ' ' + title;
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeNoteEmoji = (title) => {
|
||||||
|
if (!title) return '';
|
||||||
|
if (title.startsWith(NOTE_EMOJI + ' ')) {
|
||||||
|
return title.substring(NOTE_EMOJI.length + 1);
|
||||||
|
}
|
||||||
|
if (title.startsWith(NOTE_EMOJI)) {
|
||||||
|
return title.substring(NOTE_EMOJI.length);
|
||||||
|
}
|
||||||
|
return title;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateNoteTitle = (isNote) => {
|
||||||
|
if (!titleInput) return;
|
||||||
|
const currentTitle = titleInput.value || '';
|
||||||
|
if (isNote) {
|
||||||
|
if (!hasNoteEmoji(currentTitle)) {
|
||||||
|
titleInput.value = addNoteEmoji(currentTitle);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (hasNoteEmoji(currentTitle)) {
|
||||||
|
titleInput.value = removeNoteEmoji(currentTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const addTag = (rawTag) => {
|
const addTag = (rawTag) => {
|
||||||
const tag = normalizeTag(rawTag);
|
const tag = normalizeTag(rawTag);
|
||||||
if (!tag) return;
|
if (!tag) return;
|
||||||
@ -1804,6 +1854,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
if (noteCheckbox.checked) {
|
if (noteCheckbox.checked) {
|
||||||
tags.push('note');
|
tags.push('note');
|
||||||
|
updateNoteTitle(true);
|
||||||
|
} else {
|
||||||
|
updateNoteTitle(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateHiddenTags();
|
updateHiddenTags();
|
||||||
@ -1838,6 +1891,16 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
syncNoteCheckbox();
|
syncNoteCheckbox();
|
||||||
renderTags();
|
renderTags();
|
||||||
refreshAutocomplete();
|
refreshAutocomplete();
|
||||||
|
|
||||||
|
// Initial title update if note tag is present
|
||||||
|
if (noteCheckbox && noteCheckbox.checked && titleInput) {
|
||||||
|
const currentTitle = titleInput.value || '';
|
||||||
|
if (!hasNoteEmoji(currentTitle) && !currentTitle.trim()) {
|
||||||
|
titleInput.value = NOTE_EMOJI + ' ';
|
||||||
|
} else if (!hasNoteEmoji(currentTitle)) {
|
||||||
|
titleInput.value = addNoteEmoji(currentTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2740,6 +2803,27 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
var url = urlEl.textContent.trim();
|
var url = urlEl.textContent.trim();
|
||||||
var realUrl = (titleEl && titleEl.getAttribute('href')) || url;
|
var realUrl = (titleEl && titleEl.getAttribute('href')) || url;
|
||||||
|
|
||||||
|
// Check if this is a note bookmark (has 'note' tag)
|
||||||
|
var isNote = false;
|
||||||
|
var noteId = card.dataset.id || '';
|
||||||
|
card.querySelectorAll('.link-tag[data-tag]').forEach(function(tagEl) {
|
||||||
|
if (tagEl.dataset.tag === 'note') {
|
||||||
|
isNote = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add click handler for note bookmarks to redirect to notes page
|
||||||
|
if (isNote && titleEl) {
|
||||||
|
titleEl.addEventListener('click', function(e) {
|
||||||
|
// Only redirect if it's a left-click without modifiers
|
||||||
|
if (e.button === 0 && !e.ctrlKey && !e.metaKey && !e.shiftKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
var basePath = (typeof shaarli !== 'undefined' && shaarli.basePath) ? shaarli.basePath : '';
|
||||||
|
window.location.href = basePath + '/?searchtags=note#open-note-' + noteId;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (isMediaUrl(url) || isMediaUrl(realUrl)) {
|
if (isMediaUrl(url) || isMediaUrl(realUrl)) {
|
||||||
var actionsDiv = card.querySelector('.link-actions');
|
var actionsDiv = card.querySelector('.link-actions');
|
||||||
if (!actionsDiv) return;
|
if (!actionsDiv) return;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user