Improve asset mention UX with empty-state dropdown and icon-only chips

This commit is contained in:
Yusuf İpek
2026-02-25 19:09:37 +03:00
parent 76d37d02e5
commit 7f87d717d2
2 changed files with 43 additions and 37 deletions
+2 -3
View File
@@ -66,12 +66,11 @@ export function CommentRichText({ text, onAssetMentionClick, assets = [] }: Comm
<span <span
className={ className={
isVideoAsset isVideoAsset
? 'inline-flex h-4 shrink-0 items-center gap-0.5 rounded bg-violet-500/25 px-1 text-[9px] font-semibold tracking-wide text-violet-200' ? 'inline-flex h-4 w-4 shrink-0 items-center justify-center rounded bg-violet-500/25 text-violet-200'
: 'inline-flex h-4 shrink-0 items-center gap-0.5 rounded bg-emerald-500/25 px-1 text-[9px] font-semibold tracking-wide text-emerald-200' : 'inline-flex h-4 w-4 shrink-0 items-center justify-center rounded bg-emerald-500/25 text-emerald-200'
} }
> >
{isVideoAsset ? <Video className="h-2.5 w-2.5" /> : <ImageIcon className="h-2.5 w-2.5" />} {isVideoAsset ? <Video className="h-2.5 w-2.5" /> : <ImageIcon className="h-2.5 w-2.5" />}
{isVideoAsset ? 'VID' : 'SS'}
</span> </span>
<span className="truncate max-w-[190px] sm:max-w-[240px]"> <span className="truncate max-w-[190px] sm:max-w-[240px]">
@{label} @{label}
+41 -34
View File
@@ -109,23 +109,26 @@ export function MentionTextarea({
}; };
const handleKeyDown: React.KeyboardEventHandler<HTMLTextAreaElement> = (event) => { const handleKeyDown: React.KeyboardEventHandler<HTMLTextAreaElement> = (event) => {
if (mentionRange && filteredAssets.length > 0) { if (mentionRange) {
if (event.key === 'ArrowDown') { if (filteredAssets.length > 0) {
event.preventDefault(); if (event.key === 'ArrowDown') {
setActiveIndex((prev) => (prev + 1) % filteredAssets.length); event.preventDefault();
return; setActiveIndex((prev) => (prev + 1) % filteredAssets.length);
} return;
if (event.key === 'ArrowUp') { }
event.preventDefault(); if (event.key === 'ArrowUp') {
setActiveIndex((prev) => (prev - 1 + filteredAssets.length) % filteredAssets.length); event.preventDefault();
return; setActiveIndex((prev) => (prev - 1 + filteredAssets.length) % filteredAssets.length);
} return;
if (event.key === 'Enter' && !event.shiftKey) { }
event.preventDefault(); if (event.key === 'Enter' && !event.shiftKey) {
const selected = filteredAssets[Math.min(activeIndex, filteredAssets.length - 1)]; event.preventDefault();
if (selected) insertAssetMention(selected); const selected = filteredAssets[Math.min(activeIndex, filteredAssets.length - 1)];
return; if (selected) insertAssetMention(selected);
return;
}
} }
if (event.key === 'Escape') { if (event.key === 'Escape') {
event.preventDefault(); event.preventDefault();
closeMentions(); closeMentions();
@@ -154,25 +157,29 @@ export function MentionTextarea({
}} }}
/> />
{mentionRange && filteredAssets.length > 0 && ( {mentionRange && (
<div className="absolute left-0 right-0 bottom-full mb-1 z-30 rounded-md border bg-popover shadow-md overflow-hidden"> <div className="absolute left-0 right-0 bottom-full mb-1 z-30 rounded-md border bg-popover shadow-md overflow-hidden">
{filteredAssets.map((asset, index) => ( {filteredAssets.length === 0 ? (
<button <div className="px-2 py-1.5 text-xs text-muted-foreground">No asset found</div>
key={asset.id} ) : (
type="button" filteredAssets.map((asset, index) => (
className={cn( <button
'w-full text-left px-2 py-1.5 text-xs hover:bg-accent transition-colors', key={asset.id}
index === activeIndex && 'bg-accent' type="button"
)} className={cn(
onMouseDown={(event) => { 'w-full text-left px-2 py-1.5 text-xs hover:bg-accent transition-colors',
event.preventDefault(); index === activeIndex && 'bg-accent'
insertAssetMention(asset); )}
}} onMouseDown={(event) => {
> event.preventDefault();
<span className="font-medium">@{asset.displayName}</span> insertAssetMention(asset);
<span className="ml-2 text-muted-foreground">{asset.provider}</span> }}
</button> >
))} <span className="font-medium">@{asset.displayName}</span>
<span className="ml-2 text-muted-foreground">{asset.provider}</span>
</button>
))
)}
</div> </div>
)} )}
</div> </div>