feat: Enhance filtering capabilities by adding support for variants in Filter, Header, and Home components

This commit is contained in:
sHa
2025-06-12 17:34:43 +03:00
parent b4bd726dad
commit 1a006e75b8
4 changed files with 288 additions and 59 deletions

View File

@@ -51,11 +51,9 @@
(logo.brand && selectedBrands.includes(logo.brand));
const matchesVariants =
!selectedVariants.length ||
(logo.variant && (
Array.isArray(logo.variant)
? logo.variant.some((v) => selectedVariants.includes(v))
: selectedVariants.includes(logo.variant)
));
(logo.variants &&
Array.isArray(logo.variants) &&
logo.variants.some((v) => selectedVariants.includes(v)));
return matchesSearch && matchesTags && matchesBrands && matchesVariants;
});
@@ -223,7 +221,51 @@
const searchParam = params.get("search");
if (searchParam) {
searchQuery = searchParam;
} // Restore view mode and compact mode from localStorage
}
// Restore selected tags from URL
const tagsParam = params.get("tags");
if (tagsParam) {
selectedTags = tagsParam.split(",").filter(tag => tag.trim());
console.log("App: Restored selectedTags from URL:", selectedTags);
// Update localStorage with URL values
localStorage.setItem("selectedTags", JSON.stringify(selectedTags));
}
// Restore selected brands from URL
const brandsParam = params.get("brands");
if (brandsParam) {
selectedBrands = brandsParam.split(",").filter(brand => brand.trim());
console.log("App: Restored selectedBrands from URL:", selectedBrands);
// Update localStorage with URL values localStorage.setItem("selectedBrands", JSON.stringify(selectedBrands));
}
// Restore selected variants from URL
const variantsParam = params.get("variants");
if (variantsParam) {
selectedVariants = variantsParam.split(",").filter(variant => variant.trim());
console.log("App: Restored selectedVariants from URL:", selectedVariants);
// Update localStorage with URL values
localStorage.setItem("selectedVariants", JSON.stringify(selectedVariants));
}
// Force update window.appData after restoration
setTimeout(() => {
if (typeof window !== "undefined" && window.appData) {
window.appData.selectedTags = [...selectedTags];
window.appData.selectedBrands = [...selectedBrands];
window.appData.selectedVariants = [...selectedVariants];
console.log("App: Updated window.appData after restoration with variants:", selectedVariants);
updateFilteredLogosImmediate();
// Force re-render of components by updating references
selectedTags = [...selectedTags];
selectedBrands = [...selectedBrands];
selectedVariants = [...selectedVariants];
}
}, 100);
// Restore view mode and compact mode from localStorage
const savedViewMode = localStorage.getItem("viewMode");
if (savedViewMode === "grid" || savedViewMode === "list" || savedViewMode === "compact") {
viewMode = savedViewMode;
@@ -233,7 +275,8 @@
setCompactMode(savedCompact === "true");
}
// Restore selected tags from localStorage
// Restore selected tags from localStorage (only if not already set from URL)
if (!tagsParam) {
const savedTags = localStorage.getItem("selectedTags");
if (savedTags) {
try {
@@ -247,8 +290,10 @@
localStorage.removeItem("selectedTags");
}
}
}
// Restore selected brands from localStorage
// Restore selected brands from localStorage (only if not already set from URL)
if (!brandsParam) {
const savedBrands = localStorage.getItem("selectedBrands");
if (savedBrands) {
try {
@@ -262,8 +307,10 @@
localStorage.removeItem("selectedBrands");
}
}
}
// Restore selected variants from localStorage
// Restore selected variants from localStorage (only if not already set from URL)
if (!variantsParam) {
const savedVariants = localStorage.getItem("selectedVariants");
if (savedVariants) {
try {
@@ -277,6 +324,7 @@
localStorage.removeItem("selectedVariants");
}
}
}
});
// Make sure to apply theme whenever it changes
@@ -314,11 +362,9 @@
(logo.brand && selectedBrands.includes(logo.brand));
const matchesVariants =
!selectedVariants.length ||
(logo.variant && (
Array.isArray(logo.variant)
? logo.variant.some((v) => selectedVariants.includes(v))
: selectedVariants.includes(logo.variant)
));
(logo.variants &&
Array.isArray(logo.variants) &&
logo.variants.some((v) => selectedVariants.includes(v)));
return matchesSearch && matchesTags && matchesBrands && matchesVariants;
});
@@ -604,19 +650,53 @@
}
function addVariant(variant) {
console.log("App: Adding variant:", variant);
if (!selectedVariants.includes(variant)) {
selectedVariants = [...selectedVariants, variant];
localStorage.setItem("selectedVariants", JSON.stringify(selectedVariants));
console.log("App: Updated selectedVariants:", selectedVariants);
// Update window.appData immediately
if (typeof window !== "undefined" && window.appData) {
window.appData.selectedVariants = [...selectedVariants];
console.log("App: Updated selectedVariants in window.appData");
// Update filtered logos immediately
updateFilteredLogosImmediate();
}
}
// Close dropdown after adding variant
tagDropdownOpen = false;
// Also update the dropdown state in window.appData
if (typeof window !== "undefined" && window.appData) {
window.appData.tagDropdownOpen = false;
}
// Force reactive update
selectedVariants = selectedVariants;
}
function removeVariant(variant) {
console.log("App: Removing variant:", variant);
selectedVariants = selectedVariants.filter((v) => v !== variant);
localStorage.setItem("selectedVariants", JSON.stringify(selectedVariants));
console.log("App: Updated selectedVariants:", selectedVariants);
// Update window.appData immediately
if (typeof window !== "undefined" && window.appData) {
window.appData.selectedVariants = [...selectedVariants];
console.log("App: Updated selectedVariants in window.appData");
// Update filtered logos immediately
updateFilteredLogosImmediate();
}
// Force reactive update
selectedVariants = selectedVariants;
}
// Helper function to immediately update filtered/display logos in window.appData
function updateFilteredLogosImmediate() {
if (typeof window !== "undefined" && window.appData) {
@@ -636,11 +716,9 @@
(logo.brand && selectedBrands.includes(logo.brand));
const matchesVariants =
!selectedVariants.length ||
(logo.variant && (
Array.isArray(logo.variant)
? logo.variant.some((v) => selectedVariants.includes(v))
: selectedVariants.includes(logo.variant)
));
(logo.variants &&
Array.isArray(logo.variants) &&
logo.variants.some((v) => selectedVariants.includes(v)));
return matchesSearch && matchesTags && matchesBrands && matchesVariants;
});
@@ -659,6 +737,10 @@
window.appData.filteredLogos.length,
window.appData.displayLogos.length,
);
// Force update the main reactive statements as well
filteredLogos = [...window.appData.filteredLogos];
displayLogos = [...window.appData.displayLogos];
}
}

