Refactor MarketPage layout for improved mobile responsiveness and user experience
- Optimized the Market Header and Fuzzy Search components for better mobile display, enhancing usability on smaller screens. - Adjusted spacing and font sizes for various elements to ensure a cohesive look across devices. - Improved the active filters summary and category filters for better accessibility and visual clarity. These changes enhance the overall user experience by providing a more responsive and visually appealing interface in the MarketPage.
This commit is contained in:
parent
f2a432b6df
commit
8fe53d3d71
1 changed files with 55 additions and 51 deletions
|
|
@ -23,32 +23,36 @@
|
||||||
|
|
||||||
<!-- Market Content -->
|
<!-- Market Content -->
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<!-- Market Header -->
|
<!-- Market Header - Optimized for Mobile -->
|
||||||
<div class="flex items-center justify-between mb-8">
|
<div class="mb-4 sm:mb-6 lg:mb-8">
|
||||||
<div class="flex items-center space-x-4">
|
<!-- Market Info and Search - Responsive Layout -->
|
||||||
<Avatar v-if="marketStore.activeMarket?.opts?.logo">
|
<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-3 sm:gap-4 lg:gap-6">
|
||||||
<AvatarImage :src="marketStore.activeMarket.opts.logo" />
|
<!-- Market Info - Compact on Mobile -->
|
||||||
<AvatarFallback>M</AvatarFallback>
|
<div class="flex items-center space-x-3 sm:space-x-4">
|
||||||
</Avatar>
|
<Avatar v-if="marketStore.activeMarket?.opts?.logo" class="h-10 w-10 sm:h-12 sm:w-12">
|
||||||
<div>
|
<AvatarImage :src="marketStore.activeMarket.opts.logo" />
|
||||||
<h1 class="text-3xl font-bold">
|
<AvatarFallback>M</AvatarFallback>
|
||||||
{{ marketStore.activeMarket?.opts?.name || 'Market' }}
|
</Avatar>
|
||||||
</h1>
|
<div>
|
||||||
<p v-if="marketStore.activeMarket?.opts?.description" class="text-gray-600">
|
<h1 class="text-xl sm:text-2xl lg:text-3xl font-bold">
|
||||||
{{ marketStore.activeMarket.opts.description }}
|
{{ marketStore.activeMarket?.opts?.name || 'Market' }}
|
||||||
</p>
|
</h1>
|
||||||
|
<p v-if="marketStore.activeMarket?.opts?.description" class="text-muted-foreground text-xs sm:text-sm lg:text-base line-clamp-1 sm:line-clamp-2">
|
||||||
|
{{ marketStore.activeMarket.opts.description }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Enhanced Fuzzy Search Bar - Full Width on Mobile -->
|
||||||
|
<div class="w-full lg:flex-1 lg:max-w-md">
|
||||||
|
<MarketFuzzySearch
|
||||||
|
:data="marketStore.products"
|
||||||
|
:options="searchOptions"
|
||||||
|
@results="handleSearchResults"
|
||||||
|
@filter-category="handleCategoryFilter"
|
||||||
|
class="w-full"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Enhanced Fuzzy Search Bar -->
|
|
||||||
<div class="flex-1 max-w-md ml-8">
|
|
||||||
<MarketFuzzySearch
|
|
||||||
:data="marketStore.products"
|
|
||||||
:options="searchOptions"
|
|
||||||
@results="handleSearchResults"
|
|
||||||
@filter-category="handleCategoryFilter"
|
|
||||||
class="w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -57,28 +61,28 @@
|
||||||
<!-- Enhanced Category Filters -->
|
<!-- Enhanced Category Filters -->
|
||||||
<section
|
<section
|
||||||
v-if="allCategories.length > 0"
|
v-if="allCategories.length > 0"
|
||||||
class="mb-6"
|
class="mb-4 sm:mb-6"
|
||||||
aria-labelledby="category-filters-heading"
|
aria-labelledby="category-filters-heading"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between mb-3">
|
<div class="flex items-center justify-between mb-2 sm:mb-3">
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-2 sm:gap-4">
|
||||||
<h3 id="category-filters-heading" class="text-lg font-semibold text-gray-700">
|
<h3 id="category-filters-heading" class="text-sm sm:text-lg font-semibold text-gray-700">
|
||||||
Browse by Category
|
Browse by Category
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<!-- AND/OR Filter Mode Toggle -->
|
<!-- AND/OR Filter Mode Toggle -->
|
||||||
<div
|
<div
|
||||||
v-if="selectedCategoriesCount > 1"
|
v-if="selectedCategoriesCount > 1"
|
||||||
class="flex items-center gap-2"
|
class="flex items-center gap-1 sm:gap-2"
|
||||||
role="group"
|
role="group"
|
||||||
aria-label="Filter mode selection"
|
aria-label="Filter mode selection"
|
||||||
>
|
>
|
||||||
<span class="text-xs text-muted-foreground">Match:</span>
|
<span class="text-xs text-muted-foreground hidden sm:inline">Match:</span>
|
||||||
<Button
|
<Button
|
||||||
@click="setFilterMode('any')"
|
@click="setFilterMode('any')"
|
||||||
:variant="filterMode === 'any' ? 'default' : 'outline'"
|
:variant="filterMode === 'any' ? 'default' : 'outline'"
|
||||||
size="sm"
|
size="sm"
|
||||||
class="h-6 px-2 text-xs"
|
class="h-5 sm:h-6 px-1.5 sm:px-2 text-xs"
|
||||||
:aria-pressed="filterMode === 'any'"
|
:aria-pressed="filterMode === 'any'"
|
||||||
aria-label="Show products with any selected category"
|
aria-label="Show products with any selected category"
|
||||||
>
|
>
|
||||||
|
|
@ -88,7 +92,7 @@
|
||||||
@click="setFilterMode('all')"
|
@click="setFilterMode('all')"
|
||||||
:variant="filterMode === 'all' ? 'default' : 'outline'"
|
:variant="filterMode === 'all' ? 'default' : 'outline'"
|
||||||
size="sm"
|
size="sm"
|
||||||
class="h-6 px-2 text-xs"
|
class="h-5 sm:h-6 px-1.5 sm:px-2 text-xs"
|
||||||
:aria-pressed="filterMode === 'all'"
|
:aria-pressed="filterMode === 'all'"
|
||||||
aria-label="Show products with all selected categories"
|
aria-label="Show products with all selected categories"
|
||||||
>
|
>
|
||||||
|
|
@ -102,16 +106,16 @@
|
||||||
@click="clearAllCategoryFilters"
|
@click="clearAllCategoryFilters"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
class="text-sm"
|
class="text-xs sm:text-sm px-2 sm:px-3 py-1 sm:py-2"
|
||||||
:aria-label="`Clear all ${selectedCategoriesCount} selected category filters`"
|
:aria-label="`Clear all ${selectedCategoriesCount} selected category filters`"
|
||||||
>
|
>
|
||||||
Clear All ({{ selectedCategoriesCount }})
|
<span class="hidden sm:inline">Clear All </span><span class="sm:hidden">Clear </span>({{ selectedCategoriesCount }})
|
||||||
<X class="w-4 h-4 ml-1" aria-hidden="true" />
|
<X class="w-3 h-3 sm:w-4 sm:h-4 ml-1" aria-hidden="true" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex flex-wrap gap-3"
|
class="flex flex-wrap gap-1.5 sm:gap-3"
|
||||||
role="group"
|
role="group"
|
||||||
aria-label="Filter products by category"
|
aria-label="Filter products by category"
|
||||||
>
|
>
|
||||||
|
|
@ -130,7 +134,7 @@
|
||||||
>
|
>
|
||||||
<Badge
|
<Badge
|
||||||
:variant="category.selected ? 'default' : 'outline'"
|
:variant="category.selected ? 'default' : 'outline'"
|
||||||
class="px-4 py-2 text-sm font-medium transition-all duration-200"
|
class="px-2 py-1 sm:px-4 sm:py-2 text-xs sm:text-sm font-medium transition-all duration-200"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-primary text-primary-foreground shadow-md': category.selected,
|
'bg-primary text-primary-foreground shadow-md': category.selected,
|
||||||
'hover:bg-primary/10 hover:border-primary': !category.selected,
|
'hover:bg-primary/10 hover:border-primary': !category.selected,
|
||||||
|
|
@ -138,10 +142,10 @@
|
||||||
}"
|
}"
|
||||||
:aria-hidden="true"
|
:aria-hidden="true"
|
||||||
>
|
>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-1 sm:gap-2">
|
||||||
<span>{{ category.category }}</span>
|
<span>{{ category.category }}</span>
|
||||||
<div
|
<div
|
||||||
class="px-2 py-0.5 rounded-full text-xs font-bold transition-colors"
|
class="px-1 py-0.5 sm:px-2 rounded-full text-xs font-bold transition-colors"
|
||||||
:class="category.selected
|
:class="category.selected
|
||||||
? 'bg-primary-foreground/20 text-primary-foreground'
|
? 'bg-primary-foreground/20 text-primary-foreground'
|
||||||
: 'bg-secondary text-secondary-foreground'"
|
: 'bg-secondary text-secondary-foreground'"
|
||||||
|
|
@ -159,9 +163,9 @@
|
||||||
<!-- Selection indicator -->
|
<!-- Selection indicator -->
|
||||||
<div
|
<div
|
||||||
v-if="category.selected"
|
v-if="category.selected"
|
||||||
class="absolute -top-1 -right-1 w-3 h-3 bg-green-500 rounded-full border-2 border-white shadow-sm"
|
class="absolute -top-0.5 -right-0.5 sm:-top-1 sm:-right-1 w-2.5 h-2.5 sm:w-3 sm:h-3 bg-green-500 rounded-full border-2 border-white shadow-sm"
|
||||||
>
|
>
|
||||||
<Check class="w-2 h-2 text-white absolute top-0.5 left-0.5" />
|
<Check class="w-1.5 h-1.5 sm:w-2 sm:h-2 text-white absolute top-0 left-0 sm:top-0.5 sm:left-0.5" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -169,34 +173,34 @@
|
||||||
<!-- Active Filters Summary -->
|
<!-- Active Filters Summary -->
|
||||||
<div
|
<div
|
||||||
v-if="selectedCategoriesCount > 0"
|
v-if="selectedCategoriesCount > 0"
|
||||||
class="mt-4 p-3 bg-accent/50 rounded-lg border border-border"
|
class="mt-2 sm:mt-4 p-2 sm:p-3 bg-accent/50 rounded-lg border border-border"
|
||||||
role="region"
|
role="region"
|
||||||
aria-label="Active category filters"
|
aria-label="Active category filters"
|
||||||
aria-live="polite"
|
aria-live="polite"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-1.5 sm:gap-2">
|
||||||
<Filter class="w-4 h-4 text-accent-foreground/80" aria-hidden="true" />
|
<Filter class="w-3 h-3 sm:w-4 sm:h-4 text-accent-foreground/80" aria-hidden="true" />
|
||||||
<span class="text-sm font-medium text-accent-foreground">
|
<span class="text-xs sm:text-sm font-medium text-accent-foreground">
|
||||||
Active Filters
|
<span class="hidden sm:inline">Active Filters</span><span class="sm:hidden">Filters</span>
|
||||||
<span v-if="selectedCategoriesCount > 1" class="text-xs opacity-75">
|
<span v-if="selectedCategoriesCount > 1" class="text-xs opacity-75">
|
||||||
({{ filterMode === 'any' ? 'Any' : 'All' }} match):
|
({{ filterMode === 'any' ? 'Any' : 'All' }} match):
|
||||||
</span>
|
</span>
|
||||||
<span v-else>:</span>
|
<span v-else>:</span>
|
||||||
</span>
|
</span>
|
||||||
<div class="flex gap-1" role="list" aria-label="Selected category filters">
|
<div class="flex gap-0.5 sm:gap-1 flex-wrap" role="list" aria-label="Selected category filters">
|
||||||
<Badge
|
<Badge
|
||||||
v-for="category in selectedCategories"
|
v-for="category in selectedCategories"
|
||||||
:key="category"
|
:key="category"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
class="text-xs"
|
class="text-xs px-1.5 py-0.5"
|
||||||
role="listitem"
|
role="listitem"
|
||||||
>
|
>
|
||||||
{{ category }}
|
{{ category }}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm text-muted-foreground" aria-live="polite">
|
<div class="text-xs sm:text-sm text-muted-foreground" aria-live="polite">
|
||||||
{{ productsToDisplay.length }} products found
|
{{ productsToDisplay.length }} products found
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue