mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-03-10 09:56:20 +00:00
fix: 修复 asset 中 componentList 为空时报错的 bug
This commit is contained in:
parent
e9d8d3dbe6
commit
49517a68a9
@ -20,9 +20,12 @@
|
|||||||
],
|
],
|
||||||
"author": "xiayang.xy",
|
"author": "xiayang.xy",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ali/intl-universal": "^0.4.12",
|
||||||
"@ali/lowcode-designer": "^1.0.8-0",
|
"@ali/lowcode-designer": "^1.0.8-0",
|
||||||
"@ali/lowcode-editor-core": "^1.0.8-0",
|
"@ali/lowcode-editor-core": "^1.0.8-0",
|
||||||
"@ali/lowcode-types": "^1.0.8-0",
|
"@ali/lowcode-types": "^1.0.8-0",
|
||||||
|
"@ali/vu-layer": "^4.0.1",
|
||||||
|
"@ali/vu-uxcore-legao-design": "^1.4.0",
|
||||||
"@alifd/next": "^1.19.19",
|
"@alifd/next": "^1.19.19",
|
||||||
"react": "^16.8.1"
|
"react": "^16.8.1"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import React from "react";
|
import React from 'react';
|
||||||
import { Search, Box } from "@alifd/next";
|
import PropTypes from 'prop-types';
|
||||||
import Button from "../button/index.js";
|
import { Search, Box } from '@alifd/next';
|
||||||
import Card from "../card";
|
import Button from '../button';
|
||||||
import $i18n from "../../i18n/index.js";
|
import Card from '../card';
|
||||||
import { searchComponent, builtinSearchMap } from "../../utils";
|
import $i18n from '../../i18n/index';
|
||||||
import "./index.less";
|
import { searchComponent, builtinSearchMap } from '../../utils';
|
||||||
|
import './index.less';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配置元素的操作类型
|
* 配置元素的操作类型
|
||||||
@ -13,9 +14,9 @@ import "./index.less";
|
|||||||
* All:可拖拽也可点击
|
* All:可拖拽也可点击
|
||||||
*/
|
*/
|
||||||
export const AdditiveType = {
|
export const AdditiveType = {
|
||||||
Draggable: "additive-drag",
|
Draggable: 'additive-drag',
|
||||||
Clickable: "additive-click",
|
Clickable: 'additive-click',
|
||||||
All: "additive",
|
All: 'additive',
|
||||||
};
|
};
|
||||||
|
|
||||||
class Base extends React.Component {
|
class Base extends React.Component {
|
||||||
@ -23,33 +24,27 @@ class Base extends React.Component {
|
|||||||
metaData: PropTypes.array,
|
metaData: PropTypes.array,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
registerAdditive: PropTypes.func,
|
registerAdditive: PropTypes.func,
|
||||||
renderCustomSnippet: PropTypes.func,
|
|
||||||
actions: PropTypes.array,
|
actions: PropTypes.array,
|
||||||
getComponentInfo: PropTypes.func,
|
getComponentInfo: PropTypes.func,
|
||||||
enableSearch: PropTypes.bool,
|
enableSearch: PropTypes.bool,
|
||||||
enableCard: PropTypes.bool,
|
|
||||||
enableReport: PropTypes.bool,
|
|
||||||
placeholder: PropTypes.string,
|
placeholder: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
metaData: [],
|
metaData: [],
|
||||||
registerAdditive: () => {
|
registerAdditive: () => {},
|
||||||
return;
|
className: '',
|
||||||
},
|
|
||||||
className: "",
|
|
||||||
renderCustomSnippet: null,
|
renderCustomSnippet: null,
|
||||||
actions: [],
|
actions: [],
|
||||||
getComponentInfo: null,
|
getComponentInfo: null,
|
||||||
enableSearch: false,
|
enableSearch: false,
|
||||||
enableCard: true,
|
enableCard: true,
|
||||||
enableReport: true,
|
enableReport: true,
|
||||||
placeholder: "",
|
placeholder: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
selected: "",
|
searchText: '',
|
||||||
searchText: "",
|
|
||||||
currentCard: null,
|
currentCard: null,
|
||||||
target: null,
|
target: null,
|
||||||
currentCardImage: '',
|
currentCardImage: '',
|
||||||
@ -63,7 +58,7 @@ class Base extends React.Component {
|
|||||||
isMouseEnterCard = false;
|
isMouseEnterCard = false;
|
||||||
|
|
||||||
|
|
||||||
componentWillMount() {
|
UNSAFE_componentWillMount() {
|
||||||
// TODO get remote search map
|
// TODO get remote search map
|
||||||
this.searchMap = builtinSearchMap;
|
this.searchMap = builtinSearchMap;
|
||||||
}
|
}
|
||||||
@ -85,9 +80,9 @@ class Base extends React.Component {
|
|||||||
|
|
||||||
normalizeBundle(mode) {
|
normalizeBundle(mode) {
|
||||||
const { metaData } = this.props;
|
const { metaData } = this.props;
|
||||||
const { searchText = "" } = this.state;
|
const { searchText = '' } = this.state;
|
||||||
const groupList = metaData.filter((comp, index) => {
|
const groupList = metaData.filter((comp, index) => {
|
||||||
const { title = "", componentName = "", id = "" } = comp;
|
const { title = '', componentName = '', id = '' } = comp;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
comp.id = `${comp.componentName}_${index}`;
|
comp.id = `${comp.componentName}_${index}`;
|
||||||
}
|
}
|
||||||
@ -103,10 +98,10 @@ class Base extends React.Component {
|
|||||||
return groupList;
|
return groupList;
|
||||||
}
|
}
|
||||||
|
|
||||||
let bundle = {};
|
const bundle = {};
|
||||||
// 按一定顺序排列
|
// 按一定顺序排列
|
||||||
groupList.forEach((m) => {
|
groupList.forEach((m) => {
|
||||||
const c = m.category || "Others";
|
const c = m.category || 'Others';
|
||||||
if (!bundle[c]) {
|
if (!bundle[c]) {
|
||||||
bundle[c] = [];
|
bundle[c] = [];
|
||||||
}
|
}
|
||||||
@ -119,7 +114,7 @@ class Base extends React.Component {
|
|||||||
this.isEmpty = true;
|
this.isEmpty = true;
|
||||||
return (
|
return (
|
||||||
<Box direction="column" justify="center" align="center" className="ve-component-list-empty">
|
<Box direction="column" justify="center" align="center" className="ve-component-list-empty">
|
||||||
<img src='//g.alicdn.com/uxcore/pic/empty.png' style={{ height: 100, width: 100 }} />
|
<img src="//g.alicdn.com/uxcore/pic/empty.png" style={{ height: 100, width: 100 }} />
|
||||||
<div style={{ lineHeight: 2 }}>
|
<div style={{ lineHeight: 2 }}>
|
||||||
<div>暂无组件,请在物料站点添加</div>
|
<div>暂无组件,请在物料站点添加</div>
|
||||||
</div>
|
</div>
|
||||||
@ -131,14 +126,8 @@ class Base extends React.Component {
|
|||||||
const { placeholder } = this.props;
|
const { placeholder } = this.props;
|
||||||
return (
|
return (
|
||||||
<Search
|
<Search
|
||||||
placeholder={
|
style={{ width: '100%' }}
|
||||||
placeholder
|
placeholder={placeholder || $i18n.get({ id: 'trunkPaneSearchComponent', dm: '搜索组件' })}
|
||||||
? placeholder
|
|
||||||
: $i18n.get({
|
|
||||||
id: "trunkPaneSearchComponent",
|
|
||||||
dm: "搜索组件",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
shape="simple"
|
shape="simple"
|
||||||
size="medium"
|
size="medium"
|
||||||
hasClear
|
hasClear
|
||||||
@ -154,7 +143,7 @@ class Base extends React.Component {
|
|||||||
if (!this.hasActions()) {
|
if (!this.hasActions()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const len = actions.length;
|
// const len = actions.length;
|
||||||
// TODO:len = 1:只有一个主按钮;len = 2:一个主按钮、一个次按钮;len >=3:一个主按钮、一个次按钮、其余放在按钮组里;
|
// TODO:len = 1:只有一个主按钮;len = 2:一个主按钮、一个次按钮;len >=3:一个主按钮、一个次按钮、其余放在按钮组里;
|
||||||
return actions.map((action, idx) => {
|
return actions.map((action, idx) => {
|
||||||
return (
|
return (
|
||||||
@ -162,7 +151,7 @@ class Base extends React.Component {
|
|||||||
key={idx}
|
key={idx}
|
||||||
action={action}
|
action={action}
|
||||||
className="btn"
|
className="btn"
|
||||||
type={idx === 0 ? "primary" : "outline"}
|
type={idx === 0 ? 'primary' : 'outline'}
|
||||||
>
|
>
|
||||||
{action.title}
|
{action.title}
|
||||||
</Button>
|
</Button>
|
||||||
@ -194,7 +183,7 @@ class Base extends React.Component {
|
|||||||
componentPrototype={currentCard}
|
componentPrototype={currentCard}
|
||||||
target={target}
|
target={target}
|
||||||
customImage={currentCardImage}
|
customImage={currentCardImage}
|
||||||
subTitle={target && target.innerText || ''}
|
subTitle={target ? target.innerText : ''}
|
||||||
position="right top"
|
position="right top"
|
||||||
visible={!!currentCard}
|
visible={!!currentCard}
|
||||||
showClose
|
showClose
|
||||||
@ -219,18 +208,18 @@ class Base extends React.Component {
|
|||||||
enableSearch,
|
enableSearch,
|
||||||
className,
|
className,
|
||||||
registerAdditive = () => {
|
registerAdditive = () => {
|
||||||
return;
|
|
||||||
},
|
},
|
||||||
} = this.props;
|
} = this.props;
|
||||||
let bodyExtraClass = "";
|
let bodyExtraClass = '';
|
||||||
if (this.hasActions() && enableSearch) {
|
if (this.hasActions() && enableSearch) {
|
||||||
bodyExtraClass = "small";
|
bodyExtraClass = 'small';
|
||||||
} else if (!this.hasActions() && enableSearch) {
|
} else if (!this.hasActions() && enableSearch) {
|
||||||
bodyExtraClass = "medium";
|
bodyExtraClass = 'medium';
|
||||||
} else if (this.hasActions() && !enableSearch) {
|
} else if (this.hasActions() && !enableSearch) {
|
||||||
bodyExtraClass = "large";
|
bodyExtraClass = 'large';
|
||||||
} else {
|
} else {
|
||||||
bodyExtraClass = "";
|
bodyExtraClass = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -256,7 +245,7 @@ class Base extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`ve-component-list-foot ${
|
className={`ve-component-list-foot ${
|
||||||
this.hasActions() ? "exist" : ""
|
this.hasActions() ? 'exist' : ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{this.renderActions()}
|
{this.renderActions()}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { Button } from "@ali/vu-uxcore-legao-design";
|
import { Button } from '@ali/vu-uxcore-legao-design';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
const MyButton = (props) => {
|
const MyButton = (props) => {
|
||||||
@ -6,10 +6,10 @@ const MyButton = (props) => {
|
|||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
className={className}
|
className={className}
|
||||||
type={type || action.type || "outline"}
|
type={type || action.type || 'outline'}
|
||||||
danger={action.danger || false}
|
danger={action.danger || false}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (action.fn && typeof action.fn === "function") {
|
if (action.fn && typeof action.fn === 'function') {
|
||||||
action.fn(componentPrototype);
|
action.fn(componentPrototype);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import React from "react";
|
import React from 'react';
|
||||||
import Layer from "@ali/vu-layer";
|
import Layer from '@ali/vu-layer';
|
||||||
import { Icon } from "@alifd/next";
|
import { Icon } from '@alifd/next';
|
||||||
import $i18n from "../../i18n/index";
|
import $i18n from '../../i18n/index';
|
||||||
import Button from "../button";
|
import Button from '../button';
|
||||||
import "./index.less";
|
import './index.less';
|
||||||
|
|
||||||
export default class Card extends React.Component {
|
export default class Card extends React.Component {
|
||||||
static propTypes = {};
|
static propTypes = {};
|
||||||
@ -23,7 +23,7 @@ export default class Card extends React.Component {
|
|||||||
this.loadComponentInfo(this.props.componentPrototype);
|
this.loadComponentInfo(this.props.componentPrototype);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||||
if (nextProps.componentPrototype !== this.props.componentPrototype) {
|
if (nextProps.componentPrototype !== this.props.componentPrototype) {
|
||||||
// 延迟执行数据加载和渲染,等 props 更新之后
|
// 延迟执行数据加载和渲染,等 props 更新之后
|
||||||
this.loadComponentInfo(nextProps.componentPrototype);
|
this.loadComponentInfo(nextProps.componentPrototype);
|
||||||
@ -44,12 +44,6 @@ export default class Card extends React.Component {
|
|||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
this.setState({ errorMsg: e.message });
|
this.setState({ errorMsg: e.message });
|
||||||
console.error(e);
|
console.error(e);
|
||||||
if (VisualEngine) {
|
|
||||||
VisualEngine.ui.Popup.error({
|
|
||||||
content: e.message,
|
|
||||||
duration: 2000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +59,7 @@ export default class Card extends React.Component {
|
|||||||
<div>
|
<div>
|
||||||
<Icon type="loading" className="ve-loading-icon" size="large" />
|
<Icon type="loading" className="ve-loading-icon" size="large" />
|
||||||
<div className="ve-loading-content">
|
<div className="ve-loading-content">
|
||||||
{$i18n.get({ id: "trunkPaneLoading", dm: "加载中..." })}
|
{$i18n.get({ id: 'trunkPaneLoading', dm: '加载中...' })}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -73,7 +67,7 @@ export default class Card extends React.Component {
|
|||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
version,
|
version,
|
||||||
image = "https://img.alicdn.com/tfs/TB1XHG6ehrI8KJjy0FpXXb5hVXa-740-608.png",
|
image = 'https://img.alicdn.com/tfs/TB1XHG6ehrI8KJjy0FpXXb5hVXa-740-608.png',
|
||||||
desc,
|
desc,
|
||||||
detailUrl,
|
detailUrl,
|
||||||
actions,
|
actions,
|
||||||
@ -113,24 +107,24 @@ export default class Card extends React.Component {
|
|||||||
className="ve-operation-item"
|
className="ve-operation-item"
|
||||||
>
|
>
|
||||||
{$i18n.get({
|
{$i18n.get({
|
||||||
id: "trunkPaneDetailedDocumentation",
|
id: 'trunkPaneDetailedDocumentation',
|
||||||
dm: "详细文档",
|
dm: '详细文档',
|
||||||
})}
|
})}
|
||||||
</a>
|
</a>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
{actions
|
{
|
||||||
? actions.map((action, idx) => {
|
actions ? actions.map((action, idx) => {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
key={idx}
|
key={idx}
|
||||||
className="ve-card-action"
|
className="ve-card-action"
|
||||||
action={action}
|
action={action}
|
||||||
componentPrototype={componentPrototype}
|
componentPrototype={componentPrototype}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})
|
}) : null
|
||||||
: null}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1,23 +1,23 @@
|
|||||||
import Base, { AdditiveType } from "../base/index.js";
|
import Base, { AdditiveType } from '../base/index';
|
||||||
import Snippet from "../snippet";
|
import Snippet from '../snippet';
|
||||||
import "./index.less";
|
import './index.less';
|
||||||
|
|
||||||
// 滚动事件触发灵敏度
|
// 滚动事件触发灵敏度
|
||||||
const OFFSET_ACCURCY = 25;
|
const OFFSET_ACCURCY = 25;
|
||||||
|
|
||||||
const categoryMap = {
|
const categoryMap = {
|
||||||
General: "常用",
|
General: '常用',
|
||||||
Navigation: "导航",
|
Navigation: '导航',
|
||||||
DataEntry: "输入",
|
DataEntry: '输入',
|
||||||
DataDisplay: "展示",
|
DataDisplay: '展示',
|
||||||
Feedback: "反馈",
|
Feedback: '反馈',
|
||||||
Util: "工具",
|
Util: '工具',
|
||||||
Chart: "图表",
|
Chart: '图表',
|
||||||
Others: "其他",
|
Others: '其他',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class ComponentList extends Base {
|
export default class ComponentList extends Base {
|
||||||
static displayName = "ComponentList";
|
static displayName = 'ComponentList';
|
||||||
|
|
||||||
descRefList = new Map();
|
descRefList = new Map();
|
||||||
navRefList = new Map();
|
navRefList = new Map();
|
||||||
@ -28,10 +28,10 @@ export default class ComponentList extends Base {
|
|||||||
scroll;
|
scroll;
|
||||||
scrollTimer;
|
scrollTimer;
|
||||||
state = {
|
state = {
|
||||||
selected: "",
|
selected: '',
|
||||||
searchText: "",
|
searchText: '',
|
||||||
currentCard: null,
|
currentCard: null,
|
||||||
currentCardImage: ''
|
currentCardImage: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -40,7 +40,7 @@ export default class ComponentList extends Base {
|
|||||||
|
|
||||||
// mock 滚动结束事件
|
// mock 滚动结束事件
|
||||||
if (this.scroll) {
|
if (this.scroll) {
|
||||||
this.scroll.addEventListener("scroll", this.handleScrollEnd);
|
this.scroll.addEventListener('scroll', this.handleScrollEnd);
|
||||||
}
|
}
|
||||||
}, 20);
|
}, 20);
|
||||||
const bundle = this.normalizeBundle();
|
const bundle = this.normalizeBundle();
|
||||||
@ -60,7 +60,7 @@ export default class ComponentList extends Base {
|
|||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (this.scroll) {
|
if (this.scroll) {
|
||||||
this.scroll.removeEventListener("scroll", this.handleScrollEnd);
|
this.scroll.removeEventListener('scroll', this.handleScrollEnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ export default class ComponentList extends Base {
|
|||||||
fixSideBarView(selected) {
|
fixSideBarView(selected) {
|
||||||
const nav = this.navRefList.get(selected);
|
const nav = this.navRefList.get(selected);
|
||||||
if (nav) {
|
if (nav) {
|
||||||
nav.scrollIntoView({ block: "center" });
|
nav.scrollIntoView({ block: 'center' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,11 +167,11 @@ export default class ComponentList extends Base {
|
|||||||
{metaData.map((comp) => (
|
{metaData.map((comp) => (
|
||||||
<div
|
<div
|
||||||
className={`navigator-group-item ${AdditiveType.Draggable} ${
|
className={`navigator-group-item ${AdditiveType.Draggable} ${
|
||||||
selected === comp.id ? "active" : ""
|
selected === comp.id ? 'active' : ''
|
||||||
}`}
|
}`}
|
||||||
key={comp.id}
|
key={comp.id}
|
||||||
data-id={
|
data-id={
|
||||||
(comp.snippets && comp.snippets[0] && comp.snippets[0].id) || ""
|
(comp.snippets && comp.snippets[0] && comp.snippets[0].id) || ''
|
||||||
}
|
}
|
||||||
ref={(item) => {
|
ref={(item) => {
|
||||||
this.navRefList.set(comp.id, item);
|
this.navRefList.set(comp.id, item);
|
||||||
@ -186,7 +186,7 @@ export default class ComponentList extends Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderComponentDescriptionList(bundle) {
|
renderComponentDescriptionList(bundle) {
|
||||||
const { renderCustomSnippet = "", enableCard = true } = this.props;
|
const { renderCustomSnippet = '', enableCard = true } = this.props;
|
||||||
return (
|
return (
|
||||||
<div className="component-description-list">
|
<div className="component-description-list">
|
||||||
{Object.keys(bundle).map((cat) => {
|
{Object.keys(bundle).map((cat) => {
|
||||||
@ -208,8 +208,7 @@ export default class ComponentList extends Base {
|
|||||||
{comp.title}
|
{comp.title}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="component-description-item-icon-group">
|
<div className="component-description-item-icon-group" />
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="component-description-item-snippets">
|
<div className="component-description-item-snippets">
|
||||||
{comp.snippets &&
|
{comp.snippets &&
|
||||||
@ -227,7 +226,7 @@ export default class ComponentList extends Base {
|
|||||||
this.setState({
|
this.setState({
|
||||||
currentCard: comp,
|
currentCard: comp,
|
||||||
target: this.snippetMap.get(`${comp.id}-${idx}`),
|
target: this.snippetMap.get(`${comp.id}-${idx}`),
|
||||||
currentCardImage: snippet.thumbnail
|
currentCardImage: snippet.thumbnail,
|
||||||
});
|
});
|
||||||
this.timer = null;
|
this.timer = null;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
@ -242,7 +241,7 @@ export default class ComponentList extends Base {
|
|||||||
if (this.isMouseEnterCard) {
|
if (this.isMouseEnterCard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({currentCard: null});
|
this.setState({ currentCard: null });
|
||||||
}, 200);
|
}, 200);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -270,7 +269,7 @@ export default class ComponentList extends Base {
|
|||||||
key="content"
|
key="content"
|
||||||
className="ve-component-list-content"
|
className="ve-component-list-content"
|
||||||
onScroll={(e) => this.handleScroll(e)}
|
onScroll={(e) => this.handleScroll(e)}
|
||||||
ref={(scroll) => (this.scroll = scroll)}
|
ref={(scroll) => { this.scroll = scroll; }}
|
||||||
>
|
>
|
||||||
{this.renderComponentDescriptionList(bundle)}
|
{this.renderComponentDescriptionList(bundle)}
|
||||||
</div>,
|
</div>,
|
||||||
@ -1,41 +1,41 @@
|
|||||||
import { Fragment } from "react";
|
import { Fragment } from 'react';
|
||||||
import { Icon } from "@alifd/next";
|
import { Icon } from '@alifd/next';
|
||||||
import { AdditiveType } from "../base";
|
import { AdditiveType } from '../base';
|
||||||
import "./index.less";
|
import './index.less';
|
||||||
|
|
||||||
const Snippet = (props) => {
|
const Snippet = (props) => {
|
||||||
const {
|
const {
|
||||||
snippet,
|
snippet,
|
||||||
renderCustomSnippet = "",
|
renderCustomSnippet = '',
|
||||||
size = "small",
|
size = 'small',
|
||||||
actionsInLT,
|
actionsInLT,
|
||||||
actionsInRT,
|
actionsInRT,
|
||||||
} = props;
|
} = props;
|
||||||
const {
|
const {
|
||||||
thumbnail = "https://img.alicdn.com/tfs/TB1XHG6ehrI8KJjy0FpXXb5hVXa-740-608.png",
|
thumbnail = 'https://img.alicdn.com/tfs/TB1XHG6ehrI8KJjy0FpXXb5hVXa-740-608.png',
|
||||||
description,
|
description,
|
||||||
title = "未知"
|
title = '未知',
|
||||||
} = snippet;
|
} = snippet;
|
||||||
const snippetClassName = `component-description-item-snippet ${AdditiveType.All} ${size}`;
|
const snippetClassName = `component-description-item-snippet ${AdditiveType.All} ${size}`;
|
||||||
return (
|
return (
|
||||||
<div className={snippetClassName} key={snippet.id} data-id={snippet.id}>
|
<div className={snippetClassName} key={snippet.id} data-id={snippet.id}>
|
||||||
{typeof renderCustomSnippet === "function" ? (
|
{typeof renderCustomSnippet === 'function' ? (
|
||||||
renderCustomSnippet(snippet)
|
renderCustomSnippet(snippet)
|
||||||
) : (
|
) : (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div className="snippet-thumbnail">
|
<div className="snippet-thumbnail">
|
||||||
{typeof thumbnail === "string" && thumbnail.startsWith("http") ? (
|
{typeof thumbnail === 'string' && thumbnail.startsWith('http') ? (
|
||||||
<img alt="thumbnail" src={thumbnail} />
|
<img alt="thumbnail" src={thumbnail} />
|
||||||
) : (
|
) : (
|
||||||
<Icon className="icon" type={thumbnail || "help"} />
|
<Icon className="icon" type={thumbnail || 'help'} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="snippet-title">{description || title}</div>
|
<div className="snippet-title">{description || title}</div>
|
||||||
<div className="engine-additive-helper left-top">
|
<div className="engine-additive-helper left-top">
|
||||||
{actionsInLT ? actionsInLT : null}
|
{actionsInLT || null}
|
||||||
</div>
|
</div>
|
||||||
<div className="engine-additive-helper right-top">
|
<div className="engine-additive-helper right-top">
|
||||||
{actionsInRT ? actionsInRT : null}
|
{actionsInRT || null}
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
@ -39,7 +39,7 @@ if (PSEUDO_LANGUAGE_TAG === CURRENT_LANGUAGE) {
|
|||||||
update(CURRENT_LANGUAGE);
|
update(CURRENT_LANGUAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
export default {
|
||||||
get,
|
get,
|
||||||
update,
|
update,
|
||||||
};
|
};
|
||||||
@ -1,4 +1,4 @@
|
|||||||
module.exports = {
|
export default {
|
||||||
'en-US': require('./en-US.json'),
|
'en-US': require('./en-US.json'),
|
||||||
'zh-CN': require('./zh-CN.json'),
|
'zh-CN': require('./zh-CN.json'),
|
||||||
};
|
};
|
||||||
@ -1,15 +1,15 @@
|
|||||||
import { Component, ReactNode } from 'react';
|
import { Component, ReactNode } from 'react';
|
||||||
import { Tab } from '@alifd/next';
|
import { Tab } from '@alifd/next';
|
||||||
import ComponentList from "./components/component-list";
|
import ComponentList from './components/component-list';
|
||||||
import { AdditiveType } from "./components/base"
|
import { AdditiveType } from './components/base';
|
||||||
import { PluginProps } from '@ali/lowcode-types';
|
import { PluginProps } from '@ali/lowcode-types';
|
||||||
import { Designer } from '@ali/lowcode-designer';
|
import { Designer } from '@ali/lowcode-designer';
|
||||||
|
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
export interface IState {
|
export interface IState {
|
||||||
metaData: object[];
|
metaData: Record<string, unknown>[];
|
||||||
bizComponents: object[];
|
bizComponents: Record<string, unknown>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ComponentListPlugin extends Component<PluginProps, IState> {
|
export default class ComponentListPlugin extends Component<PluginProps, IState> {
|
||||||
@ -35,7 +35,10 @@ export default class ComponentListPlugin extends Component<PluginProps, IState>
|
|||||||
}
|
}
|
||||||
|
|
||||||
transformMetaData(componentList: any): any {
|
transformMetaData(componentList: any): any {
|
||||||
const metaData: object[] = [];
|
const metaData: Record<string, unknown>[] = [];
|
||||||
|
if (!componentList || !Array.isArray(componentList) || !componentList.length) {
|
||||||
|
return metaData;
|
||||||
|
}
|
||||||
componentList.forEach((category: any, categoryId: number) => {
|
componentList.forEach((category: any, categoryId: number) => {
|
||||||
if (Array.isArray(category?.children)) {
|
if (Array.isArray(category?.children)) {
|
||||||
category.children.forEach((comp: any, compId: number) => {
|
category.children.forEach((comp: any, compId: number) => {
|
||||||
@ -98,20 +101,7 @@ export default class ComponentListPlugin extends Component<PluginProps, IState>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const click = (e: Event) => {
|
const click = (e: Event) => { console.log(e); };
|
||||||
if (
|
|
||||||
(e.target.tagName === 'ICON'
|
|
||||||
&& e.target.parentNode
|
|
||||||
&& e.target.parentNode.classList.contains('engine-additive-helper'))
|
|
||||||
|| e.target.classList.contains('engine-additive-helper')
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const snippetId = getSnippetId(e.target, AdditiveType.Clickable);
|
|
||||||
if (!snippetId || !this.snippetsMap.get(snippetId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
shell.addEventListener('click', click);
|
shell.addEventListener('click', click);
|
||||||
|
|
||||||
@ -136,22 +126,22 @@ export default class ComponentListPlugin extends Component<PluginProps, IState>
|
|||||||
return (
|
return (
|
||||||
<div className="lowcode-component-list">
|
<div className="lowcode-component-list">
|
||||||
<Tab>
|
<Tab>
|
||||||
<Tab.Item title="基础组件" key="base-components">
|
<Tab.Item title="基础组件" key="base-components">
|
||||||
<ComponentList
|
<ComponentList
|
||||||
key="component-pane"
|
key="component-pane"
|
||||||
metaData={metaData}
|
metaData={metaData}
|
||||||
registerAdditive={(shell: Element | null) => this.registerAdditive(shell)}
|
registerAdditive={(shell: Element | null) => this.registerAdditive(shell)}
|
||||||
enableSearch
|
enableSearch
|
||||||
/>
|
/>
|
||||||
</Tab.Item>
|
</Tab.Item>
|
||||||
<Tab.Item title="业务组件" key="biz-components">
|
<Tab.Item title="业务组件" key="biz-components">
|
||||||
<ComponentList
|
<ComponentList
|
||||||
key="component-pane"
|
key="component-pane"
|
||||||
metaData={bizComponents}
|
metaData={bizComponents}
|
||||||
registerAdditive={(shell: Element | null) => this.registerAdditive(shell)}
|
registerAdditive={(shell: Element | null) => this.registerAdditive(shell)}
|
||||||
enableSearch
|
enableSearch
|
||||||
/>
|
/>
|
||||||
</Tab.Item>
|
</Tab.Item>
|
||||||
</Tab>
|
</Tab>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,99 +0,0 @@
|
|||||||
export const builtinSearchMap = {
|
|
||||||
"容器": "容器、rongqi、rq、分栏、ColumnsLayout、Columns、layout、grid",
|
|
||||||
"分栏": "分栏、ColumnsLayout、Columns、layout、grid、容器、rongqi、rq、fl、fenlan",
|
|
||||||
"卡片": "卡片、card、kapian、kp",
|
|
||||||
"选项卡": "选项卡、tab、tabs、页签、xuanxiangka、xxk、yeqian、yq",
|
|
||||||
"按钮": "按钮、button、anniu、an",
|
|
||||||
"图标": "图标、icon、tubiao、tb",
|
|
||||||
"标题": "标题、title、biaoti、bt",
|
|
||||||
"图片": "图片、image、pic、picture、tupian、tp",
|
|
||||||
"Dialog": "对话框、Dialog、弹框、弹出框、duihuakuang、dhk",
|
|
||||||
"Drawer": "抽屉、Drawer、chouti、ct、couti",
|
|
||||||
"文本": "文本、文字、text、wenzi、wz、wenben",
|
|
||||||
"链接": "链接、link、lianjie、lj",
|
|
||||||
"链接块": "链接块、链接、link、lianjie、lj、ljk",
|
|
||||||
"表单容器": "表单容器、表单、form、biaodan、bd",
|
|
||||||
"输入框": "输入框、文本框、密码框、input、shurukuang、srk、wenbenkuang、wbk",
|
|
||||||
"数字输入框": "数字输入框、数字、输入框、Number、NumberPicker、shuzi、sz、srk、shurukuang",
|
|
||||||
"单选": "单选、radio button、radio、danxuan、dx",
|
|
||||||
"多选": "复选、复选框、多选、Checkbox、check、fuxuan、fx、dx、duoxuan",
|
|
||||||
"下拉选择": "下拉选择、Select、选择器、下拉、xiala、xl、xialaxuanze、xlxz",
|
|
||||||
"开关": "开关、switch、kaiguan、kg",
|
|
||||||
"日期": "日期选择、date、DatePicker、riqi、riqixuanz、rq、rqxz",
|
|
||||||
"日期区间": "日期区间、Cascade、date、riqiqujian、rq",
|
|
||||||
"时间选择框": "时间选择、time、TimePicker、shijian、sj、shijianxuanze、sjxz、xuanzekuang",
|
|
||||||
"上传图片": "上传图片、upload、上传、shangchuan、sc、tupian",
|
|
||||||
"上传附件": "上传附件、upload、上传、shangchuan、sc、fujian",
|
|
||||||
"树形选择": "树形选择、树型选择、树选择、tree、TreeSelect、shu、sxz、shuxingxuanze",
|
|
||||||
"级联选择": "级联选择、Cascade、Cascadeselect、级联、jilian、jl、jilianxuanze、jlxz",
|
|
||||||
"地区选择": "地区选择、city、地址、address、地区、diqu、dq、diquxuanze、dqxz",
|
|
||||||
"国家选择": "国家选择、country、国家、guojia、gj、guojiaxuanze、gjxz",
|
|
||||||
"评分": "评分、Rate、Rating、星、pingfen、pf",
|
|
||||||
"明细": "明细、table、表格、表单、form、mingxi、mx",
|
|
||||||
"穿梭框": "穿梭框、transfer、chuansuokuang、csk",
|
|
||||||
"人员搜索框": "人员搜索框、employee、人员选择、选人、xuanren、xr、renyuansousuo、ryss",
|
|
||||||
"筛选": "筛选、pickable、shaixuan、sx",
|
|
||||||
"金额输入框": "金额输入框、输入框、shurukuang、srk、money、金额、jine、je",
|
|
||||||
"金额区间": "金额区间、money、金额、jine、je",
|
|
||||||
"查询": "查询、filter、chaxun、cx",
|
|
||||||
"表格": "表格、table、biaoge、bg",
|
|
||||||
"数据文本": "数据文本、Number Info、数据、shuju、sj、shujuwenben、sjwb",
|
|
||||||
"数据趋势": "数据趋势、Number Trend、数据、shuju、sj、shujuqushi、sjqs",
|
|
||||||
"勾选框": "勾选框、复选框、check box、gouxuankuang、gxk、fuxuankuang、fxk",
|
|
||||||
"图片浏览": "图片浏览、图片预览、image、pic、picture、图片、预览、tupianyulan、tupianliulan、tupian、tp、yulan、yl",
|
|
||||||
"搜索": "搜索、搜索框、查询框、查询、search、sousuo、ss",
|
|
||||||
"树形控件": "树形控件、树组件、tree、shuzujian、shuxingkongjian、shu、szj、sxkj",
|
|
||||||
"富文本框": "富文本框、RichText、fuwenben、fwb",
|
|
||||||
"步骤": "步骤、步骤条、step、steps、buzhoutiao、buzhou、bzt、bz",
|
|
||||||
"时间轴": "时间轴、时间线、timeline、shijianzhou、shijianxian、sjz、sjx",
|
|
||||||
"菜单": "菜单、menu、caidan、cd",
|
|
||||||
"气泡提示": "气泡提示、tip、tips、balloon、气泡、qipao、qp、qipaotishi、qpts",
|
|
||||||
"面包屑": "面包屑、breadcrumb、crumb、mianbaoxie、mbx",
|
|
||||||
"日历": "日历、calendar、rili、rl",
|
|
||||||
"折叠面板": "折叠面板、collapse、折叠、zhedie、zd、zhediemianban、zdmb",
|
|
||||||
"下拉菜单": "下拉菜单、dropdown、下拉、xiala、xl、xialacaidan、xlcd、菜单、caidan、cd",
|
|
||||||
"信息提示": "信息提示、message、alert、信息、提示、警示、xinxitishi、xxts、xinxi、xx、tishi、ts、消息、xiaoxi",
|
|
||||||
"进度指示器": "进度指示器、进度条、progress、jindutiao、jdt、进度、jindu、jd",
|
|
||||||
"翻页器": "翻页器、分页器、pagination、fanyeqi、fyq、分页、fenye、fy",
|
|
||||||
"轮播图": "轮播图、图片轮播、slider、轮播、lunbo、lb、lunbotu、lbt",
|
|
||||||
"底部通栏": "底部通栏、tool bar、通栏、dibutonglan、dbtl、浮动工具条、浮动、工具条、工具、fudong、gongju、toolbar、tool bar、fd、gj",
|
|
||||||
"HTML": "html",
|
|
||||||
"JSX": "jsx",
|
|
||||||
"浮动导航": "浮动导航、nav、floatNav、fudongdaohang、fddh",
|
|
||||||
"Iframe": "Iframe",
|
|
||||||
"Markdown": "Markdown",
|
|
||||||
"区段选择器": "区段选择、滑块选择、区段、滑块、选择、Range、quduan、huakuai、xuanze、qdxz、hkxz、xz"
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {string} title 组件名
|
|
||||||
* @param {string} query 搜索词
|
|
||||||
* @param {object} map 映射关系
|
|
||||||
*/
|
|
||||||
export function searchComponent(title, query, map = {}) {
|
|
||||||
if (!title || !query || !map || !map[title]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const keys = (map[title] || '').split('、');
|
|
||||||
return !!keys.find(key => {
|
|
||||||
if (!key) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return key.indexOf(query) > -1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function debounce(func, delay) {
|
|
||||||
let timer
|
|
||||||
return function(...args) {
|
|
||||||
if (timer) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
timer = setTimeout(() => {
|
|
||||||
func.apply(this, args)
|
|
||||||
clearTimeout(timer)
|
|
||||||
timer = null
|
|
||||||
}, delay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
98
packages/plugin-components-pane/src/utils.ts
Normal file
98
packages/plugin-components-pane/src/utils.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
export const builtinSearchMap = {
|
||||||
|
容器: '容器、rongqi、rq、分栏、ColumnsLayout、Columns、layout、grid',
|
||||||
|
分栏: '分栏、ColumnsLayout、Columns、layout、grid、容器、rongqi、rq、fl、fenlan',
|
||||||
|
卡片: '卡片、card、kapian、kp',
|
||||||
|
选项卡: '选项卡、tab、tabs、页签、xuanxiangka、xxk、yeqian、yq',
|
||||||
|
按钮: '按钮、button、anniu、an',
|
||||||
|
图标: '图标、icon、tubiao、tb',
|
||||||
|
标题: '标题、title、biaoti、bt',
|
||||||
|
图片: '图片、image、pic、picture、tupian、tp',
|
||||||
|
Dialog: '对话框、Dialog、弹框、弹出框、duihuakuang、dhk',
|
||||||
|
Drawer: '抽屉、Drawer、chouti、ct、couti',
|
||||||
|
文本: '文本、文字、text、wenzi、wz、wenben',
|
||||||
|
链接: '链接、link、lianjie、lj',
|
||||||
|
链接块: '链接块、链接、link、lianjie、lj、ljk',
|
||||||
|
表单容器: '表单容器、表单、form、biaodan、bd',
|
||||||
|
输入框: '输入框、文本框、密码框、input、shurukuang、srk、wenbenkuang、wbk',
|
||||||
|
数字输入框: '数字输入框、数字、输入框、Number、NumberPicker、shuzi、sz、srk、shurukuang',
|
||||||
|
单选: '单选、radio button、radio、danxuan、dx',
|
||||||
|
多选: '复选、复选框、多选、Checkbox、check、fuxuan、fx、dx、duoxuan',
|
||||||
|
下拉选择: '下拉选择、Select、选择器、下拉、xiala、xl、xialaxuanze、xlxz',
|
||||||
|
开关: '开关、switch、kaiguan、kg',
|
||||||
|
日期: '日期选择、date、DatePicker、riqi、riqixuanz、rq、rqxz',
|
||||||
|
日期区间: '日期区间、Cascade、date、riqiqujian、rq',
|
||||||
|
时间选择框: '时间选择、time、TimePicker、shijian、sj、shijianxuanze、sjxz、xuanzekuang',
|
||||||
|
上传图片: '上传图片、upload、上传、shangchuan、sc、tupian',
|
||||||
|
上传附件: '上传附件、upload、上传、shangchuan、sc、fujian',
|
||||||
|
树形选择: '树形选择、树型选择、树选择、tree、TreeSelect、shu、sxz、shuxingxuanze',
|
||||||
|
级联选择: '级联选择、Cascade、Cascadeselect、级联、jilian、jl、jilianxuanze、jlxz',
|
||||||
|
地区选择: '地区选择、city、地址、address、地区、diqu、dq、diquxuanze、dqxz',
|
||||||
|
国家选择: '国家选择、country、国家、guojia、gj、guojiaxuanze、gjxz',
|
||||||
|
评分: '评分、Rate、Rating、星、pingfen、pf',
|
||||||
|
明细: '明细、table、表格、表单、form、mingxi、mx',
|
||||||
|
穿梭框: '穿梭框、transfer、chuansuokuang、csk',
|
||||||
|
人员搜索框: '人员搜索框、employee、人员选择、选人、xuanren、xr、renyuansousuo、ryss',
|
||||||
|
筛选: '筛选、pickable、shaixuan、sx',
|
||||||
|
金额输入框: '金额输入框、输入框、shurukuang、srk、money、金额、jine、je',
|
||||||
|
金额区间: '金额区间、money、金额、jine、je',
|
||||||
|
查询: '查询、filter、chaxun、cx',
|
||||||
|
表格: '表格、table、biaoge、bg',
|
||||||
|
数据文本: '数据文本、Number Info、数据、shuju、sj、shujuwenben、sjwb',
|
||||||
|
数据趋势: '数据趋势、Number Trend、数据、shuju、sj、shujuqushi、sjqs',
|
||||||
|
勾选框: '勾选框、复选框、check box、gouxuankuang、gxk、fuxuankuang、fxk',
|
||||||
|
图片浏览: '图片浏览、图片预览、image、pic、picture、图片、预览、tupianyulan、tupianliulan、tupian、tp、yulan、yl',
|
||||||
|
搜索: '搜索、搜索框、查询框、查询、search、sousuo、ss',
|
||||||
|
树形控件: '树形控件、树组件、tree、shuzujian、shuxingkongjian、shu、szj、sxkj',
|
||||||
|
富文本框: '富文本框、RichText、fuwenben、fwb',
|
||||||
|
步骤: '步骤、步骤条、step、steps、buzhoutiao、buzhou、bzt、bz',
|
||||||
|
时间轴: '时间轴、时间线、timeline、shijianzhou、shijianxian、sjz、sjx',
|
||||||
|
菜单: '菜单、menu、caidan、cd',
|
||||||
|
气泡提示: '气泡提示、tip、tips、balloon、气泡、qipao、qp、qipaotishi、qpts',
|
||||||
|
面包屑: '面包屑、breadcrumb、crumb、mianbaoxie、mbx',
|
||||||
|
日历: '日历、calendar、rili、rl',
|
||||||
|
折叠面板: '折叠面板、collapse、折叠、zhedie、zd、zhediemianban、zdmb',
|
||||||
|
下拉菜单: '下拉菜单、dropdown、下拉、xiala、xl、xialacaidan、xlcd、菜单、caidan、cd',
|
||||||
|
信息提示: '信息提示、message、alert、信息、提示、警示、xinxitishi、xxts、xinxi、xx、tishi、ts、消息、xiaoxi',
|
||||||
|
进度指示器: '进度指示器、进度条、progress、jindutiao、jdt、进度、jindu、jd',
|
||||||
|
翻页器: '翻页器、分页器、pagination、fanyeqi、fyq、分页、fenye、fy',
|
||||||
|
轮播图: '轮播图、图片轮播、slider、轮播、lunbo、lb、lunbotu、lbt',
|
||||||
|
底部通栏: '底部通栏、tool bar、通栏、dibutonglan、dbtl、浮动工具条、浮动、工具条、工具、fudong、gongju、toolbar、tool bar、fd、gj',
|
||||||
|
HTML: 'html',
|
||||||
|
JSX: 'jsx',
|
||||||
|
浮动导航: '浮动导航、nav、floatNav、fudongdaohang、fddh',
|
||||||
|
Iframe: 'Iframe',
|
||||||
|
Markdown: 'Markdown',
|
||||||
|
区段选择器: '区段选择、滑块选择、区段、滑块、选择、Range、quduan、huakuai、xuanze、qdxz、hkxz、xz',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} title 组件名
|
||||||
|
* @param {string} query 搜索词
|
||||||
|
* @param {object} map 映射关系
|
||||||
|
*/
|
||||||
|
export function searchComponent(title, query, map = {}) {
|
||||||
|
if (!title || !query || !map || !map[title]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const keys = (map[title] || '').split('、');
|
||||||
|
return !!keys.find(key => {
|
||||||
|
if (!key) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return key.indexOf(query) > -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function debounce(func, delay) {
|
||||||
|
let timer;
|
||||||
|
return function (...args) {
|
||||||
|
if (timer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
func.apply(this, args);
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = null;
|
||||||
|
}, delay);
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user