Merge pull request #43 from Hufe921/feature/clipboard

Feature/clipboard
pr675
Hufe 4 years ago committed by GitHub
commit 149d9d6d2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1270,36 +1270,7 @@ export class CommandAdapt {
if (!payload.length) return if (!payload.length) return
const isReadonly = this.draw.isReadonly() const isReadonly = this.draw.isReadonly()
if (isReadonly) return if (isReadonly) return
const activeControl = this.control.getActiveControl() this.draw.insertElementList(payload)
if (activeControl) return
const isPartRangeInControlOutside = this.control.isPartRangeInControlOutside()
if (isPartRangeInControlOutside) return
const { startIndex, endIndex } = this.range.getRange()
if (!~startIndex && !~endIndex) return
// 格式化element
formatElementList(payload, {
isHandleFirstElement: false,
editorOptions: this.options
})
const elementList = this.draw.getElementList()
const isCollapsed = startIndex === endIndex
const start = startIndex + 1
if (!isCollapsed) {
elementList.splice(start, endIndex - startIndex)
}
const positionContext = this.position.getPositionContext()
for (let i = 0; i < payload.length; i++) {
const element = payload[i]
if (positionContext.isTable) {
element.tdId = positionContext.tdId
element.trId = positionContext.trId
element.tableId = positionContext.tableId
}
elementList.splice(start + i, 0, element)
}
const curIndex = startIndex + payload.length
this.range.setRange(curIndex, curIndex)
this.draw.render({ curIndex })
} }
} }

