feat: integrate Tagify for multi-tag support and add a new view-note modal across the application
This commit is contained in:
135
views/notes.php
135
views/notes.php
@@ -122,8 +122,11 @@ $all_users = query("SELECT id, username FROM users WHERE id != ?", [$user_id])->
|
||||
$all_groups = query("SELECT * FROM groups")->fetchAll();
|
||||
$label_names = [];
|
||||
foreach($notes as $n) {
|
||||
$lbl = trim((string)($n['label_name'] ?? ''));
|
||||
if($lbl !== '') $label_names[strtolower($lbl)] = $lbl;
|
||||
$lbls = explode(',', (string)($n['label_name'] ?? ''));
|
||||
foreach($lbls as $lbl) {
|
||||
$lbl = trim($lbl);
|
||||
if($lbl !== '') $label_names[strtolower($lbl)] = $lbl;
|
||||
}
|
||||
}
|
||||
natcasesort($label_names);
|
||||
|
||||
@@ -182,7 +185,8 @@ $note_colors = [
|
||||
<input type="text" name="title" class="form-control" placeholder="Title..." id="new-note-title" style="font-weight:600; font-size:1rem;">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="text" name="label_name" class="form-control" id="new-note-label" placeholder="Tag/Label name">
|
||||
<label style="font-size:0.8rem; color:var(--text-light); margin-bottom:0.25rem; display:block;">Tags</label>
|
||||
<input type="text" name="label_name" class="form-control tagify-input" id="new-note-label" placeholder="Add tags...">
|
||||
</div>
|
||||
</div>
|
||||
<!-- WYSIWYG Toolbar -->
|
||||
@@ -231,6 +235,36 @@ $note_colors = [
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── View Note Modal ────────────────────────────────────────────────────── -->
|
||||
<div class="modal-overlay" id="view-note-modal">
|
||||
<div class="modal-content note-modal-compact" style="max-width:760px; max-height:92vh; overflow-y:auto; padding:0; background:var(--bg);">
|
||||
<div id="view-note-header" style="padding:1.5rem; border-bottom:1px solid var(--border); background:var(--surface); position:sticky; top:0; z-index:10;">
|
||||
<div style="display:flex; justify-content:space-between; align-items:flex-start; gap:1rem;">
|
||||
<div style="flex:1; min-width:0;">
|
||||
<h2 id="view-note-title" style="margin:0 0 0.5rem 0; font-size:1.4rem; font-weight:800; line-height:1.3; color:var(--text); word-wrap:break-word;"></h2>
|
||||
<div style="display:flex; align-items:center; gap:0.5rem; flex-wrap:wrap;">
|
||||
<span id="view-note-label-container" style="display:none; align-items:center; gap:0.35rem; padding:3px 8px; border-radius:999px; background:rgba(var(--primary-rgb),0.12); color:var(--primary); font-size:0.75rem; font-weight:600;">
|
||||
<i class="fa fa-tag" style="font-size:0.7rem;"></i> <span id="view-note-label"></span>
|
||||
</span>
|
||||
<span id="view-note-date" style="font-size:0.8rem; color:var(--text-light);"></span>
|
||||
<span id="view-note-shared-badge" class="note-card-shared-badge" style="display:none;">Shared</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex; align-items:center; gap:0.5rem; flex-shrink:0;">
|
||||
<button class="btn btn-sm btn-secondary" id="view-note-edit-btn" onclick="transToEditNote()"><i class="fa fa-pencil"></i> Edit</button>
|
||||
<button type="button" style="background:none;border:none;color:var(--text-light);cursor:pointer;font-size:1.6rem; line-height:1; padding:0 4px;" onclick="document.getElementById('view-note-modal').classList.remove('active')">×</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="view-note-image-container" style="display:none; margin-top:1.25rem; border-radius:0.75rem; overflow:hidden; border:1px solid var(--border);">
|
||||
<img id="view-note-image" src="" style="width:100%; max-height:450px; object-fit:cover; display:block;">
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding:1.5rem;">
|
||||
<div id="view-note-content" class="markdown-body" style="font-size:1rem; line-height:1.7; color:var(--text);"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Edit Note Modal ────────────────────────────────────────────────────── -->
|
||||
<div class="modal-overlay" id="edit-note-modal">
|
||||
<div class="modal-content note-modal-compact" style="max-width:760px; max-height:90vh; overflow-y:auto;">
|
||||
@@ -246,7 +280,8 @@ $note_colors = [
|
||||
<input type="text" name="title" id="edit-note-title" class="form-control" placeholder="Title..." style="font-weight:600; font-size:1rem;">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="text" name="label_name" id="edit-note-label" class="form-control" placeholder="Tag/Label name">
|
||||
<label style="font-size:0.8rem; color:var(--text-light); margin-bottom:0.25rem; display:block;">Tags</label>
|
||||
<input type="text" name="label_name" id="edit-note-label" class="form-control tagify-input" placeholder="Add tags...">
|
||||
</div>
|
||||
</div>
|
||||
<div class="note-editor-toolbar">
|
||||
@@ -374,7 +409,8 @@ $note_colors = [
|
||||
data-image="<?= htmlspecialchars((string)($n['image_path'] ?? '')) ?>"
|
||||
data-shares="<?= $shares_json ?>"
|
||||
data-mine="<?= $n['is_mine'] ?>"
|
||||
onclick="openNote(this)">
|
||||
data-updated="<?= date('M j, Y, H:i', strtotime($n['updated_at'] ?: $n['created_at'])) ?>"
|
||||
onclick="viewNote(this)">
|
||||
<?php if($n['image_path'] && file_exists($n['image_path'])): ?>
|
||||
<img src="<?= htmlspecialchars($n['image_path']) ?>" alt="Note image" class="note-card-img">
|
||||
<?php endif; ?>
|
||||
@@ -382,8 +418,12 @@ $note_colors = [
|
||||
<div class="note-card-title"><?= htmlspecialchars($n['title']) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if(!empty($n['label_name'])): ?>
|
||||
<div style="display:inline-flex; align-items:center; gap:0.35rem; width:max-content; max-width:100%; padding:3px 8px; border-radius:999px; background:rgba(var(--primary-rgb),0.12); color:var(--primary); font-size:0.7rem; font-weight:600;">
|
||||
<i class="fa fa-tag" style="font-size:0.65rem;"></i> <?= htmlspecialchars($n['label_name']) ?>
|
||||
<div style="display:flex; flex-wrap:wrap; gap:0.35rem; margin-bottom:0.5rem;">
|
||||
<?php foreach(explode(',', $n['label_name']) as $lbl): $lbl=trim($lbl); if(!$lbl) continue; ?>
|
||||
<div style="display:inline-flex; align-items:center; gap:0.35rem; padding:3px 8px; border-radius:999px; background:rgba(var(--primary-rgb),0.12); color:var(--primary); font-size:0.7rem; font-weight:600;">
|
||||
<i class="fa fa-tag" style="font-size:0.65rem;"></i> <?= htmlspecialchars($lbl) ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="note-card-preview"><?= htmlspecialchars($preview) ?></div>
|
||||
@@ -478,7 +518,7 @@ function openNote(card) {
|
||||
document.getElementById('edit-note-color-input').value = color;
|
||||
document.getElementById('edit-note-preview').style.display = 'none';
|
||||
document.getElementById('edit-note-content').style.display = 'block';
|
||||
document.getElementById('edit-preview-btn').textContent = '👁 Preview';
|
||||
document.getElementById('edit-preview-btn').innerHTML = '👁 Preview';
|
||||
|
||||
// Color swatches
|
||||
document.querySelectorAll('#edit-note-colors .color-swatch').forEach(s => {
|
||||
@@ -518,6 +558,60 @@ function openNote(card) {
|
||||
document.getElementById('edit-note-modal').classList.add('active');
|
||||
}
|
||||
|
||||
let currentViewedCard = null;
|
||||
|
||||
function viewNote(card) {
|
||||
currentViewedCard = card;
|
||||
const title = card.dataset.title || 'Untitled Note';
|
||||
const content = card.dataset.content || '';
|
||||
const label = card.dataset.labelRaw || '';
|
||||
const image = card.dataset.image;
|
||||
const updatedAt = card.dataset.updated;
|
||||
const isMine = card.dataset.mine === '1';
|
||||
|
||||
document.getElementById('view-note-title').textContent = title;
|
||||
|
||||
|
||||
// Clear old tags
|
||||
document.getElementById('view-note-label-container').innerHTML = '';
|
||||
|
||||
if(label) {
|
||||
document.getElementById('view-note-label-container').style.display = 'flex';
|
||||
const lbls = label.split(',');
|
||||
lbls.forEach(l => {
|
||||
const span = document.createElement('span');
|
||||
span.style.cssText = 'display:inline-flex; align-items:center; gap:0.35rem; padding:3px 8px; border-radius:999px; background:rgba(var(--primary-rgb),0.12); color:var(--primary); font-size:0.75rem; font-weight:600;';
|
||||
span.innerHTML = `<i class="fa fa-tag" style="font-size:0.7rem;"></i> ${l.trim()}`;
|
||||
document.getElementById('view-note-label-container').appendChild(span);
|
||||
});
|
||||
} else {
|
||||
document.getElementById('view-note-label-container').style.display = 'none';
|
||||
}
|
||||
|
||||
document.getElementById('view-note-date').textContent = 'Updated ' + updatedAt;
|
||||
document.getElementById('view-note-shared-badge').style.display = !isMine ? 'inline-block' : 'none';
|
||||
|
||||
if(image) {
|
||||
document.getElementById('view-note-image-container').style.display = 'block';
|
||||
document.getElementById('view-note-image').src = image;
|
||||
} else {
|
||||
document.getElementById('view-note-image-container').style.display = 'none';
|
||||
}
|
||||
|
||||
document.getElementById('view-note-content').innerHTML = marked.parse(content);
|
||||
|
||||
document.getElementById('view-note-edit-btn').style.display = isMine ? 'inline-flex' : 'none';
|
||||
|
||||
document.getElementById('view-note-modal').classList.add('active');
|
||||
}
|
||||
|
||||
function transToEditNote() {
|
||||
if(currentViewedCard) {
|
||||
document.getElementById('view-note-modal').classList.remove('active');
|
||||
openNote(currentViewedCard);
|
||||
}
|
||||
}
|
||||
|
||||
function renderNoteShares(jsonShares) {
|
||||
const list = document.getElementById('note-shares-list');
|
||||
list.innerHTML = '';
|
||||
@@ -542,9 +636,12 @@ function renderNoteShares(jsonShares) {
|
||||
|
||||
function removeNoteShare(shareId) {
|
||||
if(!shareId) return;
|
||||
if(!confirm('Unshare this note access?')) return;
|
||||
document.getElementById('remove-note-share-id').value = String(shareId);
|
||||
document.getElementById('remove-note-share-form').submit();
|
||||
appConfirm('Unshare this note access?').then(y => {
|
||||
if(y) {
|
||||
document.getElementById('remove-note-share-id').value = String(shareId);
|
||||
document.getElementById('remove-note-share-form').submit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function filterNotes() {
|
||||
@@ -553,17 +650,21 @@ function filterNotes() {
|
||||
document.querySelectorAll('.note-filter-item').forEach(card => {
|
||||
const t = (card.dataset.title || '').toLowerCase();
|
||||
const c = (card.dataset.content || '').toLowerCase();
|
||||
const cl = (card.dataset.label || '').toLowerCase();
|
||||
const okText = !q || t.includes(q) || c.includes(q) || cl.includes(q);
|
||||
const okLabel = !lbl || cl === lbl;
|
||||
// data-label is now comma separated
|
||||
const cardLabels = (card.dataset.label || '').split(',').map(s=>s.trim());
|
||||
const okText = !q || t.includes(q) || c.includes(q) || cardLabels.join(' ').includes(q);
|
||||
const okLabel = !lbl || cardLabels.includes(lbl);
|
||||
card.style.display = (okText && okLabel) ? '' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
function deleteNote(id) {
|
||||
if(!confirm('Delete this note permanently?')) return;
|
||||
document.getElementById('delete-note-id').value = id;
|
||||
document.getElementById('delete-note-form').submit();
|
||||
appConfirm('Delete this note permanently?').then(y => {
|
||||
if(y) {
|
||||
document.getElementById('delete-note-id').value = id;
|
||||
document.getElementById('delete-note-form').submit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Fix note card colors in dark mode
|
||||
|
||||
Reference in New Issue
Block a user