fix: Make compact list readable on mobile by replacing wide bag select with compact 4-char tag pill and allowing full item text
This commit is contained in:
1 parent
946a7780f6
commit
be586ea386
2 files changed
+82
-52
No files matched your search
@@ -95,7 +95,7 @@ export const BagOverview: React.FC<BagOverviewProps> = ({
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-2 sm:gap-3">
|
||||
{visibleBags.map((b) => {
|
||||
const bagItems = items.filter((i) => i.bag === b.id);
|
||||
const bagPacked = bagItems.filter((i) => i.packed).length;
|
||||
@@ -112,43 +112,43 @@ export const BagOverview: React.FC<BagOverviewProps> = ({
|
||||
sound.playClick();
|
||||
onSelectBag(isSelected ? 'all' : b.id);
|
||||
}}
|
||||
className={`relative text-left p-3.5 border-2 border-ink transition-all flex flex-col justify-between ${
|
||||
className={`relative text-left p-2.5 sm:p-3.5 border-2 border-ink transition-all flex flex-col justify-between ${
|
||||
isSelected
|
||||
? 'bg-canary-light shadow-card-hard border-ink translate-x-0.5 translate-y-0.5'
|
||||
: 'bg-ticket-50 hover:bg-ticket-100 shadow-card-hard-sm'
|
||||
}`}
|
||||
>
|
||||
{/* Perforation holes on card edges */}
|
||||
<div className="ticket-notch-left" />
|
||||
<div className="ticket-notch-right" />
|
||||
<div className="ticket-notch-left hidden sm:block" />
|
||||
<div className="ticket-notch-right hidden sm:block" />
|
||||
|
||||
<div>
|
||||
{/* Top Barcode Header */}
|
||||
<div className="flex items-center justify-between border-b border-ink/20 pb-2 mb-2 font-mono text-[10px]">
|
||||
<span className="bg-ink text-ticket-100 px-1 py-0.5 font-bold uppercase">
|
||||
<div className="flex items-center justify-between border-b border-ink/20 pb-1.5 sm:pb-2 mb-1.5 sm:mb-2 font-mono text-[10px] gap-1">
|
||||
<span className="bg-ink text-ticket-100 px-1 py-0.5 font-bold uppercase shrink-0">
|
||||
{b.tagCode}
|
||||
</span>
|
||||
<span className="text-ink-muted tracking-tight truncate max-w-[130px]">
|
||||
<span className="text-ink-muted tracking-tight truncate">
|
||||
{b.sub}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Title & Icon */}
|
||||
<div className="flex items-center justify-between gap-2 mb-1">
|
||||
<div className="flex items-center gap-2 text-ink min-w-0">
|
||||
<span className="text-ink shrink-0">{getBagIcon(b.iconKey)}</span>
|
||||
<span className="font-display text-xl tracking-wide uppercase leading-tight truncate">
|
||||
<div className="flex items-center justify-between gap-1.5 mb-1">
|
||||
<div className="flex items-center gap-1.5 sm:gap-2 text-ink min-w-0">
|
||||
<span className="text-ink shrink-0 hidden sm:inline">{getBagIcon(b.iconKey)}</span>
|
||||
<span className="font-display text-base sm:text-xl tracking-wide uppercase leading-tight truncate">
|
||||
{b.label}
|
||||
</span>
|
||||
</div>
|
||||
{isFull && (
|
||||
<CheckCircle2 size={18} className="text-stamp-green shrink-0" />
|
||||
<CheckCircle2 size={16} className="text-stamp-green shrink-0" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* TSA / Bag Advisory Tip */}
|
||||
{b.tsaTip && (
|
||||
<p className="font-sans text-[11px] text-ink-muted leading-tight mb-3 line-clamp-2">
|
||||
<p className="font-sans text-[11px] text-ink-muted leading-tight mb-3 line-clamp-2 hidden sm:block">
|
||||
{b.tsaTip}
|
||||
</p>
|
||||
)}
|
||||
|
||||
+69
-39
@@ -53,42 +53,51 @@ export const ItemRow: React.FC<ItemRowProps> = ({
|
||||
if (compact) {
|
||||
return (
|
||||
<div
|
||||
className={`group relative flex items-center justify-between border-2 py-1.5 px-3 mb-1.5 transition-all shadow-xs ${
|
||||
className={`group relative flex items-center justify-between gap-2 border-2 py-2 px-2.5 sm:px-3 mb-1.5 transition-all shadow-xs ${
|
||||
item.packed
|
||||
? 'bg-[#ECE8DC]/70 border-ink/40 text-ink/60'
|
||||
: 'bg-ticket-50 hover:bg-ticket-100 border-ink'
|
||||
? 'bg-[#ECE8DC]/80 border-ink/40 text-ink/60'
|
||||
: 'bg-white hover:bg-ticket-50 border-ink text-ink'
|
||||
}`}
|
||||
>
|
||||
{/* Left: Checkbox & Name */}
|
||||
<div className="flex items-center gap-2.5 min-w-0 flex-1">
|
||||
{/* Left: Tap anywhere here to toggle packed */}
|
||||
<div
|
||||
onClick={() => {
|
||||
sound.playTear();
|
||||
onTogglePacked(item.id);
|
||||
}}
|
||||
className="flex items-center gap-2 sm:gap-2.5 min-w-0 flex-1 cursor-pointer select-none"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
sound.playTear();
|
||||
onTogglePacked(item.id);
|
||||
}}
|
||||
className={`w-5 h-5 rounded border-2 flex items-center justify-center font-mono font-bold shrink-0 transition-transform ${
|
||||
item.packed
|
||||
? 'border-stamp-green bg-stamp-green text-white rotate-[-3deg]'
|
||||
: 'border-ink/60 bg-white hover:border-ink'
|
||||
: 'border-ink bg-white hover:border-ink'
|
||||
}`}
|
||||
title={item.packed ? 'Mark as unpacked' : 'Check off'}
|
||||
>
|
||||
{item.packed ? <Check size={12} strokeWidth={3} /> : null}
|
||||
{item.packed ? <Check size={13} strokeWidth={3} /> : null}
|
||||
</button>
|
||||
|
||||
{/* Qty indicator */}
|
||||
<span className="font-mono text-xs font-bold text-ink shrink-0 bg-ticket-200 border border-ink/20 px-1.5 py-0.2 rounded-xs">
|
||||
<span
|
||||
className={`font-mono text-xs font-bold shrink-0 px-1.5 py-0.5 rounded-xs border ${
|
||||
item.packed
|
||||
? 'bg-ticket-200/60 border-ink/20 text-ink/50'
|
||||
: 'bg-canary/40 border-ink/40 text-ink'
|
||||
}`}
|
||||
>
|
||||
{item.quantity}×
|
||||
</span>
|
||||
|
||||
{/* Name */}
|
||||
{/* Item Name — full readability on mobile */}
|
||||
<span
|
||||
onClick={() => {
|
||||
sound.playTear();
|
||||
onTogglePacked(item.id);
|
||||
}}
|
||||
className={`font-grotesk font-bold text-xs sm:text-sm truncate cursor-pointer select-none ${
|
||||
className={`font-grotesk font-bold text-sm sm:text-base leading-snug break-words min-w-0 flex-1 ${
|
||||
item.packed ? 'line-through text-ink/50' : 'text-ink'
|
||||
}`}
|
||||
>
|
||||
@@ -96,34 +105,55 @@ export const ItemRow: React.FC<ItemRowProps> = ({
|
||||
</span>
|
||||
|
||||
{item.essential && (
|
||||
<span className="font-mono text-[9px] font-bold bg-gate-orange text-white px-1 py-0.2 uppercase shrink-0 hidden sm:inline">
|
||||
ESSENTIAL
|
||||
</span>
|
||||
<>
|
||||
<span
|
||||
className="w-2 h-2 rounded-full bg-gate-orange shrink-0 sm:hidden"
|
||||
title="Essential item"
|
||||
/>
|
||||
<span className="font-mono text-[9px] font-bold bg-gate-orange text-white px-1.5 py-0.5 uppercase shrink-0 hidden sm:inline">
|
||||
ESSENTIAL
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Right: Bag Selector & Category Pill & Actions */}
|
||||
<div className="flex items-center gap-2 shrink-0 ml-2">
|
||||
{/* Bag Selector Dropdown Pill */}
|
||||
<select
|
||||
value={item.bag}
|
||||
onChange={(e) => {
|
||||
sound.playClick();
|
||||
onUpdateBag(item.id, e.target.value);
|
||||
}}
|
||||
className="bg-white border border-ink font-mono text-[10px] font-bold px-1.5 py-0.5 cursor-pointer hover:bg-canary-light"
|
||||
title="Assigned Bag"
|
||||
{/* Right: Compact Bag Tag Pill & Desktop Actions */}
|
||||
<div className="flex items-center gap-1.5 sm:gap-2 shrink-0">
|
||||
{/* Compact Bag Tag Pill with invisible native select overlay so it never stretches mobile width */}
|
||||
<div
|
||||
className="relative inline-flex items-center"
|
||||
title={`Assigned to: ${currentBag.label}`}
|
||||
>
|
||||
{availableBags.map((b) => (
|
||||
<option key={b.id} value={b.id}>
|
||||
{b.tagCode} ({b.label})
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<span
|
||||
className={`font-mono text-[11px] font-bold px-2 py-0.5 border border-ink pointer-events-none flex items-center gap-1 ${
|
||||
item.packed
|
||||
? 'bg-ticket-100 text-ink/60 border-ink/40'
|
||||
: 'bg-ticket-100 text-ink shadow-2xs'
|
||||
}`}
|
||||
>
|
||||
<span>{currentBag.tagCode}</span>
|
||||
<span className="text-[8px] opacity-60">▾</span>
|
||||
</span>
|
||||
<select
|
||||
value={item.bag}
|
||||
onChange={(e) => {
|
||||
sound.playClick();
|
||||
onUpdateBag(item.id, e.target.value);
|
||||
}}
|
||||
aria-label={`Assign ${item.name} to bag`}
|
||||
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
|
||||
>
|
||||
{availableBags.map((b) => (
|
||||
<option key={b.id} value={b.id}>
|
||||
{b.tagCode} — {b.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Category Pill */}
|
||||
{/* Category Pill (Desktop only) */}
|
||||
<span
|
||||
className={`font-mono text-[9px] font-bold px-1.5 py-0.2 border border-ink/20 hidden md:inline ${catStyle.bg} ${catStyle.text}`}
|
||||
className={`font-mono text-[9px] font-bold px-1.5 py-0.5 border border-ink/20 hidden md:inline ${catStyle.bg} ${catStyle.text}`}
|
||||
>
|
||||
{catStyle.label}
|
||||
</span>
|
||||
@@ -135,10 +165,10 @@ export const ItemRow: React.FC<ItemRowProps> = ({
|
||||
sound.playClick();
|
||||
onDelete(item.id);
|
||||
}}
|
||||
className="p-1 text-ink/30 hover:text-stamp-red transition-colors"
|
||||
className="p-1 text-ink/35 hover:text-stamp-red transition-colors"
|
||||
title="Delete item"
|
||||
>
|
||||
<Trash2 size={13} />
|
||||
<Trash2 size={14} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user