4 Commits

Author SHA1 Message Date
Keith Solomon
785266e455 chore: Update changelog for version 1.6.1; fix filter removal bug and enhance template customization instructions #release
Some checks failed
Add release / Create release (push) Has been cancelled
2025-03-29 15:42:49 -05:00
Keith Solomon
699c06654c refactor: Enhance resource filter functionality and improve UI elements in filter forms 2025-03-29 15:35:42 -05:00
Keith Solomon
758b464121 fix: Update GitHubUpdater path to account for Windows-based paths 2025-03-29 13:18:47 -05:00
Keith Solomon
c297c27ba2 release: Bump version to 1.6.0; implement full admin configurability and automate release builds #release
Some checks failed
Add release / Create release (push) Has been cancelled
2025-03-29 12:18:54 -05:00
8 changed files with 442 additions and 163 deletions

View File

@@ -1,5 +1,17 @@
# Changelog
## 1.6.1 - 2025-03-29
- Fixed bug in filter removal functionality
- Updated templates for a better starting point for customization
## 1.6.0 - 2025-03-29
- Implement full admin configuarability
- Update action to build releases automatically
- Clean up unneeded files
- Update templates to use new admin settings
## 1.5.4 - 2025-03-03
- Fix count issue

View File

@@ -35,7 +35,8 @@ Add one of the following shortcodes to your page or post content where you want
The plugin is designed to work out of the box with minimal configuration. However, you can customize the following:
- **Templates**: Override the default templates and styles by placing your custom versions in your theme's `resource-filter` directory. You will need to add `"./resource-filter/**/*.{php,vue,js,cjs}",` to the `content` object in your `tailwind-config.js` file to compile the custom styles.
- **Templates**: Override the default templates and styles by placing your custom versions in your theme's `resource-filter` directory. You will need to add `"./resource-filter/**/*.{php,css}",` to the `content` object in your `tailwind-config.js` or `@source "./resource-filter/**/*.{php,css}` in your main css file is using Tailwind 4 to compile the custom styles.
- **Template Files**:
- `filter-form.php` - Main form template
- `filter-homepage.php` - Secondary form template for the homepage or other uses
@@ -43,18 +44,20 @@ The plugin is designed to work out of the box with minimal configuration. Howeve
- `resource-results.php` - Template for the search results
- `style.css` - Custom styles for the filter system
- **Taxonomies**: Ensure your WordPress site has the `resource_type` and `resource_subject` taxonomies set up for the `resource` post type.
## Roadmap
- ~~Repo updates~~
- ~~Admin configuration page~~
- ~~tag close functionality~~
- ~~general reset button~~
- ~~pagination~~
## Changelog
## 1.6.1 - 2025-03-29
- Fixed bug in filter removal functionality
- Updated templates for a better starting point for customization
## 1.6.0 - 2025-03-29
- Implement full admin configuarability
- Update action to build releases automatically
- Clean up unneeded files
- Update templates to use new admin settings
## 1.5.4 - 2025-03-03
- Fix count issue

View File

