bsc

Comprehensive codebase and cou...
Log | Files | Refs | Activity | README | LICENSE

root / docs / tags.md

tags.md (6750B)


      1 ---
      2 source: 'docs/tags.md'
      3 title: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:inline; margin-bottom:-2px; margin-right:6px;"><path d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z"/><circle cx="7.5" cy="7.5" r=".5" fill="currentColor"/></svg> Tags'
      4 ---
      5 
      6 <script setup>
      7 import { ref, computed } from 'vue'
      8 import { data as tagsData } from './.vitepress/tags.data.ts'
      9 
     10 const tagNames = Object.keys(tagsData).sort()
     11 
     12 // Store an array of active tags instead of a single string
     13 const activeFilters = ref([])
     14 
     15 // Compute pages that contain ALL selected tags (AND logic)
     16 const filteredPages = computed(() => {
     17   if (activeFilters.value.length === 0) return []
     18 
     19   // Start with the pages of the first selected tag
     20   let commonPages = tagsData[activeFilters.value[0]]
     21 
     22   // Intersect with pages of the other selected tags
     23   for (let i = 1; i < activeFilters.value.length; i++) {
     24     const nextTagPages = tagsData[activeFilters.value[i]]
     25     commonPages = commonPages.filter(page =>
     26       nextTagPages.some(p => p.url === page.url)
     27     )
     28   }
     29   
     30   return commonPages
     31 })
     32 
     33 function toggleFilter(tag) {
     34   const index = activeFilters.value.indexOf(tag)
     35   if (index > -1) {
     36     // Tag is currently active, remove it
     37     activeFilters.value.splice(index, 1)
     38   } else {
     39     // Tag is not active, add it
     40     activeFilters.value.push(tag)
     41   }
     42 }
     43 
     44 function humanize(tag) {
     45   return tag.replace(/[-_]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase())
     46 }
     47 </script>
     48 
     49 # Tags
     50 
     51 Browse pages grouped by tag. Click tags below to filter results, or click a tag chip anywhere on the site to jump straight here.
     52 
     53 <div v-if="!tagNames.length" class="bsc-tags-empty">
     54   No tags found yet.
     55 </div>
     56 
     57 <div v-else class="bsc-filter-bar">
     58   <button
     59     v-for="tag in tagNames"
     60     :key="tag"
     61     class="bsc-filter-chip"
     62     :class="{ 'bsc-filter-chip-active': activeFilters.includes(tag) }"
     63     @click="toggleFilter(tag)"
     64   >{{ humanize(tag) }}</button>
     65 </div>
     66 
     67 <p v-if="activeFilters.length" class="bsc-filter-status">
     68   Showing pages with <strong>all</strong> selected tags: 
     69   <strong style="color: var(--vp-c-brand-1);">{{ activeFilters.map(humanize).join(' + ') }}</strong> —
     70   <a href="#" class="bsc-filter-clear" @click.prevent="activeFilters = []">clear filters</a>
     71 </p>
     72 
     73 <!-- If filters are active, show the combined intersection of pages -->
     74 <div v-if="activeFilters.length" class="bsc-tag-section">
     75   <h2 class="bsc-tag-heading">
     76     Filtered Results
     77     <span class="bsc-tag-count">{{ filteredPages.length }}</span>
     78   </h2>
     79   
     80   <ul v-if="filteredPages.length" class="bsc-tag-pages">
     81     <li v-for="page in filteredPages" :key="page.url">
     82       <a :href="page.url">{{ page.title }}</a>
     83     </li>
     84   </ul>
     85   <div v-else class="bsc-tags-empty" style="margin-top: 16px;">
     86     No pages match all selected tags.
     87   </div>
     88 </div>
     89 
     90 <!-- If no filters are active, show all standard tag sections -->
     91 <template v-else>
     92   <div v-for="tag in tagNames" :key="tag" :id="tag" class="bsc-tag-section">
     93     <h2 class="bsc-tag-heading">
     94       <a class="bsc-tag" :href="`#${tag}`">{{ humanize(tag) }}</a>
     95       <span class="bsc-tag-count">{{ tagsData[tag].length }}</span>
     96     </h2>
     97     <ul class="bsc-tag-pages">
     98       <li v-for="page in tagsData[tag]" :key="page.url">
     99         <a :href="page.url">{{ page.title }}</a>
    100       </li>
    101     </ul>
    102   </div>
    103 </template>
    104 
    105 <style scoped>
    106 .bsc-tags-empty {
    107   color: var(--vp-c-text-2);
    108   font-size: 14px;
    109 }
    110 
    111 .bsc-filter-bar {
    112   display: flex;
    113   flex-wrap: wrap;
    114   gap: 8px;
    115   margin: 16px 0 8px;
    116 }
    117 
    118 .bsc-filter-chip {
    119   font: inherit;
    120   font-size: 13px;
    121   font-weight: 500;
    122   padding: 5px 12px;
    123   color: var(--vp-c-text-2);
    124   background: var(--vp-c-bg-soft);
    125   border: 1px solid var(--vp-c-divider);
    126   border-radius: 16px;
    127   cursor: pointer;
    128   -webkit-tap-highlight-color: transparent;
    129 }
    130 
    131 /* Only change to blue text on hover if the chip is NOT active */
    132 .bsc-filter-chip:not(.bsc-filter-chip-active):hover {
    133   color: var(--vp-c-brand-1);
    134   border-color: var(--vp-c-brand-1);
    135 }
    136 
    137 /* Lock the active state to white text so it never disappears on hover */
    138 .bsc-filter-chip-active {
    139   color: #ffffff !important;
    140   background: var(--vp-c-brand-1) !important;
    141   border-color: var(--vp-c-brand-1) !important;
    142 }
    143 
    144 .bsc-filter-status {
    145   font-size: 13px;
    146   color: var(--vp-c-text-2);
    147   margin: 0 0 12px;
    148 }
    149 
    150 .bsc-filter-clear {
    151   color: var(--vp-c-text-3);
    152   text-decoration: underline;
    153   cursor: pointer;
    154   transition: color 0.2s ease;
    155 }
    156 
    157 .bsc-filter-clear:hover {
    158   color: var(--vp-c-brand-1);
    159 }
    160 
    161 .bsc-tag-section {
    162   margin: 28px 0;
    163   padding-top: 8px;
    164   border-top: 1px solid var(--vp-c-divider);
    165 }
    166 
    167 .bsc-tag-heading {
    168   display: flex;
    169   align-items: center;
    170   gap: 10px;
    171   margin: 16px 0 10px;
    172   border: none;
    173   padding: 0;
    174 }
    175 
    176 .bsc-tag-count {
    177   font-size: 12px;
    178   color: var(--vp-c-text-3);
    179   font-weight: 400;
    180 }
    181 
    182 .bsc-tag {
    183   position: relative;
    184   display: inline-flex;
    185   align-items: center;
    186   height: 28px;
    187   padding: 0 14px 0 10px;
    188   font-size: 14px;
    189   font-weight: 500;
    190   line-height: 1;
    191   text-decoration: none;
    192   white-space: nowrap;
    193 
    194   color: var(--vp-c-brand-1);
    195   background: var(--vp-c-bg-soft);
    196 
    197   border: 1px solid var(--vp-c-divider);
    198   border-left: none;
    199   border-radius: 0 5px 5px 0;
    200 
    201   margin-left: 14px;
    202 }
    203 
    204 /* The Pointed Left Angle */
    205 .bsc-tag::after {
    206   content: '';
    207   position: absolute;
    208   left: -10px;
    209   top: 50%;
    210   margin-top: -10px;
    211   width: 20px;
    212   height: 20px;
    213 
    214   background: inherit;
    215 
    216   border-left: 1px solid var(--vp-c-divider);
    217   border-bottom: 1px solid var(--vp-c-divider);
    218   border-radius: 0 0 0 3px;
    219 
    220   transform: rotate(45deg);
    221   z-index: -1;
    222 }
    223 
    224 /* The Hole Punch Dot */
    225 .bsc-tag::before {
    226   content: '';
    227   position: absolute;
    228   left: -3px;
    229   top: 50%;
    230   margin-top: -2px;
    231   width: 4px;
    232   height: 4px;
    233   border-radius: 50%;
    234   background: var(--vp-c-brand-1);
    235   z-index: 1;
    236 }
    237 
    238 /* Hover States */
    239 .bsc-tag:hover {
    240   color: #fff;
    241   background: var(--vp-c-brand-1);
    242   border-color: var(--vp-c-brand-1);
    243 }
    244 
    245 .bsc-tag:hover::after {
    246   border-color: var(--vp-c-brand-1);
    247 }
    248 
    249 .bsc-tag:hover::before {
    250   background: #fff;
    251 }
    252 
    253 .bsc-tag-pages {
    254   margin: 0;
    255   padding-left: 4px;
    256   list-style: none;
    257   display: flex;
    258   flex-wrap: wrap;
    259   align-items: center;
    260   row-gap: 10px;
    261   column-gap: 20px;
    262 }
    263 
    264 .bsc-tag-pages li {
    265   margin: 0 !important;
    266   padding: 0;
    267   line-height: 1.6;
    268 }
    269 
    270 .bsc-tag-pages a {
    271   display: inline-block;
    272   color: var(--vp-c-brand-1);
    273   text-decoration: none;
    274   font-size: 14px;
    275   line-height: 1.6;
    276 }
    277 
    278 .bsc-tag-pages a:hover {
    279   text-decoration: underline;
    280 }
    281 </style>
© notamitgamer • Site Built: 2026-09-05 01:53:16 UTC • git-mirror commit: c170d72 [view raw info]
Originally created with stagit • modified by notamitgamer
Forked from github.com/notamitgamer/git-mirror