-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add filter function using custom VuePress component
- Loading branch information
1 parent
6e0109c
commit 40c4dd6
Showing
6 changed files
with
1,492 additions
and
3,595 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
<template> | ||
<div> | ||
<label for="category-filter">Filter by Category:</label> | ||
<div class="spacer"></div> <!-- Spacer between label and dropdown --> | ||
<div class="custom-dropdown" @click="toggleDropdown"> | ||
<div class="selected-category"> | ||
<span :class="`vp-badge ${selectedBadgeClass}`" style="vertical-align: middle;">{{ selectedCategoryLabel }}</span> | ||
</div> | ||
<div class="dropdown-options" v-if="dropdownOpen"> | ||
<div | ||
v-for="option in options" | ||
:key="option.value" | ||
:class="['dropdown-option', { selected: selectedCategory === option.value }]" | ||
@click.stop="selectCategory(option.value)" | ||
> | ||
<span :class="`vp-badge ${option.badgeClass}`" style="vertical-align: middle;">{{ option.label }}</span> | ||
</div> | ||
</div> | ||
</div> | ||
<div class="spacer"></div> <!-- Spacer between dropdown and result count --> | ||
<span class="result-count">{{ resultCount }} tool(s) found</span> | ||
|
||
<div v-html="filteredMarkdown"></div> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
import { marked } from 'marked'; | ||
export default { | ||
props: { | ||
tagName: { | ||
type: String, | ||
default: 'raw-markdown', | ||
}, | ||
}, | ||
data() { | ||
return { | ||
selectedCategory: 'all', | ||
rawMarkdown: '', | ||
resultCount: 0, | ||
dropdownOpen: false, | ||
options: [ | ||
{ value: 'all', label: 'All', badgeClass: '' }, | ||
{ value: 'free', label: 'free', badgeClass: 'info' }, | ||
{ value: 'open-source', label: 'open-source', badgeClass: 'note' }, | ||
{ value: 'commercial', label: 'commercial', badgeClass: 'danger' }, | ||
{ value: 'no-label', label: 'No Label', badgeClass: '' }, // No badge for No Label | ||
], | ||
}; | ||
}, | ||
mounted() { | ||
const markdownElement = this.$el.parentNode.querySelector(this.tagName); | ||
if (markdownElement) { | ||
this.rawMarkdown = markdownElement.innerHTML.trim(); | ||
markdownElement.style.display = 'none'; | ||
} | ||
// Add event listener to close the dropdown when clicking outside | ||
document.addEventListener('click', this.handleClickOutside); | ||
}, | ||
beforeDestroy() { | ||
// Clean up the event listener when the component is destroyed | ||
document.removeEventListener('click', this.handleClickOutside); | ||
}, | ||
computed: { | ||
selectedCategoryLabel() { | ||
const selectedOption = this.options.find(option => option.value === this.selectedCategory); | ||
return selectedOption ? selectedOption.label : 'Select Category'; | ||
}, | ||
selectedBadgeClass() { | ||
const selectedOption = this.options.find(option => option.value === this.selectedCategory); | ||
return selectedOption ? selectedOption.badgeClass : ''; | ||
}, | ||
filteredMarkdown() { | ||
const sectionRegex = /(<h[1-4].*?>.*?<\/h[1-4]>.*?)(?=<h[1-4]|$)/gs; | ||
const sections = [...this.rawMarkdown.matchAll(sectionRegex)]; | ||
const filteredSections = []; | ||
this.resultCount = 0; | ||
sections.forEach(([section]) => { | ||
const listItemRegex = /<li>.*?<\/li>/gs; | ||
const listItems = section.match(listItemRegex) || []; | ||
const matchedItems = listItems.filter((item) => { | ||
const hasLabel = /<span class="vp-badge[^>]*>.*?<\/span>/.test(item); // Check for Badge presence | ||
if (this.selectedCategory === 'all') { | ||
return true; // Include all items for 'all' | ||
} else if (this.selectedCategory === 'no-label') { | ||
return !hasLabel; // Match items without labels | ||
} else { | ||
const badgeRegex = new RegExp( | ||
`<span class="vp-badge[^>]*>\\s*${this.selectedCategory}\\s*</span>`, | ||
'i' | ||
); | ||
return badgeRegex.test(item); // Match based on the selected category | ||
} | ||
}); | ||
if (matchedItems.length > 0) { | ||
const filteredSection = section.replace(listItemRegex, (item) => { | ||
return matchedItems.includes(item) ? item : ''; | ||
}).trim(); | ||
if (filteredSection) { | ||
filteredSections.push(filteredSection); // Push only non-empty sections | ||
this.resultCount += matchedItems.length; | ||
} | ||
} | ||
}); | ||
// Render markdown for non-empty sections only | ||
return marked(filteredSections.join('\n').trim()); | ||
}, | ||
}, | ||
methods: { | ||
toggleDropdown() { | ||
this.dropdownOpen = !this.dropdownOpen; | ||
}, | ||
selectCategory(category) { | ||
this.selectedCategory = category; | ||
this.dropdownOpen = false; | ||
}, | ||
handleClickOutside(event) { | ||
const dropdown = this.$el.querySelector('.custom-dropdown'); | ||
if (dropdown && !dropdown.contains(event.target)) { | ||
this.dropdownOpen = false; // Close dropdown if clicking outside | ||
} | ||
}, | ||
}, | ||
}; | ||
</script> | ||
|
||
<style scoped> | ||
.spacer { | ||
height: 1rem; /* Space between elements */ | ||
} | ||
.custom-dropdown { | ||
position: relative; | ||
display: inline-block; | ||
cursor: pointer; | ||
border: 1px solid #dcdcdc; | ||
border-radius: 4px; | ||
background-color: #fff; /* Light mode background */ | ||
padding: 0.5rem; /* Padding for dropdown */ | ||
width: auto; /* Use dynamic width based on content */ | ||
} | ||
.selected-category { | ||
color: #333; /* Light mode text color */ | ||
font-weight: bold; /* Match label's style */ | ||
display: flex; | ||
align-items: center; | ||
} | ||
.dropdown-options { | ||
position: absolute; | ||
z-index: 1; | ||
background-color: #fff; /* Light mode dropdown background */ | ||
border: 1px solid #dcdcdc; /* Light mode border */ | ||
border-radius: 4px; | ||
min-width: 150px; /* Minimum width for the dropdown */ | ||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | ||
} | ||
.dropdown-option { | ||
padding: 0.5rem; | ||
color: #333; /* Default text color */ | ||
} | ||
.dropdown-option:hover { | ||
background-color: #f0f0f0; /* Highlight on hover */ | ||
} | ||
.dropdown-option.selected { | ||
background-color: #e0e0e0; /* Light mode selected */ | ||
} | ||
/* Dark mode styles */ | ||
[data-theme='dark'] .custom-dropdown { | ||
background-color: #333; /* Dark background */ | ||
border-color: #666; /* Dark border color */ | ||
} | ||
[data-theme='dark'] .selected-category { | ||
color: #fff; /* Dark mode text color */ | ||
} | ||
[data-theme='dark'] .dropdown-options { | ||
background-color: #444; /* Dark background */ | ||
border-color: #666; /* Dark border color */ | ||
} | ||
[data-theme='dark'] .dropdown-option { | ||
color: #fff; /* Light text color */ | ||
} | ||
[data-theme='dark'] .dropdown-option:hover { | ||
background-color: #555; /* Dark hover background */ | ||
} | ||
[data-theme='dark'] .dropdown-option.selected { | ||
background-color: #666; /* Dark selected background */ | ||
} | ||
.result-count { | ||
padding: 0.5rem 0; /* Spacing for the result count */ | ||
color: #333; /* Default text color */ | ||
} | ||
[data-theme='dark'] .result-count { | ||
color: #fff; /* Dark mode text color */ | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.