OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git-v3 / lib / web / components / repo-tabs.js

60 lines · javascript ref:
/**
 * Shared tab chrome for per-repo pages.
 *
 * Every `/repos/:name/*` page renders the same tab row at the top so
 * navigation stays consistent. Active tab is underlined.
 *
 * Pattern lifted from pear-git v1's GitHub-dark aesthetic:
 * #8b949e inactive, #c9d1d9 active, #58a6ff active-underline.
 */

import { escapeHtml } from '../helpers.js'

export const REPO_TAB_STYLES = `
<style>
  .repo-tabs { display: flex; flex-wrap: wrap; gap: 0; align-items: flex-end; border-bottom: 1px solid #30363d; margin: 0 0 1rem; }
  .repo-tabs a { padding: 0.55rem 0.9rem; color: #8b949e; text-decoration: none; border-bottom: 2px solid transparent; font-size: 0.85rem; margin-bottom: -1px; display: inline-flex; align-items: center; gap: 0.4rem; }
  .repo-tabs a:hover { color: #c9d1d9; background: #161b22; }
  .repo-tabs a.active { color: #c9d1d9; border-bottom-color: #58a6ff; font-weight: 500; background: transparent; }
  .repo-tabs a .count { font-size: 0.7rem; color: #8b949e; background: #30363d; padding: 0.05rem 0.4rem; border-radius: 10px; }
  .repo-tabs a.active .count { background: #1f6feb33; color: #c9d1d9; }
</style>`

const TABS = [
  { key: 'overview',  label: 'Overview',  path: '' },
  { key: 'files',     label: 'Files',     path: '/tree' },
  { key: 'commits',   label: 'Commits',   path: '/commits' },
  { key: 'issues',    label: 'Issues',    path: '/issues' },
  { key: 'docs',      label: 'Docs',      path: '/docs' },
  { key: 'releases',  label: 'Releases',  path: '/releases' },
  { key: 'peers',     label: 'Peers',     path: '/peers' }
]

/**
 * Render the tab row for a repo page.
 *
 * @param {string} repoName
 * @param {string} activeTab - one of TABS[i].key (overview|files|commits|issues|releases|peers)
 * @param {object} [counts] - optional { issues: N, commits: N } to show badges
 */
export function renderRepoTabs (repoName, activeTab, counts = {}, prefix = '') {
  const base = prefix + '/repos/' + encodeURIComponent(repoName)
  const countFor = (key) => {
    if (key === 'issues' && typeof counts.issues === 'number') return `<span class="count">${counts.issues}</span>`
    if (key === 'commits' && typeof counts.commits === 'number') return `<span class="count">${counts.commits}</span>`
    return ''
  }

  // Plain <div> — not <nav> — because the global stylesheet styles every
  // <nav> as the fixed top bar (see helpers.js styles()). Using <nav> here
  // would make the tabs overlay the actual navbar.
  return `
<div class="repo-tabs" role="tablist" aria-label="repo navigation">
  ${TABS.map(t =>
    `<a href="${base}${t.path}"${t.key === activeTab ? ' class="active"' : ''} role="tab"${t.key === activeTab ? ' aria-selected="true"' : ''}>` +
    `${escapeHtml(t.label)}${countFor(t.key)}` +
    '</a>'
  ).join('')}
</div>`
}