From e9d8d3dbe698a4f3b3dad736ac959dd581bd01b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E7=A6=85?= Date: Wed, 9 Sep 2020 21:53:44 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E7=BB=84=E4=BB=B6=E9=9D=A2?= =?UTF-8?q?=E6=9D=BF=E6=94=AF=E6=8C=81=E4=B8=9A=E5=8A=A1=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E7=8B=AC=E7=AB=8B=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/demo/public/assets.json | 50 ++-- packages/plugin-components-pane/package.json | 1 - .../src/components/base/index.js | 270 +++++++++++++++++ .../src/components/base/index.less | 116 ++++++++ .../src/components/button/index.js | 22 ++ .../src/components/button/index.less | 18 ++ .../src/components/card/index.js | 147 +++++++++ .../src/components/card/index.less | 130 ++++++++ .../src/components/component-list/index.js | 279 ++++++++++++++++++ .../src/components/component-list/index.less | 117 ++++++++ .../src/components/snippet/index.js | 46 +++ .../src/components/snippet/index.less | 120 ++++++++ .../plugin-components-pane/src/i18n/index.js | 45 +++ .../src/i18n/strings/en-US.json | 27 ++ .../src/i18n/strings/index.js | 4 + .../src/i18n/strings/zh-CN.json | 26 ++ packages/plugin-components-pane/src/index.tsx | 34 ++- packages/plugin-components-pane/src/utils.js | 99 +++++++ 18 files changed, 1518 insertions(+), 33 deletions(-) create mode 100644 packages/plugin-components-pane/src/components/base/index.js create mode 100644 packages/plugin-components-pane/src/components/base/index.less create mode 100644 packages/plugin-components-pane/src/components/button/index.js create mode 100644 packages/plugin-components-pane/src/components/button/index.less create mode 100644 packages/plugin-components-pane/src/components/card/index.js create mode 100644 packages/plugin-components-pane/src/components/card/index.less create mode 100644 packages/plugin-components-pane/src/components/component-list/index.js create mode 100644 packages/plugin-components-pane/src/components/component-list/index.less create mode 100644 packages/plugin-components-pane/src/components/snippet/index.js create mode 100644 packages/plugin-components-pane/src/components/snippet/index.less create mode 100644 packages/plugin-components-pane/src/i18n/index.js create mode 100644 packages/plugin-components-pane/src/i18n/strings/en-US.json create mode 100644 packages/plugin-components-pane/src/i18n/strings/index.js create mode 100644 packages/plugin-components-pane/src/i18n/strings/zh-CN.json create mode 100644 packages/plugin-components-pane/src/utils.js diff --git a/packages/demo/public/assets.json b/packages/demo/public/assets.json index 24b3061c6..ef8650e05 100644 --- a/packages/demo/public/assets.json +++ b/packages/demo/public/assets.json @@ -13107,30 +13107,6 @@ } ], "componentList": [ - { - "title": "Util", - "children": [ - { - "componentName": "Dropdown", - "title": "下拉菜单", - "icon": "", - "package": "@alifd/next", - "library": "Next", - "snippets": [ - { - "title": "下拉菜单", - "screenshot": "", - "schema": { - "componentName": "Dropdown", - "props": { "trigger": [{ "componentName": "Button", "props": { "type": "primary" }, "children": "确定" }], "triggerType": "click"}, - "children": [{ "componentName": "Menu", "props": { "style": { "width": 200 } }, "children": [{ "componentName": "Menu.Item", "props": {}, "children": "Option 1" }, { "componentName": "Menu.Item", "props": { "disabled": false }, "children": "option 2" }, { "componentName": "Menu.Item", "props": { "disabled": false }, "children": "option 3" }]}] - } - } - ] - } - ], - "icon": "" - }, { "title": "DataDisplay", "children": [ @@ -15388,5 +15364,31 @@ ], "icon": "" } + ], + "bizComponentList": [ + { + "title": "Util", + "children": [ + { + "componentName": "Dropdown", + "title": "下拉菜单", + "icon": "", + "package": "@alifd/next", + "library": "Next", + "snippets": [ + { + "title": "下拉菜单", + "screenshot": "", + "schema": { + "componentName": "Dropdown", + "props": { "trigger": [{ "componentName": "Button", "props": { "type": "primary" }, "children": "确定" }], "triggerType": "click"}, + "children": [{ "componentName": "Menu", "props": { "style": { "width": 200 } }, "children": [{ "componentName": "Menu.Item", "props": {}, "children": "Option 1" }, { "componentName": "Menu.Item", "props": { "disabled": false }, "children": "option 2" }, { "componentName": "Menu.Item", "props": { "disabled": false }, "children": "option 3" }]}] + } + } + ] + } + ], + "icon": "" + } ] } diff --git a/packages/plugin-components-pane/package.json b/packages/plugin-components-pane/package.json index 47686b482..36060b79b 100644 --- a/packages/plugin-components-pane/package.json +++ b/packages/plugin-components-pane/package.json @@ -23,7 +23,6 @@ "@ali/lowcode-designer": "^1.0.8-0", "@ali/lowcode-editor-core": "^1.0.8-0", "@ali/lowcode-types": "^1.0.8-0", - "@ali/ve-component-list": "^1.1.1", "@alifd/next": "^1.19.19", "react": "^16.8.1" }, diff --git a/packages/plugin-components-pane/src/components/base/index.js b/packages/plugin-components-pane/src/components/base/index.js new file mode 100644 index 000000000..97283f518 --- /dev/null +++ b/packages/plugin-components-pane/src/components/base/index.js @@ -0,0 +1,270 @@ +import React from "react"; +import { Search, Box } from "@alifd/next"; +import Button from "../button/index.js"; +import Card from "../card"; +import $i18n from "../../i18n/index.js"; +import { searchComponent, builtinSearchMap } from "../../utils"; +import "./index.less"; + +/** + * 配置元素的操作类型 + * Draggable:可拖拽 + * Clickable:可点击 + * All:可拖拽也可点击 + */ +export const AdditiveType = { + Draggable: "additive-drag", + Clickable: "additive-click", + All: "additive", +}; + +class Base extends React.Component { + static propTypes = { + metaData: PropTypes.array, + className: PropTypes.string, + registerAdditive: PropTypes.func, + renderCustomSnippet: PropTypes.func, + actions: PropTypes.array, + getComponentInfo: PropTypes.func, + enableSearch: PropTypes.bool, + enableCard: PropTypes.bool, + enableReport: PropTypes.bool, + placeholder: PropTypes.string, + }; + + static defaultProps = { + metaData: [], + registerAdditive: () => { + return; + }, + className: "", + renderCustomSnippet: null, + actions: [], + getComponentInfo: null, + enableSearch: false, + enableCard: true, + enableReport: true, + placeholder: "", + }; + + state = { + selected: "", + searchText: "", + currentCard: null, + target: null, + currentCardImage: '', + }; + + shell = null; + isEmpty = false; + mode = 'advance'; + searchMap = {}; + timer = null; + isMouseEnterCard = false; + + + componentWillMount() { + // TODO get remote search map + this.searchMap = builtinSearchMap; + } + + hasActions() { + const { actions } = this.props; + if (!actions || !Array.isArray(actions) || !actions.length) { + return false; + } + return true; + } + + onSearch(val) { + this.setState({ + searchText: val, + currentCard: null, // 清空卡片 + }); + } + + normalizeBundle(mode) { + const { metaData } = this.props; + const { searchText = "" } = this.state; + const groupList = metaData.filter((comp, index) => { + const { title = "", componentName = "", id = "" } = comp; + if (!id) { + comp.id = `${comp.componentName}_${index}`; + } + const query = searchText.toLowerCase(); + return ( + !![title, componentName].find( + (it) => it.toLowerCase().indexOf(query) > -1 + ) || !!searchComponent(title, query, this.searchMap) + ); + }); + + if (mode === 'simple') { + return groupList; + } + + let bundle = {}; + // 按一定顺序排列 + groupList.forEach((m) => { + const c = m.category || "Others"; + if (!bundle[c]) { + bundle[c] = []; + } + bundle[c].push(m); + }); + return bundle; + } + + renderEmptyData() { + this.isEmpty = true; + return ( + + +
+
暂无组件,请在物料站点添加
+
+
+ ); + } + + renderHeader() { + const { placeholder } = this.props; + return ( + + ); + } + + renderActions() { + const { actions = [] } = this.props; + if (!this.hasActions()) { + return null; + } + const len = actions.length; + // TODO:len = 1:只有一个主按钮;len = 2:一个主按钮、一个次按钮;len >=3:一个主按钮、一个次按钮、其余放在按钮组里; + return actions.map((action, idx) => { + return ( + + ); + }); + } + + renderBundle() { + const { metaData } = this.props; + if (!metaData || !Array.isArray(metaData) || !metaData.length) { + return this.renderEmptyData(); + } + const bundle = this.normalizeBundle(this.mode); + if (!Object.keys(bundle).length || (Array.isArray(bundle) && bundle.length === 0)) { + return this.renderEmptyData(); + } + this.isEmpty = false; + return this.getBundle ? this.getBundle(bundle) : this.renderEmptyData(); + } + + renderCard() { + const { currentCard, target, currentCardImage } = this.state; + const { getComponentInfo } = this.props; + + if (!currentCard || !getComponentInfo) return null; + + return ( + { + this.setState({ currentCard: null }); + this.isMouseEnterCard = false; + }} + offset={{ + top: 10, + left: 10, + }} + onMouseEnter={() => { + this.isMouseEnterCard = true; + }} + /> + ); + } + + render() { + const { + enableSearch, + className, + registerAdditive = () => { + return; + }, + } = this.props; + let bodyExtraClass = ""; + if (this.hasActions() && enableSearch) { + bodyExtraClass = "small"; + } else if (!this.hasActions() && enableSearch) { + bodyExtraClass = "medium"; + } else if (this.hasActions() && !enableSearch) { + bodyExtraClass = "large"; + } else { + bodyExtraClass = ""; + } + + return ( +
+ {enableSearch ? ( +
+ {this.renderHeader()} +
+ ) : null} +
{ + if (this.shell || !shell || this.isEmpty) { + return; + } + if (!this.shell) { + this.shell = shell; + } + registerAdditive(shell); + }} + > + {this.renderBundle()} +
+
+ {this.renderActions()} +
+ {this.renderCard ? this.renderCard() : null} +
+ ); + } +} + +export default Base; diff --git a/packages/plugin-components-pane/src/components/base/index.less b/packages/plugin-components-pane/src/components/base/index.less new file mode 100644 index 000000000..d257a0cbe --- /dev/null +++ b/packages/plugin-components-pane/src/components/base/index.less @@ -0,0 +1,116 @@ +@import "~@ali/ve-less-variables/index.less"; + +.ve-component-list { + position: absolute; + top: 45px; + right: 0; + bottom: 0; + left: 0; + .ve-component-list-head { + height: 56px; + padding: 12px; + border-bottom: 1px solid var(--color-line-normal, rgba(31, 56, 88, 0.1)); + .ve-search-control { + width: 256px; + height: 32px; + margin: 0; + } + } + .ve-component-list-body { + display: flex; + height: 100%; + overflow: auto; + .ve-component-list-empty { + width: 100%; + padding: 20px; + text-align: center; + &.kuma-empty-data.normal .kuma-empty-data-icon { + width: 50px; + height: 50px; + background-size: 100% 100%; + display: inline-block; + } + } + .component-description-item-icon-group { + display: flex; + justify-content: flex-start; + align-items: center; + .component-description-item-icon { + margin-left: 4px; + &.tag { + display: flex; + justify-content: center; + align-items: center; + background: rgba(249, 189, 15, 0.1); + color: #ff6f00; + font-size: 12px; + width: 48px; + height: 24px; + } + } + icon { + position: relative; + font-size: 14px; + cursor: pointer; + color: rgba(31, 56, 88, 0.4); + transition: color @transition-duration @transition-ease; + &:hover { + color: rgba(31, 56, 88, 0.6); + } + a { + position: absolute; + left: 0; + top: 0; + display: inline-block; + width: 14px; + height: 14px; + } + } + } + &.small { + height: calc(100% - 56px - 48px); + } + &.medium { + height: calc(100% - 56px); + } + &.large { + height: calc(100% - 48px); + } + } + .ve-component-list-foot { + display: flex; + justify-content: center; + align-items: center; + position: absolute; + bottom: 0; + .btn { + margin-right: 8px; + flex: 1; + &:last-child { + margin-right: 0; + } + } + .kuma-button-primary { + background-color: var(--color-brand, @brand-color-1); + border-color: var(--color-brand, @brand-color-1); + border-radius: @global-border-radius; + color: var(--color-text-reverse, @white-alpha-2); + transition: background-color @transition-duration @transition-ease; + &:hover { + background-color: var(--color-brand-dark, @brand-color-3); + border-color: var(--color-brand-dark, @brand-color-3); + transition: background-color @transition-duration @transition-ease; + } + } + &.exist { + width: 100%; + height: 48px; + border-top: 1px solid var(--color-line-normal, rgba(31, 56, 88, 0.1)); + background: #fff; + padding: 0 12px; + } + } + ::-webkit-scrollbar { + display: none; + } +} diff --git a/packages/plugin-components-pane/src/components/button/index.js b/packages/plugin-components-pane/src/components/button/index.js new file mode 100644 index 000000000..d1d385e22 --- /dev/null +++ b/packages/plugin-components-pane/src/components/button/index.js @@ -0,0 +1,22 @@ +import { Button } from "@ali/vu-uxcore-legao-design"; +import './index.less'; + +const MyButton = (props) => { + const { action, componentPrototype, type, className } = props; + return ( + + ); +}; + +export default MyButton; diff --git a/packages/plugin-components-pane/src/components/button/index.less b/packages/plugin-components-pane/src/components/button/index.less new file mode 100644 index 000000000..ef62922e6 --- /dev/null +++ b/packages/plugin-components-pane/src/components/button/index.less @@ -0,0 +1,18 @@ +.ve-component-list { + .kuma-button-outline { + border-color: #0079f2; + color: #0079f2; + &:hover { + border-color: #0079f2; + color: #0079f2; + background-color: #f0f7ff; + } + &.kuma-button-danger { + border-color: #f04631; + color: #f04631; + &:hover { + background-color: rgba(240, 70, 49, 0.06); + } + } + } +} diff --git a/packages/plugin-components-pane/src/components/card/index.js b/packages/plugin-components-pane/src/components/card/index.js new file mode 100644 index 000000000..04883bf1e --- /dev/null +++ b/packages/plugin-components-pane/src/components/card/index.js @@ -0,0 +1,147 @@ +import React from "react"; +import Layer from "@ali/vu-layer"; +import { Icon } from "@alifd/next"; +import $i18n from "../../i18n/index"; +import Button from "../button"; +import "./index.less"; + +export default class Card extends React.Component { + static propTypes = {}; + + constructor(props) { + super(props); + + this.state = { + isLoading: true, + errorMsg: false, + componentInfo: {}, + }; + } + + componentDidMount() { + if (!this.props.getComponentInfo) return; + this.loadComponentInfo(this.props.componentPrototype); + } + + componentWillReceiveProps(nextProps) { + if (nextProps.componentPrototype !== this.props.componentPrototype) { + // 延迟执行数据加载和渲染,等 props 更新之后 + this.loadComponentInfo(nextProps.componentPrototype); + } + } + + loadComponentInfo(componentPrototype) { + if (!this.props.getComponentInfo) return; + this.setState({ isLoading: true }); + this.props + .getComponentInfo(componentPrototype) + .then((componentInfo) => { + this.setState({ + componentInfo, + isLoading: false, + }); + }) + .catch((e) => { + this.setState({ errorMsg: e.message }); + console.error(e); + if (VisualEngine) { + VisualEngine.ui.Popup.error({ + content: e.message, + duration: 2000, + }); + } + }); + } + + render() { + const { componentPrototype, subTitle, customImage } = this.props; + const { componentInfo } = this.state; + + const loadingContent = this.state.errorMsg ? ( +
+
{this.state.errorMsg}
+
+ ) : ( +
+ +
+ {$i18n.get({ id: "trunkPaneLoading", dm: "加载中..." })} +
+
+ ); + + const { + title, + version, + image = "https://img.alicdn.com/tfs/TB1XHG6ehrI8KJjy0FpXXb5hVXa-740-608.png", + desc, + detailUrl, + actions, + } = componentInfo; + + let layerContent = null; + const cardTitle = subTitle && !subTitle.includes(title) ? `${title}(${subTitle})` : title; + + if (this.state.isLoading) { + layerContent = ( +
+ {loadingContent} +
+ ); + } else { + layerContent = ( +
+
+
+ {cardTitle} + {version} +
+
+ {cardTitle} +
+
+
+
+

{desc}

+
+
+ {detailUrl ? ( + + {$i18n.get({ + id: "trunkPaneDetailedDocumentation", + dm: "详细文档", + })} + + ) : null} +
+ {actions + ? actions.map((action, idx) => { + return ( +
+
+
+
+ ); + } + + return ( + + {layerContent} + + ); + } +} diff --git a/packages/plugin-components-pane/src/components/card/index.less b/packages/plugin-components-pane/src/components/card/index.less new file mode 100644 index 000000000..00d7960eb --- /dev/null +++ b/packages/plugin-components-pane/src/components/card/index.less @@ -0,0 +1,130 @@ +@import "~@ali/ve-less-variables/index.less"; + +.ve-card-wrapper { + width: 320px; + max-height: 360px; + overflow: auto; + justify-content: center; + + &.ve-card-wrapper-loading { + display: flex; + justify-content: center; + text-align: center; + overflow: hidden; + } + + .ve-card-top { + height: 186px; + background: rgba(31, 56, 88, 0.1); + padding: 16px; + + .ve-card-title { + display: flex; + align-items: center; + padding-bottom: 12px; + position: relative; + height: 31px; + color: rgba(0, 0, 0, 0.8); + + .title { + line-height: 22px; + font-size: 16px; + } + + .version { + margin-left: 8px; + font-size: 12px; + line-height: 22px; + color: rgba(31, 56, 88, 0.4); + } + } + + .ve-card-image-wrapper { + background-color: #fff; + .ve-card-image { + max-width: 100%; + height: 120px; + margin: 0 auto; + display: block; + padding: 10px; + } + } + } + + .ve-card-bottom { + padding: 8px 16px 16px 16px; + .ve-card-description { + user-select: text; + text-align: left; + line-height: 18px; + font-size: 12px; + color: rgba(0, 0, 0, 0.6); + letter-spacing: 0; + line-height: 18px; + width: 280px; + + p { + margin-bottom: 8px; + } + + p:last-child { + margin-bottom: 0; + } + } + + .ve-operation-container { + margin-top: 12px; + position: relative; + .actions { + margin-top: 16px; + } + } + } + + .ve-operation-item { + color: @brand-color-1; + cursor: pointer; + margin-right: 8px; + } + + .ve-operation-delete { + position: absolute; + top: 0; + right: 0; + } + + .ve-card-more-operation { + display: flex; + justify-content: space-between; + > a { + cursor: pointer; + } + } + + .ve-loading-icon { + animation: spining 0.9s infinite linear; + } + .ve-loading-content { + padding: 10px; + text-align: center; + } + + .ve-card-action { + padding: 0 8px !important; + margin-right: 8px; + } +} + +.ve-component-list { + .vu-layer { + border-radius: 4px !important; + .vu-layer-content { + padding-top: 0 !important; + } + } + + .vu-layer .vu-layer-close { + right: 9px !important; + top: 9px !important; + } +} diff --git a/packages/plugin-components-pane/src/components/component-list/index.js b/packages/plugin-components-pane/src/components/component-list/index.js new file mode 100644 index 000000000..b14eb7f78 --- /dev/null +++ b/packages/plugin-components-pane/src/components/component-list/index.js @@ -0,0 +1,279 @@ +import Base, { AdditiveType } from "../base/index.js"; +import Snippet from "../snippet"; +import "./index.less"; + +// 滚动事件触发灵敏度 +const OFFSET_ACCURCY = 25; + +const categoryMap = { + General: "常用", + Navigation: "导航", + DataEntry: "输入", + DataDisplay: "展示", + Feedback: "反馈", + Util: "工具", + Chart: "图表", + Others: "其他", +}; + +export default class ComponentList extends Base { + static displayName = "ComponentList"; + + descRefList = new Map(); + navRefList = new Map(); + descHeightList = new Map(); + snippetMap = new Map(); + currentScrollHeight = 0; + scrollFlag = true; + scroll; + scrollTimer; + state = { + selected: "", + searchText: "", + currentCard: null, + currentCardImage: '' + }; + + componentDidMount() { + setTimeout(() => { + this.calDescHeightList(); + + // mock 滚动结束事件 + if (this.scroll) { + this.scroll.addEventListener("scroll", this.handleScrollEnd); + } + }, 20); + const bundle = this.normalizeBundle(); + if (!bundle) { + return; + } + const cats = Object.keys(bundle); + if (cats.length > 0) { + const k = cats[0]; + const comps = bundle[k]; + if (!comps || !Array.isArray(comps) || !comps.length) { + return; + } + this.setState({ selected: comps[0].id }); + } + } + + componentWillUnmount() { + if (this.scroll) { + this.scroll.removeEventListener("scroll", this.handleScrollEnd); + } + } + + handleScrollEnd = () => { + clearTimeout(this.scrollTimer); + this.scrollTimer = setTimeout(() => { + // 滚动结束时归位 + this.scrollFlag = true; + }, 100); + }; + + toggleComponent(id) { + const element = this.descRefList.get(id); + // 切换组件时滚动事件回调不可用 + this.scrollFlag = false; + element.scrollIntoView(); + this.setState({ selected: id }); + } + + handleScroll(e) { + // 清空卡片 + if (this.state.currentCard) { + this.setState({ currentCard: null }); + } + clearTimeout(this.timer); + this.timer = null; + + if (!this.scrollFlag) { + return; + } + const element = e.target; + const { scrollTop } = element; + + // 延迟处理 + if (Math.abs(scrollTop - this.currentScrollHeight) < OFFSET_ACCURCY) { + return; + } + this.currentScrollHeight = scrollTop; + + // 处理导航块滚动高亮效果 + const heightList = [...this.descHeightList.entries()]; + let selected; + // 当在顶部 + if (scrollTop >= 0 && scrollTop < heightList[0][1]) { + selected = heightList[0][0]; + } else if (scrollTop >= heightList[heightList.length - 1][1]) { + // 底部 + selected = heightList[heightList.length - 1][0]; + } else { + // 当在中部 + for (let i = 0; i < heightList.length - 2; i++) { + const height1 = heightList[i][1]; + const height2 = heightList[i + 1][1]; + if (scrollTop > height1 && scrollTop < height2) { + selected = heightList[i + 1][0]; + } + } + } + this.fixSideBarView(selected); + this.setState({ selected }); + } + + // 导航高亮块超出视口时滚动 + fixSideBarView(selected) { + const nav = this.navRefList.get(selected); + if (nav) { + nav.scrollIntoView({ block: "center" }); + } + } + + calDescHeightList() { + let height = 0; + this.descRefList.forEach((ele, key) => { + if (!ele) { + return; + } + height += ele.clientHeight; + this.descHeightList.set(key, height); + }); + } + + renderNavigator(bundle) { + return ( +
+ {Object.keys(bundle).map((c) => { + const catTitle = categoryMap[c] || c; + const catData = bundle[c]; + return this.renderNavigatorGroup(catTitle, catData); + })} +
+ ); + } + + renderNavigatorGroup(title, metaData) { + const { selected } = this.state; + if (!metaData) { + return null; + } + return ( +
+
+
{title}
+
+ {metaData.map((comp) => ( +
{ + this.navRefList.set(comp.id, item); + }} + onClick={() => this.toggleComponent(comp.id)} + > + {comp.title} +
+ ))} +
+ ); + } + + renderComponentDescriptionList(bundle) { + const { renderCustomSnippet = "", enableCard = true } = this.props; + return ( +
+ {Object.keys(bundle).map((cat) => { + const catData = bundle[cat]; + if (!catData) { + return null; + } + return catData.map((comp) => ( +
{ + this.descRefList.set(comp.id, item); + }} + > +
+ {comp.title ? ( +
+ {comp.title} +
+ ) : null} +
+
+
+
+ {comp.snippets && + comp.snippets.map((snippet, idx) => ( +
{ + this.snippetMap.set(`${comp.id}-${idx}`, i); + }} + onMouseEnter={() => { + if (!enableCard) { + return; + } + this.timer = setTimeout(() => { + this.setState({ + currentCard: comp, + target: this.snippetMap.get(`${comp.id}-${idx}`), + currentCardImage: snippet.thumbnail + }); + this.timer = null; + }, 1000); + }} + onMouseLeave={() => { + if (!enableCard) { + return; + } + clearTimeout(this.timer); + this.timer = null; + setTimeout(() => { + if (this.isMouseEnterCard) { + return; + } + this.setState({currentCard: null}); + }, 200); + }} + > + +
+ ))} +
+
+ )); + })} +
+ ); + } + + getBundle(bundle) { + return [ +
+ {this.renderNavigator(bundle)} +
, +
this.handleScroll(e)} + ref={(scroll) => (this.scroll = scroll)} + > + {this.renderComponentDescriptionList(bundle)} +
, + ]; + } +} diff --git a/packages/plugin-components-pane/src/components/component-list/index.less b/packages/plugin-components-pane/src/components/component-list/index.less new file mode 100644 index 000000000..62f60096f --- /dev/null +++ b/packages/plugin-components-pane/src/components/component-list/index.less @@ -0,0 +1,117 @@ +@import "~@ali/ve-less-variables/index.less"; + +@text-color-active: #0079f2; + +.ve-component-list { + .ve-component-list-body { + .ve-component-list-sidebar { + flex: 0 0 110px; + width: 110px; + // height: 100%; + padding-top: 11px; + border-right: 1px solid var(--color-line-normal, rgba(31, 56, 88, 0.1)); + overflow: auto; + .ve-component-list-navigator { + .navigator-group { + margin-bottom: 8px; + & > div { + display: flex; + align-items: center; + width: 100%; + } + .navigator-group-head { + padding: 0px 12px; + color: @dark-alpha-2; + .navigator-group-title { + padding: 4px 0; + width: 100%; + line-height: 16px; + font-size: @fontSize-5; + font-weight: 700; + border-bottom: 1px solid + var(--color-line-normal, rgba(31, 56, 88, 0.1)); + } + } + .navigator-group-item { + height: 32px; + padding: 8px 12px; + color: #666666; + cursor: pointer; + .navigator-group-item-title { + font-size: @fontSize-5; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-align: left; + } + &:hover { + color: @text-color-active; + } + &.active { + background: rgba(0, 121, 242, 0.1); + color: @text-color-active; + border-right: 2px solid @text-color-active; + } + } + } + } + } + .ve-component-list-content { + flex: 1; + width: 250px; + // height: 100%; + overflow-y: auto; + overflow-x: hidden; + .component-description-list { + padding: 10px 4px; + .component-description-item { + padding-bottom: 24px; + .component-description-item-header { + display: flex; + justify-content: flex-start; + align-items: center; + .component-description-item-title { + font-family: @font-family; + font-size: @fontSize-5; + font-weight: 700; + color: @text-color; + line-height: 20px; + padding-left: 8px; + } + .component-description-item-icon-group { + display: flex; + justify-content: flex-start; + align-items: center; + .component-description-item-icon { + margin-left: 4px; + } + icon { + position: relative; + font-size: 14px; + cursor: pointer; + color: rgba(31, 56, 88, 0.4); + transition: color @transition-duration @transition-ease; + &:hover { + color: rgba(31, 56, 88, 0.6); + } + a { + position: absolute; + left: 0; + top: 0; + display: inline-block; + width: 14px; + height: 14px; + } + } + } + } + .component-description-item-snippets { + display: flex; + justify-content: flex-start; + flex-flow: row wrap; + } + } + } + } + } +} diff --git a/packages/plugin-components-pane/src/components/snippet/index.js b/packages/plugin-components-pane/src/components/snippet/index.js new file mode 100644 index 000000000..fed12c8e7 --- /dev/null +++ b/packages/plugin-components-pane/src/components/snippet/index.js @@ -0,0 +1,46 @@ +import { Fragment } from "react"; +import { Icon } from "@alifd/next"; +import { AdditiveType } from "../base"; +import "./index.less"; + +const Snippet = (props) => { + const { + snippet, + renderCustomSnippet = "", + size = "small", + actionsInLT, + actionsInRT, + } = props; + const { + thumbnail = "https://img.alicdn.com/tfs/TB1XHG6ehrI8KJjy0FpXXb5hVXa-740-608.png", + description, + title = "未知" + } = snippet; + const snippetClassName = `component-description-item-snippet ${AdditiveType.All} ${size}`; + return ( +
+ {typeof renderCustomSnippet === "function" ? ( + renderCustomSnippet(snippet) + ) : ( + +
+ {typeof thumbnail === "string" && thumbnail.startsWith("http") ? ( + thumbnail + ) : ( + + )} +
+
{description || title}
+
+ {actionsInLT ? actionsInLT : null} +
+
+ {actionsInRT ? actionsInRT : null} +
+
+ )} +
+ ); +}; + +export default Snippet; diff --git a/packages/plugin-components-pane/src/components/snippet/index.less b/packages/plugin-components-pane/src/components/snippet/index.less new file mode 100644 index 000000000..6c3809bfa --- /dev/null +++ b/packages/plugin-components-pane/src/components/snippet/index.less @@ -0,0 +1,120 @@ +@import "~@ali/ve-less-variables/index.less"; + +@text-color-active: #0079f2; + +.component-description-item-snippet { + position: relative; + &.small { + width: 64px; + height: 66px; + .snippet-thumbnail { + height: 40px; + img, + .icon { + max-width: 56px; + max-height: 32px; + } + } + } + &.large { + width: 132px; + height: 126px; + .snippet-thumbnail { + height: 100px; + img, + .icon { + max-width: 116px; + max-height: 83px; + } + } + } + margin: 12px 8px 0 8px; + cursor: grab; + .snippet-title { + width: 100%; + padding: 0 4px; + font-size: @fontSize-5; + color: #666666; + text-align: center; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + .snippet-thumbnail { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + margin-bottom: 9px; + border-radius: @global-border-radius; + border: 1px dashed #cdd2d9; + transition: border @transition-duration @transition-ease; + img, + .icon { + width: auto; + height: auto; + } + svg { + font-size: 28px; + } + svg * { + fill: rgba(31, 56, 88, 0.6) !important; + } + } + .component-description-item-icon { + &.tag { + display: flex; + justify-content: center; + align-items: center; + background: rgba(249, 189, 15, 0.1); + color: #ff6f00; + font-size: 12px; + border-radius: 3px; + width: 48px; + height: 24px; + } + } + .engine-additive-helper { + &.left-top { + position: absolute; + left: 0; + top: 0; + } + &.right-top { + display: none; + } + icon { + position: relative; + font-size: 14px; + cursor: pointer; + color: rgba(31, 56, 88, 0.4); + transition: color @transition-duration @transition-ease; + &:hover { + color: rgba(31, 56, 88, 0.6); + } + a { + position: absolute; + left: 0; + top: 0; + display: inline-block; + width: 14px; + height: 14px; + } + } + } + &:hover { + .snippet-thumbnail { + border: 2px solid @text-color-active; + transition: border @transition-duration @transition-ease; + } + .right-top { + display: block; + position: absolute; + right: 0; + top: 0; + height: 24px; + display: flex; + align-items: center; + } + } +} diff --git a/packages/plugin-components-pane/src/i18n/index.js b/packages/plugin-components-pane/src/i18n/index.js new file mode 100644 index 000000000..eae0b99e3 --- /dev/null +++ b/packages/plugin-components-pane/src/i18n/index.js @@ -0,0 +1,45 @@ +const { provideIntl, destroyIntl } = require('@ali/intl-universal'); +const strings = require('./strings'); + +let intl; +const MEDUSA_APP_NAME = 'legao-designer'; +const PSEUDO_LANGUAGE_TAG = 'pd-KV'; +const CURRENT_LANGUAGE = (window.locale || '').replace(/_/, '-') || 'zh-CN'; + +function update(language) { + destroyIntl(); + intl = provideIntl({ + locale: language, + messagesAIO: strings, + }); +} + +function get(id, variable) { + if (!intl) update(); + let string = ''; + let key = ''; + if (typeof id === 'string') { + key = id; + string = intl.formatMessage({ id }, variable); + } + if (typeof id === 'object' && id.dm) { + id.defaultMessage = id.dm; + } + key = id.id; + string = intl.formatMessage(id, variable); + if (CURRENT_LANGUAGE === PSEUDO_LANGUAGE_TAG) { + return `##@@@${key}##${MEDUSA_APP_NAME}@@@##${string}`; + } + return string; +} + +if (PSEUDO_LANGUAGE_TAG === CURRENT_LANGUAGE) { + update('en-US'); +} else { + update(CURRENT_LANGUAGE); +} + +module.exports = { + get, + update, +}; diff --git a/packages/plugin-components-pane/src/i18n/strings/en-US.json b/packages/plugin-components-pane/src/i18n/strings/en-US.json new file mode 100644 index 000000000..f6873f90d --- /dev/null +++ b/packages/plugin-components-pane/src/i18n/strings/en-US.json @@ -0,0 +1,27 @@ +{ + "trunkPaneTheComponentPrototypeView": "The component prototype view does not exist and may be caused by the following reasons:", + "trunkPaneDetailedComponentInformationIs": "Detailed component information is already printed in the Console", + "trunkPaneIsTheComponentRemoved": "Is the component removed because the component cannot find the prototype view?", + "trunkPaneLoading": "Loading...", + "trunkPaneFoundComponentNameRComponentName": "Found component name: {rComponentName} has a problem", + "trunkPaneDetailedDocumentation": "Detailed documentation", + "trunkPaneComponentComponentInfoComponentNameDeletedSuccessfully": "Component {componentInfoComponentName} deleted successfully", + "trunkPaneUpdateComponent": "Update component", + "trunkPaneComponentLibrary": "Component library", + "trunkPaneSearchComponent": "Search component", + "trunkPaneCustomize": "customize", + "trunkPaneTheComponentInQuestion": "The component in question:", + "trunkPaneComponentMarket": "Component market", + "trunkPaneUncategorized": "uncategorized", + "trunkPaneNoSearchResultsYet": "No search results yet", + "trunkPaneAll": "All", + "trunkPaneClickToTheLEGO": "Click ? to the Component Center", + "trunkPaneClickToTroubleshootThe": "Click to troubleshoot the issue", + "trunkPaneComponent": "Component", + "trunkPaneComponentCenter": "Component center", + "trunkPaneClose": "╳ Close", + "trunkPaneDetail": "Detail", + "trunkPaneEdit": "Edit", + "trunkPaneDev": "Dev", + "trunkPaneHelp": "Help" +} diff --git a/packages/plugin-components-pane/src/i18n/strings/index.js b/packages/plugin-components-pane/src/i18n/strings/index.js new file mode 100644 index 000000000..9b61c93e1 --- /dev/null +++ b/packages/plugin-components-pane/src/i18n/strings/index.js @@ -0,0 +1,4 @@ +module.exports = { + 'en-US': require('./en-US.json'), + 'zh-CN': require('./zh-CN.json'), +}; diff --git a/packages/plugin-components-pane/src/i18n/strings/zh-CN.json b/packages/plugin-components-pane/src/i18n/strings/zh-CN.json new file mode 100644 index 000000000..82f52faf1 --- /dev/null +++ b/packages/plugin-components-pane/src/i18n/strings/zh-CN.json @@ -0,0 +1,26 @@ +{ + "trunkPaneAll": "全部", + "trunkPaneFoundComponentNameRComponentName": "发现组件名为:{rComponentName}存在问题", + "trunkPaneNoResultsYet": "暂无结果", + "trunkPaneUpdateComponent": "更新组件", + "trunkPaneClickToTroubleshootThe": "点击排查问题", + "trunkPaneClickToTheLEGO": "点击 ? 前往组件中心", + "trunkPaneComponentLibrary": "组件库", + "trunkPaneComponentCenter": "组件中心", + "trunkPaneDetailedComponentInformationIs": "详细的组件信息已经打印在 Console 中", + "trunkPaneTheComponentPrototypeView": "组件原型视图不存在,可能由以下原因导致:", + "trunkPaneLoading": "加载中...", + "trunkPaneTheComponentInQuestion": "存在问题的组件:", + "trunkPaneIsTheComponentRemoved": "由于该组件找不到原型视图,是否移除组件?", + "trunkPaneDetailedDocumentation": "详细文档", + "trunkPaneUncategorized": "未分类", + "trunkPaneComponentMarket": "组件市场", + "trunkPaneCustomize": "自定义", + "trunkPaneSearchComponent": "搜索组件", + "trunkPaneComponent": "组件", + "trunkPaneClose": "╳ 关闭", + "trunkPaneDetail": "详情", + "trunkPaneEdit": "编辑", + "detrunkPaneDev": "开发版", + "trunkPaneHelp": "帮助" +} diff --git a/packages/plugin-components-pane/src/index.tsx b/packages/plugin-components-pane/src/index.tsx index d351734f0..b3f5ee578 100644 --- a/packages/plugin-components-pane/src/index.tsx +++ b/packages/plugin-components-pane/src/index.tsx @@ -1,5 +1,7 @@ import { Component, ReactNode } from 'react'; -import ComponentList, { AdditiveType } from "@ali/ve-component-list"; +import { Tab } from '@alifd/next'; +import ComponentList from "./components/component-list"; +import { AdditiveType } from "./components/base" import { PluginProps } from '@ali/lowcode-types'; import { Designer } from '@ali/lowcode-designer'; @@ -7,6 +9,7 @@ import './index.scss'; export interface IState { metaData: object[]; + bizComponents: object[]; } export default class ComponentListPlugin extends Component { @@ -18,6 +21,7 @@ export default class ComponentListPlugin extends Component super(props); this.state = { metaData: [], + bizComponents: [], }; } @@ -61,9 +65,11 @@ export default class ComponentListPlugin extends Component const { editor } = this.props; const assets = editor.get('assets') || {}; const metaData = this.transformMetaData(assets.componentList); + const bizComponents = this.transformMetaData(assets.bizComponentList); this.setState({ metaData, + bizComponents, }); }; @@ -126,15 +132,27 @@ export default class ComponentListPlugin extends Component } render(): ReactNode { - const { metaData } = this.state; + const { metaData, bizComponents } = this.state; return (
- this.registerAdditive(shell)} - enableSearch - /> + + + this.registerAdditive(shell)} + enableSearch + /> + + + this.registerAdditive(shell)} + enableSearch + /> + +
); } diff --git a/packages/plugin-components-pane/src/utils.js b/packages/plugin-components-pane/src/utils.js new file mode 100644 index 000000000..fc46ee975 --- /dev/null +++ b/packages/plugin-components-pane/src/utils.js @@ -0,0 +1,99 @@ +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) + } +} From 49517a68a97c7fc5afe4f2a861f85088facd249d Mon Sep 17 00:00:00 2001 From: "mark.ck" Date: Thu, 10 Sep 2020 12:58:46 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20asset=20?= =?UTF-8?q?=E4=B8=AD=20componentList=20=E4=B8=BA=E7=A9=BA=E6=97=B6?= =?UTF-8?q?=E6=8A=A5=E9=94=99=E7=9A=84=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/plugin-components-pane/package.json | 3 + .../components/base/{index.js => index.tsx} | 77 +++++++-------- .../components/button/{index.js => index.tsx} | 6 +- .../components/card/{index.js => index.tsx} | 52 +++++----- .../component-list/{index.js => index.tsx} | 51 +++++----- .../snippet/{index.js => index.tsx} | 26 ++--- .../src/i18n/{index.js => index.ts} | 2 +- .../src/i18n/strings/{index.js => index.ts} | 2 +- packages/plugin-components-pane/src/index.tsx | 60 +++++------ packages/plugin-components-pane/src/utils.js | 99 ------------------- packages/plugin-components-pane/src/utils.ts | 98 ++++++++++++++++++ 11 files changed, 225 insertions(+), 251 deletions(-) rename packages/plugin-components-pane/src/components/base/{index.js => index.tsx} (79%) rename packages/plugin-components-pane/src/components/button/{index.js => index.tsx} (69%) rename packages/plugin-components-pane/src/components/card/{index.js => index.tsx} (74%) rename packages/plugin-components-pane/src/components/component-list/{index.js => index.tsx} (88%) rename packages/plugin-components-pane/src/components/snippet/{index.js => index.tsx} (59%) rename packages/plugin-components-pane/src/i18n/{index.js => index.ts} (98%) rename packages/plugin-components-pane/src/i18n/strings/{index.js => index.ts} (79%) delete mode 100644 packages/plugin-components-pane/src/utils.js create mode 100644 packages/plugin-components-pane/src/utils.ts diff --git a/packages/plugin-components-pane/package.json b/packages/plugin-components-pane/package.json index 36060b79b..414afdced 100644 --- a/packages/plugin-components-pane/package.json +++ b/packages/plugin-components-pane/package.json @@ -20,9 +20,12 @@ ], "author": "xiayang.xy", "dependencies": { + "@ali/intl-universal": "^0.4.12", "@ali/lowcode-designer": "^1.0.8-0", "@ali/lowcode-editor-core": "^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", "react": "^16.8.1" }, diff --git a/packages/plugin-components-pane/src/components/base/index.js b/packages/plugin-components-pane/src/components/base/index.tsx similarity index 79% rename from packages/plugin-components-pane/src/components/base/index.js rename to packages/plugin-components-pane/src/components/base/index.tsx index 97283f518..a7664223b 100644 --- a/packages/plugin-components-pane/src/components/base/index.js +++ b/packages/plugin-components-pane/src/components/base/index.tsx @@ -1,10 +1,11 @@ -import React from "react"; -import { Search, Box } from "@alifd/next"; -import Button from "../button/index.js"; -import Card from "../card"; -import $i18n from "../../i18n/index.js"; -import { searchComponent, builtinSearchMap } from "../../utils"; -import "./index.less"; +import React from 'react'; +import PropTypes from 'prop-types'; +import { Search, Box } from '@alifd/next'; +import Button from '../button'; +import Card from '../card'; +import $i18n from '../../i18n/index'; +import { searchComponent, builtinSearchMap } from '../../utils'; +import './index.less'; /** * 配置元素的操作类型 @@ -13,9 +14,9 @@ import "./index.less"; * All:可拖拽也可点击 */ export const AdditiveType = { - Draggable: "additive-drag", - Clickable: "additive-click", - All: "additive", + Draggable: 'additive-drag', + Clickable: 'additive-click', + All: 'additive', }; class Base extends React.Component { @@ -23,33 +24,27 @@ class Base extends React.Component { metaData: PropTypes.array, className: PropTypes.string, registerAdditive: PropTypes.func, - renderCustomSnippet: PropTypes.func, actions: PropTypes.array, getComponentInfo: PropTypes.func, enableSearch: PropTypes.bool, - enableCard: PropTypes.bool, - enableReport: PropTypes.bool, placeholder: PropTypes.string, }; static defaultProps = { metaData: [], - registerAdditive: () => { - return; - }, - className: "", + registerAdditive: () => {}, + className: '', renderCustomSnippet: null, actions: [], getComponentInfo: null, enableSearch: false, enableCard: true, enableReport: true, - placeholder: "", + placeholder: '', }; state = { - selected: "", - searchText: "", + searchText: '', currentCard: null, target: null, currentCardImage: '', @@ -63,7 +58,7 @@ class Base extends React.Component { isMouseEnterCard = false; - componentWillMount() { + UNSAFE_componentWillMount() { // TODO get remote search map this.searchMap = builtinSearchMap; } @@ -85,9 +80,9 @@ class Base extends React.Component { normalizeBundle(mode) { const { metaData } = this.props; - const { searchText = "" } = this.state; + const { searchText = '' } = this.state; const groupList = metaData.filter((comp, index) => { - const { title = "", componentName = "", id = "" } = comp; + const { title = '', componentName = '', id = '' } = comp; if (!id) { comp.id = `${comp.componentName}_${index}`; } @@ -103,10 +98,10 @@ class Base extends React.Component { return groupList; } - let bundle = {}; + const bundle = {}; // 按一定顺序排列 groupList.forEach((m) => { - const c = m.category || "Others"; + const c = m.category || 'Others'; if (!bundle[c]) { bundle[c] = []; } @@ -119,7 +114,7 @@ class Base extends React.Component { this.isEmpty = true; return ( - +
暂无组件,请在物料站点添加
@@ -131,14 +126,8 @@ class Base extends React.Component { const { placeholder } = this.props; return ( =3:一个主按钮、一个次按钮、其余放在按钮组里; return actions.map((action, idx) => { return ( @@ -162,7 +151,7 @@ class Base extends React.Component { key={idx} action={action} className="btn" - type={idx === 0 ? "primary" : "outline"} + type={idx === 0 ? 'primary' : 'outline'} > {action.title} @@ -194,7 +183,7 @@ class Base extends React.Component { componentPrototype={currentCard} target={target} customImage={currentCardImage} - subTitle={target && target.innerText || ''} + subTitle={target ? target.innerText : ''} position="right top" visible={!!currentCard} showClose @@ -219,18 +208,18 @@ class Base extends React.Component { enableSearch, className, registerAdditive = () => { - return; + }, } = this.props; - let bodyExtraClass = ""; + let bodyExtraClass = ''; if (this.hasActions() && enableSearch) { - bodyExtraClass = "small"; + bodyExtraClass = 'small'; } else if (!this.hasActions() && enableSearch) { - bodyExtraClass = "medium"; + bodyExtraClass = 'medium'; } else if (this.hasActions() && !enableSearch) { - bodyExtraClass = "large"; + bodyExtraClass = 'large'; } else { - bodyExtraClass = ""; + bodyExtraClass = ''; } return ( @@ -256,7 +245,7 @@ class Base extends React.Component {
{this.renderActions()} diff --git a/packages/plugin-components-pane/src/components/button/index.js b/packages/plugin-components-pane/src/components/button/index.tsx similarity index 69% rename from packages/plugin-components-pane/src/components/button/index.js rename to packages/plugin-components-pane/src/components/button/index.tsx index d1d385e22..929915344 100644 --- a/packages/plugin-components-pane/src/components/button/index.js +++ b/packages/plugin-components-pane/src/components/button/index.tsx @@ -1,4 +1,4 @@ -import { Button } from "@ali/vu-uxcore-legao-design"; +import { Button } from '@ali/vu-uxcore-legao-design'; import './index.less'; const MyButton = (props) => { @@ -6,10 +6,10 @@ const MyButton = (props) => { return (
diff --git a/packages/plugin-components-pane/src/components/component-list/index.js b/packages/plugin-components-pane/src/components/component-list/index.tsx similarity index 88% rename from packages/plugin-components-pane/src/components/component-list/index.js rename to packages/plugin-components-pane/src/components/component-list/index.tsx index b14eb7f78..79f0425bc 100644 --- a/packages/plugin-components-pane/src/components/component-list/index.js +++ b/packages/plugin-components-pane/src/components/component-list/index.tsx @@ -1,23 +1,23 @@ -import Base, { AdditiveType } from "../base/index.js"; -import Snippet from "../snippet"; -import "./index.less"; +import Base, { AdditiveType } from '../base/index'; +import Snippet from '../snippet'; +import './index.less'; // 滚动事件触发灵敏度 const OFFSET_ACCURCY = 25; const categoryMap = { - General: "常用", - Navigation: "导航", - DataEntry: "输入", - DataDisplay: "展示", - Feedback: "反馈", - Util: "工具", - Chart: "图表", - Others: "其他", + General: '常用', + Navigation: '导航', + DataEntry: '输入', + DataDisplay: '展示', + Feedback: '反馈', + Util: '工具', + Chart: '图表', + Others: '其他', }; export default class ComponentList extends Base { - static displayName = "ComponentList"; + static displayName = 'ComponentList'; descRefList = new Map(); navRefList = new Map(); @@ -28,10 +28,10 @@ export default class ComponentList extends Base { scroll; scrollTimer; state = { - selected: "", - searchText: "", + selected: '', + searchText: '', currentCard: null, - currentCardImage: '' + currentCardImage: '', }; componentDidMount() { @@ -40,7 +40,7 @@ export default class ComponentList extends Base { // mock 滚动结束事件 if (this.scroll) { - this.scroll.addEventListener("scroll", this.handleScrollEnd); + this.scroll.addEventListener('scroll', this.handleScrollEnd); } }, 20); const bundle = this.normalizeBundle(); @@ -60,7 +60,7 @@ export default class ComponentList extends Base { componentWillUnmount() { 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) { const nav = this.navRefList.get(selected); if (nav) { - nav.scrollIntoView({ block: "center" }); + nav.scrollIntoView({ block: 'center' }); } } @@ -167,11 +167,11 @@ export default class ComponentList extends Base { {metaData.map((comp) => (
{ this.navRefList.set(comp.id, item); @@ -186,7 +186,7 @@ export default class ComponentList extends Base { } renderComponentDescriptionList(bundle) { - const { renderCustomSnippet = "", enableCard = true } = this.props; + const { renderCustomSnippet = '', enableCard = true } = this.props; return (
{Object.keys(bundle).map((cat) => { @@ -208,8 +208,7 @@ export default class ComponentList extends Base { {comp.title}
) : null} -
-
+
{comp.snippets && @@ -227,7 +226,7 @@ export default class ComponentList extends Base { this.setState({ currentCard: comp, target: this.snippetMap.get(`${comp.id}-${idx}`), - currentCardImage: snippet.thumbnail + currentCardImage: snippet.thumbnail, }); this.timer = null; }, 1000); @@ -242,7 +241,7 @@ export default class ComponentList extends Base { if (this.isMouseEnterCard) { return; } - this.setState({currentCard: null}); + this.setState({ currentCard: null }); }, 200); }} > @@ -270,7 +269,7 @@ export default class ComponentList extends Base { key="content" className="ve-component-list-content" onScroll={(e) => this.handleScroll(e)} - ref={(scroll) => (this.scroll = scroll)} + ref={(scroll) => { this.scroll = scroll; }} > {this.renderComponentDescriptionList(bundle)}
, diff --git a/packages/plugin-components-pane/src/components/snippet/index.js b/packages/plugin-components-pane/src/components/snippet/index.tsx similarity index 59% rename from packages/plugin-components-pane/src/components/snippet/index.js rename to packages/plugin-components-pane/src/components/snippet/index.tsx index fed12c8e7..a8f299659 100644 --- a/packages/plugin-components-pane/src/components/snippet/index.js +++ b/packages/plugin-components-pane/src/components/snippet/index.tsx @@ -1,41 +1,41 @@ -import { Fragment } from "react"; -import { Icon } from "@alifd/next"; -import { AdditiveType } from "../base"; -import "./index.less"; +import { Fragment } from 'react'; +import { Icon } from '@alifd/next'; +import { AdditiveType } from '../base'; +import './index.less'; const Snippet = (props) => { const { snippet, - renderCustomSnippet = "", - size = "small", + renderCustomSnippet = '', + size = 'small', actionsInLT, actionsInRT, } = props; const { - thumbnail = "https://img.alicdn.com/tfs/TB1XHG6ehrI8KJjy0FpXXb5hVXa-740-608.png", + thumbnail = 'https://img.alicdn.com/tfs/TB1XHG6ehrI8KJjy0FpXXb5hVXa-740-608.png', description, - title = "未知" + title = '未知', } = snippet; const snippetClassName = `component-description-item-snippet ${AdditiveType.All} ${size}`; return (
- {typeof renderCustomSnippet === "function" ? ( + {typeof renderCustomSnippet === 'function' ? ( renderCustomSnippet(snippet) ) : (
- {typeof thumbnail === "string" && thumbnail.startsWith("http") ? ( + {typeof thumbnail === 'string' && thumbnail.startsWith('http') ? ( thumbnail ) : ( - + )}
{description || title}
- {actionsInLT ? actionsInLT : null} + {actionsInLT || null}
- {actionsInRT ? actionsInRT : null} + {actionsInRT || null}
)} diff --git a/packages/plugin-components-pane/src/i18n/index.js b/packages/plugin-components-pane/src/i18n/index.ts similarity index 98% rename from packages/plugin-components-pane/src/i18n/index.js rename to packages/plugin-components-pane/src/i18n/index.ts index eae0b99e3..9cecc0762 100644 --- a/packages/plugin-components-pane/src/i18n/index.js +++ b/packages/plugin-components-pane/src/i18n/index.ts @@ -39,7 +39,7 @@ if (PSEUDO_LANGUAGE_TAG === CURRENT_LANGUAGE) { update(CURRENT_LANGUAGE); } -module.exports = { +export default { get, update, }; diff --git a/packages/plugin-components-pane/src/i18n/strings/index.js b/packages/plugin-components-pane/src/i18n/strings/index.ts similarity index 79% rename from packages/plugin-components-pane/src/i18n/strings/index.js rename to packages/plugin-components-pane/src/i18n/strings/index.ts index 9b61c93e1..01d106371 100644 --- a/packages/plugin-components-pane/src/i18n/strings/index.js +++ b/packages/plugin-components-pane/src/i18n/strings/index.ts @@ -1,4 +1,4 @@ -module.exports = { +export default { 'en-US': require('./en-US.json'), 'zh-CN': require('./zh-CN.json'), }; diff --git a/packages/plugin-components-pane/src/index.tsx b/packages/plugin-components-pane/src/index.tsx index b3f5ee578..97a12bbb2 100644 --- a/packages/plugin-components-pane/src/index.tsx +++ b/packages/plugin-components-pane/src/index.tsx @@ -1,15 +1,15 @@ import { Component, ReactNode } from 'react'; import { Tab } from '@alifd/next'; -import ComponentList from "./components/component-list"; -import { AdditiveType } from "./components/base" +import ComponentList from './components/component-list'; +import { AdditiveType } from './components/base'; import { PluginProps } from '@ali/lowcode-types'; import { Designer } from '@ali/lowcode-designer'; import './index.scss'; export interface IState { - metaData: object[]; - bizComponents: object[]; + metaData: Record[]; + bizComponents: Record[]; } export default class ComponentListPlugin extends Component { @@ -35,7 +35,10 @@ export default class ComponentListPlugin extends Component } transformMetaData(componentList: any): any { - const metaData: object[] = []; + const metaData: Record[] = []; + if (!componentList || !Array.isArray(componentList) || !componentList.length) { + return metaData; + } componentList.forEach((category: any, categoryId: number) => { if (Array.isArray(category?.children)) { category.children.forEach((comp: any, compId: number) => { @@ -98,20 +101,7 @@ export default class ComponentListPlugin extends Component return; } - const click = (e: Event) => { - 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; - } - }; + const click = (e: Event) => { console.log(e); }; shell.addEventListener('click', click); @@ -136,22 +126,22 @@ export default class ComponentListPlugin extends Component return (
- - this.registerAdditive(shell)} - enableSearch - /> - - - this.registerAdditive(shell)} - enableSearch - /> - + + this.registerAdditive(shell)} + enableSearch + /> + + + this.registerAdditive(shell)} + enableSearch + /> +
); diff --git a/packages/plugin-components-pane/src/utils.js b/packages/plugin-components-pane/src/utils.js deleted file mode 100644 index fc46ee975..000000000 --- a/packages/plugin-components-pane/src/utils.js +++ /dev/null @@ -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) - } -} diff --git a/packages/plugin-components-pane/src/utils.ts b/packages/plugin-components-pane/src/utils.ts new file mode 100644 index 000000000..c8b6cbb64 --- /dev/null +++ b/packages/plugin-components-pane/src/utils.ts @@ -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); + }; +}