feat: enhance canvas and layout components with new features and improvements
- Added remote image patterns to the Next.js configuration for enhanced image handling. - Updated TypeScript configuration to exclude the 'implement' directory. - Refactored layout component to fetch initial authentication token and pass it to Providers. - Replaced CanvasToolbar with CanvasSidebar for improved UI layout and functionality. - Enhanced Canvas component with new drag-and-drop file upload capabilities and batch node movement. - Updated various node components to support new status handling and improved user interactions. - Added debounced saving for note and prompt nodes to optimize performance.
This commit is contained in:
@@ -1,28 +1,72 @@
|
||||
"use client";
|
||||
|
||||
import { type Node, type NodeProps } from "@xyflow/react";
|
||||
|
||||
import { useState, useCallback } from "react";
|
||||
import { type NodeProps, type Node } from "@xyflow/react";
|
||||
import { useMutation } from "convex/react";
|
||||
import { api } from "@/convex/_generated/api";
|
||||
import type { Id } from "@/convex/_generated/dataModel";
|
||||
import BaseNodeWrapper from "./base-node-wrapper";
|
||||
|
||||
export type FrameNodeData = {
|
||||
type FrameNodeData = {
|
||||
label?: string;
|
||||
exportWidth?: number;
|
||||
exportHeight?: number;
|
||||
resolution?: string;
|
||||
_status?: string;
|
||||
_statusMessage?: string;
|
||||
};
|
||||
|
||||
export type FrameNode = Node<FrameNodeData, "frame">;
|
||||
|
||||
export default function FrameNode({ data, selected }: NodeProps<FrameNode>) {
|
||||
const resolution =
|
||||
data.exportWidth && data.exportHeight
|
||||
? `${data.exportWidth}x${data.exportHeight}`
|
||||
: undefined;
|
||||
export default function FrameNode({ id, data, selected }: NodeProps<FrameNode>) {
|
||||
const updateData = useMutation(api.nodes.updateData);
|
||||
const [editingLabel, setEditingLabel] = useState<string | null>(null);
|
||||
|
||||
const displayLabel = data.label ?? "Frame";
|
||||
const isEditing = editingLabel !== null;
|
||||
|
||||
const handleDoubleClick = useCallback(() => {
|
||||
setEditingLabel(displayLabel);
|
||||
}, [displayLabel]);
|
||||
|
||||
const handleBlur = useCallback(() => {
|
||||
if (editingLabel !== null && editingLabel !== data.label) {
|
||||
updateData({
|
||||
nodeId: id as Id<"nodes">,
|
||||
data: {
|
||||
...data,
|
||||
label: editingLabel,
|
||||
_status: undefined,
|
||||
_statusMessage: undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
setEditingLabel(null);
|
||||
}, [editingLabel, data, id, updateData]);
|
||||
|
||||
return (
|
||||
<BaseNodeWrapper selected={selected} className="min-h-[200px] min-w-[300px] border-blue-500/30 p-3">
|
||||
<div className="text-xs font-medium text-blue-500">
|
||||
{data.label || "Frame"} {resolution ? `(${resolution})` : ""}
|
||||
</div>
|
||||
<BaseNodeWrapper
|
||||
selected={selected}
|
||||
className="min-w-[300px] min-h-[200px] p-3 border-blue-500/30"
|
||||
>
|
||||
{isEditing ? (
|
||||
<input
|
||||
value={editingLabel}
|
||||
onChange={(e) => setEditingLabel(e.target.value)}
|
||||
onBlur={handleBlur}
|
||||
onKeyDown={(e) => e.key === "Enter" && handleBlur()}
|
||||
autoFocus
|
||||
className="nodrag text-xs font-medium text-blue-500 bg-transparent border-0 outline-none w-full"
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
onDoubleClick={handleDoubleClick}
|
||||
className="text-xs font-medium text-blue-500 cursor-text"
|
||||
>
|
||||
🖥️ {displayLabel}{" "}
|
||||
{data.resolution && (
|
||||
<span className="text-muted-foreground">({data.resolution})</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</BaseNodeWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user