bi-sdk-react 0.0.63 → 0.0.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/js/bi-sdk.es.js +117 -43
- package/dist/types/components/typing.d.ts +1 -0
- package/dist/umd/js/bi-sdk.umd.min.js +115 -41
- package/package.json +1 -1
- package/src/components/PageDesigner.tsx +197 -3
- package/src/components/layout/PageItem.tsx +71 -2
- package/src/components/panel/LayerPanel.tsx +3 -0
- package/src/components/panel/PropertiesPanel.tsx +20 -0
- package/src/components/plugins/@antd/items/HtmlRender.tsx +1 -1
- package/src/components/typing.ts +1 -0
package/package.json
CHANGED
|
@@ -7,12 +7,27 @@ import {
|
|
|
7
7
|
MobileOutlined,
|
|
8
8
|
RedoOutlined,
|
|
9
9
|
SaveOutlined,
|
|
10
|
+
SearchOutlined,
|
|
10
11
|
TabletOutlined,
|
|
11
12
|
UndoOutlined,
|
|
12
13
|
ZoomInOutlined,
|
|
13
14
|
ZoomOutOutlined,
|
|
14
15
|
} from "@ant-design/icons";
|
|
15
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
AutoComplete,
|
|
18
|
+
Button,
|
|
19
|
+
Divider,
|
|
20
|
+
Drawer,
|
|
21
|
+
Empty,
|
|
22
|
+
Flex,
|
|
23
|
+
Input,
|
|
24
|
+
Modal,
|
|
25
|
+
Radio,
|
|
26
|
+
Space,
|
|
27
|
+
Tag,
|
|
28
|
+
Tooltip,
|
|
29
|
+
Typography,
|
|
30
|
+
} from "antd";
|
|
16
31
|
import React, { useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
17
32
|
import styled from "styled-components";
|
|
18
33
|
import { IconFont } from "./icon/IconFont";
|
|
@@ -189,6 +204,63 @@ const Container = styled.div`
|
|
|
189
204
|
}
|
|
190
205
|
`;
|
|
191
206
|
|
|
207
|
+
const Search = styled(Flex)`
|
|
208
|
+
color: var(--ant-color-text-label);
|
|
209
|
+
background: var(--ant-color-bg-text-hover);
|
|
210
|
+
border: var(--ant-color-border) solid 1px;
|
|
211
|
+
padding: 0 20px 0 8px;
|
|
212
|
+
border-radius: var(--ant-border-radius);
|
|
213
|
+
height: 24px;
|
|
214
|
+
font-size: 12px;
|
|
215
|
+
cursor: pointer;
|
|
216
|
+
user-select: none;
|
|
217
|
+
|
|
218
|
+
&:hover {
|
|
219
|
+
background: var(--ant-color-bg-text-active);
|
|
220
|
+
}
|
|
221
|
+
`;
|
|
222
|
+
|
|
223
|
+
const SearchList = styled.ul`
|
|
224
|
+
padding: 0;
|
|
225
|
+
margin: 0;
|
|
226
|
+
list-style: none;
|
|
227
|
+
li {
|
|
228
|
+
padding: 10px;
|
|
229
|
+
border-bottom: dotted 1px var(--ant-color-border);
|
|
230
|
+
cursor: pointer;
|
|
231
|
+
|
|
232
|
+
&:last-child {
|
|
233
|
+
border-bottom: none;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
&:hover {
|
|
237
|
+
background: var(--ant-color-primary-bg);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
h5 {
|
|
241
|
+
margin: 0;
|
|
242
|
+
color: var(--ant-color-text-label);
|
|
243
|
+
font-size: 14px;
|
|
244
|
+
|
|
245
|
+
small {
|
|
246
|
+
color: var(--ant-color-text-description);
|
|
247
|
+
|
|
248
|
+
&:before {
|
|
249
|
+
content: "(ID: ";
|
|
250
|
+
}
|
|
251
|
+
&:after {
|
|
252
|
+
content: ")";
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
div {
|
|
258
|
+
margin: 8px 0 0 0;
|
|
259
|
+
color: var(--ant-color-text-description);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
`;
|
|
263
|
+
|
|
192
264
|
const ImportModal: React.FC<{
|
|
193
265
|
open?: boolean;
|
|
194
266
|
onCancel?: () => void;
|
|
@@ -287,6 +359,8 @@ export const PageDesigner = React.forwardRef<any, PageDesignerProps>(
|
|
|
287
359
|
const [designable, setDesignable] = useState(true);
|
|
288
360
|
const [importModalOpen, setImportModalOpen] = useState(false);
|
|
289
361
|
const [aiPaneOpen, setAiPaneOpen] = useState(false);
|
|
362
|
+
const [searchKey, setSearchKey] = useState("");
|
|
363
|
+
const [searchOpen, setSearchOpen] = useState(false);
|
|
290
364
|
const containerStyle = useMemo(() => {
|
|
291
365
|
const left = showLeft ? "250px" : "";
|
|
292
366
|
const right = showRight ? "400px" : "";
|
|
@@ -470,11 +544,34 @@ export const PageDesigner = React.forwardRef<any, PageDesignerProps>(
|
|
|
470
544
|
return rightPanelActiveKey;
|
|
471
545
|
}, [selectedItem, rightPanelActiveKey]);
|
|
472
546
|
|
|
473
|
-
|
|
474
547
|
const selectedPlugin: PluginType | undefined = useMemo(() => {
|
|
475
548
|
return (plugins || []).find((p) => p.key === selectedItem?.type);
|
|
476
549
|
}, [plugins, selectedItem?.type]);
|
|
477
550
|
|
|
551
|
+
const searchedItems: SchemaItemType[] = useMemo(() => {
|
|
552
|
+
if (!searchKey?.trim()?.length) {
|
|
553
|
+
return [];
|
|
554
|
+
}
|
|
555
|
+
const list: SchemaItemType[] = [];
|
|
556
|
+
const compute = (arr?: any[]) => {
|
|
557
|
+
if (!Array.isArray(arr)) return;
|
|
558
|
+
arr.forEach((item) => {
|
|
559
|
+
if (JSON.stringify(item).includes(searchKey)) {
|
|
560
|
+
list.push(item);
|
|
561
|
+
}
|
|
562
|
+
if (Array.isArray(item.children)) {
|
|
563
|
+
compute(item.children);
|
|
564
|
+
} else if (typeof item.children === "object" && item.children) {
|
|
565
|
+
Object.keys(item.children).forEach((k) =>
|
|
566
|
+
compute(item.children[k]),
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
};
|
|
571
|
+
compute(schema?.items || []);
|
|
572
|
+
return list;
|
|
573
|
+
}, [schema?.items, searchKey]);
|
|
574
|
+
|
|
478
575
|
return (
|
|
479
576
|
<PageProvider
|
|
480
577
|
pageId={pageId}
|
|
@@ -673,7 +770,9 @@ export const PageDesigner = React.forwardRef<any, PageDesignerProps>(
|
|
|
673
770
|
renderNode={datasetPanel}
|
|
674
771
|
/>
|
|
675
772
|
)}
|
|
676
|
-
{leftPanelActiveKey === "global-dataset" &&
|
|
773
|
+
{leftPanelActiveKey === "global-dataset" && (
|
|
774
|
+
<GlobalDatasetPanel />
|
|
775
|
+
)}
|
|
677
776
|
</div>
|
|
678
777
|
</div>
|
|
679
778
|
<div className="center" style={{ flex: "1 1 auto" }}>
|
|
@@ -681,6 +780,21 @@ export const PageDesigner = React.forwardRef<any, PageDesignerProps>(
|
|
|
681
780
|
title="页面"
|
|
682
781
|
extra={
|
|
683
782
|
<Space>
|
|
783
|
+
<Search
|
|
784
|
+
align="center"
|
|
785
|
+
gap={8}
|
|
786
|
+
onClick={() => setSearchOpen(true)}
|
|
787
|
+
>
|
|
788
|
+
<SearchOutlined />
|
|
789
|
+
<Typography.Text
|
|
790
|
+
style={{
|
|
791
|
+
fontSize: 12,
|
|
792
|
+
color: "var(--ant-color-text-label)",
|
|
793
|
+
}}
|
|
794
|
+
>
|
|
795
|
+
Quick search...
|
|
796
|
+
</Typography.Text>
|
|
797
|
+
</Search>
|
|
684
798
|
<Button
|
|
685
799
|
size="small"
|
|
686
800
|
type={!designable ? "primary" : "default"}
|
|
@@ -709,6 +823,7 @@ export const PageDesigner = React.forwardRef<any, PageDesignerProps>(
|
|
|
709
823
|
}
|
|
710
824
|
/>
|
|
711
825
|
<div
|
|
826
|
+
id="page-canvas-wrapper"
|
|
712
827
|
className={[
|
|
713
828
|
"body beautified_scrollbar always",
|
|
714
829
|
deviceType,
|
|
@@ -864,6 +979,85 @@ export const PageDesigner = React.forwardRef<any, PageDesignerProps>(
|
|
|
864
979
|
onEffect={handleAiEffect}
|
|
865
980
|
/>
|
|
866
981
|
</Drawer>
|
|
982
|
+
<Modal
|
|
983
|
+
open={searchOpen}
|
|
984
|
+
title={
|
|
985
|
+
<Input
|
|
986
|
+
placeholder="搜索页面元素 ..."
|
|
987
|
+
variant="borderless"
|
|
988
|
+
prefix={
|
|
989
|
+
<SearchOutlined
|
|
990
|
+
style={{ color: "var(--ant-color-text-label)" }}
|
|
991
|
+
/>
|
|
992
|
+
}
|
|
993
|
+
value={searchKey}
|
|
994
|
+
onChange={(e) => setSearchKey(e.target.value)}
|
|
995
|
+
/>
|
|
996
|
+
}
|
|
997
|
+
closeIcon={
|
|
998
|
+
<Typography.Link
|
|
999
|
+
unselectable="on"
|
|
1000
|
+
style={{ color: "var(--ant-color-text-quaternary)" }}
|
|
1001
|
+
>
|
|
1002
|
+
ESC
|
|
1003
|
+
</Typography.Link>
|
|
1004
|
+
}
|
|
1005
|
+
footer={null}
|
|
1006
|
+
onCancel={() => setSearchOpen(false)}
|
|
1007
|
+
styles={{
|
|
1008
|
+
container: { padding: 0 },
|
|
1009
|
+
header: {
|
|
1010
|
+
padding: "16px 12px",
|
|
1011
|
+
borderBottom: "solid 1px var(--ant-color-border)",
|
|
1012
|
+
},
|
|
1013
|
+
body: {
|
|
1014
|
+
padding: "0 12px 12px 12px",
|
|
1015
|
+
overflow: "auto",
|
|
1016
|
+
maxHeight: 500,
|
|
1017
|
+
},
|
|
1018
|
+
}}
|
|
1019
|
+
>
|
|
1020
|
+
<SearchList>
|
|
1021
|
+
{searchedItems.length === 0 ? (
|
|
1022
|
+
<Empty
|
|
1023
|
+
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
|
1024
|
+
description={
|
|
1025
|
+
searchKey?.trim()?.length
|
|
1026
|
+
? "未找到页面元素"
|
|
1027
|
+
: "请输入关键字进行搜索..."
|
|
1028
|
+
}
|
|
1029
|
+
/>
|
|
1030
|
+
) : (
|
|
1031
|
+
searchedItems.map((item) => (
|
|
1032
|
+
<li
|
|
1033
|
+
key={item.id}
|
|
1034
|
+
onClick={() => {
|
|
1035
|
+
setSelectedItem(item);
|
|
1036
|
+
document
|
|
1037
|
+
.getElementById(`page-item-${item.id}`)
|
|
1038
|
+
?.scrollIntoView({ behavior: "smooth" });
|
|
1039
|
+
setSearchOpen(false);
|
|
1040
|
+
}}
|
|
1041
|
+
>
|
|
1042
|
+
<Typography.Title level={5} style={{ margin: 0 }}>
|
|
1043
|
+
<Tag
|
|
1044
|
+
variant="filled"
|
|
1045
|
+
color="#108ee9"
|
|
1046
|
+
style={{ marginRight: 8 }}
|
|
1047
|
+
>
|
|
1048
|
+
页面元素
|
|
1049
|
+
</Tag>
|
|
1050
|
+
{item.name}
|
|
1051
|
+
<small>{item.id}</small>
|
|
1052
|
+
</Typography.Title>
|
|
1053
|
+
<Typography.Paragraph>
|
|
1054
|
+
{item.description || "无描述内容"}
|
|
1055
|
+
</Typography.Paragraph>
|
|
1056
|
+
</li>
|
|
1057
|
+
))
|
|
1058
|
+
)}
|
|
1059
|
+
</SearchList>
|
|
1060
|
+
</Modal>
|
|
867
1061
|
</PageProvider>
|
|
868
1062
|
);
|
|
869
1063
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { draggable } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
|
|
2
|
-
import React, { useContext, useEffect, useRef, useState } from "react";
|
|
2
|
+
import React, { useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { PageContext } from "../context/PageContext";
|
|
4
4
|
import { SchemaItemType } from "../typing";
|
|
5
5
|
import styled from "styled-components";
|
|
@@ -63,6 +63,19 @@ const ToolbarWrap = styled.div`
|
|
|
63
63
|
}
|
|
64
64
|
`;
|
|
65
65
|
|
|
66
|
+
const Hidden = styled.div`
|
|
67
|
+
user-select: none;
|
|
68
|
+
font-size: 12px;
|
|
69
|
+
text-align: center;
|
|
70
|
+
color: var(--ant-color-text-placeholder);
|
|
71
|
+
background: var(--ant-color-bg-layout);
|
|
72
|
+
border: dotted 1px #ccc;
|
|
73
|
+
|
|
74
|
+
&:before {
|
|
75
|
+
content: "组件已隐藏";
|
|
76
|
+
}
|
|
77
|
+
`;
|
|
78
|
+
|
|
66
79
|
const Toolbar: React.FC<{
|
|
67
80
|
item: SchemaItemType;
|
|
68
81
|
ancestors: SchemaItemType[];
|
|
@@ -241,6 +254,8 @@ export const PageItem: React.FC<PageItemProps> = ({
|
|
|
241
254
|
onRemoveAtItem,
|
|
242
255
|
forceUpdate,
|
|
243
256
|
fetch,
|
|
257
|
+
globalVars,
|
|
258
|
+
env,
|
|
244
259
|
} = useContext(PageContext);
|
|
245
260
|
const [isDragging, setIsDragging] = useState(false);
|
|
246
261
|
const [toolbarContainer, setToolbarContainer] =
|
|
@@ -274,6 +289,33 @@ export const PageItem: React.FC<PageItemProps> = ({
|
|
|
274
289
|
| React.ComponentType<any>
|
|
275
290
|
| undefined;
|
|
276
291
|
|
|
292
|
+
const hidden = useMemo(() => {
|
|
293
|
+
const expression = item.hideExpression;
|
|
294
|
+
if (!expression?.length) {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
const func = new Function(
|
|
299
|
+
"$g",
|
|
300
|
+
"$e",
|
|
301
|
+
`
|
|
302
|
+
"use strict";
|
|
303
|
+
const window = undefined;
|
|
304
|
+
const document = undefined;
|
|
305
|
+
const global = undefined;
|
|
306
|
+
const process = undefined;
|
|
307
|
+
const require = undefined;
|
|
308
|
+
const module = undefined;
|
|
309
|
+
const exports = undefined;
|
|
310
|
+
return ${expression} === true;
|
|
311
|
+
`,
|
|
312
|
+
);
|
|
313
|
+
return func(globalVars, env.global);
|
|
314
|
+
} catch (e) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
}, [item.hideExpression, globalVars, env]);
|
|
318
|
+
|
|
277
319
|
useEffect(() => {
|
|
278
320
|
const updateToolbarPosition = () => {
|
|
279
321
|
if (!toolbarEl.current) return;
|
|
@@ -316,7 +358,7 @@ export const PageItem: React.FC<PageItemProps> = ({
|
|
|
316
358
|
|
|
317
359
|
if (selectedItem && selectedItem === item && designable) {
|
|
318
360
|
const div = document.createElement("div");
|
|
319
|
-
div.setAttribute("id", "page-item-toolbar")
|
|
361
|
+
div.setAttribute("id", "page-item-toolbar");
|
|
320
362
|
div.style.position = "absolute";
|
|
321
363
|
div.style.zIndex = "1000";
|
|
322
364
|
div.style.display = "flex";
|
|
@@ -370,6 +412,33 @@ export const PageItem: React.FC<PageItemProps> = ({
|
|
|
370
412
|
|
|
371
413
|
if (!Comp) return null;
|
|
372
414
|
|
|
415
|
+
if (hidden) {
|
|
416
|
+
return designable ? (
|
|
417
|
+
<>
|
|
418
|
+
<Hidden
|
|
419
|
+
id={`page-item-${item.id}`}
|
|
420
|
+
data-item-id={item.id}
|
|
421
|
+
className={`page-item-component ${designable ? "designable" : ""} ${
|
|
422
|
+
selectedItem === item ? "selected" : ""
|
|
423
|
+
}`}
|
|
424
|
+
></Hidden>
|
|
425
|
+
{toolbarContainer &&
|
|
426
|
+
ReactDOM.createPortal(
|
|
427
|
+
<Toolbar
|
|
428
|
+
item={item}
|
|
429
|
+
ancestors={ancestors}
|
|
430
|
+
onAddAt={fetch?.ai?.chat ? handleAddAtItem : undefined}
|
|
431
|
+
onCopy={handleCopy}
|
|
432
|
+
onDelete={handleDelete}
|
|
433
|
+
onUpdate={forceUpdate}
|
|
434
|
+
onSelect={onSelect}
|
|
435
|
+
/>,
|
|
436
|
+
toolbarContainer,
|
|
437
|
+
)}
|
|
438
|
+
</>
|
|
439
|
+
) : null;
|
|
440
|
+
}
|
|
441
|
+
|
|
373
442
|
return (
|
|
374
443
|
<>
|
|
375
444
|
<Comp
|
|
@@ -229,6 +229,9 @@ export const LayerPanel: React.FC = () => {
|
|
|
229
229
|
onSelect={(_, info: any) => {
|
|
230
230
|
if (info.node.dataRef.type === "slot") return;
|
|
231
231
|
onSelect && onSelect(info.node.dataRef.item);
|
|
232
|
+
document
|
|
233
|
+
.getElementById(`page-item-${info.node.dataRef.item.id}`)
|
|
234
|
+
?.scrollIntoView({ behavior: "smooth" });
|
|
232
235
|
}}
|
|
233
236
|
onDrop={onDrop}
|
|
234
237
|
onExpand={(keys) => setExpandedKeys(keys as string[])}
|
|
@@ -271,6 +271,26 @@ export const PropertiesPanel: React.FC<{
|
|
|
271
271
|
}
|
|
272
272
|
/>
|
|
273
273
|
</Form.Item>
|
|
274
|
+
<Form.Item label="隐藏组件" tooltip={
|
|
275
|
+
<>
|
|
276
|
+
<div>当下方表达式(JS)不为空且为true时组件隐藏。</div>
|
|
277
|
+
<div><small>1.环境变量:$e.变量名。</small></div>
|
|
278
|
+
<div><small>2.全局变量:$g.变量名。</small></div>
|
|
279
|
+
<div><small>示例:$e.a === '1'。</small></div>
|
|
280
|
+
</>
|
|
281
|
+
}>
|
|
282
|
+
<Input.TextArea
|
|
283
|
+
size="small"
|
|
284
|
+
value={selectedItem?.hideExpression}
|
|
285
|
+
onChange={(e) =>
|
|
286
|
+
onUpdateSelectedItem &&
|
|
287
|
+
onUpdateSelectedItem({
|
|
288
|
+
...selectedItem,
|
|
289
|
+
hideExpression: e.target.value,
|
|
290
|
+
})
|
|
291
|
+
}
|
|
292
|
+
/>
|
|
293
|
+
</Form.Item>
|
|
274
294
|
</Form>
|
|
275
295
|
{selectedItem?.datasource && <Divider>数据源</Divider>}
|
|
276
296
|
{selectedItem?.datasource && (
|
|
@@ -36,7 +36,7 @@ export const HtmlRender: React.FC<HtmlRenderProps> = ({
|
|
|
36
36
|
const [signal, setSignal] = useState<number>(0);
|
|
37
37
|
const datasource = useDatasource({ item, signal });
|
|
38
38
|
|
|
39
|
-
const keys = useMemo(() => Object.keys(datasource || {}), [datasource]);
|
|
39
|
+
const keys = useMemo(() => Array.isArray(datasource) ? [] : Object.keys(datasource || {}), [datasource]);
|
|
40
40
|
const func = (path: string) =>
|
|
41
41
|
new Function(
|
|
42
42
|
"$g",
|