refactor: Enhance resource filter functionality and improve UI elements in filter forms

This commit is contained in:
Keith Solomon
2025-03-29 15:35:42 -05:00
parent 758b464121
commit 699c06654c
5 changed files with 413 additions and 150 deletions

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

@@ -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));
}