Update nodes

This commit is contained in:
Nate Kelley 2025-07-28 22:49:44 -06:00
parent d323df1f0f
commit c5794a2593
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
6 changed files with 147 additions and 200 deletions

View File

@ -236,32 +236,35 @@ export function ToolbarSplitButtonSecondary({
); );
} }
export function ToolbarToggleItem({ export const ToolbarToggleItem = React.forwardRef<
className, React.ElementRef<typeof ToolbarPrimitive.ToggleItem>,
size = 'sm', React.ComponentProps<typeof ToolbarPrimitive.ToggleItem> &
variant, VariantProps<typeof toolbarButtonVariants>
...props >(function ToolbarToggleItem({ className, size = 'sm', variant, ...props }, ref) {
}: React.ComponentProps<typeof ToolbarPrimitive.ToggleItem> &
VariantProps<typeof toolbarButtonVariants>) {
return ( return (
<ToolbarPrimitive.ToggleItem <ToolbarPrimitive.ToggleItem
ref={ref}
className={cn(toolbarButtonVariants({ size, variant }), className)} className={cn(toolbarButtonVariants({ size, variant }), className)}
{...props} {...props}
/> />
); );
} });
export function ToolbarGroup({ children, className }: React.ComponentProps<'div'>) { export const ToolbarGroup = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(
return ( function ToolbarGroup({ children, className }, ref) {
<div className={cn('group/toolbar-group', 'relative hidden has-[button]:flex', className)}> return (
<div className="flex items-center">{children}</div> <div
ref={ref}
className={cn('group/toolbar-group', 'relative hidden has-[button]:flex', className)}>
<div className="flex items-center">{children}</div>
<div className="mx-1.5 py-0.5 group-last/toolbar-group:hidden!"> <div className="mx-1.5 py-0.5 group-last/toolbar-group:hidden!">
<Separator orientation="vertical" /> <Separator orientation="vertical" />
</div>
</div> </div>
</div> );
); }
} );
type TooltipProps<T extends React.ElementType> = { type TooltipProps<T extends React.ElementType> = {
tooltip?: React.ReactNode; tooltip?: React.ReactNode;

View File

@ -19,7 +19,7 @@ import {
import { useSelected } from 'platejs/react'; import { useSelected } from 'platejs/react';
import { Button } from '@/components/ui/buttons'; import { Button } from '@/components/ui/buttons';
import { TooltipBase, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipBase, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { expandListItemsWithChildren } from '@platejs/list'; import { expandListItemsWithChildren } from '@platejs/list';
@ -185,54 +185,51 @@ const DragHandle = React.memo(function DragHandle({
const editor = useEditorRef(); const editor = useEditorRef();
const element = useElement(); const element = useElement();
return ( return (
<TooltipBase> <Tooltip title="Drag to move">
<TooltipTrigger asChild> <div
<div className="flex size-full items-center justify-center"
className="flex size-full items-center justify-center" onClick={() => {
onClick={() => { editor.getApi(BlockSelectionPlugin).blockSelection.set(element.id as string);
editor.getApi(BlockSelectionPlugin).blockSelection.set(element.id as string); }}
}} onMouseDown={(e) => {
onMouseDown={(e) => { resetPreview();
resetPreview(); if (e.button !== 0 || e.shiftKey) return; // Only left mouse button
if (e.button !== 0 || e.shiftKey) return; // Only left mouse button const elements = createDragPreviewElements(editor, {
const elements = createDragPreviewElements(editor, { currentBlock: element
currentBlock: element });
previewRef.current?.append(...elements);
previewRef.current?.classList.remove('hidden');
editor.setOption(DndPlugin, 'multiplePreviewRef', previewRef);
}}
onMouseEnter={() => {
if (isDragging) return;
const blockSelection = editor
.getApi(BlockSelectionPlugin)
.blockSelection.getNodes({ sort: true });
const selectedBlocks =
blockSelection.length > 0 ? blockSelection : editor.api.blocks({ mode: 'highest' });
// Process selection to include list children
const processedBlocks = expandListItemsWithChildren(editor, selectedBlocks);
const ids = processedBlocks.map((block) => block[0].id as string);
if (ids.length > 1 && ids.includes(element.id as string)) {
const previewTop = calculatePreviewTop(editor, {
blocks: processedBlocks.map((block) => block[0]),
element
}); });
previewRef.current?.append(...elements); setPreviewTop(previewTop);
previewRef.current?.classList.remove('hidden'); } else {
editor.setOption(DndPlugin, 'multiplePreviewRef', previewRef); setPreviewTop(0);
}} }
onMouseEnter={() => { }}
if (isDragging) return; onMouseUp={() => {
const blockSelection = editor resetPreview();
.getApi(BlockSelectionPlugin) }}
.blockSelection.getNodes({ sort: true }); role="button">
const selectedBlocks = <div className="text-muted-foreground">
blockSelection.length > 0 ? blockSelection : editor.api.blocks({ mode: 'highest' }); <GripDotsVertical />
// Process selection to include list children
const processedBlocks = expandListItemsWithChildren(editor, selectedBlocks);
const ids = processedBlocks.map((block) => block[0].id as string);
if (ids.length > 1 && ids.includes(element.id as string)) {
const previewTop = calculatePreviewTop(editor, {
blocks: processedBlocks.map((block) => block[0]),
element
});
setPreviewTop(previewTop);
} else {
setPreviewTop(0);
}
}}
onMouseUp={() => {
resetPreview();
}}
role="button">
<div className="text-muted-foreground">
<GripDotsVertical />
</div>
</div> </div>
</TooltipTrigger> </div>
<TooltipContent>Drag to move</TooltipContent> </Tooltip>
</TooltipBase>
); );
}); });
const DropLine = React.memo(function DropLine({ const DropLine = React.memo(function DropLine({

View File

@ -27,6 +27,7 @@ import { Button } from '@/components/ui/buttons';
import { PopoverBase, PopoverContent, PopoverAnchor } from '@/components/ui/popover'; import { PopoverBase, PopoverContent, PopoverAnchor } from '@/components/ui/popover';
import { Separator } from '@/components/ui/separator'; import { Separator } from '@/components/ui/separator';
import { import {
Tooltip,
TooltipBase, TooltipBase,
TooltipContent, TooltipContent,
TooltipProvider, TooltipProvider,
@ -86,22 +87,13 @@ export const ColumnElement = withHOC(
const ColumnDragHandle = React.memo(function ColumnDragHandle() { const ColumnDragHandle = React.memo(function ColumnDragHandle() {
return ( return (
<TooltipProvider> <TooltipProvider>
<TooltipBase> <Tooltip title="Drag to move column">
<TooltipTrigger asChild> <Button
<Button variant="ghost" className="h-5 !px-1"> variant="ghost"
<div className="h-5 !px-1"
className="text-icon-color" onClick={(e) => e.stopPropagation()}
onClick={(event) => { prefix={<GripDots />}></Button>
event.stopPropagation(); </Tooltip>
event.preventDefault();
}}>
<GripDots />
</div>
</Button>
</TooltipTrigger>
<TooltipContent>Drag to move column</TooltipContent>
</TooltipBase>
</TooltipProvider> </TooltipProvider>
); );
}); });

View File

@ -33,6 +33,7 @@ import {
import { Button } from '@/components/ui/buttons'; import { Button } from '@/components/ui/buttons';
import { import {
Tooltip,
TooltipBase, TooltipBase,
TooltipContent, TooltipContent,
TooltipProvider, TooltipProvider,
@ -458,26 +459,23 @@ function EmojiPickerNavigation({
.getGrid() .getGrid()
.sections() .sections()
.map(({ id }) => ( .map(({ id }) => (
<TooltipBase key={id}> <Tooltip key={id} title={i18n.categories[id]}>
<TooltipTrigger asChild> <button
<button className={cn(
className={cn( 'text-muted-foreground hover:bg-muted hover:text-muted-foreground flex min-h-5 items-center justify-center rounded-full fill-current p-1.5',
'text-muted-foreground hover:bg-muted hover:text-muted-foreground flex min-h-5 items-center justify-center rounded-full fill-current p-1.5', id === focusedCategory &&
id === focusedCategory && 'bg-accent text-accent-foreground pointer-events-none fill-current'
'bg-accent text-accent-foreground pointer-events-none fill-current' )}
)} onClick={() => {
onClick={() => { onClick(id);
onClick(id); }}
}} aria-label={i18n.categories[id]}
aria-label={i18n.categories[id]} type="button">
type="button"> <div className="inline-flex size-5 items-center justify-center text-lg">
<div className="inline-flex size-5 items-center justify-center text-lg"> {icons.categories[id].outline}
{icons.categories[id].outline} </div>
</div> </button>
</button> </Tooltip>
</TooltipTrigger>
<TooltipContent side="bottom">{i18n.categories[id]}</TooltipContent>
</TooltipBase>
))} ))}
</div> </div>
</nav> </nav>

View File

@ -17,6 +17,7 @@ import {
DropdownMenuTrigger DropdownMenuTrigger
} from '@/components/ui/dropdown-menu'; } from '@/components/ui/dropdown-menu';
import { import {
Tooltip,
TooltipBase, TooltipBase,
TooltipContent, TooltipContent,
TooltipProvider, TooltipProvider,
@ -333,14 +334,7 @@ function ColorDropdownMenuItem({
/> />
); );
return name ? ( return name ? <Tooltip title={name}>{content}</Tooltip> : content;
<TooltipBase>
<TooltipTrigger>{content}</TooltipTrigger>
<TooltipContent className="mb-1 capitalize">{name}</TooltipContent>
</TooltipBase>
) : (
content
);
} }
export function ColorDropdownMenuItems({ export function ColorDropdownMenuItems({

View File

@ -13,7 +13,7 @@ import {
DropdownMenuSeparator DropdownMenuSeparator
} from '@/components/ui/dropdown-menu'; } from '@/components/ui/dropdown-menu';
import { Separator } from '@/components/ui/separator'; import { Separator } from '@/components/ui/separator';
import { TooltipBase, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip } from '@/components/ui/tooltip';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
export const Toolbar = React.forwardRef< export const Toolbar = React.forwardRef<
@ -31,17 +31,18 @@ export const Toolbar = React.forwardRef<
Toolbar.displayName = 'Toolbar'; Toolbar.displayName = 'Toolbar';
export function ToolbarToggleGroup({ export const ToolbarToggleGroup = React.forwardRef<
className, React.ElementRef<typeof ToolbarPrimitive.ToolbarToggleGroup>,
...props React.ComponentProps<typeof ToolbarPrimitive.ToolbarToggleGroup>
}: React.ComponentProps<typeof ToolbarPrimitive.ToolbarToggleGroup>) { >(function ToolbarToggleGroup({ className, ...props }, ref) {
return ( return (
<ToolbarPrimitive.ToolbarToggleGroup <ToolbarPrimitive.ToolbarToggleGroup
ref={ref}
className={cn('flex items-center', className)} className={cn('flex items-center', className)}
{...props} {...props}
/> />
); );
} });
export function ToolbarLink({ export function ToolbarLink({
className, className,
@ -121,55 +122,54 @@ type ToolbarButtonProps = {
} & Omit<React.ComponentPropsWithoutRef<typeof ToolbarToggleItem>, 'asChild' | 'value'> & } & Omit<React.ComponentPropsWithoutRef<typeof ToolbarToggleItem>, 'asChild' | 'value'> &
VariantProps<typeof toolbarButtonVariants>; VariantProps<typeof toolbarButtonVariants>;
export const ToolbarButton = withTooltip(function ToolbarButton({ export const ToolbarButton = withTooltip(
children, React.forwardRef<HTMLButtonElement, ToolbarButtonProps>(function ToolbarButton(
className, { children, className, isDropdown, pressed, size = 'sm', variant, ...props },
isDropdown, ref
pressed, ) {
size = 'sm', return typeof pressed === 'boolean' ? (
variant, <ToolbarToggleGroup disabled={props.disabled} value="single" type="single">
...props <ToolbarToggleItem
}: ToolbarButtonProps) { ref={ref}
return typeof pressed === 'boolean' ? ( className={cn(
<ToolbarToggleGroup disabled={props.disabled} value="single" type="single"> toolbarButtonVariants({
<ToolbarToggleItem size,
variant
}),
isDropdown && 'justify-between gap-1 pr-1',
className
)}
value={pressed ? 'single' : ''}
{...props}>
{isDropdown ? (
<>
<div className="flex flex-1 items-center gap-2 whitespace-nowrap">{children}</div>
<div className="text-muted-foreground size-3.5" data-icon>
<ChevronDown />
</div>
</>
) : (
children
)}
</ToolbarToggleItem>
</ToolbarToggleGroup>
) : (
<ToolbarPrimitive.Button
ref={ref}
className={cn( className={cn(
toolbarButtonVariants({ toolbarButtonVariants({
size, size,
variant variant
}), }),
isDropdown && 'justify-between gap-1 pr-1', isDropdown && 'pr-1',
className className
)} )}
value={pressed ? 'single' : ''}
{...props}> {...props}>
{isDropdown ? ( {children}
<> </ToolbarPrimitive.Button>
<div className="flex flex-1 items-center gap-2 whitespace-nowrap">{children}</div> );
<div className="text-muted-foreground size-3.5" data-icon> })
<ChevronDown /> );
</div>
</>
) : (
children
)}
</ToolbarToggleItem>
</ToolbarToggleGroup>
) : (
<ToolbarPrimitive.Button
className={cn(
toolbarButtonVariants({
size,
variant
}),
isDropdown && 'pr-1',
className
)}
{...props}>
{children}
</ToolbarPrimitive.Button>
);
});
export function ToolbarSplitButton({ export function ToolbarSplitButton({
className, className,
@ -239,20 +239,19 @@ export function ToolbarSplitButtonSecondary({
); );
} }
export function ToolbarToggleItem({ export const ToolbarToggleItem = React.forwardRef<
className, React.ElementRef<typeof ToolbarPrimitive.ToggleItem>,
size = 'sm', React.ComponentProps<typeof ToolbarPrimitive.ToggleItem> &
variant, VariantProps<typeof toolbarButtonVariants>
...props >(function ToolbarToggleItem({ className, size = 'sm', variant, ...props }, ref) {
}: React.ComponentProps<typeof ToolbarPrimitive.ToggleItem> &
VariantProps<typeof toolbarButtonVariants>) {
return ( return (
<ToolbarPrimitive.ToggleItem <ToolbarPrimitive.ToggleItem
ref={ref}
className={cn(toolbarButtonVariants({ size, variant }), className)} className={cn(toolbarButtonVariants({ size, variant }), className)}
{...props} {...props}
/> />
); );
} });
export function ToolbarGroup({ children, className }: React.ComponentProps<'div'>) { export function ToolbarGroup({ children, className }: React.ComponentProps<'div'>) {
return ( return (
@ -268,9 +267,6 @@ export function ToolbarGroup({ children, className }: React.ComponentProps<'div'
type TooltipProps<T extends React.ElementType> = { type TooltipProps<T extends React.ElementType> = {
tooltip?: React.ReactNode; tooltip?: React.ReactNode;
tooltipContentProps?: Omit<React.ComponentPropsWithoutRef<typeof TooltipContent>, 'children'>;
tooltipProps?: Omit<React.ComponentPropsWithoutRef<typeof TooltipBase>, 'children'>;
tooltipTriggerProps?: React.ComponentPropsWithoutRef<typeof TooltipTrigger>;
} & React.ComponentProps<T>; } & React.ComponentProps<T>;
function withTooltip<T extends React.ElementType>(Component: T) { function withTooltip<T extends React.ElementType>(Component: T) {
@ -290,15 +286,7 @@ function withTooltip<T extends React.ElementType>(Component: T) {
const component = React.createElement(Component, componentProps); const component = React.createElement(Component, componentProps);
if (tooltip && mounted) { if (tooltip && mounted) {
return ( return <Tooltip title={tooltip}>{component}</Tooltip>;
<TooltipBase {...tooltipProps}>
<TooltipTrigger asChild {...tooltipTriggerProps}>
{component}
</TooltipTrigger>
<TooltipContent {...tooltipContentProps}>{tooltip}</TooltipContent>
</TooltipBase>
);
} }
return component; return component;
@ -310,31 +298,6 @@ function withTooltip<T extends React.ElementType>(Component: T) {
return ExtendComponent; return ExtendComponent;
} }
function TooltipContent({
children,
className,
// CHANGE
sideOffset = 4,
...props
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
return (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
className={cn(
'bg-primary text-primary-foreground z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance',
className
)}
data-slot="tooltip-content"
sideOffset={sideOffset}
{...props}>
{children}
{/* CHANGE */}
{/* <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-primary fill-primary" /> */}
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal>
);
}
export function ToolbarMenuGroup({ export function ToolbarMenuGroup({
children, children,
className, className,