UNPKG

@ovine/craft

Version:
211 lines (175 loc) 5.16 kB
/** * 悬浮/或者选中 选中时的高亮 * * TODO: * 1. 完善toolbar 快捷操作 * 2. 针对不同类型不同toolbar操作 * 3. UI美化 * * BUG: * 1. 监听 选中 dom 宽高变化,更新 高亮 UI * 2. 某些类型 --- 选中时禁用点击 * 3. 布局要跟随 preview */ import { debounce, includes, throttle } from 'lodash' import { observer } from 'mobx-react' import React, { useEffect, useRef, useState } from 'react' import { referenceStore } from '@/components/reference/store' import { domId } from '@/constants' import { onNodeMenus } from '../actions' import { usePreviewStore } from '../store' import { StyledAttacher } from './styled' const displayNone = { display: 'none' } export default observer(() => { const { setHoverId, setSelectedId, hoverId, selectedId, selectedInfo, renderSchema, } = usePreviewStore() const [hover, setHover] = useState({ style: displayNone }) const [selected, setSelected] = useState({ style: displayNone, tipStyle: displayNone, toolbarStyle: displayNone, }) const $wrap = useRef(null) const cancelHover = () => { setHover({ style: displayNone }) setHoverId('') } const cancelSelected = () => { setSelected({ style: displayNone, toolbarStyle: displayNone }) setSelectedId('') } const onActive = (type, $ele, parentRect) => { // 没有 data-id 标记的内容,不处理 if (!$ele.length) { return } const activeId = $ele.data('id') // 已经选择对象的不能被hover,或者再次选择 if (type !== 'updateSelected' && $wrap.current?.dataset.selected === activeId) { cancelHover() return } // 计算位置 const { width, height, left, right, top } = $ele[0].getBoundingClientRect() const style = { display: 'block', width, height, left: left - parentRect.left, top: top - parentRect.top, } if (type === 'hover') { setHover({ style }) return } if (includes(['selected', 'updateSelected'], type)) { const toolbarStyle = { display: 'block', right: parentRect.right - right, top: top - parentRect.top - 25, } const tipStyle = { display: width >= 100 ? 'block' : 'none' } cancelHover() setSelected({ style, toolbarStyle, tipStyle }) } } const setActiveById = (type, id) => { if (!id) { return } const $preview = $(`#${domId.editorPreview}`) const parentRect = $preview[0].getBoundingClientRect() const $active = $preview.find(`[data-id="${id}"]`) onActive(type, $active, parentRect) } const updateSelected = debounce(() => { if (hoverId) { cancelHover() } setActiveById('updateSelected', $wrap.current?.dataset.selected) }, 100) const onSelected = throttle(() => { // 取消选中 if (!selectedId) { referenceStore.setSchema({}) cancelSelected() return } // selectedId 改变, 高亮与关联面板 同时更新 referenceStore.setSchema(selectedInfo.schema) updateSelected() }, 100) const onHover = throttle(() => { if (hoverId) { setActiveById('hover', hoverId) } else { cancelHover() } }, 100) const onMounted = () => { const $preview = $(`#${domId.editorPreview}>[data-preview]`) const onNodeActive = throttle((type, event) => { const $ele = $(event.target).closest('[data-id]') const activeId = $ele.data('id') if (!activeId) { return } if (type === 'hover') { setHoverId(activeId) return } if (type === 'selected') { const isSelected = $wrap.current?.dataset.selected === activeId // 左键 点击选择 if (event.button === 0) { setSelectedId(activeId) } if (event.button === 2) { // 已经选中 并且是鼠标右键 if (isSelected && event.button === 2) { onNodeMenus({ position: { x: event.pageX, y: event.pageY, }, }) } } } }, 100) $(window).on('resize', updateSelected) // 当窗口变化时 更新 $preview.parent().scroll(updateSelected) // 当滚动时 更新 $preview .on('contextmenu', () => false) .on('mouseleave', cancelHover) .on('mousedown', (e) => onNodeActive('selected', e)) .on('mouseover', (e) => onNodeActive('hover', e)) .on('click', (e) => { // button 类型的点击一律过滤 if ($(e.target).closest('button')) { e.stopPropagation() } }) return () => { $preview.off() } } useEffect(onMounted, []) useEffect(onSelected, [selectedId]) useEffect(onHover, [hoverId]) useEffect(updateSelected, [renderSchema]) return ( <StyledAttacher ref={$wrap} data-selected={selectedId}> <div className="attach"> <div className="hlbox selected" style={selected.style} /> <div className="hlbox hover" style={hover.style} /> </div> </StyledAttacher> ) })