tags
This commit is contained in:
1 parent
a9603aa0e8
commit
719f9d51ff
13 files changed
+625
-70
No files matched your search
@@ -1,7 +1,7 @@
|
||||
// ── Board rendering & card interactions ──
|
||||
|
||||
import { $, esc, api } from './util.js';
|
||||
import { state, sortedCards, getChildren, getRecent, getFavorites, isFavorite, SORT_MODES, SORT_ICONS, CARD_COLORS } from './state.js';
|
||||
import { state, sortedCards, parseTags, tagChipHtml, getChildren, getRecent, getFavorites, isFavorite, SORT_MODES, SORT_ICONS, CARD_COLORS } from './state.js';
|
||||
import { stripMd, getTodos, firstImage, leadingImage } from './markdown.js';
|
||||
import { pushReorder, pushCardEdit, pushBatch, snapshotCard } from './undo.js';
|
||||
|
||||
@@ -131,6 +131,13 @@ export function renderBoard() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Tag filter (intersects with bucket filter) — hierarchical: #nase matches #nase/popel
|
||||
if (state.activeTags.length) {
|
||||
cards = cards.filter(c => {
|
||||
const ct = parseTags(c);
|
||||
return state.activeTags.every(at => ct.some(t => t === at || t.startsWith(at + '/')));
|
||||
});
|
||||
}
|
||||
// Current and journal have fixed sort order, skip global sort
|
||||
if (state.activeBucket !== 'current' && state.activeBucket !== 'journal') {
|
||||
cards = sortedCards(cards);
|
||||
@@ -161,6 +168,7 @@ export function renderBoard() {
|
||||
const todos = getTodos(c.body);
|
||||
const todoPct = todos ? Math.round(todos.done / todos.total * 100) : 0;
|
||||
const hasCover = !!coverImg;
|
||||
const tags = parseTags(c);
|
||||
|
||||
return `
|
||||
<div class="board-card ${isGroup ? 'card-group ' : ''}card-glow ${hasCover ? 'card-has-cover' : 'bg-t-surface'} border border-t-border cursor-pointer hover:border-t-muted transition-colors relative group"
|
||||
@@ -180,6 +188,7 @@ export function renderBoard() {
|
||||
<span class="font-pixel text-[9px] ${todos.done === todos.total ? 'text-t-accent' : 'text-t-dim'}">${todos.done}/${todos.total}</span>
|
||||
</div></div>` : ''}
|
||||
<p class="text-xs ${hasCover ? 'text-t-text' : 'text-t-muted'} leading-relaxed line-clamp-5 flex-1">${esc(stripMd(c.body))}</p>
|
||||
${tags.length ? `<div class="flex flex-wrap gap-1 mt-1.5 card-layer">${tags.slice(0, 5).map(t => tagChipHtml(t)).join('')}${tags.length > 5 ? `<span class="tag-chip tag-chip-more">+${tags.length - 5}</span>` : ''}</div>` : ''}
|
||||
<div class="mt-auto pt-2 flex items-center gap-2 text-[10px] ${hasCover ? 'text-t-text' : 'text-t-dim'} font-pixel">
|
||||
<span class="flex items-center gap-1"><i class="ph ph-clock text-xs"></i>${(c.updated_at || c.created_at || '').substring(0, 10)}</span>
|
||||
${c.bucket_name ? `<span class="flex items-center gap-1"><i class="ph ph-folder text-xs"></i>${esc(c.bucket_name)}</span>` : ''}
|
||||
@@ -291,6 +300,22 @@ function bindCardEvents() {
|
||||
badge.addEventListener('click', e => { e.stopPropagation(); state.activeGroup = badge.dataset.group; renderBoard(); });
|
||||
});
|
||||
|
||||
board.querySelectorAll('.tag-seg[data-tag]').forEach(seg => {
|
||||
seg.addEventListener('click', e => {
|
||||
e.stopPropagation();
|
||||
const tag = seg.dataset.tag;
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
const idx = state.activeTags.indexOf(tag);
|
||||
if (idx >= 0) state.activeTags.splice(idx, 1);
|
||||
else state.activeTags.push(tag);
|
||||
} else {
|
||||
state.activeTags = state.activeTags.length === 1 && state.activeTags[0] === tag ? [] : [tag];
|
||||
}
|
||||
window._renderTagBar?.();
|
||||
renderBoard();
|
||||
});
|
||||
});
|
||||
|
||||
board.querySelectorAll('[data-id]').forEach(el => {
|
||||
const id = el.dataset.id;
|
||||
const children = getChildren(id);
|
||||
|
||||
Reference in new issue
Block a user