feat: double click the selected text through the segmenter #510
This commit is contained in:
@@ -1,38 +1,63 @@
|
|||||||
import { ZERO } from '../../../dataset/constant/Common'
|
import { ZERO } from '../../../dataset/constant/Common'
|
||||||
|
import { TEXTLIKE_ELEMENT_TYPE } from '../../../dataset/constant/Element'
|
||||||
import { NUMBER_LIKE_REG } from '../../../dataset/constant/Regular'
|
import { NUMBER_LIKE_REG } from '../../../dataset/constant/Regular'
|
||||||
|
import { ControlComponent } from '../../../dataset/enum/Control'
|
||||||
|
import { IRange } from '../../../interface/Range'
|
||||||
import { CanvasEvent } from '../CanvasEvent'
|
import { CanvasEvent } from '../CanvasEvent'
|
||||||
|
|
||||||
function dblclick(host: CanvasEvent, evt: MouseEvent) {
|
// 通过分词器获取单词所在选区
|
||||||
|
function getWordRangeBySegmenter(host: CanvasEvent): IRange | null {
|
||||||
|
if (!Intl.Segmenter) return null
|
||||||
const draw = host.getDraw()
|
const draw = host.getDraw()
|
||||||
const LETTER_REG = draw.getLetterReg()
|
const cursorPosition = draw.getPosition().getCursorPosition()
|
||||||
const position = draw.getPosition()
|
if (!cursorPosition) return null
|
||||||
const positionContext = position.getPositionByXY({
|
const rangeManager = draw.getRange()
|
||||||
x: evt.offsetX,
|
const paragraphInfo = rangeManager.getRangeParagraphInfo()
|
||||||
y: evt.offsetY
|
if (!paragraphInfo) return null
|
||||||
})
|
// 组装段落文本
|
||||||
// 图片预览
|
const paragraphText =
|
||||||
if (positionContext.isImage && positionContext.isDirectHit) {
|
paragraphInfo?.elementList
|
||||||
draw.getPreviewer().render()
|
?.map(e =>
|
||||||
return
|
!e.type ||
|
||||||
}
|
(TEXTLIKE_ELEMENT_TYPE.includes(e.type) &&
|
||||||
// 切换区域
|
e.controlComponent !== ControlComponent.CHECKBOX)
|
||||||
if (draw.getIsPagingMode()) {
|
? e.value
|
||||||
if (!~positionContext.index && positionContext.zone) {
|
: ZERO
|
||||||
draw.getZone().setZone(positionContext.zone)
|
)
|
||||||
draw.clearSideEffect()
|
.join('') || ''
|
||||||
position.setPositionContext({
|
if (!paragraphText) return null
|
||||||
isTable: false
|
// 光标所在位置
|
||||||
})
|
const cursorStartIndex = cursorPosition.index
|
||||||
return
|
// 段落首字符相对文档起始位置
|
||||||
|
const offset = paragraphInfo.startIndex
|
||||||
|
const segmenter = new Intl.Segmenter(undefined, { granularity: 'word' })
|
||||||
|
const segments = segmenter.segment(paragraphText)
|
||||||
|
// 新的光标位置
|
||||||
|
let startIndex = -1
|
||||||
|
let endIndex = -1
|
||||||
|
for (const { segment, index, isWordLike } of segments) {
|
||||||
|
const realSegmentStartIndex = index + offset
|
||||||
|
if (
|
||||||
|
isWordLike &&
|
||||||
|
cursorStartIndex >= realSegmentStartIndex &&
|
||||||
|
cursorStartIndex < realSegmentStartIndex + segment.length
|
||||||
|
) {
|
||||||
|
startIndex = realSegmentStartIndex - 1
|
||||||
|
endIndex = startIndex + segment.length
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 复选框双击时是切换选择状态,禁用扩选
|
return ~startIndex && ~endIndex ? { startIndex, endIndex } : null
|
||||||
if (positionContext.isCheckbox && positionContext.isDirectHit) return
|
}
|
||||||
// 自动扩选文字
|
|
||||||
const cursorPosition = position.getCursorPosition()
|
// 通过光标位置获取单词所在选区
|
||||||
if (!cursorPosition) return
|
function getWordRangeByCursor(host: CanvasEvent): IRange | null {
|
||||||
|
const draw = host.getDraw()
|
||||||
|
const cursorPosition = draw.getPosition().getCursorPosition()
|
||||||
|
if (!cursorPosition) return null
|
||||||
const { value, index } = cursorPosition
|
const { value, index } = cursorPosition
|
||||||
// 判断是否是数字或英文
|
// 判断是否是数字或英文
|
||||||
|
const LETTER_REG = draw.getLetterReg()
|
||||||
let upCount = 0
|
let upCount = 0
|
||||||
let downCount = 0
|
let downCount = 0
|
||||||
const isNumber = NUMBER_LIKE_REG.test(value)
|
const isNumber = NUMBER_LIKE_REG.test(value)
|
||||||
@@ -67,11 +92,46 @@ function dblclick(host: CanvasEvent, evt: MouseEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 设置选中区域
|
// 新的光标位置
|
||||||
const startIndex = index - upCount - 1
|
const startIndex = index - upCount - 1
|
||||||
if (startIndex < 0) return
|
if (startIndex < 0) return null
|
||||||
|
return {
|
||||||
|
startIndex,
|
||||||
|
endIndex: index + downCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dblclick(host: CanvasEvent, evt: MouseEvent) {
|
||||||
|
const draw = host.getDraw()
|
||||||
|
const position = draw.getPosition()
|
||||||
|
const positionContext = position.getPositionByXY({
|
||||||
|
x: evt.offsetX,
|
||||||
|
y: evt.offsetY
|
||||||
|
})
|
||||||
|
// 图片预览
|
||||||
|
if (positionContext.isImage && positionContext.isDirectHit) {
|
||||||
|
draw.getPreviewer().render()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 切换区域
|
||||||
|
if (draw.getIsPagingMode()) {
|
||||||
|
if (!~positionContext.index && positionContext.zone) {
|
||||||
|
draw.getZone().setZone(positionContext.zone)
|
||||||
|
draw.clearSideEffect()
|
||||||
|
position.setPositionContext({
|
||||||
|
isTable: false
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 复选框双击时是切换选择状态,禁用扩选
|
||||||
|
if (positionContext.isCheckbox && positionContext.isDirectHit) return
|
||||||
|
// 自动扩选文字-分词处理,优先使用分词器否则降级使用光标所在位置
|
||||||
const rangeManager = draw.getRange()
|
const rangeManager = draw.getRange()
|
||||||
rangeManager.setRange(startIndex, index + downCount)
|
const segmenterRange =
|
||||||
|
getWordRangeBySegmenter(host) || getWordRangeByCursor(host)
|
||||||
|
if (!segmenterRange) return
|
||||||
|
rangeManager.setRange(segmenterRange.startIndex, segmenterRange.endIndex)
|
||||||
// 刷新文档
|
// 刷新文档
|
||||||
draw.render({
|
draw.render({
|
||||||
isSubmitHistory: false,
|
isSubmitHistory: false,
|
||||||
|
|||||||
@@ -8,7 +8,12 @@ import { IEditorOption } from '../../interface/Editor'
|
|||||||
import { IElement } from '../../interface/Element'
|
import { IElement } from '../../interface/Element'
|
||||||
import { EventBusMap } from '../../interface/EventBus'
|
import { EventBusMap } from '../../interface/EventBus'
|
||||||
import { IRangeStyle } from '../../interface/Listener'
|
import { IRangeStyle } from '../../interface/Listener'
|
||||||
import { IRange, RangeRowArray, RangeRowMap } from '../../interface/Range'
|
import {
|
||||||
|
IRange,
|
||||||
|
IRangeParagraphInfo,
|
||||||
|
RangeRowArray,
|
||||||
|
RangeRowMap
|
||||||
|
} from '../../interface/Range'
|
||||||
import { getAnchorElement } from '../../utils/element'
|
import { getAnchorElement } from '../../utils/element'
|
||||||
import { Draw } from '../draw/Draw'
|
import { Draw } from '../draw/Draw'
|
||||||
import { EventBus } from '../event/eventbus/EventBus'
|
import { EventBus } from '../event/eventbus/EventBus'
|
||||||
@@ -208,10 +213,12 @@ export class RangeManager {
|
|||||||
return rangeRow
|
return rangeRow
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取选区段落元素列表
|
// 获取选区段落信息
|
||||||
public getRangeParagraphElementList(): IElement[] | null {
|
public getRangeParagraphInfo(): IRangeParagraphInfo | null {
|
||||||
const { startIndex, endIndex } = this.range
|
const { startIndex, endIndex } = this.range
|
||||||
if (!~startIndex && !~endIndex) return null
|
if (!~startIndex && !~endIndex) return null
|
||||||
|
/// 起始元素位置
|
||||||
|
let startPositionIndex = -1
|
||||||
// 需要改变的元素列表
|
// 需要改变的元素列表
|
||||||
const rangeElementList: IElement[] = []
|
const rangeElementList: IElement[] = []
|
||||||
// 选区行信息
|
// 选区行信息
|
||||||
@@ -224,10 +231,22 @@ export class RangeManager {
|
|||||||
const rowArray = rangeRow.get(position.pageNo)
|
const rowArray = rangeRow.get(position.pageNo)
|
||||||
if (!rowArray) continue
|
if (!rowArray) continue
|
||||||
if (rowArray.includes(position.rowNo)) {
|
if (rowArray.includes(position.rowNo)) {
|
||||||
|
if (!~startPositionIndex) {
|
||||||
|
startPositionIndex = position.index
|
||||||
|
}
|
||||||
rangeElementList.push(elementList[p])
|
rangeElementList.push(elementList[p])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rangeElementList
|
if (!rangeElementList.length) return null
|
||||||
|
return {
|
||||||
|
elementList: rangeElementList,
|
||||||
|
startIndex: startPositionIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取选区段落元素列表
|
||||||
|
public getRangeParagraphElementList(): IElement[] | null {
|
||||||
|
return this.getRangeParagraphInfo()?.elementList || null
|
||||||
}
|
}
|
||||||
|
|
||||||
public getIsSelectAll() {
|
public getIsSelectAll() {
|
||||||
|
|||||||
@@ -29,3 +29,8 @@ export type RangeContext = {
|
|||||||
zone: EditorZone
|
zone: EditorZone
|
||||||
isTable: boolean
|
isTable: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IRangeParagraphInfo {
|
||||||
|
elementList: IElement[]
|
||||||
|
startIndex: number
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user