11import { memo , useCallback , useState } from "react" ;
2+ import type { RevealType } from "@/lib/graph/types" ;
23import { isIterableType , type NodeType } from "@/lib/parser/node" ;
34import { cn } from "@/lib/utils" ;
45import { useStatusStore } from "@/stores/statusStore" ;
56import { useTree } from "@/stores/treeStore" ;
67import { useTranslations } from "next-intl" ;
8+ import EditableText from "./EditableText" ;
79import { SourceHandle } from "./Handle" ;
8- import Popover from "./Popover" ;
910import useClickNode from "./useClickNode" ;
1011
1112interface KvProps {
@@ -26,20 +27,22 @@ interface KvProps {
2627
2728const KV = memo ( ( props : KvProps ) => {
2829 const isIterable = isIterableType ( props . nodeType ) ;
29- const keyClassNamesWithoutHighlight = props . keyClassNames . slice ( 0 , 1 ) ;
30- const valueClassNamesWithoutHighlight = props . valueClassNames . slice ( 0 , 1 ) ;
3130
32- const [ isInput , setIsInput ] = useState ( false ) ;
33- const [ content , setContent ] = useState ( props . valueText ) ;
31+ const [ inputMode , setInputMode ] = useState < RevealType | "" > ( "" ) ;
3432 const tree = useTree ( ) ;
3533 const { onClick, cancelClickNode } = useClickNode ( ) ;
3634 const t = useTranslations ( ) ;
3735
3836 const addToEditQueue = useStatusStore ( ( state ) => state . addToEditQueue ) ;
39- const callEdit = useCallback ( ( ) => {
40- setIsInput ( false ) ;
41- addToEditQueue ( { treeNodeId : props . id , value : content , version : tree . version } ) ;
42- } , [ props . id , content , tree . version ] ) ;
37+ const onEdit = useCallback (
38+ ( value : string ) => {
39+ if ( inputMode ) {
40+ addToEditQueue ( { treeNodeId : props . id , type : inputMode , value, version : tree . version } ) ;
41+ setInputMode ( "" ) ;
42+ }
43+ } ,
44+ [ props . id , inputMode , tree . version , addToEditQueue ] ,
45+ ) ;
4346
4447 return (
4548 < div
@@ -51,9 +54,9 @@ const KV = memo((props: KvProps) => {
5154 title = { isIterable ? t ( "double_click_to_reveal_first_child" ) : "" }
5255 style = { { width : props . width } }
5356 data-tree-id = { props . id }
54- onClick = { isInput ? undefined : ( e ) => onClick ( e , props . id , "key " , "graphClick" ) }
57+ onClick = { inputMode ? undefined : ( e ) => onClick ( e , props . id , "keyValue " , "graphClick" ) }
5558 onDoubleClick = { ( e ) => {
56- if ( ! ( isIterable && ! isInput ) ) {
59+ if ( ! ( isIterable && ! inputMode ) ) {
5760 return ;
5861 }
5962
@@ -68,50 +71,33 @@ const KV = memo((props: KvProps) => {
6871 }
6972 } }
7073 >
71- < Popover width = { props . width } hlClassNames = { keyClassNamesWithoutHighlight } text = { props . keyText } >
72- < div className = { cn ( "graph-k hover:bg-yellow-100" , ...props . keyClassNames ) } > { props . keyText } </ div >
73- </ Popover >
74- < Popover width = { props . width } hlClassNames = { valueClassNamesWithoutHighlight } text = { content } >
75- {
76- // If in input mode, render an input field for editing the content
77- isInput ? (
78- < input
79- className = { cn ( "graph-v" , ...valueClassNamesWithoutHighlight ) }
80- style = { { width : props . valueWidth } }
81- value = { content }
82- // Stop the click event from propagating to prevent unwanted parent element clicks
83- onClick = { ( e ) => e . stopPropagation ( ) }
84- onChange = { ( e ) => setContent ( e . target . value ) }
85- onFocus = { ( e ) => ( e . target as HTMLInputElement ) . select ( ) }
86- autoFocus
87- onBlur = { callEdit }
88- onKeyDown = { ( e ) => {
89- if ( e . key === "Enter" ) {
90- callEdit ( ) ;
91- }
92- } }
93- />
94- ) : (
95- // If not in input mode, render a div displaying the content
96- < div
97- className = { cn ( "graph-v hover:bg-yellow-100" , ...props . valueClassNames ) }
98- title = { isIterable ? t ( "double_click_to_reveal_first_child" ) : t ( "double_click_to_enter_edit_mode" ) }
99- onClick = { ( e ) => onClick ( e , props . id , "value" , "graphClick" ) }
100- // Double-click to enter input mode
101- onDoubleClick = { ( ) => {
102- if ( isIterable ) {
103- return ;
104- }
105-
106- cancelClickNode ( ) ;
107- setIsInput ( true ) ;
108- } }
109- >
110- { content }
111- </ div >
112- )
113- }
114- </ Popover >
74+ < EditableText
75+ classNames = { [ "graph-k" , ...props . keyClassNames ] }
76+ text = { props . keyText }
77+ onDoubleClick = { ( ) => {
78+ cancelClickNode ( ) ;
79+ setInputMode ( "key" ) ;
80+ } }
81+ onClick = { ( e ) => onClick ( e , props . id , "key" , "graphClick" ) }
82+ onEdit = { ( value ) => onEdit ( value ) }
83+ title = { t ( "double_click_to_enter_edit_mode" ) }
84+ popoverWidth = { props . width }
85+ widthInInput = { props . keyWidth }
86+ />
87+ < EditableText
88+ classNames = { [ "graph-v" , ...props . valueClassNames ] }
89+ text = { props . valueText }
90+ isIterable = { isIterable }
91+ onDoubleClick = { ( ) => {
92+ cancelClickNode ( ) ;
93+ setInputMode ( "value" ) ;
94+ } }
95+ onClick = { ( e ) => onClick ( e , props . id , "value" , "graphClick" ) }
96+ onEdit = { ( value ) => onEdit ( value ) }
97+ title = { isIterable ? t ( "double_click_to_reveal_first_child" ) : t ( "double_click_to_enter_edit_mode" ) }
98+ widthInInput = { props . valueWidth }
99+ popoverWidth = { props . width }
100+ />
115101 { props . hasChildren && (
116102 < SourceHandle id = { props . keyText } indexInParent = { props . index } isChildrenHidden = { props . isChildrenHidden } />
117103 ) }
0 commit comments