@@ -6,7 +6,7 @@ jQuery(document).ready(function ($) {
*
* @param {number} paged The current page number (default is 1).
*/
function triggerFiltering(paged = 1) {
window.triggerFiltering = function (paged = 1) { // Attach to the window object
let searchTerm = $('#search').val();
let appliedFilters = [];
@@ -29,8 +29,8 @@ jQuery(document).ready(function ($) {
}
taxonomyFilters[taxonomy].push({
value: $(this).val(),
text: $(this).closest('label').text().trim() // Get the text associated with the checkbox
value: $(this).val(),
text: $(this).closest('label').text().trim() // Get the text associated with the checkbox
});
});
@@ -98,7 +98,8 @@ jQuery(document).ready(function ($) {
$('.pagination').html('');
}
});
}
};
// Handle sort order change
$('#sortOrder').on('change', function () {
@@ -157,15 +158,17 @@ document.addEventListener('DOMContentLoaded', function () {
button.addEventListener('click', function () {
const dropdown = this.parentElement;
// Close all other dropdowns
// Close all other dropdowns and update aria-expanded
document.querySelectorAll('.custom-dropdown').forEach(function (otherDropdown) {
if (otherDropdown !== dropdown) {
otherDropdown.classList.remove('open');
otherDropdown.querySelector('.dropdown-toggle').setAttribute('aria-expanded', 'false');
}
});
// Toggle the current dropdown
dropdown.classList.toggle('open');
// Toggle the current dropdown and update aria-expanded
const isOpen = dropdown.classList.toggle('open');
this.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
});
});

View File

@@ -4,8 +4,9 @@
* Plugin URI: https://github.com/Vincent-Design-Inc/resource-filter
* Update URI: https://github.com/Vincent-Design-Inc/resource-filter
* Description: Adds filtering for the content typed by various taxonomies.
* Version: 1.5.4
* Version: 1.6.1
* Author: Keith Solomon
* Author URI: https://vincentdesign.ca
*/
if (!defined('ABSPATH')) { exit; } // Prevent direct access
@@ -557,7 +558,7 @@ class ContentFilterPlugin {
}
new ContentFilterPlugin();
$gitHubUpdater = new GitHubUpdater(__FILE__);
$gitHubUpdater = new GitHubUpdater(plugin_dir_path(__FILE__).'resource-filter.php');
$gitHubUpdater->setChangelog('CHANGELOG.md');
$gitHubUpdater->setPluginIcon('assets/icon-256x256.png');
$gitHubUpdater->setPluginBannerLarge('assets/banner.jpg');

View File

@@ -5,11 +5,16 @@ $selected_taxonomies = get_option('content_filter_taxonomies', []); // Get selec
?>
<form class="px-4 sm:px-0" id="resource-filter">
<!-- Search Field - Theme -->
<!-- Search Form - Plugin -->
<div class="search-text">
<input class="bg-light border-secondary border-2 full-width" type="text" id="search" name="search" placeholder="Search..." value="<?php echo isset($search) ? esc_attr($search) : ''; ?>">
<button class="bg-primary text-white" type="submit">Search</button>
<button type="reset" class="bg-danger text-white" id="clear-search">&times;</button>
<div class="search-input-wrapper flex-grow">
<input class="full-width bg-[#F8F8F8] border-[#8B8B8B] border rounded-xl" type="text" id="search" name="search"
placeholder="Search resources..." value="<?php echo isset($search) ? esc_attr($search) : ''; ?>">
<button type="reset" id="clear-search">&times;</button>
</div>
<div class="flex justify-center md:justify-start">
<button class="btn btn-primary bg-primary text-white rounded-xl w-full md:w-auto px-4 py-2" type="submit">Search</button>
</div>
</div>
<div class="search-tax">
@@ -20,8 +25,10 @@ $selected_taxonomies = get_option('content_filter_taxonomies', []); // Get selec
if (!empty($terms) && !empty($taxonomy_obj)): ?>
<div class="custom-dropdown">
<button type="button" class="dropdown-toggle"><?php echo esc_html($taxonomy_obj->labels->singular_name); ?></button>
<div class="dropdown-menu">
<button id="<?php echo esc_attr($taxonomy); ?>_toggle" type="button" class="dropdown-toggle" aria-haspopup="true" aria-expanded="false">
<div id="<?php echo esc_attr($taxonomy); ?>_text" class="dropdown-text"><?php echo esc_html($taxonomy_obj->labels->singular_name); ?></div>
</button>
<div class="dropdown-menu taxonomy-filter" data-taxonomy="<?php echo esc_attr($taxonomy); ?>">
<?php foreach ($terms as $term): ?>
<label>
<input type="checkbox" name="<?php echo esc_attr($taxonomy); ?>[]" value="<?php echo esc_attr($term->slug); ?>" <?php echo (isset($_POST[$taxonomy]) && in_array($term->slug, (array) $_POST[$taxonomy])) ? 'checked' : ''; ?>>
@@ -34,3 +41,33 @@ $selected_taxonomies = get_option('content_filter_taxonomies', []); // Get selec
<?php endforeach; ?>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', function () {
const form = document.getElementById('resource-filter');
const resetButton = document.getElementById('clear-search');
resetButton.addEventListener('click', function (e) {
e.preventDefault(); // Prevent the default reset behavior
// Clear all input fields
form.reset();
// Clear all checkboxes
const checkboxes = form.querySelectorAll('input[type="checkbox"]');
checkboxes.forEach(checkbox => checkbox.checked = false);
// Reset dropdown text to taxonomy name
document.querySelectorAll('.custom-dropdown').forEach(function (dropdown) {
const taxonomy = dropdown.querySelector('.dropdown-toggle').id.replace('_toggle', '');
const taxonomyName = taxonomy.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase()); // Convert to title case
dropdown.querySelector('.dropdown-text').textContent = taxonomyName;
});
// Trigger filtering without reloading the page
if (typeof triggerFiltering === 'function') {
triggerFiltering(1); // Call the function with the first page
}
});
});
</script>

