feat: add graceful degradation and conditional favorites filter
Add progressive enhancement to hide filter panel when JavaScript is
disabled, and conditionally render favorites filter based on login status.
Search Component:
- Added showFilters state (default false)
- Set showFilters to true in onMount when JS is enabled
- Wrapped FilterPanel in {#if showFilters} for graceful degradation
- Filters hidden without JavaScript, visible with JS
FilterPanel:
- Split grid layout into two variants:
- with-favorites: 5 columns (120px 120px 1fr 160px 90px)
- without-favorites: 4 columns (120px 120px 1fr 160px)
- Conditionally render FavoritesFilter only when isLoggedIn
- Apply appropriate class based on login status
FavoritesFilter:
- Simplified template (no internal login check)
- Only rendered when user is logged in via FilterPanel
UX:
- Non-JS browsers: Simple search only, filters gracefully hidden
- Not logged in: 4-column layout without favorites filter
- Logged in: 5-column layout with favorites filter
This commit is contained in:
@@ -10,8 +10,7 @@
|
||||
} = $props();
|
||||
|
||||
const isEnglish = $derived(lang === 'en');
|
||||
const label = $derived(isEnglish ? 'Favorites Only' : 'Nur Favoriten');
|
||||
const loginRequiredLabel = $derived(isEnglish ? 'Login required' : 'Anmeldung erforderlich');
|
||||
const label = $derived(isEnglish ? 'Favorites' : 'Favoriten');
|
||||
|
||||
let checked = $state(enabled);
|
||||
|
||||
@@ -57,24 +56,13 @@
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.login-required {
|
||||
font-size: 0.85rem;
|
||||
color: var(--nord3);
|
||||
font-style: italic;
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="filter-section">
|
||||
<div class="filter-label">{label}</div>
|
||||
{#if isLoggedIn}
|
||||
<Toggle
|
||||
bind:checked={checked}
|
||||
label=""
|
||||
on:change={handleChange}
|
||||
/>
|
||||
{:else}
|
||||
<div class="login-required">{loginRequiredLabel}</div>
|
||||
{/if}
|
||||
<Toggle
|
||||
bind:checked={checked}
|
||||
label=""
|
||||
on:change={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -79,11 +79,18 @@
|
||||
|
||||
.filter-panel {
|
||||
display: grid;
|
||||
grid-template-columns: 120px 120px 1fr 160px 140px;
|
||||
gap: 2rem;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.filter-panel.with-favorites {
|
||||
grid-template-columns: 120px 120px 1fr 160px 90px;
|
||||
}
|
||||
|
||||
.filter-panel.without-favorites {
|
||||
grid-template-columns: 120px 120px 1fr 160px;
|
||||
}
|
||||
|
||||
@media (max-width: 968px) {
|
||||
.toggle-button {
|
||||
display: flex;
|
||||
@@ -125,7 +132,7 @@
|
||||
<span class="arrow" class:open={filtersOpen}>▼</span>
|
||||
</button>
|
||||
|
||||
<div class="filter-panel" class:open={filtersOpen}>
|
||||
<div class="filter-panel" class:open={filtersOpen} class:with-favorites={isLoggedIn} class:without-favorites={!isLoggedIn}>
|
||||
<CategoryFilter
|
||||
categories={availableCategories}
|
||||
selected={selectedCategory}
|
||||
@@ -154,11 +161,13 @@
|
||||
{months}
|
||||
/>
|
||||
|
||||
<FavoritesFilter
|
||||
enabled={selectedFavoritesOnly}
|
||||
onToggle={onFavoritesToggle}
|
||||
{isLoggedIn}
|
||||
{lang}
|
||||
/>
|
||||
{#if isLoggedIn}
|
||||
<FavoritesFilter
|
||||
enabled={selectedFavoritesOnly}
|
||||
onToggle={onFavoritesToggle}
|
||||
{isLoggedIn}
|
||||
{lang}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
});
|
||||
|
||||
let searchQuery = $state('');
|
||||
let showFilters = $state(false);
|
||||
|
||||
// Filter data loaded from APIs
|
||||
let availableTags = $state([]);
|
||||
@@ -246,6 +247,9 @@
|
||||
clearButton.style.display = 'flex';
|
||||
}
|
||||
|
||||
// Enable filter panel for JS-enabled browsers
|
||||
showFilters = true;
|
||||
|
||||
// Get initial search value from URL if present
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const urlQuery = urlParams.get('q');
|
||||
@@ -340,20 +344,22 @@ scale: 0.8 0.8;
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<FilterPanel
|
||||
availableCategories={categories}
|
||||
{availableTags}
|
||||
{availableIcons}
|
||||
{selectedCategory}
|
||||
{selectedTags}
|
||||
{selectedIcon}
|
||||
{selectedSeasons}
|
||||
{selectedFavoritesOnly}
|
||||
{lang}
|
||||
{isLoggedIn}
|
||||
onCategoryChange={handleCategoryChange}
|
||||
onTagToggle={handleTagToggle}
|
||||
onIconChange={handleIconChange}
|
||||
onSeasonChange={handleSeasonChange}
|
||||
onFavoritesToggle={handleFavoritesToggle}
|
||||
/>
|
||||
{#if showFilters}
|
||||
<FilterPanel
|
||||
availableCategories={categories}
|
||||
{availableTags}
|
||||
{availableIcons}
|
||||
{selectedCategory}
|
||||
{selectedTags}
|
||||
{selectedIcon}
|
||||
{selectedSeasons}
|
||||
{selectedFavoritesOnly}
|
||||
{lang}
|
||||
{isLoggedIn}
|
||||
onCategoryChange={handleCategoryChange}
|
||||
onTagToggle={handleTagToggle}
|
||||
onIconChange={handleIconChange}
|
||||
onSeasonChange={handleSeasonChange}
|
||||
onFavoritesToggle={handleFavoritesToggle}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user