feat: colors view and color label thumbnail ornament
Adds a Colors entry under Views that buckets photos in canonical R-O-Y-G-B-P order (plus an Uncolored tail), mirroring the rated/tags grouping pattern. Surfaces the color label on each thumbnail as a small swatch chip preceding the rating badge in the BL corner so labels are visible everywhere, not just inside the new view. Also types color_label on the Photo interface — the backend already returned it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import clsx from 'clsx'
|
||||
import { photos as photosApi } from '../../services/api'
|
||||
import type { Photo } from '../../types/photo'
|
||||
import { usePhotoStore } from '../../store/photoStore'
|
||||
import { COLOR_LABEL_OPTIONS } from '../../constants/colorLabels'
|
||||
|
||||
/** Custom MIME used by HeapsPanel to recognise our drag payload. */
|
||||
export const PHOTO_DRAG_MIME = 'application/x-mulita-photos'
|
||||
@@ -28,12 +29,21 @@ export const THUMB_BADGE_BASE =
|
||||
export const THUMB_BADGE_SQUARE = 'w-5 justify-center !px-0'
|
||||
/** Standard icon size for any lucide glyph inside a badge. */
|
||||
export const THUMB_BADGE_ICON = 'h-3 w-3'
|
||||
export const THUMB_BADGE_PRIMARY =
|
||||
'bg-primary shadow-[0_0_0_1px_rgba(0,0,0,0.55),inset_0_1px_0_rgba(255,255,255,0.28),0_1px_2px_rgba(0,0,0,0.5)]'
|
||||
/** Chiseled-pixel frame: 1px outline + inset top highlight + drop shadow.
|
||||
* Shared by every "user-affirmed" variant so any swatch (primary, pick,
|
||||
* color label) reads as part of the same badge family. */
|
||||
const THUMB_BADGE_FRAME =
|
||||
'shadow-[0_0_0_1px_rgba(0,0,0,0.55),inset_0_1px_0_rgba(255,255,255,0.28),0_1px_2px_rgba(0,0,0,0.5)]'
|
||||
export const THUMB_BADGE_PRIMARY = `bg-primary ${THUMB_BADGE_FRAME}`
|
||||
export const THUMB_BADGE_NEUTRAL =
|
||||
'bg-black/75 backdrop-blur-sm shadow-[0_0_0_1px_rgba(255,255,255,0.12),inset_0_1px_0_rgba(255,255,255,0.08),0_1px_2px_rgba(0,0,0,0.55)]'
|
||||
export const THUMB_BADGE_PICK =
|
||||
'bg-pick shadow-[0_0_0_1px_rgba(0,0,0,0.55),inset_0_1px_0_rgba(255,255,255,0.28),0_1px_2px_rgba(0,0,0,0.5)]'
|
||||
export const THUMB_BADGE_PICK = `bg-pick ${THUMB_BADGE_FRAME}`
|
||||
|
||||
/** Tailwind bg-class for each color label, looked up at render time so the
|
||||
* classnames are statically present in the source for the JIT to scan. */
|
||||
const COLOR_LABEL_BG: Record<string, string> = Object.fromEntries(
|
||||
COLOR_LABEL_OPTIONS.map((o) => [o.value, o.className])
|
||||
)
|
||||
|
||||
// Auto-retry schedule (ms). Backend generates thumbs on-demand via Celery, so
|
||||
// the first hit on a freshly-scanned library returns 404 "not ready" until
|
||||
@@ -254,18 +264,30 @@ export function PhotoThumbnail({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* BL — rating */}
|
||||
{photo.rating > 0 && (
|
||||
<div
|
||||
className={clsx(
|
||||
'absolute bottom-1 left-1 gap-0.5',
|
||||
THUMB_BADGE_BASE,
|
||||
THUMB_BADGE_PRIMARY
|
||||
{/* BL — color label + rating. Color comes first (left of rating)
|
||||
* so the swatch reads as a "category dot" prefixing the stars. */}
|
||||
{(photo.color_label || photo.rating > 0) && (
|
||||
<div className="absolute bottom-1 left-1 flex items-center gap-1">
|
||||
{photo.color_label && COLOR_LABEL_BG[photo.color_label] && (
|
||||
<div
|
||||
className={clsx(
|
||||
THUMB_BADGE_BASE,
|
||||
THUMB_BADGE_SQUARE,
|
||||
COLOR_LABEL_BG[photo.color_label],
|
||||
THUMB_BADGE_FRAME
|
||||
)}
|
||||
title={`Color label: ${photo.color_label}`}
|
||||
/>
|
||||
)}
|
||||
{photo.rating > 0 && (
|
||||
<div
|
||||
className={clsx('gap-0.5', THUMB_BADGE_BASE, THUMB_BADGE_PRIMARY)}
|
||||
>
|
||||
{Array.from({ length: photo.rating }).map((_, i) => (
|
||||
<Star key={i} className={clsx(THUMB_BADGE_ICON, 'fill-white')} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
{Array.from({ length: photo.rating }).map((_, i) => (
|
||||
<Star key={i} className={clsx(THUMB_BADGE_ICON, 'fill-white')} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user