View File

@@ -2,7 +2,8 @@
if (!defined('ABSPATH')) { exit; } // Prevent direct access
// Get count from AJAX or direct POST
$count = isset($resTotal) ? esc_html($resTotal) : 0;
global $postsTotal;
$count = isset($postsTotal) ? esc_html($postsTotal) : 0;
// Initialize filters array
$filters = [];
@@ -54,21 +55,21 @@ if (!empty($filters)) {
<p><strong>Showing <span id="result-count"><?php echo esc_html($count); ?></span> resource(s)</strong></p>
<div class="sort-filters flex items-start gap-4">
<!-- Sort Container -->
<div id="sort-container">
<label for="sortOrder">Sort by:</label>
<select id="sortOrder">
<option value="date_desc" <?php selected(isset($_GET['sortOrder']) ? $_GET['sortOrder'] : '', 'date_desc'); ?>>Newest First</option>
<option value="date_asc" <?php selected(isset($_GET['sortOrder']) ? $_GET['sortOrder'] : '', 'date_asc'); ?>>Oldest First</option>
<option value="title_asc" <?php selected(isset($_GET['sortOrder']) ? $_GET['sortOrder'] : '', 'title_asc'); ?>>Title (A-Z)</option>
<option value="title_desc" <?php selected(isset($_GET['sortOrder']) ? $_GET['sortOrder'] : '', 'title_desc'); ?>>Title (Z-A)</option>
</select>
</div>
<!-- Applied Filters -->
<p>
<strong>Filters applied:</strong><br>
<span id="applied-filters"><?php echo $filterHtml; ?></span>
</p>
</div>
<!-- Sort Container -->
<div id="sort-container">
<label for="sortOrder"><strong>Sort by:</strong></label>
<select id="sortOrder" name="sortOrder">
<option value="date_desc" <?php selected(isset($_GET['sortOrder']) ? $_GET['sortOrder'] : '', 'date_desc'); ?>>Newest First</option>
<option value="date_asc" <?php selected(isset($_GET['sortOrder']) ? $_GET['sortOrder'] : '', 'date_asc'); ?>>Oldest First</option>
<option value="title_asc" <?php selected(isset($_GET['sortOrder']) ? $_GET['sortOrder'] : '', 'title_asc'); ?>>Title (A-Z)</option>
<option value="title_desc" <?php selected(isset($_GET['sortOrder']) ? $_GET['sortOrder'] : '', 'title_desc'); ?>>Title (Z-A)</option>
</select>
</div>
</div>

View File

@@ -2,8 +2,8 @@
if (!defined('ABSPATH')) { exit; } // Prevent direct access
// Define dynamic post type and taxonomy
// $postType = isset($postType) ? $postType : 'post'; // Default to 'post' if not set
// $taxonomy = isset($taxonomy) ? $taxonomy : 'category'; // Default to 'category' if not set
$postType = isset($postType) ? $postType : 'post'; // Default to 'post' if not set
$taxonomy = isset($taxonomy) ? $taxonomy : 'category'; // Default to 'category' if not set
if (!empty($resources)) :
foreach ($resources as $resource) :

View File

@@ -1,86 +1,280 @@
.search-text {
display: flex;
gap: 0;
margin-bottom: 1.25rem;
.full-width {
border: 1px solid #ccc;
border-right: none;
font-size: 1rem;
padding: 1rem;
width: 100%;
}
button {
border: 1px solid #ccc;
font-size: 1rem;
padding: .5rem 1rem;
}
}
.search-tax {
align-items: flex-start;
display: flex;
flex-direction: column;
gap: 1rem;
margin-bottom: 1.25rem;
position: relative;
}
.filter-options { padding-top: .5rem; }
.filter-options label {
display: block;
margin-bottom: 5px;
@media (min-width: 768px) {
.search-text {
align-items: flex-start;
flex-direction: row;
}
}
/* Dropdown Container */
.custom-dropdown {
display: inline-block;
position: relative;
width: 100%;
.search-input-wrapper {
display: flex;
width: 100%;
}
.full-width {
align-items: center;
background: #fff;
border: 1px solid #ccc;
border-right: none;
border-radius: 10px 0 0 10px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
display: flex;
font-size: 0.95rem;
font-weight: 500;
padding: 12px 15px;
transition: all 0.25s ease-in-out;
width: 100%;
}
.full-width:focus {
border-color: #4E6ACA;
box-shadow: 0 2px 8px rgba(78, 106, 202, 0.25);
outline: none;
}
.full-width:hover {
border-color: #4E6ACA;
box-shadow: 0 2px 5px rgba(78, 106, 202, 0.15);
}
#clear-search {
align-items: center;
background: #fff;
border: 1px solid #ccc;
border-radius: 0 10px 10px 0;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
display: flex;
font-size: 1rem;
justify-content: center;
padding: .75rem 1rem;
transition: all 0.25s ease-in-out;
}
#clear-search:hover {
border-color: #4E6ACA;
box-shadow: 0 2px 5px rgba(78, 106, 202, 0.15);
color: #4E6ACA;
}
.search-tax {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
margin-bottom: 1.25rem;
position: relative;
}
@media (min-width: 640px) {
.search-tax {
grid-template-columns: repeat(1, 1fr);
}
}
/* Dropdown Button */
.custom-dropdown .dropdown-toggle {
background: #f0f0f0;
border: 1px solid #ccc;
border-radius: 4px;
cursor: pointer;
padding: 10px 15px;
@media (min-width: 1024px) {
.search-tax {
grid-template-columns: repeat(3, 1fr);
}
}
/* Dropdown Menu (Hidden by Default) */
.custom-dropdown .dropdown-menu {
background: #fff;
border: 1px solid #ccc;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
display: none;
left: 0;
max-height: fit-content;
overflow-y: auto;
padding: 10px;
position: absolute;
top: 100%;
width: 100%;
z-index: 10;
.filter-options {
padding-top: .5rem;
}
.filter-options label {
display: block;
margin-bottom: 5px;
}
/* Dropdown Container */
.custom-dropdown {
position: relative;
width: 100%;
margin-bottom: 1rem;
}
.dropdown-toggle {
align-items: center;
background: #fff;
border: 1px solid #ccc;
border-radius: 10px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
cursor: pointer;
display: flex;
font-size: 0.95rem;
font-weight: 500;
justify-content: space-between;
max-width: 16rem;
padding: 12px 15px;
transition: all 0.25s ease-in-out;
width: 100%;
}
.dropdown-toggle:hover {
border-color: #4E6ACA;
box-shadow: 0 2px 5px rgba(78, 106, 202, 0.15);
}
.dropdown-toggle::after {
color: #666;
content: '\e804';
font-family: "fontello", sans-serif;
font-size: 0.8rem;
margin-left: 0.5rem;
transition: transform 0.3s ease, color 0.2s ease;
}
.custom-dropdown.open .dropdown-toggle {
border-color: #4E6ACA;
box-shadow: 0 2px 8px rgba(78, 106, 202, 0.25);
}
.custom-dropdown.open .dropdown-toggle::after {
color: #4E6ACA;
content: '\e804';
transform: rotate(180deg);
}
/* Dropdown Menu (Hidden by Default) */
.dropdown-menu {
animation: fadeIn 0.25s ease;
background: #fff;
border: 1px solid #ccc;
border-radius: 10px;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
display: none;
left: 0;
max-height: 350px;
overflow-y: auto;
padding: 15px;
position: absolute;
top: calc(100% + 5px);
width: 100%;
z-index: 50 !important;
}
/* Improved responsive widths */
@media (min-width: 640px) {
.dropdown-menu {
max-width: 750px;
width: 200%;
}
}
/* Show Dropdown Menu */
@media (min-width: 1280px) {
.dropdown-menu {
max-width: 800px;
width: 250%;
}
}
.dropdown-menu::-webkit-scrollbar {
width: 8px;
}
.dropdown-menu::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
.dropdown-menu::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 4px;
}
.dropdown-menu::-webkit-scrollbar-thumb:hover {
background: #aaa;
}
/* Checkbox Labels - Improved touch target sizes */
.dropdown-menu label {
align-items: center;
border-radius: 6px;
cursor: pointer;
display: flex;
font-size: 0.85rem;
margin-bottom: 4px;
padding: 10px;
transition: all 0.2s ease;
}
.dropdown-menu label:hover {
background: #f5f7ff;
}
.dropdown-menu input[type="checkbox"] {
accent-color: #4E6ACA;
cursor: pointer;
margin-right: 0.75rem;
min-width: 16px;
transform: scale(1.2);
}
.custom-dropdown.open .dropdown-menu {
display: grid;
gap: 0.5rem;
grid-template-columns: 1fr;
}
@media (min-width: 768px) {
.custom-dropdown.open .dropdown-menu {
display: grid;
gap: .5rem;
grid-template-columns: repeat(auto-fill, minmax(15ch, 1fr));
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 1024px) {
.custom-dropdown.open .dropdown-menu {
grid-template-columns: repeat(2, minmax(150px, 1fr));
}
}
@media (min-width: 1280px) {
.custom-dropdown.open .dropdown-menu {
grid-template-columns: repeat(4, minmax(150px, 1fr));
}
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-5px);
}
/* Checkbox Labels */
.dropdown-menu label {
display: block;
margin-bottom: 8px;
to {
opacity: 1;
transform: translateY(0);
}
}
.dropdown-menu input[type="checkbox"] {
margin-right: 8px;
}
.dropdown-text {
overflow: hidden;
text-align: left;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
}
#sort-container {
width: fit-content;
}
#sortOrder {
border: 1px solid #ccc;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
cursor: pointer;
display:flex;
font-size: 0.95rem;
font-weight: 500;
max-width: fit-content;
padding: 2px 4px;
transition: all 0.25s ease-in-out;
width: 100%;
}
#resource-filter-summary {
@@ -90,81 +284,109 @@
margin-bottom: 1.25rem;
width: 100%;
p { margin: 0; padding: 0; }
p {
margin: 0;
padding: 0;
@apply leading-5;
&:first-child {
@apply leading-none;
}
}
#applied-filters {
@apply italic;
font-size: 14px;
margin-top: 15px;
.filter-item {
background: #f0f0f0;
border: 1px solid #ccc;
border-radius: 20px;
display: inline-block;
margin: 5px;
padding: 5px 10px;
button.remove-filter {
background: none;
border: none;
color: #0073aa;
cursor: pointer;
font-size: 16px;
margin-left: 5px;
&:hover {
color: #d63638;
}
}
}
}
#sort-container {
@apply m-0 p-0 leading-5;
label {
@apply font-bold;
}
select {
@apply mt-1;
}
}
}
#resource-results {
display: grid;
gap: 1.5rem;
grid-template-columns: repeat(1, minmax(0, 1fr));
gap: 1rem;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
#applied-filters {
margin-top: 15px;
font-size: 14px;
}
.filter-item {
background: #f0f0f0;
border: 1px solid #ccc;
border-radius: 20px;
display: inline-block;
margin: 5px;
padding: 5px 10px;
}
.filter-item button.remove-filter {
background: none;
border: none;
color: #0073aa;
cursor: pointer;
font-size: 16px;
margin-left: 5px;
}
.filter-item button.remove-filter:hover { color: #d63638; }
.pagination {
display: flex;
justify-content: center;
padding: 20px 0;
margin-top: 20px;
ul {
display: flex;
list-style: none;
gap: 8px;
padding: 0;
}
a,
span {
border: 1px solid #ddd;
border-radius: 4px;
color: #4E6ACA;
margin: 0 5px;
padding: 8px 12px;
text-decoration: none;
}
a:hover {
background: #4E6ACA;
color: #fff;
}
.current {
background: #4E6ACA;
border-color: #4E6ACA;
color: #fff;
padding: 8px 12px;
}
}
.pagination ul {
list-style: none;
display: flex;
gap: 8px;
padding: 0;
}
.pagination a,
.pagination span {
margin: 0 5px;
padding: 8px 12px;
text-decoration: none;
color: #0073aa;
border: 1px solid #ddd;
border-radius: 4px;
}
.pagination a:hover {
background: #0073aa;
color: #fff;
}
.pagination .current {
background: #0073aa;
color: #fff;
border-color: #0073aa;
}
@media (min-width: 768px) { /* Tailwind 'md' breakpoint */
@media (min-width: 768px) {
/* Tailwind 'md' breakpoint */
#resource-results {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (min-width: 1024px) { /* Tailwind 'lg' breakpoint */
@media (min-width: 1024px) {
/* Tailwind 'lg' breakpoint */
#resource-results {
grid-template-columns: repeat(4, minmax(0, 1fr));
}