Compare commits

...

2 Commits

Author SHA1 Message Date
652be41da4 Merge fix/category-filter-position into main
Fix category chips rendering in wrong position (between search and add button).
Chips now appear inline after the Categories toggle pill.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 01:27:20 +08:00
85a9882d26 Fix category chips appearing in wrong position
Category chips were rendering as a separate flex row that got pushed to the
far right (between search and add button). Flatten the layout so chips appear
inline immediately after the Categories toggle pill, separated by a divider.

Remove redundant wrapper divs from TodosPage, PeoplePage, LocationsPage —
CategoryFilterBar now owns its own flex-1 sizing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 01:22:28 +08:00
4 changed files with 116 additions and 127 deletions

View File

@ -291,21 +291,19 @@ export default function LocationsPage() {
<div className="border-b bg-card px-4 md:px-6 min-h-[4rem] flex items-center gap-2 md:gap-4 flex-wrap py-2 md:py-0 md:h-16 md:flex-nowrap shrink-0"> <div className="border-b bg-card px-4 md:px-6 min-h-[4rem] flex items-center gap-2 md:gap-4 flex-wrap py-2 md:py-0 md:h-16 md:flex-nowrap shrink-0">
<h1 className="font-heading text-xl md:text-2xl font-bold tracking-tight">Locations</h1> <h1 className="font-heading text-xl md:text-2xl font-bold tracking-tight">Locations</h1>
<div className="w-full md:flex-1 md:w-auto min-w-0 order-last md:order-none"> <CategoryFilterBar
<CategoryFilterBar categories={orderedCategories}
categories={orderedCategories} activeFilters={activeFilters}
activeFilters={activeFilters} pinnedLabel="Frequent"
pinnedLabel="Frequent" showPinned={showPinned}
showPinned={showPinned} onToggleAll={() => setActiveFilters([])}
onToggleAll={() => setActiveFilters([])} onTogglePinned={() => setShowPinned((v) => !v)}
onTogglePinned={() => setShowPinned((v) => !v)} onToggleCategory={handleToggleCategory}
onToggleCategory={handleToggleCategory} onSelectAllCategories={selectAllCategories}
onSelectAllCategories={selectAllCategories} onReorderCategories={reorderCategories}
onReorderCategories={reorderCategories} searchValue={search}
searchValue={search} onSearchChange={setSearch}
onSearchChange={setSearch} />
/>
</div>
<Button onClick={() => setShowForm(true)} size="sm" aria-label="Add location"> <Button onClick={() => setShowForm(true)} size="sm" aria-label="Add location">
<Plus className="h-4 w-4 md:mr-2" /><span className="hidden md:inline">Add Location</span> <Plus className="h-4 w-4 md:mr-2" /><span className="hidden md:inline">Add Location</span>

View File

@ -560,28 +560,26 @@ export default function PeoplePage() {
{/* Header */} {/* Header */}
<div className="border-b bg-card px-4 md:px-6 min-h-[4rem] flex items-center gap-2 md:gap-4 flex-wrap py-2 md:py-0 md:h-16 md:flex-nowrap shrink-0"> <div className="border-b bg-card px-4 md:px-6 min-h-[4rem] flex items-center gap-2 md:gap-4 flex-wrap py-2 md:py-0 md:h-16 md:flex-nowrap shrink-0">
<h1 className="font-heading text-xl md:text-2xl font-bold tracking-tight">People</h1> <h1 className="font-heading text-xl md:text-2xl font-bold tracking-tight">People</h1>
<div className="w-full md:flex-1 md:w-auto min-w-0 order-last md:order-none"> <CategoryFilterBar
<CategoryFilterBar activeFilters={activeFilters}
activeFilters={activeFilters} pinnedLabel="Favourites"
pinnedLabel="Favourites" showPinned={showPinned}
showPinned={showPinned} categories={orderedCategories}
categories={orderedCategories} onToggleAll={toggleAll}
onToggleAll={toggleAll} onTogglePinned={togglePinned}
onTogglePinned={togglePinned} onToggleCategory={toggleCategory}
onToggleCategory={toggleCategory} onSelectAllCategories={selectAllCategories}
onSelectAllCategories={selectAllCategories} onReorderCategories={reorderCategories}
onReorderCategories={reorderCategories} searchValue={search}
searchValue={search} onSearchChange={setSearch}
onSearchChange={setSearch} extraPinnedFilters={[
extraPinnedFilters={[ {
{ label: 'Umbral',
label: 'Umbral', isActive: showUmbralOnly,
isActive: showUmbralOnly, onToggle: () => setShowUmbralOnly((p) => !p),
onToggle: () => setShowUmbralOnly((p) => !p), },
}, ]}
]} />
/>
</div>
<div className="relative" ref={addDropdownRef}> <div className="relative" ref={addDropdownRef}>
<div className="flex"> <div className="flex">
<Button <Button

View File

@ -146,90 +146,72 @@ export default function CategoryFilterBar({
}; };
return ( return (
<div className="flex flex-col gap-2 md:flex-row md:items-center md:gap-2"> <div className="flex items-center gap-2 overflow-x-auto min-w-0 flex-1">
{/* Top row: pills + search */} {/* All pill */}
<div className="flex items-center gap-2 overflow-x-auto min-w-0 flex-1"> <button
{/* All pill */} type="button"
<button onClick={onToggleAll}
type="button" aria-label="Show all"
onClick={onToggleAll} className={pillBase}
aria-label="Show all" style={isAllActive ? activePillStyle : undefined}
className={pillBase} >
style={isAllActive ? activePillStyle : undefined} <span
className={
isAllActive ? '' : 'text-muted-foreground hover:text-foreground hover:bg-card-elevated'
}
> >
<span All
className={ </span>
isAllActive ? '' : 'text-muted-foreground hover:text-foreground hover:bg-card-elevated' </button>
}
> {/* Pinned pill */}
All <button
type="button"
onClick={onTogglePinned}
aria-label={`Toggle ${pinnedLabel}`}
className={pillBase}
style={showPinned ? activePillStyle : undefined}
>
<span className={showPinned ? '' : 'text-muted-foreground hover:text-foreground'}>
{pinnedLabel}
</span>
</button>
{/* Extra pinned filters (e.g. "Umbral") */}
{extraPinnedFilters.map((epf) => (
<button
key={epf.label}
type="button"
onClick={epf.onToggle}
aria-label={`Filter by ${epf.label}`}
className={pillBase}
style={epf.isActive ? activePillStyle : undefined}
>
<span className={epf.isActive ? '' : 'text-muted-foreground hover:text-foreground'}>
{epf.label}
</span> </span>
</button> </button>
))}
{/* Pinned pill */} {/* Categories pill */}
{categories.length > 0 && (
<button <button
type="button" type="button"
onClick={onTogglePinned} onClick={() => setOtherOpen((p) => !p)}
aria-label={`Toggle ${pinnedLabel}`} aria-label="Toggle category filters"
className={pillBase} className={pillBase}
style={showPinned ? activePillStyle : undefined} style={otherOpen ? activePillStyle : undefined}
> >
<span className={showPinned ? '' : 'text-muted-foreground hover:text-foreground'}> <span className={otherOpen ? '' : 'text-muted-foreground hover:text-foreground'}>
{pinnedLabel} Categories
</span> </span>
</button> </button>
)}
{/* Extra pinned filters (e.g. "Umbral") */} {/* Expanded category chips — inline after Categories pill */}
{extraPinnedFilters.map((epf) => (
<button
key={epf.label}
type="button"
onClick={epf.onToggle}
aria-label={`Filter by ${epf.label}`}
className={pillBase}
style={epf.isActive ? activePillStyle : undefined}
>
<span className={epf.isActive ? '' : 'text-muted-foreground hover:text-foreground'}>
{epf.label}
</span>
</button>
))}
{/* Categories pill */}
{categories.length > 0 && (
<button
type="button"
onClick={() => setOtherOpen((p) => !p)}
aria-label="Toggle category filters"
className={pillBase}
style={otherOpen ? activePillStyle : undefined}
>
<span className={otherOpen ? '' : 'text-muted-foreground hover:text-foreground'}>
Categories
</span>
</button>
)}
{/* Search */}
<div className="flex-1" />
<div className="relative shrink-0">
<Search className="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground pointer-events-none" />
<Input
ref={searchInputRef}
type="search"
placeholder="Search..."
value={searchValue}
onChange={(e) => onSearchChange(e.target.value)}
className="w-28 sm:w-52 h-8 pl-8 text-sm ring-inset"
aria-label="Search"
/>
</div>
</div>
{/* Expanded categories row — shows below on mobile, inline on desktop */}
{categories.length > 0 && otherOpen && ( {categories.length > 0 && otherOpen && (
<div className="flex items-center gap-1.5 overflow-x-auto pb-1 md:pb-0"> <>
{/* "All" chip inside categories — non-draggable */} <div className="w-px h-5 bg-border shrink-0" />
{onSelectAllCategories && ( {onSelectAllCategories && (
<button <button
type="button" type="button"
@ -250,8 +232,6 @@ export default function CategoryFilterBar({
</span> </span>
</button> </button>
)} )}
{/* Draggable category chips */}
<DndContext <DndContext
sensors={sensors} sensors={sensors}
collisionDetection={closestCenter} collisionDetection={closestCenter}
@ -272,8 +252,23 @@ export default function CategoryFilterBar({
))} ))}
</SortableContext> </SortableContext>
</DndContext> </DndContext>
</div> </>
)} )}
{/* Search — pushed to the right */}
<div className="flex-1" />
<div className="relative shrink-0">
<Search className="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground pointer-events-none" />
<Input
ref={searchInputRef}
type="search"
placeholder="Search..."
value={searchValue}
onChange={(e) => onSearchChange(e.target.value)}
className="w-28 sm:w-52 h-8 pl-8 text-sm ring-inset"
aria-label="Search"
/>
</div>
</div> </div>
); );
} }

