Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 106 additions & 19 deletions docs/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,7 @@ function updateVisibilityAndSort() {
renderCards(sortedUsers);
updateCounts(sortedUsers);
updateResultsMessage(sortedUsers);
updateActiveFilterIndicators();
}

/**
Expand Down Expand Up @@ -1030,32 +1031,118 @@ function updateResultsMessage(sortedUsers) {
}
}

// ============================================================================
// ACTIVE FILTER INDICATORS
// ============================================================================
const DEFAULT_FILTER_VALUES = {
searchInput: '',
sortBy: 'followers-desc',
followersFilter: '0',
maxFollowersFilter: '999999999',
minReposFilter: '0',
maxReposFilter: '999999',
minForksFilter: '0',
maxForksFilter: '999999',
sponsorsFilter: 'any',
sponsoringFilter: 'any',
avatarAgeFilter: 'any',
minStarsFilter: '0',
languageFilter: '',
lastRepoActivityFilter: 'any',
lastCommitFilter: 'any',
};

const FILTER_LABELS = {
searchInput: 'Search',
sortBy: 'Sort',
followersFilter: 'Min followers',
maxFollowersFilter: 'Max followers',
minReposFilter: 'Min repos',
maxReposFilter: 'Max repos',
minForksFilter: 'Min forks',
maxForksFilter: 'Max forks',
sponsorsFilter: 'Sponsors',
sponsoringFilter: 'Sponsoring',
avatarAgeFilter: 'Avatar updated',
minStarsFilter: 'Min stars',
languageFilter: 'Language',
lastRepoActivityFilter: 'Repo activity',
lastCommitFilter: 'Public commit',
};

function getControlDisplayValue(element) {
if (!element) return '';
if (element.tagName === 'SELECT') {
return (
element.options[element.selectedIndex]?.textContent.trim() || element.value
);
}
return element.value.trim();
}

function getActiveFilterSummaries() {
return Object.entries(DEFAULT_FILTER_VALUES)
.map(([id, defaultValue]) => {
const element = document.getElementById(id);
if (!element || element.value === defaultValue) return null;
const displayValue = getControlDisplayValue(element);
return displayValue ? `${FILTER_LABELS[id]}: ${displayValue}` : null;
})
.filter(Boolean);
Comment on lines +1083 to +1091

Copilot AI Apr 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getActiveFilterSummaries() trims the displayed value for text inputs, but it decides “active vs default” using the raw element.value. For searchInput, the filter logic currently uses .value.toLowerCase() without .trim() (see getActiveFilters()), so whitespace-only input will (a) filter out results while (b) producing no active badge because displayValue becomes empty and is dropped. Consider trimming searchInput in the filter logic and using the same trimmed value when determining whether a text input deviates from its default, so the indicator and filtering behavior stay consistent.

Copilot uses AI. Check for mistakes.
}
Comment on lines +1083 to +1092

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation of getActiveFilterSummaries can produce badges with empty values (e.g., "Search: ") if a user enters only whitespace into a text input. It is better to verify that the display value is not empty before including it in the summary list.

Suggested change
function getActiveFilterSummaries() {
return Object.entries(DEFAULT_FILTER_VALUES)
.map(([id, defaultValue]) => {
const element = document.getElementById(id);
if (!element || element.value === defaultValue) return null;
return `${FILTER_LABELS[id]}: ${getControlDisplayValue(element)}`;
})
.filter(Boolean);
}
function getActiveFilterSummaries() {
return Object.entries(DEFAULT_FILTER_VALUES)
.map(([id, defaultValue]) => {
const element = document.getElementById(id);
if (!element || element.value === defaultValue) return null;
const displayValue = getControlDisplayValue(element);
return displayValue ? `${FILTER_LABELS[id]}: ${displayValue}` : null;
})
.filter(Boolean);
}


function renderActiveFilterIndicator(container, activeFilters) {
if (!container) return;

if (!activeFilters.length) {
container.style.display = 'none';
container.replaceChildren();
return;
}

container.style.display = 'flex';
container.replaceChildren();

const label = document.createElement('span');
label.className = 'active-filters-label';
label.textContent = 'Active:';
container.appendChild(label);

activeFilters.forEach((filterText) => {
const badge = document.createElement('span');
badge.className = 'active-filter-badge';
badge.textContent = filterText;
container.appendChild(badge);
});

const clearButton = document.createElement('button');
clearButton.type = 'button';
clearButton.className = 'active-filters-clear';
clearButton.textContent = 'Clear all';
clearButton.addEventListener('click', resetFilters);
container.appendChild(clearButton);
}

function updateActiveFilterIndicators() {
const activeFilters = getActiveFilterSummaries();
renderActiveFilterIndicator(
document.getElementById('activeFiltersSummary'),
activeFilters,
);
renderActiveFilterIndicator(
document.getElementById('activeFiltersSummaryDesktop'),
activeFilters,
);
}
Comment on lines +1126 to +1136

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Calculate the active filter summaries once and pass them to the rendering functions to improve efficiency and avoid redundant DOM lookups and string processing.

function updateActiveFilterIndicators() {
  const activeFilters = getActiveFilterSummaries();
  renderActiveFilterIndicator(
    document.getElementById('activeFiltersSummary'),
    activeFilters,
  );
  renderActiveFilterIndicator(
    document.getElementById('activeFiltersSummaryDesktop'),
    activeFilters,
  );
}


// ============================================================================
// RESET FILTERS
// ============================================================================
/**
* Reset all filters to default values
*/
function resetFilters() {
const defaults = {
searchInput: '',
sortBy: 'followers-desc',
followersFilter: '0',
maxFollowersFilter: '999999999',
minReposFilter: '0',
maxReposFilter: '999999',
minForksFilter: '0',
maxForksFilter: '999999',
sponsorsFilter: 'any',
sponsoringFilter: 'any',
avatarAgeFilter: 'any',
minStarsFilter: '0',
languageFilter: '',
lastRepoActivityFilter: 'any',
lastCommitFilter: 'any',
};

Object.entries(defaults).forEach(([id, value]) => {
Object.entries(DEFAULT_FILTER_VALUES).forEach(([id, value]) => {
const element = document.getElementById(id);
if (element) element.value = value;
});
Expand Down
46 changes: 46 additions & 0 deletions docs/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,52 @@ h1 {
display: none;
}

.active-filters-summary {
display: none;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: 6px;
margin-top: 8px;
}

.active-filters-label {
font-weight: 600;
}

.active-filter-badge {
padding: 3px 8px;
background: rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.35);
border-radius: 999px;
}

.active-filters-summary-desktop .active-filter-badge {
background: #eef2ff;
border-color: #c7d2fe;
color: #4f46e5;
}

.active-filters-clear {
padding: 3px 8px;
border: 0;
border-radius: 999px;
background: rgba(255, 100, 100, 0.28);
color: inherit;
cursor: pointer;
font: inherit;
font-weight: 600;
}

.active-filters-clear:hover {
background: rgba(255, 100, 100, 0.45);
}

.active-filters-summary-desktop .active-filters-clear {
background: #fee2e2;
color: #b91c1c;
}

/* ============================================================================ */
/* LOADING STATE */
/* ============================================================================ */
Expand Down
5 changes: 5 additions & 0 deletions layouts/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ <h2>🔍 Find Your Favorite Developer</h2>
Showing <strong id="visibleCount">0</strong> of
<strong id="totalCount">0</strong> developers
</div>
<div id="activeFiltersSummary" class="active-filters-summary"></div>
<div id="noResults" style="display: none">
<strong>No developers found</strong> matching your filters.
</div>
Expand Down Expand Up @@ -453,6 +454,10 @@ <h2 id="featuredUserName" class="featured-name"></h2>
Showing <strong id="visibleCountDesktop">0</strong> of
<strong id="totalCountDesktop">0</strong> developers
</div>
<div
id="activeFiltersSummaryDesktop"
class="active-filters-summary active-filters-summary-desktop"
></div>
<div id="noResultsDesktop" style="display: none">
<strong>No developers found</strong> matching your filters.
</div>
Expand Down
Loading