diff --git a/public/data/logos.json b/public/data/logos.json
index e0f1324..60ac57d 100644
--- a/public/data/logos.json
+++ b/public/data/logos.json
@@ -70,7 +70,8 @@
"tags": [
"Design",
"Software"
- ]
+ ],
+ "variants": ["logo_only", "icon"]
},
{
"name": "Affinity Photo",
@@ -80,7 +81,8 @@
"brand": "Affinity",
"tags": [
"Software"
- ]
+ ],
+ "variants": ["logo_only", "icon"]
},
{
"name": "Affinity Publisher",
@@ -91,7 +93,8 @@
"tags": [
"Design",
"Software"
- ]
+ ],
+ "variants": ["logo_only", "icon"]
},
{
"name": "Amazon",
diff --git a/src/App.svelte b/src/App.svelte
index b26b78c..bc2db30 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -22,6 +22,7 @@
let allTags = [];
let selectedTags = [];
let selectedBrands = [];
+ let selectedVariants = [];
let tagDropdownOpen = false;
let showModal = false;
let selectedLogo = null;
@@ -48,7 +49,14 @@
const matchesBrands =
!selectedBrands.length ||
(logo.brand && selectedBrands.includes(logo.brand));
- return matchesSearch && matchesTags && matchesBrands;
+ const matchesVariants =
+ !selectedVariants.length ||
+ (logo.variant && (
+ Array.isArray(logo.variant)
+ ? logo.variant.some((v) => selectedVariants.includes(v))
+ : selectedVariants.includes(logo.variant)
+ ));
+ return matchesSearch && matchesTags && matchesBrands && matchesVariants;
});
window.appData.displayLogos =
@@ -100,6 +108,7 @@
allTags: [],
selectedTags: [],
selectedBrands: [],
+ selectedVariants: [],
tagDropdownOpen,
compactMode,
setSearchQuery,
@@ -118,6 +127,8 @@
onDownload: downloadLogo,
addBrand,
removeBrand,
+ addVariant,
+ removeVariant,
};
}
@@ -157,6 +168,7 @@
allTags,
selectedTags,
selectedBrands,
+ selectedVariants,
tagDropdownOpen,
compactMode,
setSearchQuery,
@@ -175,6 +187,8 @@
onDownload: downloadLogo,
addBrand,
removeBrand,
+ addVariant,
+ removeVariant,
};
console.log(
"App: Updated window.appData after loading with",
@@ -248,6 +262,21 @@
localStorage.removeItem("selectedBrands");
}
}
+
+ // Restore selected variants from localStorage
+ const savedVariants = localStorage.getItem("selectedVariants");
+ if (savedVariants) {
+ try {
+ const parsedVariants = JSON.parse(savedVariants);
+ if (Array.isArray(parsedVariants)) {
+ selectedVariants = parsedVariants;
+ console.log("App: Restored selectedVariants from localStorage:", selectedVariants);
+ }
+ } catch (error) {
+ console.error("App: Error parsing saved variants:", error);
+ localStorage.removeItem("selectedVariants");
+ }
+ }
});
// Make sure to apply theme whenever it changes
@@ -268,7 +297,7 @@
).values(),
).sort((a, b) => a.text.localeCompare(b.text));
- // Update the filtering logic to include brand filtering in the reactive statement
+ // Update the filtering logic to include brand and variant filtering in the reactive statement
$: filteredLogos = logos.filter((logo) => {
const matchesSearch =
logo.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
@@ -283,7 +312,14 @@
const matchesBrands =
!selectedBrands.length ||
(logo.brand && selectedBrands.includes(logo.brand));
- return matchesSearch && matchesTags && matchesBrands;
+ const matchesVariants =
+ !selectedVariants.length ||
+ (logo.variant && (
+ Array.isArray(logo.variant)
+ ? logo.variant.some((v) => selectedVariants.includes(v))
+ : selectedVariants.includes(logo.variant)
+ ));
+ return matchesSearch && matchesTags && matchesBrands && matchesVariants;
});
$: displayLogos =
@@ -317,6 +353,7 @@
allTags,
selectedTags,
selectedBrands,
+ selectedVariants,
tagDropdownOpen,
compactMode,
setSearchQuery,
@@ -333,6 +370,8 @@
toggleTag,
addBrand,
removeBrand,
+ addVariant,
+ removeVariant,
getTagObj,
closeDropdown,
setCompactMode,
@@ -564,6 +603,20 @@
}
}
+ function addVariant(variant) {
+ if (!selectedVariants.includes(variant)) {
+ selectedVariants = [...selectedVariants, variant];
+ localStorage.setItem("selectedVariants", JSON.stringify(selectedVariants));
+ updateFilteredLogosImmediate();
+ }
+ }
+
+ function removeVariant(variant) {
+ selectedVariants = selectedVariants.filter((v) => v !== variant);
+ localStorage.setItem("selectedVariants", JSON.stringify(selectedVariants));
+ updateFilteredLogosImmediate();
+ }
+
// Helper function to immediately update filtered/display logos in window.appData
function updateFilteredLogosImmediate() {
if (typeof window !== "undefined" && window.appData) {
@@ -581,7 +634,14 @@
const matchesBrands =
!selectedBrands.length ||
(logo.brand && selectedBrands.includes(logo.brand));
- return matchesSearch && matchesTags && matchesBrands;
+ const matchesVariants =
+ !selectedVariants.length ||
+ (logo.variant && (
+ Array.isArray(logo.variant)
+ ? logo.variant.some((v) => selectedVariants.includes(v))
+ : selectedVariants.includes(logo.variant)
+ ));
+ return matchesSearch && matchesTags && matchesBrands && matchesVariants;
});
window.appData.displayLogos =
diff --git a/src/components/Filter.svelte b/src/components/Filter.svelte
new file mode 100644
index 0000000..52e7d89
--- /dev/null
+++ b/src/components/Filter.svelte
@@ -0,0 +1,806 @@
+
+
+
+
+
+ {#if tagDropdownOpen}
+
+
+
+
+
+
+ {#if filteredAvailableTags.length > 0 || tagSearchQuery || allBrands.length > 0}
+
+
+
+
+
+
+
+
+
+ {#if activeTab === "categories"}
+ {#if filteredAllTags.length > 0}
+
+ {#each filteredAllTags as tagObj}
+ {@const isSelected = selectedTags.includes(tagObj.text)}
+
+ {/each}
+
+ {:else}
+
+ {tagSearchQuery
+ ? "No categories match your search"
+ : "No available categories"}
+
+ {/if}
+ {:else}
+ {#if filteredAllBrands.length > 0}
+
+ {#each filteredAllBrands as brand}
+ {@const isSelected = selectedBrands.includes(brand)}
+
+ {/each}
+
+ {:else}
+
+ {tagSearchQuery
+ ? "No brands match your search"
+ : "No available brands"}
+
+ {/if}
+ {/if}
+
+ {/if}
+
+ {#if selectedTags.length > 0 || selectedBrands.length > 0 || compactMode}
+
+
+
+
+ {/if}
+
+ {/if}
+
+
+
+ {#each selectedTags as tagText}
+
+ {/each}
+
+ {#each selectedBrands as brand}
+
+ {/each}
+
+ {#if compactMode}
+
+ {/if}
+
+
+
+
diff --git a/src/components/Header.svelte b/src/components/Header.svelte
index 9d99484..3266422 100644
--- a/src/components/Header.svelte
+++ b/src/components/Header.svelte
@@ -1,5 +1,9 @@
@@ -147,502 +45,32 @@
{allLogos ? allLogos.length : 0} images in gallery
{/if}
-
+
@@ -692,520 +120,6 @@
margin: 1rem 0;
}
- .search-bar {
- margin-bottom: 0;
- width: 100%;
- max-width: 500px;
- position: relative;
- display: flex;
- align-items: center;
- }
-
- .search-bar input {
- width: 100%;
- padding: 0.75rem;
- border: 1px solid var(--color-border);
- border-radius: 4px;
- font-size: 1rem;
- background: var(--color-card);
- color: var(--color-text);
- padding-right: 4em;
- }
-
- .clear-btn {
- position: absolute;
- right: 2.5em; /* Positioned to leave space for hotkey hint */
- background: none;
- border: none;
- padding: 0;
- margin: 0;
- cursor: pointer;
- color: #888;
- display: flex;
- align-items: center;
- justify-content: center;
- height: 100%;
- }
-
- .clear-btn:hover {
- color: #f44336;
- }
-
- .hotkey-hint {
- position: absolute;
- right: 0.5em;
- display: flex;
- align-items: center;
- pointer-events: none;
- }
-
- .hotkey-hint kbd {
- background: var(--color-border);
- color: var(--color-text);
- border: 1px solid var(--color-border);
- border-radius: 3px;
- padding: 0.2em 0.4em;
- font-size: 0.75em;
- font-family: monospace;
- font-weight: bold;
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
- }
-
- .theme-switcher {
- display: flex;
- align-items: center;
- gap: 0.2rem;
- margin-left: auto;
- }
-
- .filter-section {
- display: grid;
- grid-template-columns: auto 1fr;
- gap: 0.5rem;
- align-items: flex-start;
- position: relative;
- }
-
- /* Base styles for all selected filter buttons */
- .selected-filter-btn {
- border: none;
- border-radius: 4px;
- padding: 0.1em 0.7em;
- font-size: 0.75em;
- font-weight: 300;
- letter-spacing: 0.02em;
- cursor: pointer;
- display: flex;
- align-items: center;
- gap: 0.3em;
- opacity: 1;
- transition: background 0.2s, color 0.2s, transform 0.1s;
- position: relative;
- color: #fff;
- }
-
- .selected-filter-btn:hover {
- filter: brightness(1.6);
- }
-
- .selected-filter-btn .close {
- margin-left: 0.4em;
- font-size: 1.1em;
- font-weight: bold;
- cursor: pointer;
- opacity: 0.7;
- transition: opacity 0.2s;
- }
-
- .selected-filter-btn .close:hover {
- opacity: 1;
- }
-
- /* Type-specific styles */
- .selected-tag {
- background: var(--color-accent);
- color: #fff;
- }
-
- .selected-tag:hover {
- background: var(--color-accent);
- }
-
- .selected-brand {
- background: #27ae60;
- color: #fff;
- }
-
- .selected-brand:hover {
- background: var(--additional-color);
- }
-
- .compact-indicator {
- background: var(--color-border);
- color: var(--color-text);
- opacity: 0.8;
- }
-
- .compact-indicator:hover {
- opacity: 1;
- background: var(--color-text);
- color: var(--color-card);
- }
-
- .selected-tag {
- background: var(--color-accent);
- align-items: center;
- }
-
- .compact-indicator {
- background: var(--color-border);
- color: var(--color-text);
- }
-
- .compact-indicator:hover {
- opacity: 1;
- background: var(--color-text);
- color: var(--color-card);
- }
-
- .filter-dropdown {
- grid-column: 1;
- position: relative;
- display: inline-block;
- }
-
- .filter-toggle {
- background: var(--color-card);
- color: var(--color-text);
- border: 1px solid var(--color-border);
- border-radius: 6px;
- padding: 0.6em 0.8em;
- font-size: 0.9em;
- cursor: pointer;
- transition:
- background 0.2s,
- color 0.2s,
- border-color 0.2s;
- display: flex;
- align-items: center;
- justify-content: center;
- position: relative;
- gap: 0.3rem;
- height: 40px;
- }
-
- .filter-toggle:hover,
- .filter-toggle.active {
- background: var(--color-accent);
- color: #fff;
- border-color: var(--color-accent);
- }
-
- .filter-count {
- background: var(--color-accent);
- color: #fff;
- border-radius: 50%;
- min-width: 16px;
- height: 16px;
- font-size: 0.7em;
- font-weight: 600;
- display: flex;
- align-items: center;
- justify-content: center;
- position: absolute;
- bottom: -6px;
- right: -6px;
- padding: 0 2px;
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
- }
-
- .filter-toggle.active .filter-count {
- background: #fff;
- color: var(--color-accent);
- }
-
- .filter-dropdown-panel {
- position: absolute;
- left: 0;
- top: 100%;
- margin-top: 0.5rem;
- min-width: 250px;
- background: var(--color-card);
- color: var(--color-text);
- border: 1px solid var(--color-border);
- border-radius: 8px;
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
- z-index: 1000;
- padding: 1rem;
- display: flex;
- flex-direction: column;
- gap: 0.5rem;
- }
-
- .filter-options {
- display: flex;
- flex-direction: column;
- gap: 0.5rem;
- }
-
- .filter-separator {
- height: 1px;
- background: var(--color-border);
- margin: 0.5rem 0;
- }
-
- .filter-tabs {
- display: flex;
- margin-bottom: 0.5rem;
- }
-
- .filter-tab {
- background: none;
- border: none;
- color: var(--color-text);
- cursor: pointer;
- padding: 0.5rem 0.75rem;
- border-bottom: 2px solid transparent;
- transition: color 0.2s, border-color 0.2s;
- font-size: 0.9rem;
- opacity: 0.7;
- border-radius: 0;
- }
-
- .filter-tab:hover {
- opacity: 1;
- }
-
- .filter-tab.active {
- color: var(--color-accent);
- border-bottom-color: var(--color-accent);
- opacity: 1;
- font-weight: 500;
- }
-
- .tags-search-bar {
- position: relative;
- margin-bottom: 0.5rem;
- }
-
- .tags-search-input {
- width: 100%;
- padding: 0.5rem 2.5rem 0.5rem 0.5rem;
- border: 1px solid var(--color-border);
- border-radius: 4px;
- font-size: 0.85em;
- background: var(--color-card);
- color: var(--color-text);
- }
-
- .tags-search-clear {
- position: absolute;
- right: 0.5rem;
- top: 50%;
- transform: translateY(-50%);
- background: none;
- border: none;
- padding: 0.2rem;
- cursor: pointer;
- color: #888;
- display: flex;
- align-items: center;
- justify-content: center;
- border-radius: 2px;
- transition:
- color 0.2s,
- background 0.2s;
- }
-
- .tags-search-clear:hover {
- color: #f44336;
- }
-
- .filter-tags-list {
- display: flex;
- flex-direction: column;
- gap: 0.2rem;
- max-height: 200px;
- overflow-y: auto;
- }
-
- .filter-tag-item {
- background: none;
- border: none;
- padding: 0.4rem 0.5rem;
- display: flex;
- align-items: center;
- gap: 0.5rem;
- cursor: pointer;
- border-radius: 4px;
- transition: background 0.2s;
- text-align: left;
- color: var(--color-text);
- }
-
- .filter-tag-item:hover {
- background: var(--color-border);
- }
-
- .filter-tag-item.selected {
- background: none;
- color: var(--color-text);
- }
-
- .filter-tag-item.selected:hover {
- background: var(--color-border);
- opacity: 1;
- }
-
- .filter-brand-item.selected {
- background: none;
- color: var(--color-text);
- }
-
- .filter-brand-item.selected:hover {
- background: var(--color-border);
- }
-
- .tag-icon {
- width: 16px;
- height: 16px;
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
- position: relative;
- font-size: 14px;
- }
-
- .filter-tag-item .tag-icon .hover-icon {
- opacity: 0;
- transition: opacity 0.2s;
- position: absolute;
- }
-
- .filter-tag-item:hover .tag-icon .hover-icon {
- opacity: 1;
- }
-
- .filter-tag-item:hover .tag-icon .permanent-icon {
- opacity: 0;
- }
-
- .filter-option-item:hover .option-icon .hover-icon {
- opacity: 1;
- }
-
- .filter-option-item:hover .option-icon .permanent-icon {
- opacity: 0;
- }
-
- .tag-text {
- font-size: 0.85em;
- font-weight: 500;
- }
-
- .brand-text {
- font-size: 0.85em;
- font-weight: 500;
- }
-
- .no-tags {
- color: #888;
- font-size: 0.85em;
- padding: 1rem;
- text-align: center;
- font-style: italic;
- }
-
- .filter-option-item {
- background: none;
- border: none;
- padding: 0.4rem 0.5rem;
- display: flex;
- align-items: center;
- gap: 0.5rem;
- cursor: pointer;
- border-radius: 4px;
- transition: background 0.2s;
- text-align: left;
- color: var(--color-text);
- width: 100%;
- }
-
- .filter-option-item:hover {
- background: var(--color-border);
- }
-
- .option-icon {
- width: 16px;
- height: 16px;
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
- position: relative;
- font-size: 14px;
- }
-
- .filter-option-item .option-icon .hover-icon {
- opacity: 0;
- transition: opacity 0.2s;
- position: absolute;
- }
-
- .filter-option-item:hover .option-icon .hover-icon {
- opacity: 1;
- }
-
- .filter-option-item:hover .option-icon > *:not(.hover-icon) {
- opacity: 0;
- }
-
- .permanent-icon {
- position: absolute;
- transition: opacity 0.2s;
- }
-
- .hover-icon {
- opacity: 0;
- transition: opacity 0.2s;
- position: absolute;
- }
-
- .clear-all-section {
- display: flex;
- flex-direction: column;
- }
-
- .clear-all-button {
- background: none;
- border: none;
- padding: 0.4rem 0.5rem;
- display: flex;
- align-items: center;
- gap: 0.5rem;
- cursor: pointer;
- border-radius: 4px;
- transition: background 0.2s;
- text-align: left;
- color: var(--color-text);
- width: 100%;
- }
-
- .clear-all-button:hover {
- background: var(--color-border);
- }
-
- .clear-all-icon {
- width: 16px;
- height: 16px;
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
- font-size: 14px;
- }
-
- .clear-all-text {
- font-size: 0.85em;
- font-weight: 500;
- }
-
-
-
-
-
- .selected-filters {
- grid-column: 2;
- display: flex;
- flex-wrap: wrap;
- gap: 0.3rem;
- align-items: flex-end;
- min-height: 2.5rem;
- max-height: 5rem;
- overflow: hidden;
- }
-
@media (max-width: 700px) {
.header-row {
display: grid;
@@ -1220,11 +134,6 @@
grid-row: 1;
}
- .theme-switcher {
- grid-column: 2;
- grid-row: 1;
- }
-
.logo-count {
grid-column: 1 / -1;
grid-row: 2;
@@ -1241,17 +150,17 @@
margin: 1rem 0;
}
- .search-bar {
+ .header-controls :global(.search-bar) {
grid-column: 1;
grid-row: 1;
}
- .view-toggle {
+ .header-controls :global(.view-toggle) {
grid-column: 2;
grid-row: 1;
}
- .filter-section {
+ .header-controls :global(.filter-section) {
grid-column: 1 / -1;
grid-row: 2;
}
diff --git a/src/components/ListViewSwitcher.svelte b/src/components/ListViewSwitcher.svelte
new file mode 100644
index 0000000..dfaa743
--- /dev/null
+++ b/src/components/ListViewSwitcher.svelte
@@ -0,0 +1,143 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/SearchBar.svelte b/src/components/SearchBar.svelte
new file mode 100644
index 0000000..b022d0e
--- /dev/null
+++ b/src/components/SearchBar.svelte
@@ -0,0 +1,156 @@
+
+
+
+
+ {#if searchQuery}
+
+ {/if}
+
+ /
+
+
+
+
diff --git a/src/components/ThemeSwitcher.svelte b/src/components/ThemeSwitcher.svelte
new file mode 100644
index 0000000..859747d
--- /dev/null
+++ b/src/components/ThemeSwitcher.svelte
@@ -0,0 +1,84 @@
+
+
+
+
+