View File

@ -168,21 +168,19 @@ export default function TodosPage() {
</div> </div>
{/* Category filter bar (All + Completed + Categories with drag) */} {/* Category filter bar (All + Completed + Categories with drag) */}
<div className="w-full md:flex-1 md:w-auto min-w-0 order-last md:order-none"> <CategoryFilterBar
<CategoryFilterBar activeFilters={activeFilters}
activeFilters={activeFilters} pinnedLabel="Completed"
pinnedLabel="Completed" showPinned={showCompleted}
showPinned={showCompleted} categories={orderedCategories}
categories={orderedCategories} onToggleAll={toggleAll}
onToggleAll={toggleAll} onTogglePinned={toggleCompleted}
onTogglePinned={toggleCompleted} onToggleCategory={toggleCategory}
onToggleCategory={toggleCategory} onSelectAllCategories={selectAllCategories}
onSelectAllCategories={selectAllCategories} onReorderCategories={reorderCategories}
onReorderCategories={reorderCategories} searchValue={search}
searchValue={search} onSearchChange={setSearch}
onSearchChange={setSearch} />
/>
</div>
<Button onClick={handleCreateNew} size="sm"> <Button onClick={handleCreateNew} size="sm">
<Plus className="h-4 w-4 md:mr-2" /><span className="hidden md:inline">Add Todo</span> <Plus className="h-4 w-4 md:mr-2" /><span className="hidden md:inline">Add Todo</span>