Skip to content

Commit

Permalink
Add filter function using custom VuePress component
Browse files Browse the repository at this point in the history
  • Loading branch information
trolologuy committed Nov 2, 2024
1 parent 6e0109c commit 40c4dd6
Show file tree
Hide file tree
Showing 6 changed files with 1,492 additions and 3,595 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,17 @@ You can either find the source [markdown file here](docs/readme.md), or access [

For development mode:

1. Run `yarn install` to install all dependencies.
2. Run `yarn vuepress dev docs`
1. Run `npm install` to install all dependencies.
2. Run `npm run docs:dev`
3. Open [http://localhost:8080/useful-tools/](http://localhost:8080/useful-tools/) in your browser.

To generate the static files:

1. Run `yarn install` to install all dependencies.
2. Run `yarn vuepress build docs`
1. Run `npm install` to install all dependencies.
2. Run `npm run docs:build`

To update the dependencies:
1. Run `yarn upgrade`
1. Run `npm upgrade`

</details>

Expand Down
214 changes: 214 additions & 0 deletions docs/.vuepress/components/ToolFilter.vue
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>
7 changes: 7 additions & 0 deletions docs/.vuepress/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getDirname, path } from 'vuepress/utils'
import { defineUserConfig } from 'vuepress'
import { viteBundler } from '@vuepress/bundler-vite'
import { defaultTheme } from '@vuepress/theme-default'
Expand All @@ -6,6 +7,7 @@ import { backToTopPlugin } from '@vuepress/plugin-back-to-top'
import { nprogressPlugin } from '@vuepress/plugin-nprogress'
import { docsearchPlugin } from '@vuepress/plugin-docsearch'
import { linksCheckPlugin } from '@vuepress/plugin-links-check'
import { registerComponentsPlugin } from '@vuepress/plugin-register-components'

export default defineUserConfig({
bundler: viteBundler({
Expand All @@ -20,6 +22,11 @@ export default defineUserConfig({
sidebar: 'heading',
}),
plugins: [
registerComponentsPlugin({
components: {
ToolFilter: path.resolve(__dirname, './components/ToolFilter.vue'),
},
}),
activeHeaderLinksPlugin({
sidebarLinkSelector: '.sidebar-link',
headerAnchorSelector: '.header-anchor'
Expand Down
Loading

0 comments on commit 40c4dd6

Please sign in to comment.