@@ -9,11 +9,11 @@ import {
9
9
LeftClose ,
10
10
LeftCommon ,
11
11
LeftOpen ,
12
- PadDiv ,
13
12
ScrollBar ,
14
13
Tooltip ,
15
14
UnfoldIcon ,
16
15
UnShow ,
16
+ TacoButton ,
17
17
} from "lowcoder-design" ;
18
18
import React , { ReactNode , useCallback , useContext , useMemo , useState } from "react" ;
19
19
import { hookCompCategory } from "comps/hooks/hookCompTypes" ;
@@ -32,13 +32,33 @@ import type { UICompType } from "comps/uiCompRegistry";
32
32
import { CollapseWrapper , DirectoryTreeStyle , Node } from "./styledComponents" ;
33
33
import { DataNode , EventDataNode } from "antd/es/tree" ;
34
34
import { isAggregationApp } from "util/appUtils" ;
35
+ import Modal from "antd/es/modal/Modal" ;
36
+ import copyToClipboard from "copy-to-clipboard" ;
35
37
36
38
const CollapseTitleWrapper = styled . div `
37
39
display: flex;
38
40
width: fit-content;
39
41
max-width: calc(100% - 8px);
40
42
` ;
41
43
44
+ const PadDiv = styled . div `
45
+ padding: 8px;
46
+ display: flex;
47
+ justify-content: space-between;
48
+ align-items: center;
49
+
50
+ &:hover .copy-icon {
51
+ visibility: visible;
52
+ }
53
+ ` ;
54
+
55
+ const CopyIcon = styled ( CopyTextButton ) `
56
+ visibility: hidden;
57
+ margin-left: 8px;
58
+ color: #1890ff;
59
+ cursor: pointer;
60
+ ` ;
61
+
42
62
function getLen ( config :string | boolean | number ) {
43
63
if ( typeof config === "number" ) {
44
64
return ( config + "" ) . toString ( ) . length ;
@@ -49,47 +69,48 @@ function getLen(config: string | boolean | number) {
49
69
return 0 ;
50
70
}
51
71
52
- function toDataView ( value :any , name :string , desc ?:ReactNode ) {
72
+ function toDataView ( value :any , name :string , desc ?:ReactNode , modal ?: boolean ) {
53
73
const str = typeof value === "function" ?"Function" :safeJSONStringify ( value ) ;
54
74
const descRecord :Record < string , ReactNode > = { } ;
75
+ const shortenedString = modal === true ?( getLen ( str ) > 42 ?str . slice ( 0 , 42 ) + "..." :str ) :( getLen ( str ) > 20 ?str . slice ( 0 , 20 ) + "..." :str ) ;
55
76
descRecord [ name ] = desc ;
56
77
if ( Array . isArray ( value ) ) {
57
78
const dataChild :Record < string , any > = { } ;
58
79
value . forEach ( ( valueChild , index ) => {
59
80
dataChild [ index ] = valueChild ;
60
81
} ) ;
61
82
return (
62
- < CollapseView name = { name } desc = { descRecord } data = { dataChild } isArray = { true } key = { name } />
83
+ < CollapseView name = { name } desc = { descRecord } data = { dataChild } isArray = { true } key = { name } modal = { modal } />
63
84
) ;
64
85
} else if ( _ . isPlainObject ( value ) ) {
65
- return < CollapseView name = { name } desc = { descRecord } data = { value } key = { name } /> ;
66
- }
67
-
68
- return (
69
- < PadDiv key = { name } >
70
- < Tooltip title = { desc } placement = { "right" } >
71
- < Label label = { name } />
72
- ​
73
- </ Tooltip >
74
-
75
- < Tooltip
76
- title = {
77
- getLen ( str ) > 50 ?(
78
- < div style = { { display :"flex" , wordBreak :"break-all" } } >
79
- { getLen ( str ) > 300 ?str . slice ( 0 , 300 ) + "..." :str }
80
- < CopyTextButton text = { value } style = { { color :"#fff" , margin :"4px 0 0 6px" } } />
81
- </ div >
82
- ) :null
83
- }
84
- placement = { "right" }
86
+ return (
87
+ < CollapseView name = { name } desc = { descRecord } data = { value } key = { name } modal = { modal } />
88
+ ) ;
89
+ } else {
90
+ return (
91
+ < PadDiv
92
+ style = { { marginLeft :"20px" , borderBottom :"1px solid #f0f0f0" , height :"32px" , display :'flex' , justifyContent :'space-between' , alignItems :'center' } }
93
+ key = { name }
85
94
>
86
- ​
87
- < Label color = "#FF9816" label = { getLen ( str ) > 50 ?str . slice ( 0 , 50 ) + "..." :str } />
88
- </ Tooltip >
89
- </ PadDiv >
90
- ) ;
95
+ < Tooltip title = { desc } placement = { "right" } >
96
+ < Label label = { name } /> ​
97
+ </ Tooltip >
98
+
99
+ < div style = { { display :"flex" , wordBreak :"break-all" , textAlign :"right" } } >
100
+ < span style = { { marginRight :"10px" } } > { shortenedString } </ span >
101
+ { getLen ( str ) > 0 &&
102
+ < CopyTextButton text = { value } style = { { color :"#ccc" , marginRight :"0px" , marginTop :"4px" } } />
103
+ }
104
+ </ div >
105
+
106
+ </ PadDiv >
107
+ ) ;
108
+ }
91
109
}
92
110
111
+
112
+ export default toDataView ;
113
+
93
114
function sliceArr ( arr :string [ ] ) {
94
115
let preArr :string [ ] = [ ] ;
95
116
let afterArr :string [ ] = [ ] ;
@@ -104,23 +125,23 @@ function sliceArr(arr: string[]) {
104
125
return { preArr, afterArr} as const ;
105
126
}
106
127
107
- function toData ( props :{ data :Record < string , any > ; desc ?:Record < string , ReactNode > } ) {
128
+ function toData ( props :{ data :Record < string , any > ; desc ?:Record < string , ReactNode > , modal ?: boolean } ) {
108
129
const totalArr = Object . keys ( props . data ) ;
109
130
const sliceFn = sliceArr ;
110
131
return (
111
132
< div >
112
133
{ totalArr . length < 30 ?(
113
134
totalArr . map ( ( name ) => {
114
- return toDataView ( props . data [ name ] , name , props . desc ?. [ name ] ) ;
135
+ return toDataView ( props . data [ name ] , name , props . desc ?. [ name ] , props . modal ) ;
115
136
} )
116
137
) :(
117
138
< >
118
139
{ sliceFn ( totalArr ) . preArr . map ( ( name ) => {
119
- return toDataView ( props . data [ name ] , name , props . desc ?. [ name ] ) ;
140
+ return toDataView ( props . data [ name ] , name , props . desc ?. [ name ] , props . modal ) ;
120
141
} ) }
121
142
< UnShow num = { totalArr . length - 6 } />
122
143
{ sliceFn ( totalArr ) . afterArr . map ( ( name ) => {
123
- return toDataView ( props . data [ name ] , name , props . desc ?. [ name ] ) ;
144
+ return toDataView ( props . data [ name ] , name , props . desc ?. [ name ] , props . modal ) ;
124
145
} ) }
125
146
</ >
126
147
) }
@@ -137,6 +158,8 @@ const CollapseView = React.memo(
137
158
onClick ?:( compName :string ) => void ;
138
159
isSelected ?:boolean ;
139
160
isOpen ?:boolean ;
161
+ children ?:React . ReactNode ; // Accept children
162
+ modal ?:boolean ;
140
163
} ) => {
141
164
const { data= { } } = props ;
142
165
const onlyOne = Object . keys ( data ) . length === 1 ;
@@ -148,47 +171,53 @@ const CollapseView = React.memo(
148
171
{
149
172
key :props . name ,
150
173
title :(
151
- < Tooltip
152
- title = { props . desc ?. [ props . name ] }
153
- placement = { "right" }
154
- >
155
- < CollapseTitleWrapper onClick = { ( ) => props . onClick && props . onClick ( props . name ) } >
156
- < Title
157
- style = { {
158
- whiteSpace :"nowrap" ,
159
- textOverflow :"ellipsis" ,
160
- overflow :"hidden" ,
161
- } }
162
- label = { props . name }
163
- hasChild = { Object . keys ( data ) . length > 0 }
164
- />
165
- < Title
166
- style = { { flexShrink :0 } }
167
- color = "#8B8FA3"
168
- label = { `${ props . isArray ?"[]" :"{}" } ${ trans (
169
- props . isArray
170
- ?onlyOne
171
- ?"leftPanel.propTipArr"
172
- :"leftPanel.propTipsArr"
173
- :onlyOne
174
- ?"leftPanel.propTip"
175
- :"leftPanel.propTips" ,
176
- {
177
- num :Object . keys ( data ) . length ,
178
- }
179
- ) } `}
180
- />
181
- </ CollapseTitleWrapper >
182
- </ Tooltip >
174
+ < div style = { { display :'flex' , justifyContent :'space-between' , alignItems :'center' } } >
175
+ < Tooltip
176
+ title = { props . desc ?. [ props . name ] }
177
+ placement = { "right" }
178
+ >
179
+ < CollapseTitleWrapper onClick = { ( ) => props . onClick && props . onClick ( props . name ) } >
180
+ < Title
181
+ style = { {
182
+ whiteSpace :"nowrap" ,
183
+ textOverflow :"ellipsis" ,
184
+ overflow :"hidden" ,
185
+ } }
186
+ label = { props . name }
187
+ hasChild = { Object . keys ( data ) . length > 0 }
188
+ />
189
+ < Title
190
+ style = { { flexShrink :0 } }
191
+ color = "#8B8FA3"
192
+ label = { `${ props . isArray ?"[]" :"{}" } ${ trans (
193
+ props . isArray
194
+ ?onlyOne
195
+ ?"leftPanel.propTipArr"
196
+ :"leftPanel.propTipsArr"
197
+ :onlyOne
198
+ ?"leftPanel.propTip"
199
+ :"leftPanel.propTips" ,
200
+ {
201
+ num :Object . keys ( data ) . length ,
202
+ }
203
+ ) } `}
204
+ />
205
+ </ CollapseTitleWrapper >
206
+ </ Tooltip >
207
+ { Object . keys ( data ) . length > 0 &&
208
+ < CopyTextButton text = { JSON . stringify ( data ) } style = { { color :"#aaa" , marginRight :"8px" } } />
209
+ }
210
+ </ div >
183
211
) ,
184
- data :toData ( { data, desc :props . desc } ) ,
212
+ data :toData ( { data, desc :props . desc , modal : props . modal } ) ,
185
213
} ,
186
214
] }
187
215
/>
188
216
) ;
189
217
}
190
218
) ;
191
219
220
+
192
221
interface LeftContentProps {
193
222
uiComp :InstanceType < typeof UIComp > ;
194
223
}
@@ -324,13 +353,23 @@ export const LeftContent = (props: LeftContentProps) => {
324
353
const getTreeNode = ( node :NodeItem , uiCompInfos :CompInfo [ ] ) => {
325
354
const info = showData . find ( ( item ) => item . key === node . key ) ;
326
355
const data = uiCompInfos . find ( ( item ) => item . name === node . title ) ;
356
+
357
+ const prepareData = ( data :Record < string , any > , desc ?:Record < string , ReactNode > ) => {
358
+ return (
359
+ < div >
360
+ { Object . keys ( data ) . map ( ( name ) => {
361
+ return toDataView ( data [ name ] , name , desc ?. [ name ] , true ) ;
362
+ } ) }
363
+ </ div >
364
+ ) ;
365
+ } ;
366
+
327
367
return (
328
- < Node >
368
+ < Node key = { node . key } >
329
369
< span >
330
- < span > { node . title } </ span >
331
- { data &&
332
- ! ! Object . keys ( data . data ) ?. length &&
333
- ( info ?. show ?(
370
+ < span > { node . title } </ span >
371
+ { data && ! ! Object . keys ( data . data ) ?. length && (
372
+ info ?. show ?(
334
373
< Tooltip
335
374
placement = "right"
336
375
title = { trans ( "leftPanel.collapseTip" , { component :node . title } ) }
@@ -353,7 +392,7 @@ export const LeftContent = (props: LeftContentProps) => {
353
392
setShowData ( newData ) ;
354
393
} }
355
394
>
356
- < LeftOpen />
395
+
357
396
</ div >
358
397
</ Tooltip >
359
398
) :(
@@ -389,24 +428,29 @@ export const LeftContent = (props: LeftContentProps) => {
389
428
< LeftClose />
390
429
</ div >
391
430
</ Tooltip >
392
- ) ) }
431
+ )
432
+ ) }
393
433
</ span >
394
434
{ info ?. show && data && (
395
- < CollapseWrapper title = "" $clientX = { info ?. clientX } onClick = { ( e ) => e . stopPropagation ( ) } >
396
- < ScrollBar style = { { maxHeight :"400px" } } >
397
- < CollapseView
398
- key = { data . name }
399
- name = { data . name }
400
- desc = { data . dataDesc }
401
- data = { data . data }
402
- isOpen = { true }
403
- />
404
- </ ScrollBar >
405
- </ CollapseWrapper >
435
+ < Modal
436
+ title = { data . name }
437
+ open = { info . show }
438
+ onOk = { ( ) => setShowData ( [ ] ) }
439
+ cancelButtonProps = { { style :{ display :'none' } } }
440
+ maskClosable = { true } // Prevent closing on background click
441
+ >
442
+ < div
443
+ style = { { whiteSpace :'pre-wrap' , wordWrap :'break-word' , maxHeight :"calc(100vh - 400px)" , overflow :"scroll" } }
444
+ onClick = { ( e ) => e . stopPropagation ( ) } // Prevent closing on clicking inside the modal
445
+ >
446
+ { prepareData ( data . data , data . dataDesc ) }
447
+ </ div >
448
+ </ Modal >
406
449
) }
407
450
</ Node >
408
451
) ;
409
452
} ;
453
+
410
454
411
455
const getTreeUI = ( type :TreeUIKey ) => {
412
456
const uiCompInfos = _ . sortBy ( editorState . uiCompInfoList ( ) , [ ( x ) => x . name ] ) ;
@@ -434,15 +478,11 @@ export const LeftContent = (props: LeftContentProps) => {
434
478
return (
435
479
< DirectoryTreeStyle
436
480
treeData = { explorerData }
437
- // icon={(props: NodeItem) => props.type && (CompStateIcon[props.type] || <LeftCommon />)}
438
481
icon = { ( props :any ) => props . type && (
439
482
< div style = { { margin :'16px 4px 0 -4px' } } >
440
483
{ CompStateIcon [ props . type as UICompType ] || < LeftCommon /> }
441
484
</ div >
442
485
) }
443
- // switcherIcon={({ expanded }: { expanded: boolean }) =>
444
- // expanded ? <FoldedIcon /> : <UnfoldIcon />
445
- // }
446
486
switcherIcon = { ( props :any ) =>
447
487
props . expanded ?< FoldedIcon /> :< UnfoldIcon />
448
488
}