Files
lemonspace_app/components/canvas/nodes/frame-node.tsx
Matthias ca40f5cb13 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.
2026-03-25 17:58:58 +01:00

73 lines
2.1 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
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";
type FrameNodeData = {
label?: string;
resolution?: string;
_status?: string;
_statusMessage?: string;
};
export type FrameNode = Node<FrameNodeData, "frame">;
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-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>
);
}