Skip to content

Commit 4e5e0b1

Browse files
author
FalkWolsky
committed
Copy-Able Data from Components in State Display
1 parent 2e38751 commit 4e5e0b1

File tree

1 file changed

+127
-87
lines changed

1 file changed

+127
-87
lines changed

client/packages/lowcoder/src/pages/editor/LeftContent.tsx

Lines changed: 127 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import {
99
LeftClose,
1010
LeftCommon,
1111
LeftOpen,
12-
PadDiv,
1312
ScrollBar,
1413
Tooltip,
1514
UnfoldIcon,
1615
UnShow,
16+
TacoButton,
1717
} from "lowcoder-design";
1818
import React, { ReactNode, useCallback, useContext, useMemo, useState } from "react";
1919
import { hookCompCategory } from "comps/hooks/hookCompTypes";
@@ -32,13 +32,33 @@ import type { UICompType } from "comps/uiCompRegistry";
3232
import { CollapseWrapper, DirectoryTreeStyle, Node } from "./styledComponents";
3333
import { DataNode, EventDataNode } from "antd/es/tree";
3434
import { isAggregationApp } from "util/appUtils";
35+
import Modal from "antd/es/modal/Modal";
36+
import copyToClipboard from "copy-to-clipboard";
3537

3638
const CollapseTitleWrapper = styled.div`
3739
display: flex;
3840
width: fit-content;
3941
max-width: calc(100% - 8px);
4042
`;
4143

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+
4262
function getLen(config: string | boolean | number) {
4363
if (typeof config === "number") {
4464
return (config + "").toString().length;
@@ -49,47 +69,48 @@ function getLen(config: string | boolean | number) {
4969
return 0;
5070
}
5171

52-
function toDataView(value: any, name: string, desc?: ReactNode) {
72+
function toDataView(value: any, name: string, desc?: ReactNode, modal?: boolean) {
5373
const str = typeof value === "function" ? "Function" : safeJSONStringify(value);
5474
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);
5576
descRecord[name] = desc;
5677
if (Array.isArray(value)) {
5778
const dataChild: Record<string, any> = {};
5879
value.forEach((valueChild, index) => {
5980
dataChild[index] = valueChild;
6081
});
6182
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} />
6384
);
6485
} 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-
&#8203;
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}
8594
>
86-
&#8203;
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} /> &#8203;
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+
}
91109
}
92110

111+
112+
export default toDataView;
113+
93114
function sliceArr(arr: string[]) {
94115
let preArr: string[] = [];
95116
let afterArr: string[] = [];
@@ -104,23 +125,23 @@ function sliceArr(arr: string[]) {
104125
return { preArr, afterArr } as const;
105126
}
106127

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}) {
108129
const totalArr = Object.keys(props.data);
109130
const sliceFn = sliceArr;
110131
return (
111132
<div>
112133
{totalArr.length < 30 ? (
113134
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);
115136
})
116137
) : (
117138
<>
118139
{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);
120141
})}
121142
<UnShow num={totalArr.length - 6} />
122143
{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);
124145
})}
125146
</>
126147
)}
@@ -137,6 +158,8 @@ const CollapseView = React.memo(
137158
onClick?: (compName: string) => void;
138159
isSelected?: boolean;
139160
isOpen?: boolean;
161+
children?: React.ReactNode; // Accept children
162+
modal?: boolean;
140163
}) => {
141164
const { data = {} } = props;
142165
const onlyOne = Object.keys(data).length === 1;
@@ -148,47 +171,53 @@ const CollapseView = React.memo(
148171
{
149172
key: props.name,
150173
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>
183211
),
184-
data: toData({ data, desc: props.desc }),
212+
data: toData({ data, desc: props.desc, modal: props.modal}),
185213
},
186214
]}
187215
/>
188216
);
189217
}
190218
);
191219

220+
192221
interface LeftContentProps {
193222
uiComp: InstanceType<typeof UIComp>;
194223
}
@@ -324,13 +353,23 @@ export const LeftContent = (props: LeftContentProps) => {
324353
const getTreeNode = (node: NodeItem, uiCompInfos: CompInfo[]) => {
325354
const info = showData.find((item) => item.key === node.key);
326355
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+
327367
return (
328-
<Node>
368+
<Node key={node.key}>
329369
<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 ? (
334373
<Tooltip
335374
placement="right"
336375
title={trans("leftPanel.collapseTip", { component: node.title })}
@@ -353,7 +392,7 @@ export const LeftContent = (props: LeftContentProps) => {
353392
setShowData(newData);
354393
}}
355394
>
356-
<LeftOpen />
395+
357396
</div>
358397
</Tooltip>
359398
) : (
@@ -389,24 +428,29 @@ export const LeftContent = (props: LeftContentProps) => {
389428
<LeftClose />
390429
</div>
391430
</Tooltip>
392-
))}
431+
)
432+
)}
393433
</span>
394434
{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>
406449
)}
407450
</Node>
408451
);
409452
};
453+
410454

411455
const getTreeUI = (type: TreeUIKey) => {
412456
const uiCompInfos = _.sortBy(editorState.uiCompInfoList(), [(x) => x.name]);
@@ -434,15 +478,11 @@ export const LeftContent = (props: LeftContentProps) => {
434478
return (
435479
<DirectoryTreeStyle
436480
treeData={explorerData}
437-
// icon={(props: NodeItem) => props.type && (CompStateIcon[props.type] || <LeftCommon />)}
438481
icon={(props: any) => props.type && (
439482
<div style={{ margin: '16px 4px 0 -4px'}}>
440483
{CompStateIcon[props.type as UICompType] || <LeftCommon />}
441484
</div>
442485
)}
443-
// switcherIcon={({ expanded }: { expanded: boolean }) =>
444-
// expanded ? <FoldedIcon /> : <UnfoldIcon />
445-
// }
446486
switcherIcon={(props: any) =>
447487
props.expanded ? <FoldedIcon /> : <UnfoldIcon />
448488
}

0 commit comments

Comments
 (0)