Tag Enhancement + bugfixes
This commit is contained in:
@@ -3,11 +3,13 @@
|
||||
import { useState } from 'react';
|
||||
import { Input } from '../ui/Input';
|
||||
import Button from '../ui/Button';
|
||||
import TagDisplay from '../tags/TagDisplay';
|
||||
import { Story, Tag } from '../../types/api';
|
||||
|
||||
interface ToolbarLayoutProps {
|
||||
stories: Story[];
|
||||
tags: Tag[];
|
||||
totalElements: number;
|
||||
searchQuery: string;
|
||||
selectedTags: string[];
|
||||
viewMode: 'grid' | 'list';
|
||||
@@ -26,6 +28,7 @@ interface ToolbarLayoutProps {
|
||||
export default function ToolbarLayout({
|
||||
stories,
|
||||
tags,
|
||||
totalElements,
|
||||
searchQuery,
|
||||
selectedTags,
|
||||
viewMode,
|
||||
@@ -41,9 +44,17 @@ export default function ToolbarLayout({
|
||||
children
|
||||
}: ToolbarLayoutProps) {
|
||||
const [tagSearchExpanded, setTagSearchExpanded] = useState(false);
|
||||
const [tagSearch, setTagSearch] = useState('');
|
||||
|
||||
const popularTags = tags.slice(0, 6);
|
||||
const remainingTagsCount = Math.max(0, tags.length - 6);
|
||||
|
||||
// Filter remaining tags based on search query
|
||||
const remainingTags = tags.slice(6);
|
||||
const filteredRemainingTags = tagSearch
|
||||
? remainingTags.filter(tag => tag.name.toLowerCase().includes(tagSearch.toLowerCase()))
|
||||
: remainingTags;
|
||||
|
||||
const remainingTagsCount = Math.max(0, remainingTags.length);
|
||||
|
||||
return (
|
||||
<div className="max-w-7xl mx-auto p-6 max-md:p-4">
|
||||
@@ -53,7 +64,7 @@ export default function ToolbarLayout({
|
||||
<div className="flex justify-between items-start mb-6 max-md:flex-col max-md:gap-4">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold theme-header">Your Story Library</h1>
|
||||
<p className="theme-text mt-1">{stories.length} stories in your collection</p>
|
||||
<p className="theme-text mt-1">{totalElements} stories in your collection</p>
|
||||
</div>
|
||||
<div className="max-md:self-end">
|
||||
<Button variant="secondary" onClick={onRandomStory}>
|
||||
@@ -142,17 +153,20 @@ export default function ToolbarLayout({
|
||||
All Stories
|
||||
</button>
|
||||
{popularTags.map((tag) => (
|
||||
<button
|
||||
<div
|
||||
key={tag.id}
|
||||
onClick={() => onTagToggle(tag.name)}
|
||||
className={`px-3 py-1 rounded-full text-xs font-medium transition-colors ${
|
||||
selectedTags.includes(tag.name)
|
||||
? 'bg-blue-500 text-white'
|
||||
: 'bg-gray-100 dark:bg-gray-700 theme-text hover:bg-blue-100 dark:hover:bg-blue-900'
|
||||
className={`cursor-pointer transition-all hover:scale-105 ${
|
||||
selectedTags.includes(tag.name) ? 'ring-2 ring-blue-500 ring-offset-1' : ''
|
||||
}`}
|
||||
>
|
||||
{tag.name} ({tag.storyCount})
|
||||
</button>
|
||||
<TagDisplay
|
||||
tag={{...tag, name: `${tag.name} (${tag.storyCount})`}}
|
||||
size="sm"
|
||||
clickable={true}
|
||||
className={selectedTags.includes(tag.name) ? 'bg-blue-500 text-white border-blue-500' : ''}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{remainingTagsCount > 0 && (
|
||||
<button
|
||||
@@ -163,7 +177,7 @@ export default function ToolbarLayout({
|
||||
</button>
|
||||
)}
|
||||
<div className="ml-auto text-sm theme-text">
|
||||
Showing {stories.length} stories
|
||||
Showing {stories.length} of {totalElements} stories
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -174,9 +188,15 @@ export default function ToolbarLayout({
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Search from all available tags..."
|
||||
value={tagSearch}
|
||||
onChange={(e) => setTagSearch(e.target.value)}
|
||||
className="flex-1"
|
||||
/>
|
||||
<Button variant="secondary">Search</Button>
|
||||
{tagSearch && (
|
||||
<Button variant="ghost" onClick={() => setTagSearch('')}>
|
||||
Clear
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => setTagSearchExpanded(false)}
|
||||
@@ -185,19 +205,28 @@ 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">
|
||||
{tags.slice(6).map((tag) => (
|
||||
<button
|
||||
{filteredRemainingTags.length === 0 && tagSearch ? (
|
||||
<div className="col-span-4 text-center text-sm text-gray-500 py-4">
|
||||
No tags match "{tagSearch}"
|
||||
</div>
|
||||
) : (
|
||||
filteredRemainingTags.map((tag) => (
|
||||
<div
|
||||
key={tag.id}
|
||||
onClick={() => onTagToggle(tag.name)}
|
||||
className={`px-2 py-1 rounded text-xs font-medium transition-colors ${
|
||||
selectedTags.includes(tag.name)
|
||||
? 'bg-blue-500 text-white'
|
||||
: 'bg-white dark:bg-gray-700 theme-text hover:bg-blue-100 dark:hover:bg-blue-900'
|
||||
className={`cursor-pointer transition-all hover:scale-105 ${
|
||||
selectedTags.includes(tag.name) ? 'ring-2 ring-blue-500 ring-offset-1' : ''
|
||||
}`}
|
||||
>
|
||||
{tag.name} ({tag.storyCount})
|
||||
</button>
|
||||
))}
|
||||
<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' : ''}`}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user