mirror of
https://github.com/linsa-io/linsa.git
synced 2026-04-23 08:48:37 +02:00
fix(link): improve UX, maintain state (#154)
* fix(topic): handleSelectLearningState missing depth * fix(link): use active index instead of native focus * chore(palette): use atom for maintain state * chore(link): prevent keydown if command palette active * fix: responsive link item * chore: add active item index state to LinkRoute * fix: ability to press enter go to edit mode
This commit is contained in:
@@ -90,82 +90,76 @@ export const LinkItem: React.FC<LinkItemProps> = ({
|
||||
onFocus={() => setActiveItemIndex(index)}
|
||||
onBlur={() => setActiveItemIndex(null)}
|
||||
onKeyDown={handleKeyDown}
|
||||
className={cn("relative flex h-14 cursor-default items-center outline-none xl:h-11", {
|
||||
"bg-muted-foreground/10": isActive,
|
||||
"hover:bg-muted/50": !isActive
|
||||
})}
|
||||
className={cn(
|
||||
"relative cursor-default outline-none",
|
||||
"grid grid-cols-[auto_1fr_auto] items-center gap-x-2 px-2 py-2 sm:px-4 sm:py-2",
|
||||
{
|
||||
"bg-muted-foreground/10": isActive,
|
||||
"hover:bg-muted/50": !isActive
|
||||
}
|
||||
)}
|
||||
onDoubleClick={handleRowDoubleClick}
|
||||
>
|
||||
<div className="flex grow justify-between gap-x-6 px-6 max-lg:px-4">
|
||||
<div className="flex min-w-0 items-center gap-x-4">
|
||||
<Popover
|
||||
open={openPopoverForId === personalLink.id}
|
||||
onOpenChange={(open: boolean) => setOpenPopoverForId(open ? personalLink.id : null)}
|
||||
>
|
||||
<PopoverTrigger asChild>
|
||||
<Button size="sm" type="button" role="combobox" variant="secondary" className="size-7 shrink-0 p-0">
|
||||
{selectedLearningState?.icon ? (
|
||||
<LaIcon name={selectedLearningState.icon} className={cn(selectedLearningState.className)} />
|
||||
) : (
|
||||
<LaIcon name="Circle" />
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
className="w-52 rounded-lg p-0"
|
||||
side="bottom"
|
||||
align="start"
|
||||
onCloseAutoFocus={e => e.preventDefault()}
|
||||
>
|
||||
<LearningStateSelectorContent
|
||||
showSearch={false}
|
||||
searchPlaceholder="Search state..."
|
||||
value={personalLink.learningState}
|
||||
onSelect={handleLearningStateSelect}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<Popover
|
||||
open={openPopoverForId === personalLink.id}
|
||||
onOpenChange={(open: boolean) => setOpenPopoverForId(open ? personalLink.id : null)}
|
||||
>
|
||||
<PopoverTrigger asChild>
|
||||
<Button size="sm" type="button" role="combobox" variant="secondary" className="size-7 shrink-0 p-0">
|
||||
{selectedLearningState?.icon ? (
|
||||
<LaIcon name={selectedLearningState.icon} className={cn(selectedLearningState.className)} />
|
||||
) : (
|
||||
<LaIcon name="Circle" />
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
className="w-52 rounded-lg p-0"
|
||||
side="bottom"
|
||||
align="start"
|
||||
onCloseAutoFocus={e => e.preventDefault()}
|
||||
>
|
||||
<LearningStateSelectorContent
|
||||
showSearch={false}
|
||||
searchPlaceholder="Search state..."
|
||||
value={personalLink.learningState}
|
||||
onSelect={handleLearningStateSelect}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
{personalLink.icon && (
|
||||
<Image
|
||||
src={personalLink.icon}
|
||||
alt={personalLink.title}
|
||||
className="size-5 rounded-full"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
)}
|
||||
<div className="w-full min-w-0 flex-auto">
|
||||
<div className="gap-x-2 space-y-0.5 xl:flex xl:flex-row">
|
||||
<p className="text-primary hover:text-primary line-clamp-1 text-sm font-medium xl:truncate">
|
||||
{personalLink.title}
|
||||
</p>
|
||||
{personalLink.url && (
|
||||
<div className="group flex items-center gap-x-1">
|
||||
<LaIcon
|
||||
name="Link"
|
||||
aria-hidden="true"
|
||||
className="text-muted-foreground group-hover:text-primary flex-none"
|
||||
/>
|
||||
<Link
|
||||
href={personalLink.url}
|
||||
passHref
|
||||
prefetch={false}
|
||||
target="_blank"
|
||||
onClick={e => e.stopPropagation()}
|
||||
className="text-muted-foreground hover:text-primary text-xs"
|
||||
>
|
||||
<span className="xl:truncate">{personalLink.url}</span>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex min-w-0 flex-col items-start gap-y-1 overflow-hidden md:flex-row md:items-center md:gap-x-2">
|
||||
{personalLink.icon && (
|
||||
<Image
|
||||
src={personalLink.icon}
|
||||
alt={personalLink.title}
|
||||
className="size-5 shrink-0 rounded-full"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
)}
|
||||
<div className="flex min-w-0 flex-col items-start gap-y-1 overflow-hidden md:flex-row md:items-center md:gap-x-2">
|
||||
<p className="text-primary hover:text-primary truncate text-sm font-medium">{personalLink.title}</p>
|
||||
{personalLink.url && (
|
||||
<div className="text-muted-foreground flex min-w-0 shrink items-center gap-x-1">
|
||||
<LaIcon name="Link" aria-hidden="true" className="size-3 flex-none" />
|
||||
<Link
|
||||
href={personalLink.url}
|
||||
passHref
|
||||
prefetch={false}
|
||||
target="_blank"
|
||||
onClick={e => e.stopPropagation()}
|
||||
className="hover:text-primary truncate text-xs"
|
||||
>
|
||||
{personalLink.url}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 items-center gap-x-4">
|
||||
{personalLink.topic && <Badge variant="secondary">{personalLink.topic.prettyName}</Badge>}
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center justify-end">
|
||||
{personalLink.topic && <Badge variant="secondary">{personalLink.topic.prettyName}</Badge>}
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user