UNPKG

@ovine/craft

Version:
158 lines (157 loc) 7.21 kB
/** * 组件选择器 * TODO: * 1. 修复 当无滚动区域时,每次都nav自动跳到最后一个问题。(这是bootstrap问题,完全理解不了为什么,要选到最后一项) * 正确逻辑是:默认不做任何操作 * https://github.com/twbs/bootstrap/blob/main/js/src/scrollspy.js#L209 * 2. 完成 选择后 将 组件添加到对应位置 */ var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import { Drawer } from 'amis'; import { flattenTree, filterTree } from 'amis/lib/utils/helper'; import _ from 'lodash'; import React, { useEffect, useMemo, useRef } from 'react'; import { app } from '@core/app'; import { useImmer, useSubscriber } from '@core/utils/hooks'; import { publish } from '@core/utils/message'; import { onAddNode } from "../preview/actions"; import { message, domId } from "../../constants"; import nodeConfig from "./nodes"; import { StyledSelector } from "./styled"; const Selector = (props) => { const { label: headerLabel = '添加组件' } = props.info; const [state, setState] = useImmer({ searchText: '', }); const storeRef = useRef({}); const { searchText } = state; const items = useMemo(() => { // 没有查询时 返回所有数据 if (!searchText) { return flattenTree(nodeConfig); } const filteredItems = filterTree(nodeConfig, (item) => { const { type = '', label = '', parent } = item; // 父级 默认全部通过匹配,直接判断 子级 的数据 if (!parent) { return true; } // 只判断 type 与 label 两个数据 return type.indexOf(searchText) > -1 || label.indexOf(searchText) > -1; }); return flattenTree( // 过滤掉 没有 子级 的数据 filterTree(filteredItems, (item) => (!item.parent && !!item.children.length) || !!item.parent)); }, [searchText]); const onSearchChange = _.throttle((e) => { const { value } = e.currentTarget; setState((d) => { d.searchText = value; }); }, 100); const onItemClick = (item, e) => { const $item = $(e.currentTarget); $item .addClass('active') .siblings() .removeClass('active'); onAddNode({ container: props.info, node: item, position: { x: e.pageX, y: e.pageY, }, }); }; useEffect(() => { // 使用了 bootstrap scrollspy 组件 https://getbootstrap.com/docs/4.5/components/scrollspy const $scrollSpy = $(`#${domId.editorSelector} [data-spy="scroll"]`); const $scrollNav = $(`#${domId.editorSelectorNav}`); storeRef.current.$scrollSpy = $scrollSpy; $scrollSpy.scrollspy(); $scrollNav.on('click', '.nav-link', (e) => { const $item = $(e.target); if ($item.hasClass('active')) { return; } $scrollSpy.scrollspy('dispose'); $item .addClass('active') .siblings() .removeClass('active'); const $targetItem = $($item.attr('href')); $scrollSpy.animate({ scrollTop: $scrollSpy.scrollTop() + $targetItem.offset().top - $scrollSpy.offset().top }, 200, () => { $scrollSpy.scrollspy('refresh'); }); }); return () => { $scrollSpy.scrollspy('dispose'); }; }, []); useEffect(() => { const { $scrollSpy } = storeRef.current; if (!items.length) { $scrollSpy.scrollspy('dispose'); } else { $scrollSpy.scrollspy('refresh'); } }, [items.length]); return (React.createElement(StyledSelector, { id: domId.editorSelector }, React.createElement("div", { className: "selector-title" }, React.createElement("p", null, headerLabel)), React.createElement("div", { className: "selector-input" }, React.createElement("div", { className: "input" }, React.createElement("input", { placeholder: "\u641C\u7D22\u53EF\u7528\u7EC4\u4EF6", value: searchText, onChange: onSearchChange }), React.createElement("i", { className: "fa fa-search" }))), items.length === 0 && (React.createElement("div", { className: "selector-empty" }, React.createElement("i", { className: "fa fa-chain-broken" }), React.createElement("span", null, "\u6682\u672A\u627E\u5230\u5BF9\u5E94\u7EC4\u4EF6"))), React.createElement("div", { className: "selector-list" }, React.createElement("div", { id: domId.editorSelectorNav, className: "selector-nav" }, items.map((item, index) => { if (!item.parent) { return (React.createElement("div", { key: index, className: "title nav-link", href: `#spy-${item.type}` }, React.createElement("i", { className: `fa fa-${item.icon} p-r-xs` }), React.createElement("span", null, item.label))); } return (React.createElement("div", { key: index, className: "item nav-link", href: `#spy-${item.parent}-${item.type}` }, item.label)); })), React.createElement("div", { className: "selector-content", "data-target": `#${domId.editorSelectorNav}`, "data-spy": "scroll" }, items.map((item, index) => { if (!item.parent) { return (React.createElement("div", { key: index, id: `spy-${item.type}`, className: "title" }, item.label)); } return (React.createElement("div", { key: index, id: `spy-${item.parent}-${item.type}`, className: "item", onClick: (e) => onItemClick(item, e) }, React.createElement("img", { src: item.img, alt: item.label }), React.createElement("div", null, React.createElement("h6", null, item.label), React.createElement("p", null, item.desc)))); }))))); }; export const toggleSelector = (info = {}) => { publish(message.toggleSelector, info); }; export default () => { const [info, setInfo] = useImmer({}); const { isShow = false } = info, resetInfo = __rest(info, ["isShow"]); const onHide = () => { setInfo((d) => { d.isShow = false; }); }; useSubscriber(message.toggleSelector, (data = {}) => { setInfo(() => data); }); return (React.createElement(Drawer, { theme: app.theme.getName(), size: "md", onHide: onHide, show: isShow, position: "right" }, React.createElement(Selector, { info: resetInfo }))); };