bi-sdk-react 0.0.63 → 0.0.65
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 +77 -3
- 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[];
|
|
@@ -188,7 +201,12 @@ const Toolbar: React.FC<{
|
|
|
188
201
|
</Tooltip>
|
|
189
202
|
)}
|
|
190
203
|
<Tooltip title="源码编辑">
|
|
191
|
-
<a
|
|
204
|
+
<a
|
|
205
|
+
onClick={(e) => {
|
|
206
|
+
e.stopPropagation();
|
|
207
|
+
setVisible(true);
|
|
208
|
+
}}
|
|
209
|
+
>
|
|
192
210
|
<IconFont type="icon-json" />
|
|
193
211
|
</a>
|
|
194
212
|
</Tooltip>
|
|
@@ -241,6 +259,8 @@ export const PageItem: React.FC<PageItemProps> = ({
|
|
|
241
259
|
onRemoveAtItem,
|
|
242
260
|
forceUpdate,
|
|
243
261
|
fetch,
|
|
262
|
+
globalVars,
|
|
263
|
+
env,
|
|
244
264
|
} = useContext(PageContext);
|
|
245
265
|
const [isDragging, setIsDragging] = useState(false);
|
|
246
266
|
const [toolbarContainer, setToolbarContainer] =
|
|
@@ -274,6 +294,33 @@ export const PageItem: React.FC<PageItemProps> = ({
|
|
|
274
294
|
| React.ComponentType<any>
|
|
275
295
|
| undefined;
|
|
276
296
|
|
|
297
|
+
const hidden = useMemo(() => {
|
|
298
|
+
const expression = item.hideExpression;
|
|
299
|
+
if (!expression?.length) {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
try {
|
|
303
|
+
const func = new Function(
|
|
304
|
+
"$g",
|
|
305
|
+
"$e",
|
|
306
|
+
`
|
|
307
|
+
"use strict";
|
|
308
|
+
const window = undefined;
|
|
309
|
+
const document = undefined;
|
|
310
|
+
const global = undefined;
|
|
311
|
+
const process = undefined;
|
|
312
|
+
const require = undefined;
|
|
313
|
+
const module = undefined;
|
|
314
|
+
const exports = undefined;
|
|
315
|
+
return ${expression} === true;
|
|
316
|
+
`,
|
|
317
|
+
);
|
|
318
|
+
return func(globalVars, env.global);
|
|
319
|
+
} catch (e) {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
}, [item.hideExpression, globalVars, env]);
|
|
323
|
+
|
|
277
324
|
useEffect(() => {
|
|
278
325
|
const updateToolbarPosition = () => {
|
|
279
326
|
if (!toolbarEl.current) return;
|
|
@@ -316,7 +363,7 @@ export const PageItem: React.FC<PageItemProps> = ({
|
|
|
316
363
|
|
|
317
364
|
if (selectedItem && selectedItem === item && designable) {
|
|
318
365
|
const div = document.createElement("div");
|
|
319
|
-
div.setAttribute("id", "page-item-toolbar")
|
|
366
|
+
div.setAttribute("id", "page-item-toolbar");
|
|
320
367
|
div.style.position = "absolute";
|
|
321
368
|
div.style.zIndex = "1000";
|
|
322
369
|
div.style.display = "flex";
|
|
@@ -370,6 +417,33 @@ export const PageItem: React.FC<PageItemProps> = ({
|
|
|
370
417
|
|
|
371
418
|
if (!Comp) return null;
|
|
372
419
|
|
|
420
|
+
if (hidden) {
|
|
421
|
+
return designable ? (
|
|
422
|
+
<>
|
|
423
|
+
<Hidden
|
|
424
|
+
id={`page-item-${item.id}`}
|
|
425
|
+
data-item-id={item.id}
|
|
426
|
+
className={`page-item-component ${designable ? "designable" : ""} ${
|
|
427
|
+
selectedItem === item ? "selected" : ""
|
|
428
|
+
}`}
|
|
429
|
+
></Hidden>
|
|
430
|
+
{toolbarContainer &&
|
|
431
|
+
ReactDOM.createPortal(
|
|
432
|
+
<Toolbar
|
|
433
|
+
item={item}
|
|
434
|
+
ancestors={ancestors}
|
|
435
|
+
onAddAt={fetch?.ai?.chat ? handleAddAtItem : undefined}
|
|
436
|
+
onCopy={handleCopy}
|
|
437
|
+
onDelete={handleDelete}
|
|
438
|
+
onUpdate={forceUpdate}
|
|
439
|
+
onSelect={onSelect}
|
|
440
|
+
/>,
|
|
441
|
+
toolbarContainer,
|
|
442
|
+
)}
|
|
443
|
+
</>
|
|
444
|
+
) : null;
|
|
445
|
+
}
|
|
446
|
+
|
|
373
447
|
return (
|
|
374
448
|
<>
|
|
375
449
|
<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",
|