@ -1,14 +1,17 @@
import { debounce } from '../../utils' import { debounce } from '../../utils'
import { getElementListByHTML } from '../../utils/clipboard'
import { Draw } from '../draw/Draw' import { Draw } from '../draw/Draw'
import { CanvasEvent } from '../event/CanvasEvent' import { CanvasEvent } from '../event/CanvasEvent'
export class CursorAgent { export class CursorAgent {
private draw: Draw
private container: HTMLDivElement private container: HTMLDivElement
private agentCursorDom: HTMLTextAreaElement private agentCursorDom: HTMLTextAreaElement
private canvasEvent: CanvasEvent private canvasEvent: CanvasEvent
constructor(draw: Draw, canvasEvent: CanvasEvent) { constructor(draw: Draw, canvasEvent: CanvasEvent) {
this.draw = draw
this.container = draw.getContainer() this.container = draw.getContainer()
this.canvasEvent = canvasEvent this.canvasEvent = canvasEvent
// 代理光标绘制 // 代理光标绘制
@ -40,9 +43,34 @@ export class CursorAgent {
} }
private _paste(evt: ClipboardEvent) { private _paste(evt: ClipboardEvent) {
const text = evt.clipboardData?.getData('text') const clipboardData = evt.clipboardData
if (text) { if (!clipboardData) return
this.canvasEvent.input(text) // 从粘贴板提取数据
let isHTML = false
for (let i = 0; i < clipboardData.items.length; i++) {
const item = clipboardData.items[i]
if (item.type === 'text/html') {
isHTML = true
break
}
}
for (let i = 0; i < clipboardData.items.length; i++) {
const item = clipboardData.items[i]
if (item.kind !== 'string') continue
if (item.type === 'text/plain' && !isHTML) {
item.getAsString(plainText => {
const elementList = plainText.split('').map(value => ({
value
}))
this.draw.insertElementList(elementList)
})
}
if (item.type === 'text/html' && isHTML) {
item.getAsString(htmlText => {
const elementList = getElementListByHTML(htmlText)
this.draw.insertElementList(elementList)
})
}
} }
evt.preventDefault() evt.preventDefault()
} }

@ -40,6 +40,7 @@ import { zipElementList } from '../../utils/element'
import { CheckboxParticle } from './particle/CheckboxParticle' import { CheckboxParticle } from './particle/CheckboxParticle'
import { DeepRequired } from '../../interface/Common' import { DeepRequired } from '../../interface/Common'
import { ControlComponent } from '../../dataset/enum/Control' import { ControlComponent } from '../../dataset/enum/Control'
import { formatElementList } from '../../utils/element'
export class Draw { export class Draw {
@ -285,6 +286,41 @@ export class Draw {
return this.elementList return this.elementList
} }
public insertElementList(payload: IElement[]) {
if (!payload.length) return
const activeControl = this.control.getActiveControl()
if (activeControl) return
const isPartRangeInControlOutside = this.control.isPartRangeInControlOutside()
if (isPartRangeInControlOutside) return
const { startIndex, endIndex } = this.range.getRange()
if (!~startIndex && !~endIndex) return
formatElementList(payload, {
isHandleFirstElement: false,
editorOptions: this.options
})
const elementList = this.getElementList()
const isCollapsed = startIndex === endIndex
const start = startIndex + 1
if (!isCollapsed) {
elementList.splice(start, endIndex - startIndex)
}
const positionContext = this.position.getPositionContext()
for (let i = 0; i < payload.length; i++) {
const element = payload[i]
if (positionContext.isTable) {
element.tdId = positionContext.tdId
element.trId = positionContext.trId
element.tableId = positionContext.tableId
}
elementList.splice(start + i, 0, element)
}
const curIndex = startIndex + payload.length
this.range.setRange(curIndex, curIndex)
this.render({
curIndex
})
}
public getOriginalElementList() { public getOriginalElementList() {
return this.elementList return this.elementList
} }

@ -6,7 +6,7 @@ import { MouseEventButton } from '../../dataset/enum/Event'
import { KeyMap } from '../../dataset/enum/Keymap' import { KeyMap } from '../../dataset/enum/Keymap'
import { IElement } from '../../interface/Element' import { IElement } from '../../interface/Element'
import { ICurrentPosition } from '../../interface/Position' import { ICurrentPosition } from '../../interface/Position'
import { writeTextByElementList } from '../../utils/clipboard' import { writeElementList } from '../../utils/clipboard'
import { Cursor } from '../cursor/Cursor' import { Cursor } from '../cursor/Cursor'
import { Draw } from '../draw/Draw' import { Draw } from '../draw/Draw'
import { HyperlinkParticle } from '../draw/particle/HyperlinkParticle' import { HyperlinkParticle } from '../draw/particle/HyperlinkParticle'
@ -571,7 +571,7 @@ export class CanvasEvent {
const { startIndex, endIndex } = this.range.getRange() const { startIndex, endIndex } = this.range.getRange()
const elementList = this.draw.getElementList() const elementList = this.draw.getElementList()
if (startIndex !== endIndex) { if (startIndex !== endIndex) {
writeTextByElementList(elementList.slice(startIndex + 1, endIndex + 1)) writeElementList(elementList.slice(startIndex + 1, endIndex + 1))
let curIndex: number let curIndex: number
if (activeControl) { if (activeControl) {
curIndex = this.control.cut() curIndex = this.control.cut()
@ -588,7 +588,7 @@ export class CanvasEvent {
const { startIndex, endIndex } = this.range.getRange() const { startIndex, endIndex } = this.range.getRange()
const elementList = this.draw.getElementList() const elementList = this.draw.getElementList()
if (startIndex !== endIndex) { if (startIndex !== endIndex) {
writeTextByElementList(elementList.slice(startIndex + 1, endIndex + 1)) writeElementList(elementList.slice(startIndex + 1, endIndex + 1))
} }
} }

@ -1,43 +1,135 @@
import { IElement } from '..' import { IElement } from '..'
import { HORIZON_TAB, WRAP, ZERO } from '../dataset/constant/Common' import { ZERO } from '../dataset/constant/Common'
import { TEXTLIKE_ELEMENT_TYPE } from '../dataset/constant/Element' import { TEXTLIKE_ELEMENT_TYPE } from '../dataset/constant/Element'
import { ElementType } from '../dataset/enum/Element' import { ElementType } from '../dataset/enum/Element'
import { zipElementList } from './element'
export function writeText(text: string) { export function writeClipboardItem(text: string, html: string) {
if (!text) return if (!text || !html) return
window.navigator.clipboard.writeText(text.replaceAll(ZERO, `\n`)) const plainText = new Blob([text], { type: 'text/plain' })
const htmlText = new Blob([html], { type: 'text/html' })
// @ts-ignore
const item = new ClipboardItem({
[plainText.type]: plainText,
[htmlText.type]: htmlText
})
window.navigator.clipboard.write([item])
} }
export function writeTextByElementList(elementList: IElement[]) { export function writeElementList(elementList: IElement[]) {
let text = `` const clipboardDom: HTMLDivElement = document.createElement('div')
function pickTextFromElement(payload: IElement[]) { function buildDomFromElementList(payload: IElement[]) {
for (let e = 0; e < payload.length; e++) { for (let e = 0; e < payload.length; e++) {
const element = payload[e] const element = payload[e]
// 构造表格
if (element.type === ElementType.TABLE) { if (element.type === ElementType.TABLE) {
if (e !== 0) { const tableDom: HTMLTableElement = document.createElement('table')
text += WRAP
}
const trList = element.trList! const trList = element.trList!
for (let t = 0; t < trList.length; t++) { for (let t = 0; t < trList.length; t++) {
const trDom = document.createElement('tr')
const tr = trList[t] const tr = trList[t]
for (let d = 0; d < tr.tdList.length; d++) { for (let d = 0; d < tr.tdList.length; d++) {
const tdDom = document.createElement('td')
const td = tr.tdList[d] const td = tr.tdList[d]
// 排除td首个元素 tdDom.innerText = td.value[0].value
pickTextFromElement(td.value.slice(1, td.value.length)) trDom.append(tdDom)
if (d !== tr.tdList.length - 1) {
// td之间加水平制表符
text += HORIZON_TAB
} }
tableDom.append(trDom)
} }
// tr后加换行符 clipboardDom.append(tableDom)
text += WRAP } else if (element.type === ElementType.HYPERLINK) {
const a = document.createElement('a')
a.innerText = element.valueList![0].value
if (element.url) {
a.href = element.url
} }
clipboardDom.append(a)
} else if (!element.type || TEXTLIKE_ELEMENT_TYPE.includes(element.type)) { } else if (!element.type || TEXTLIKE_ELEMENT_TYPE.includes(element.type)) {
text += element.value const span = document.createElement('span')
let text = ''
if (element.type === ElementType.CONTROL) {
text = element.control!.value?.[0]?.value || ''
} else {
text = element.value
}
if (!text) continue
span.innerText = text.replace(new RegExp(`${ZERO}`, 'g'), '\n')
if (element.color) {
span.style.color = element.color
}
if (element.bold) {
span.style.fontWeight = '600'
}
if (element.italic) {
span.style.fontStyle = 'italic'
}
if (element.size) {
span.style.fontSize = `${element.size}px`
}
clipboardDom.append(span)
}
}
}
buildDomFromElementList(zipElementList(elementList))
// 写入剪贴板
const text = clipboardDom.innerText
const html = clipboardDom.innerHTML
if (!text || !html) return
writeClipboardItem(text, html)
}
export function getElementListByHTML(htmlText: string): IElement[] {
const elementList: IElement[] = []
function findTextNode(dom: Element | Node) {
if (dom.nodeType === 3) {
const style = window.getComputedStyle(dom.parentNode as Element)
const value = dom.textContent
if (value) {
elementList.push({
value,
color: style.color,
bold: Number(style.fontWeight) > 500,
italic: style.fontStyle.includes('italic'),
size: Math.floor(Number(style.fontSize.replace('px', '')))
})
}
} else if (dom.nodeType === 1) {
const childNodes = dom.childNodes
for (let n = 0; n < childNodes.length; n++) {
const node = childNodes[n]
// br元素与display:block元素需换行
if (node.nodeName === 'BR') {
elementList.push({
value: '\n'
})
} else {
findTextNode(node)
if (node.nodeType === 1 && n !== childNodes.length - 1) {
const display = window.getComputedStyle(node as Element).display
if (display === 'block') {
elementList.push({
value: '\n'
})
}
}
}
}
} }
} }
// 追加dom
const clipboardDom = document.createElement('div')
clipboardDom.innerHTML = htmlText
document.body.appendChild(clipboardDom)
const deleteNodes: ChildNode[] = []
clipboardDom.childNodes.forEach(child => {
if (child.nodeType !== 1) {
deleteNodes.push(child)
} }
pickTextFromElement(elementList) })
if (!text) return deleteNodes.forEach(node => node.remove())
writeText(text.replace(new RegExp(`^${ZERO}`), '')) // 搜索文本节点
findTextNode(clipboardDom)
// 移除dom
clipboardDom.remove()
return elementList
} }
Loading…
Cancel
Save