View File

@@ -3,18 +3,21 @@
export let allTags = [];
export let selectedTags = [];
export let selectedBrands = [];
export let selectedVariants = [];
export let tagDropdownOpen = false;
export let toggleDropdown = () => console.log("toggleDropdown not provided");
export let addTag = () => console.log("addTag not provided");
export let removeTag = () => console.log("removeTag not provided");
export let addBrand = () => console.log("addBrand not provided");
export let removeBrand = () => console.log("removeBrand not provided");
export let addVariant = () => console.log("addVariant not provided");
export let removeVariant = () => console.log("removeVariant not provided");
export let getTagObj = (tag) => ({ text: tag });
export let compactMode = false;
export let setCompactMode = () => {};
let tagSearchQuery = ""; // Search query for filtering tags
let activeTab = "categories"; // "categories" or "brands"
let activeTab = "categories"; // "categories", "brands", or "variants"
// Filter available tags based on search query
$: filteredAvailableTags = allTags.filter(
@@ -37,11 +40,26 @@
)
).sort();
// Compute all unique variants
$: allVariants = Array.from(
new Set(
allLogos
.filter((logo) => logo.variants && Array.isArray(logo.variants))
.flatMap((logo) => logo.variants)
.filter((variant) => variant && variant.trim() !== "")
)
).sort();
// Filter brands based on search query
$: filteredAllBrands = allBrands.filter((brand) =>
brand.toLowerCase().includes(tagSearchQuery.toLowerCase())
);
// Filter variants based on search query
$: filteredAllVariants = allVariants.filter((variant) =>
variant.toLowerCase().includes(tagSearchQuery.toLowerCase())
);
function toggleBrand(brand) {
if (selectedBrands.includes(brand)) {
selectedBrands = selectedBrands.filter(b => b !== brand);
@@ -56,6 +74,17 @@
updateFilterParams();
}
function toggleVariant(variant) {
if (selectedVariants.includes(variant)) {
removeVariant(variant);
} else {
addVariant(variant);
}
// Update URL parameters
updateFilterParams();
}
function updateFilterParams() {
const params = new URLSearchParams(window.location.search);
@@ -73,6 +102,13 @@
params.delete("brands");
}
// Update variants
if (selectedVariants.length > 0) {
params.set("variants", selectedVariants.join(","));
} else {
params.delete("variants");
}
const newUrl = window.location.pathname + (params.toString() ? "?" + params.toString() : "");
history.replaceState(null, "", newUrl);
}
@@ -98,9 +134,9 @@
fill="currentColor"
/>
</svg>
{#if selectedTags.length + selectedBrands.length + (compactMode ? 1 : 0) > 0}
{#if selectedTags.length + selectedBrands.length + selectedVariants.length + (compactMode ? 1 : 0) > 0}
<span class="filter-count"
>{selectedTags.length + selectedBrands.length + (compactMode ? 1 : 0)}</span
>{selectedTags.length + selectedBrands.length + selectedVariants.length + (compactMode ? 1 : 0)}</span
>
{/if}
</button>
@@ -138,7 +174,7 @@
</button>
</div>
{#if filteredAvailableTags.length > 0 || tagSearchQuery || allBrands.length > 0}
{#if filteredAvailableTags.length > 0 || tagSearchQuery || allBrands.length > 0 || allVariants.length > 0}
<div class="filter-separator"></div>
<div class="filter-tabs-section">
<div class="filter-tabs">
@@ -156,12 +192,19 @@
>
Brands
</button>
<button
class="filter-tab"
class:active={activeTab === "variants"}
on:click={() => activeTab = "variants"}
>
Variants
</button>
</div>
<div class="tags-search-bar">
<input
type="text"
placeholder={activeTab === "categories" ? "Search categories..." : "Search brands..."}
placeholder={activeTab === "categories" ? "Search categories..." : activeTab === "brands" ? "Search brands..." : "Search variants..."}
bind:value={tagSearchQuery}
class="tags-search-input"
/>
@@ -234,7 +277,7 @@
: "No available categories"}
</div>
{/if}
{:else}
{:else if activeTab === "brands"}
{#if filteredAllBrands.length > 0}
<div class="filter-tags-list">
{#each filteredAllBrands as brand}
@@ -275,11 +318,49 @@
: "No available brands"}
</div>
{/if}
{:else if activeTab === "variants"}
{#if filteredAllVariants.length > 0}
<div class="filter-tags-list">
{#each filteredAllVariants as variant}
{@const isSelected = selectedVariants.includes(variant)}
<button
class="filter-tag-item filter-variant-item"
class:selected={isSelected}
on:click={() => toggleVariant(variant)}
aria-label={isSelected
? `Remove variant: ${variant}`
: `Add variant: ${variant}`}
>
<span class="tag-icon">
<span class="permanent-icon">
{#if isSelected}
✔︎
{/if}
</span>
<span class="hover-icon">
{#if isSelected}
{:else}
✔︎
{/if}
</span>
</span>
<span class="variant-text">{variant}</span>
</button>
{/each}
</div>
{:else}
<div class="no-tags">
{tagSearchQuery
? "No variants match your search"
: "No available variants"}
</div>
{/if}
{/if}
</div>
{/if}
{#if selectedTags.length > 0 || selectedBrands.length > 0 || compactMode}
{#if selectedTags.length > 0 || selectedBrands.length > 0 || selectedVariants.length > 0 || compactMode}
<div class="filter-separator"></div>
<div class="clear-all-section">
<button
@@ -287,6 +368,7 @@
on:click={() => {
selectedTags.forEach(tag => removeTag(tag));
selectedBrands.forEach(brand => removeBrand(brand));
[...selectedVariants].forEach(variant => removeVariant(variant));
if (compactMode) {
setCompactMode(false);
compactMode = false;
@@ -326,6 +408,17 @@
</button>
{/each}
{#each selectedVariants as variant}
<button
class="selected-filter-btn selected-variant"
aria-label={`Remove variant: ${variant}`}
on:click={() => removeVariant(variant)}
>
{variant}
<span class="close">&times;</span>
</button>
{/each}
{#if compactMode}
<button
class="selected-filter-btn compact-indicator"
@@ -438,6 +531,15 @@
background: var(--additional-color);
}
.selected-variant {
background: #9b59b6;
color: #fff;
}
.selected-variant:hover {
background: #8e44ad;
}
.compact-indicator {
background: var(--color-border);
color: var(--color-text);
@@ -648,6 +750,15 @@
background: var(--color-border);
}
.filter-variant-item.selected {
background: none;
color: var(--color-text);
}
.filter-variant-item.selected:hover {
background: var(--color-border);
}
.tag-icon {
width: 16px;
height: 16px;
@@ -691,6 +802,11 @@
font-weight: 500;
}
.variant-text {
font-size: 0.85em;
font-weight: 500;
}
.no-tags {
color: #888;
font-size: 0.85em;

View File

@@ -18,12 +18,15 @@
export let allTags = [];
export let selectedTags = [];
export let selectedBrands = [];
export let selectedVariants = [];
export let tagDropdownOpen = false;
export let toggleDropdown = () => console.log("toggleDropdown not provided");
export let addTag = () => console.log("addTag not provided");
export let removeTag = () => console.log("removeTag not provided");
export let addBrand = () => console.log("addBrand not provided");
export let removeBrand = () => console.log("removeBrand not provided");
export let addVariant = () => console.log("addVariant not provided");
export let removeVariant = () => console.log("removeVariant not provided");
export let getTagObj = (tag) => ({ text: tag });
export let compactMode = false;
export let setCompactMode = () => {};
@@ -38,7 +41,7 @@
<h1>Logo Gallery</h1>
</div>
<span class="logo-count">
{#if (searchQuery && searchQuery.trim() !== "") || selectedTags.length > 0 || selectedBrands.length > 0 || compactMode}
{#if (searchQuery && searchQuery.trim() !== "") || selectedTags.length > 0 || selectedBrands.length > 0 || selectedVariants.length > 0 || compactMode}
{displayLogos ? displayLogos.length : 0} of {allLogos ? allLogos.length : 0} images
displayed
{:else}
@@ -55,12 +58,15 @@
{allTags}
{selectedTags}
{selectedBrands}
{selectedVariants}
{tagDropdownOpen}
{toggleDropdown}
{addTag}
{removeTag}
{addBrand}
{removeBrand}
{addVariant}
{removeVariant}
{getTagObj}
{compactMode}
{setCompactMode}

View File

@@ -15,8 +15,11 @@
let selectedTags = [];
let allTags = [];
let selectedBrands = [];
let selectedVariants = [];
let addBrand = () => {};
let removeBrand = () => {};
let addVariant = () => {};
let removeVariant = () => {};
let setTheme = () => {};
@@ -31,6 +34,7 @@
allTags = [],
selectedTags = [],
selectedBrands = [],
selectedVariants = [],
tagDropdownOpen = false,
compactMode = false,
setSearchQuery = () => {},
@@ -43,6 +47,8 @@
removeTag = () => {},
addBrand = () => {},
removeBrand = () => {},
addVariant = () => {},
removeVariant = () => {},
toggleTag = () => {},
getTagObj = () => ({ text: "" }),
closeDropdown = () => {},
@@ -83,6 +89,18 @@
selectedBrands = window.appData.selectedBrands;
}
if (window.appData.addVariant && typeof window.appData.addVariant === 'function') {
addVariant = window.appData.addVariant;
}
if (window.appData.removeVariant && typeof window.appData.removeVariant === 'function') {
removeVariant = window.appData.removeVariant;
}
if (window.appData.selectedVariants) {
selectedVariants = window.appData.selectedVariants;
}
// Set up reactivity with window.appData
const interval = setInterval(() => {
if (window.appData) {
@@ -108,6 +126,10 @@
if (window.appData.selectedBrands) {
selectedBrands = [...window.appData.selectedBrands];
}
if (window.appData.selectedVariants) {
selectedVariants = [...window.appData.selectedVariants];
}
}
}, 100);
@@ -200,12 +222,15 @@
allTags={allTags}
selectedTags={selectedTags}
selectedBrands={selectedBrands}
selectedVariants={selectedVariants}
{tagDropdownOpen}
{toggleDropdown}
{addTag}
{removeTag}
addBrand={addBrand}
removeBrand={removeBrand}
addVariant={addVariant}
removeVariant={removeVariant}
getTagObj={(tag) => (window.appData?.getTagObj ? window.appData.getTagObj(tag) : {text: tag})}
{compactMode}
{setCompactMode}