layout enhancement. Reading position reset

This commit is contained in:
Stefan Hardegger
2025-09-16 09:34:27 +02:00
parent f92dcc5314
commit c92308c24a
5 changed files with 452 additions and 65 deletions

View File

@@ -98,7 +98,7 @@ export default function ToolbarLayout({
</div>
{/* Sort */}
<div>
<div className="max-md:order-3">
<select
value={`${sortOption}_${sortDirection}`}
onChange={(e) => {
@@ -108,7 +108,7 @@ export default function ToolbarLayout({
onSortDirectionToggle();
}
}}
className="w-full px-3 py-2 border rounded-lg theme-card border-gray-300 dark:border-gray-600"
className="w-full px-3 py-2 border rounded-lg theme-card border-gray-300 dark:border-gray-600 max-md:text-sm"
>
<option value="lastRead_desc">Sort: Last Read </option>
<option value="lastRead_asc">Sort: Last Read </option>
@@ -123,27 +123,58 @@ export default function ToolbarLayout({
</select>
</div>
{/* View Toggle & Clear */}
<div className="flex gap-2">
{/* View Toggle, Advanced Filters & Clear */}
<div className="flex gap-2 max-md:order-2">
<div className="flex border theme-border rounded-lg overflow-hidden">
<Button
variant={viewMode === 'grid' ? 'primary' : 'ghost'}
onClick={() => onViewModeChange('grid')}
className="rounded-none border-0"
className="rounded-none border-0 max-md:px-2 max-md:text-sm"
>
Grid
<span className="max-md:hidden"> Grid</span>
<span className="md:hidden"></span>
</Button>
<Button
variant={viewMode === 'list' ? 'primary' : 'ghost'}
onClick={() => onViewModeChange('list')}
className="rounded-none border-0"
className="rounded-none border-0 max-md:px-2 max-md:text-sm"
>
List
<span className="max-md:hidden"> List</span>
<span className="md:hidden"></span>
</Button>
</div>
{(searchQuery || selectedTags.length > 0) && (
<Button variant="ghost" onClick={onClearFilters}>
Clear
{/* Advanced Filters Button */}
<Button
variant={filterExpanded && activeTab === 'advanced' ? 'primary' : 'ghost'}
onClick={() => {
if (!filterExpanded) {
// Panel closed → open and switch to advanced tab
setFilterExpanded(true);
setActiveTab('advanced');
} else if (activeTab !== 'advanced') {
// Panel open but wrong tab → just switch to advanced tab
setActiveTab('advanced');
} else {
// Panel open and on advanced tab → close panel
setFilterExpanded(false);
}
}}
className="max-md:text-sm max-md:px-2"
>
<span className="max-md:hidden"> Advanced</span>
<span className="md:hidden"></span>
{activeAdvancedFiltersCount > 0 && (
<span className="ml-1 text-xs bg-blue-500 text-white px-1.5 py-0.5 rounded font-bold max-md:ml-0.5 max-md:px-1">
{activeAdvancedFiltersCount}
</span>
)}
</Button>
{(searchQuery || selectedTags.length > 0 || activeAdvancedFiltersCount > 0) && (
<Button variant="ghost" onClick={onClearFilters} className="max-md:text-sm max-md:px-2">
<span className="max-md:hidden">Clear</span>
<span className="md:hidden"></span>
</Button>
)}
</div>
@@ -181,20 +212,31 @@ export default function ToolbarLayout({
</div>
))}
{/* Filter expand button with counts */}
<button
onClick={() => setFilterExpanded(!filterExpanded)}
className={`px-3 py-1 rounded-full text-xs font-medium border-2 border-dashed transition-colors ${
filterExpanded || activeAdvancedFiltersCount > 0 || remainingTagsCount > 0
? 'bg-blue-50 dark:bg-blue-900/20 border-blue-500 text-blue-700 dark:text-blue-300'
: 'bg-gray-50 dark:bg-gray-800 theme-text border-gray-300 dark:border-gray-600 hover:border-blue-500'
}`}
>
{remainingTagsCount > 0 && `+${remainingTagsCount} tags`}
{remainingTagsCount > 0 && activeAdvancedFiltersCount > 0 && ' • '}
{activeAdvancedFiltersCount > 0 && `${activeAdvancedFiltersCount} filters`}
{remainingTagsCount === 0 && activeAdvancedFiltersCount === 0 && 'More Filters'}
</button>
{/* More Tags Button */}
{remainingTagsCount > 0 && (
<button
onClick={() => {
if (!filterExpanded) {
// Panel closed → open and switch to tags tab
setFilterExpanded(true);
setActiveTab('tags');
} else if (activeTab !== 'tags') {
// Panel open but wrong tab → just switch to tags tab
setActiveTab('tags');
} else {
// Panel open and on tags tab → close panel
setFilterExpanded(false);
}
}}
className={`px-3 py-1 rounded-full text-xs font-medium border-2 transition-colors ${
filterExpanded && activeTab === 'tags'
? 'bg-blue-100 dark:bg-blue-900/30 border-blue-500 text-blue-700 dark:text-blue-300'
: 'border-dashed bg-gray-50 dark:bg-gray-800 theme-text border-gray-300 dark:border-gray-600 hover:border-blue-500'
}`}
>
+{remainingTagsCount} more tags
</button>
)}
<div className="ml-auto text-sm theme-text">
Showing {stories.length} of {totalElements} stories
@@ -207,32 +249,36 @@ export default function ToolbarLayout({
{/* Tab Navigation */}
<div className="flex gap-1 mb-4">
<button
onClick={() => setActiveTab('tags')}
onClick={() => setActiveTab('advanced')}
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
activeTab === 'tags'
? 'bg-white dark:bg-gray-700 theme-text shadow-sm'
: 'theme-text hover:bg-white/50 dark:hover:bg-gray-700/50'
activeTab === 'advanced'
? 'bg-blue-500 text-white shadow-sm'
: 'theme-text hover:bg-blue-50 dark:hover:bg-blue-900/20 border border-blue-200 dark:border-blue-800'
}`}
>
📋 Tags
{remainingTagsCount > 0 && (
<span className="ml-1 text-xs bg-gray-200 dark:bg-gray-600 px-1 rounded">
{remainingTagsCount}
Advanced Filters
{activeAdvancedFiltersCount > 0 && (
<span className="ml-1 text-xs bg-white text-blue-500 px-1.5 py-0.5 rounded font-bold">
{activeAdvancedFiltersCount}
</span>
)}
</button>
<button
onClick={() => setActiveTab('advanced')}
onClick={() => setActiveTab('tags')}
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
activeTab === 'advanced'
? 'bg-white dark:bg-gray-700 theme-text shadow-sm'
: 'theme-text hover:bg-white/50 dark:hover:bg-gray-700/50'
activeTab === 'tags'
? 'bg-blue-500 text-white shadow-sm'
: 'theme-text hover:bg-blue-50 dark:hover:bg-blue-900/20 border border-blue-200 dark:border-blue-800'
}`}
>
Advanced
{activeAdvancedFiltersCount > 0 && (
<span className="ml-1 text-xs bg-blue-500 text-white px-1 rounded">
{activeAdvancedFiltersCount}
📋 More Tags
{remainingTagsCount > 0 && (
<span className={`ml-1 text-xs px-1.5 py-0.5 rounded font-bold ${
activeTab === 'tags'
? 'bg-white text-blue-500'
: 'bg-gray-200 dark:bg-gray-600'
}`}>
{remainingTagsCount}
</span>
)}
</button>
@@ -255,9 +301,9 @@ export default function ToolbarLayout({
</Button>
)}
</div>
<div className="grid grid-cols-4 gap-2 max-h-40 overflow-y-auto max-md:grid-cols-2">
<div className="grid grid-cols-4 gap-2 max-h-40 overflow-y-auto max-md:grid-cols-2 max-sm:grid-cols-1">
{filteredRemainingTags.length === 0 && tagSearch ? (
<div className="col-span-4 text-center text-sm text-gray-500 py-4">
<div className="col-span-4 max-md:col-span-2 max-sm:col-span-1 text-center text-sm text-gray-500 py-4">
No tags match "{tagSearch}"
</div>
) : (
@@ -269,9 +315,9 @@ export default function ToolbarLayout({
selectedTags.includes(tag.name) ? 'ring-2 ring-blue-500 ring-offset-1' : ''
}`}
>
<TagDisplay
tag={{...tag, name: `${tag.name} (${tag.storyCount})`}}
size="sm"
<TagDisplay
tag={{...tag, name: `${tag.name} (${tag.storyCount})`}}
size="sm"
clickable={true}
className={`w-full ${selectedTags.includes(tag.name) ? 'bg-blue-500 text-white border-blue-500' : ''}`}
/>