text highlight and og image
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
"use client";
|
||||
|
||||
import { createPortal } from "react-dom";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { type Position, type ToolbarAction } from "./shared";
|
||||
|
||||
export function FloatingToolbar({
|
||||
position,
|
||||
actions,
|
||||
isMobile,
|
||||
}: {
|
||||
position: Position;
|
||||
actions: ToolbarAction[];
|
||||
isMobile: boolean;
|
||||
}) {
|
||||
if (isMobile) {
|
||||
// Mobile: fixed bottom bar
|
||||
return createPortal(
|
||||
<div
|
||||
className="fixed z-80 bottom-0 inset-x-0 border-t border-white/10 bg-black shadow-2xl px-3 py-2.5 pb-[max(0.75rem,env(safe-area-inset-bottom))] animate-in fade-in-0 slide-in-from-bottom-4 dark:bg-[#1a1a1a] dark:border-white/15"
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
>
|
||||
<div className="flex items-center justify-around gap-1">
|
||||
{actions.map((action) => (
|
||||
<button
|
||||
key={action.key}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
action.onClick();
|
||||
}}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
className={cn(
|
||||
"flex flex-col items-center gap-1.5 px-4 py-2.5 rounded-lg text-sm font-medium transition-colors min-w-16 [&_svg]:size-5",
|
||||
action.className ??
|
||||
"text-white/70 active:bg-white/10 active:text-white",
|
||||
)}
|
||||
>
|
||||
{action.icon}
|
||||
<span>{action.label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
);
|
||||
}
|
||||
|
||||
// Desktop: floating above the selection
|
||||
const toolbarWidth = actions.length * 44 + 8;
|
||||
const left = Math.max(
|
||||
8,
|
||||
Math.min(
|
||||
position.x - toolbarWidth / 2,
|
||||
window.innerWidth - toolbarWidth - 8,
|
||||
),
|
||||
);
|
||||
const top = Math.max(8, position.y - 48);
|
||||
|
||||
return createPortal(
|
||||
<div
|
||||
className="fixed z-80 flex items-center gap-1 rounded-xl border border-white/10 bg-black shadow-2xl px-2 py-1.5 animate-in fade-in-0 zoom-in-95 slide-in-from-bottom-2 dark:bg-[#1a1a1a] dark:border-white/15"
|
||||
style={{ left, top }}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
>
|
||||
{actions.map((action) => (
|
||||
<button
|
||||
key={action.key}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
action.onClick();
|
||||
}}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 px-3 py-2 rounded-lg text-sm font-medium transition-colors",
|
||||
action.className ??
|
||||
"text-white/70 hover:bg-white/10 hover:text-white",
|
||||
)}
|
||||
title={action.label}
|
||||
>
|
||||
{action.icon}
|
||||
<span>{action.label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>,
|
||||
document.body,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user