@ovine/craft
Version:
Ovine json editor.
211 lines (175 loc) • 5.16 kB
JSX
/**
* 悬浮/或者选中 选中时的高亮
*
* 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>
)
})