mirror of
https://github.com/tiennm99/programming-fengshui.git
synced 2026-09-02 18:20:09 +00:00
feat(ui): phase-02 url state, gitlab tooltip, hide empty kim card
- Persist source/view/sort toggles via URL query params (?s=…&v=…&o=…). Short keys keep shared URLs readable; defaults are stripped to keep the bare URL clean. history.replaceState (no history pollution). Init seeds toggles from URL with clamp-to-default validation. - View class is applied BEFORE first render so renderGrid sees the correct show-all state on direct deep-link loads. - Add #source-note pill next to the source tag; shows "91 ngôn ngữ — palette riêng so với GitHub" when GitLab selected, hidden otherwise. Plus title attr on the source-toggle group with the full GitHub-vs-GitLab disparity. - In TIOBE view, hide cards with zero TIOBE-ranked langs (currently KIM has 0 of 19) — all-langs view still shows every Ngũ Hành card so the wheel stays intact. - source-tag firstChild text-node overwrite preserves the source-note span sibling. Closes phase-02 of plan 260428-1003-implement-all-remaining (items 2, 3, 4).
This commit is contained in:
+1
-1
@@ -40,7 +40,7 @@
|
||||
|
||||
<section class="elements">
|
||||
<h2>Ngũ Hành & ngôn ngữ</h2>
|
||||
<p class="mode-tag" id="source-tag">Nguồn: GitHub Linguist</p>
|
||||
<p class="mode-tag" id="source-tag">Nguồn: GitHub Linguist <span class="source-note" id="source-note" hidden></span></p>
|
||||
<div class="modern-controls">
|
||||
<div class="control-group">
|
||||
<span class="control-label">Nguồn màu</span>
|
||||
|
||||
+65
-9
@@ -28,12 +28,34 @@ const SORT_OPTIONS = [
|
||||
{ key: 'hue', label: 'Cầu vồng' },
|
||||
];
|
||||
|
||||
// Short keys keep the URL readable when shared.
|
||||
const QUERY_KEYS = { source: 's', view: 'v', sort: 'o' };
|
||||
|
||||
function readQueryState() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
return {
|
||||
source: params.get(QUERY_KEYS.source) || DEFAULT_SOURCE,
|
||||
view: params.get(QUERY_KEYS.view) || 'tiobe',
|
||||
sort: params.get(QUERY_KEYS.sort) || 'tiobe',
|
||||
};
|
||||
}
|
||||
|
||||
function writeQueryParam(key, value, defaultValue) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
if (value === defaultValue) params.delete(QUERY_KEYS[key]);
|
||||
else params.set(QUERY_KEYS[key], value);
|
||||
const qs = params.toString();
|
||||
const next = qs ? `?${qs}${window.location.hash}` : window.location.pathname + window.location.hash;
|
||||
history.replaceState(null, '', next);
|
||||
}
|
||||
|
||||
const refs = {
|
||||
grid: null,
|
||||
legend: null,
|
||||
section: null,
|
||||
debug: null,
|
||||
sourceTag: null,
|
||||
sourceNote: null,
|
||||
};
|
||||
|
||||
let lastBuckets = null;
|
||||
@@ -98,7 +120,22 @@ async function loadAndRender(sourceKey) {
|
||||
lastBuckets = buckets;
|
||||
applySortAndRender();
|
||||
if (refs.legend) refs.legend.textContent = LEGEND_TEXT;
|
||||
if (refs.sourceTag) refs.sourceTag.textContent = `Nguồn: ${source.label} Linguist`;
|
||||
if (refs.sourceTag) {
|
||||
// First child is the text node; preserve the `#source-note` span sibling.
|
||||
const first = refs.sourceTag.firstChild;
|
||||
const tagText = `Nguồn: ${source.label} Linguist `;
|
||||
if (first && first.nodeType === Node.TEXT_NODE) first.nodeValue = tagText;
|
||||
else refs.sourceTag.prepend(document.createTextNode(tagText));
|
||||
}
|
||||
if (refs.sourceNote) {
|
||||
if (sourceKey === 'gitlab') {
|
||||
refs.sourceNote.hidden = false;
|
||||
refs.sourceNote.textContent = '91 ngôn ngữ — palette riêng so với GitHub';
|
||||
} else {
|
||||
refs.sourceNote.hidden = true;
|
||||
refs.sourceNote.textContent = '';
|
||||
}
|
||||
}
|
||||
renderDebugPanel({ skipped, borderline }, refs.debug);
|
||||
} catch (err) {
|
||||
console.error('[programming-fengshui] failed to render modern grid:', err);
|
||||
@@ -116,29 +153,48 @@ function init() {
|
||||
refs.section = section;
|
||||
refs.debug = document.getElementById('debug-panel');
|
||||
refs.sourceTag = document.getElementById('source-tag');
|
||||
refs.sourceNote = document.getElementById('source-note');
|
||||
|
||||
// Seed toggle defaults from URL query params; clamp unknown values.
|
||||
const initial = readQueryState();
|
||||
const validSource = SOURCES[initial.source] ? initial.source : DEFAULT_SOURCE;
|
||||
const validView = VIEW_OPTIONS.some((o) => o.key === initial.view) ? initial.view : 'tiobe';
|
||||
const validSort = SORT_OPTIONS.some((o) => o.key === initial.sort) ? initial.sort : 'tiobe';
|
||||
|
||||
currentSort = validSort;
|
||||
// Apply view class BEFORE first render so renderGrid sees the right state
|
||||
// (drives the empty-card hide logic in render-elements.js).
|
||||
if (validView === 'all') section?.classList.add('show-all');
|
||||
|
||||
const sourceToggleEl = document.getElementById('source-toggle');
|
||||
sourceToggleEl?.setAttribute('title', 'GitHub có 664 ngôn ngữ; GitLab có 91 và dùng palette riêng');
|
||||
|
||||
mountSegmentedControl(
|
||||
document.getElementById('source-toggle'),
|
||||
sourceToggleEl,
|
||||
Object.entries(SOURCES).map(([key, s]) => ({ key, label: s.label })),
|
||||
DEFAULT_SOURCE,
|
||||
loadAndRender,
|
||||
validSource,
|
||||
(key) => { writeQueryParam('source', key, DEFAULT_SOURCE); loadAndRender(key); },
|
||||
'Nguồn dữ liệu màu',
|
||||
);
|
||||
mountSegmentedControl(
|
||||
document.getElementById('view-toggle'),
|
||||
VIEW_OPTIONS,
|
||||
'tiobe',
|
||||
(key) => section?.classList.toggle('show-all', key === 'all'),
|
||||
validView,
|
||||
(key) => {
|
||||
writeQueryParam('view', key, 'tiobe');
|
||||
section?.classList.toggle('show-all', key === 'all');
|
||||
applySortAndRender();
|
||||
},
|
||||
'Phạm vi hiển thị ngôn ngữ',
|
||||
);
|
||||
mountSegmentedControl(
|
||||
document.getElementById('sort-toggle'),
|
||||
SORT_OPTIONS,
|
||||
'tiobe',
|
||||
(key) => { currentSort = key; applySortAndRender(); },
|
||||
validSort,
|
||||
(key) => { writeQueryParam('sort', key, 'tiobe'); currentSort = key; applySortAndRender(); },
|
||||
'Sắp xếp ngôn ngữ',
|
||||
);
|
||||
loadAndRender(DEFAULT_SOURCE);
|
||||
loadAndRender(validSource);
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
|
||||
@@ -39,10 +39,15 @@ function buildChip(name, color, { rank = null } = {}) {
|
||||
|
||||
export function renderGrid(buckets, mountEl) {
|
||||
if (!mountEl) return;
|
||||
// In TIOBE view, skip cards with zero TIOBE-ranked langs — empty
|
||||
// "0 ngôn ngữ" headings look broken. All-langs view always shows
|
||||
// every element so the Ngũ Hành wheel stays intact.
|
||||
const showAll = mountEl.closest('.elements')?.classList.contains('show-all') ?? false;
|
||||
const fragment = document.createDocumentFragment();
|
||||
for (const { key, label } of ELEMENTS) {
|
||||
const langs = buckets[key] || [];
|
||||
const tiobeCount = langs.filter((l) => l.rank).length;
|
||||
if (!showAll && tiobeCount === 0) continue;
|
||||
const card = document.createElement('article');
|
||||
card.className = `card ${key}`;
|
||||
const h3 = document.createElement('h3');
|
||||
|
||||
@@ -16,7 +16,7 @@ Single pass through every remaining item in `plans/todo.md` and the post-cleanup
|
||||
| # | Title | Status | Items |
|
||||
|---|-------|--------|-------|
|
||||
| 01 | Style & markup polish | **done** | items 5–10, 13, 14, 15 (CSS one-liners + chip role + decision docs) |
|
||||
| 02 | Functional toggles & tooltip | pending | items 2, 3, 4 (URL persistence, GitLab tooltip, hide empty KIM in TIOBE view) |
|
||||
| 02 | Functional toggles & tooltip | **done** | items 2, 3, 4 (URL persistence, GitLab tooltip, hide empty KIM in TIOBE view) |
|
||||
| 03 | Additive features | pending | items 11, 12 (Top-5 non-TIOBE peek, lunar SVG body texture) |
|
||||
| 04 | OG social card image | pending | item 1 (generate `assets/og-card.png`, may block on tooling) |
|
||||
|
||||
|
||||
@@ -138,6 +138,18 @@ body {
|
||||
text-align: center; font-size: 0.85rem; font-style: italic;
|
||||
color: var(--muted); font-weight: 400; margin: -0.5rem 0 1rem;
|
||||
}
|
||||
.source-note {
|
||||
display: inline-block;
|
||||
margin-left: 0.4rem;
|
||||
padding: 0.05rem 0.5rem;
|
||||
border-radius: 999px;
|
||||
background: var(--bg-tint);
|
||||
color: var(--accent-ink);
|
||||
font-size: 0.7rem;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
/* ---------- Card grid ---------- */
|
||||
/* Grid stays auto-fit (not forced 5-col) — the Ngũ-Hành wheel reads at any
|
||||
|
||||
Reference in New Issue
Block a user