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
className={
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 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-violet-500/25 text-violet-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 ? 'VID' : 'SS'}
</span>
<span className="truncate max-w-[190px] sm:max-w-[240px]">
@{label}
+41 -34
View File
@@ -109,23 +109,26 @@ export function MentionTextarea({
};
const handleKeyDown: React.KeyboardEventHandler<HTMLTextAreaElement> = (event) => {
if (mentionRange && filteredAssets.length > 0) {
if (event.key === 'ArrowDown') {
event.preventDefault();
setActiveIndex((prev) => (prev + 1) % filteredAssets.length);
return;
}
if (event.key === 'ArrowUp') {
event.preventDefault();
setActiveIndex((prev) => (prev - 1 + filteredAssets.length) % filteredAssets.length);
return;
}
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
const selected = filteredAssets[Math.min(activeIndex, filteredAssets.length - 1)];
if (selected) insertAssetMention(selected);
return;
if (mentionRange) {
if (filteredAssets.length > 0) {
if (event.key === 'ArrowDown') {
event.preventDefault();
setActiveIndex((prev) => (prev + 1) % filteredAssets.length);
return;
}
if (event.key === 'ArrowUp') {
event.preventDefault();
setActiveIndex((prev) => (prev - 1 + filteredAssets.length) % filteredAssets.length);
return;
}
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
const selected = filteredAssets[Math.min(activeIndex, filteredAssets.length - 1)];
if (selected) insertAssetMention(selected);
return;
}
}
if (event.key === 'Escape') {
event.preventDefault();
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">
{filteredAssets.map((asset, index) => (
<button
key={asset.id}
type="button"
className={cn(
'w-full text-left px-2 py-1.5 text-xs hover:bg-accent transition-colors',
index === activeIndex && 'bg-accent'
)}
onMouseDown={(event) => {
event.preventDefault();
insertAssetMention(asset);
}}
>
<span className="font-medium">@{asset.displayName}</span>
<span className="ml-2 text-muted-foreground">{asset.provider}</span>
</button>
))}
{filteredAssets.length === 0 ? (
<div className="px-2 py-1.5 text-xs text-muted-foreground">No asset found</div>
) : (
filteredAssets.map((asset, index) => (
<button
key={asset.id}
type="button"
className={cn(
'w-full text-left px-2 py-1.5 text-xs hover:bg-accent transition-colors',
index === activeIndex && 'bg-accent'
)}
onMouseDown={(event) => {
event.preventDefault();
insertAssetMention(asset);
}}
>
<span className="font-medium">@{asset.displayName}</span>
<span className="ml-2 text-muted-foreground">{asset.provider}</span>
</button>
))
)}
</div>
)}
</div>