文本元素接入ProseMirror

This commit is contained in:
pipipi-pikachu 2021-01-01 15:21:12 +08:00
parent 22887f022e
commit 1c7b2181bc
16 changed files with 277 additions and 113 deletions

View File

@ -0,0 +1,53 @@
.ProseMirror, .ProseMirror-static {
outline: 0;
border: 0;
font-size: 20px;
line-height: 1.5;
word-break: break-word;
font-family: '微软雅黑';
::selection {
background-color: rgba(27, 110, 232, 0.3);
color: inherit;
}
p + p {
margin-top: 5px;
}
ul {
list-style-type: disc;
padding-inline-start: 20px;
li {
list-style-type: disc;
}
}
ol {
list-style-type: decimal;
padding-inline-start: 20px;
li {
list-style-type: decimal;
}
}
code {
background-color: #eee;
padding: 1px 3px;
margin: 0 1px;
border-radius: 2px;
font-family: inherit;
}
blockquote {
overflow: hidden;
padding-right: 1.2em;
padding-left: 1.2em;
margin-left: 0;
margin-right: 0;
font-style: italic;
border-left: 5px solid #ccc;
}
}

View File

@ -2,6 +2,8 @@ import { createApp } from 'vue'
import App from './App.vue' import App from './App.vue'
import store from './store' import store from './store'
import 'prosemirror-view/style/prosemirror.css'
import '@/assets/styles/prosemirror.scss'
import '@/assets/styles/global.scss' import '@/assets/styles/global.scss'
import 'animate.css' import 'animate.css'

View File

@ -24,7 +24,7 @@ export const slides: Slide[] = [
}, },
opacity: 1, opacity: 1,
lock: false, lock: false,
content: '<div style=\'text-align: center;\'><span style=\'font-size: 28px;\'><span style=\'color: rgb(232, 107, 153); font-weight: bold;\'>一段测试文字</span>,字号固定为<span style=\'font-weight: bold; font-style: italic; text-decoration-line: underline;\'>28px</span></span></div>', content: '<p style=\'text-align: center;\'><span style=\'font-size: 28px;\'><span style=\'color: rgb(232, 107, 153); font-weight: bold;\'>一段测试文字</span>,字号固定为<span style=\'font-weight: bold; font-style: italic; text-decoration-line: underline;\'>28px</span></span></p>',
}, },
{ {
id: 'xxx3', id: 'xxx3',

28
src/prosemirror/index.ts Normal file
View File

@ -0,0 +1,28 @@
import { EditorState } from 'prosemirror-state'
import { EditorView } from 'prosemirror-view'
import { Schema, DOMParser } from 'prosemirror-model'
import { buildPlugins } from './plugins/index'
import { schemaNodes, schemaMarks } from './schema/index'
const schema = new Schema({
nodes: schemaNodes,
marks: schemaMarks,
})
export const createDocument = (content: string) => {
const htmlString = `<div>${content}</div>`
const parser = new window.DOMParser()
const element = parser.parseFromString(htmlString, 'text/html').body.firstElementChild
return DOMParser.fromSchema(schema).parse(element as Element)
}
export const initProsemirrorEditor = (dom: Element, content: string, props = {}) => {
return new EditorView(dom, {
state: EditorState.create({
doc: createDocument(content),
plugins: buildPlugins(schema),
}),
...props,
})
}

View File

@ -15,7 +15,7 @@ const orderedListRule = (nodeType: NodeType) => (
/^(\d+)\.\s$/, /^(\d+)\.\s$/,
nodeType, nodeType,
match => ({order: +match[1]}), match => ({order: +match[1]}),
(match, node) => node.childCount + node.attrs.order == +match[1], (match, node) => node.childCount + node.attrs.order === +match[1],
) )
) )

View File

@ -1,79 +1,79 @@
import { marks } from 'prosemirror-schema-basic' import { marks } from 'prosemirror-schema-basic'
import { Node } from 'prosemirror-model' import { MarkSpec } from 'prosemirror-model'
const subscript = { const subscript: MarkSpec = {
excludes: 'subscript', excludes: 'subscript',
parseDOM: [ parseDOM: [
{ tag: 'sub' }, { tag: 'sub' },
{ {
style: 'vertical-align', style: 'vertical-align',
getAttrs: (value: string) => value === 'sub' && null getAttrs: value => value === 'sub' && null
}, },
], ],
toDOM: () => ['sub', 0], toDOM: () => ['sub', 0],
} }
const superscript = { const superscript: MarkSpec = {
excludes: 'superscript', excludes: 'superscript',
parseDOM: [ parseDOM: [
{ tag: 'sup' }, { tag: 'sup' },
{ {
style: 'vertical-align', style: 'vertical-align',
getAttrs: (value: string) => value === 'super' && null getAttrs: value => value === 'super' && null
}, },
], ],
toDOM: () => ['sup', 0], toDOM: () => ['sup', 0],
} }
const strikethrough = { const strikethrough: MarkSpec = {
parseDOM: [ parseDOM: [
{ tag: 'strike' }, { tag: 'strike' },
{ {
style: 'text-decoration', style: 'text-decoration',
getAttrs: (value: string) => value === 'line-through' && null getAttrs: value => value === 'line-through' && null
}, },
{ {
style: 'text-decoration-line', style: 'text-decoration-line',
getAttrs: (value: string) => value === 'line-through' && null getAttrs: value => value === 'line-through' && null
}, },
], ],
toDOM: () => ['span', { style: 'text-decoration-line: line-through' }, 0], toDOM: () => ['span', { style: 'text-decoration-line: line-through' }, 0],
} }
const underline = { const underline: MarkSpec = {
parseDOM: [ parseDOM: [
{ tag: 'u' }, { tag: 'u' },
{ {
style: 'text-decoration', style: 'text-decoration',
getAttrs: (value: string) => value === 'underline' && null getAttrs: value => value === 'underline' && null
}, },
{ {
style: 'text-decoration-line', style: 'text-decoration-line',
getAttrs: (value: string) => value === 'underline' && null getAttrs: value => value === 'underline' && null
}, },
], ],
toDOM: () => ['span', { style: 'text-decoration: underline' }, 0], toDOM: () => ['span', { style: 'text-decoration: underline' }, 0],
} }
const forecolor = { const forecolor: MarkSpec = {
attrs: { attrs: {
color: {}, color: {},
}, },
parseDOM: [ parseDOM: [
{ {
style: 'color', style: 'color',
getAttrs: (color: string) => color ? { color } : {} getAttrs: color => color ? { color } : {}
}, },
], ],
toDOM: (node: Node) => { toDOM: mark => {
const { color } = node.attrs const { color } = mark.attrs
let style = '' let style = ''
if(color) style += `color: ${color};` if(color) style += `color: ${color};`
return ['span', { style }, 0] return ['span', { style }, 0]
}, },
} }
const backcolor = { const backcolor: MarkSpec = {
attrs: { attrs: {
backcolor: {}, backcolor: {},
}, },
@ -82,18 +82,18 @@ const backcolor = {
parseDOM: [ parseDOM: [
{ {
tag: 'span[style*=background-color]', tag: 'span[style*=background-color]',
getAttrs: (backcolor: string) => backcolor ? { backcolor } : {} getAttrs: backcolor => backcolor ? { backcolor } : {}
}, },
], ],
toDOM: (node: Node) => { toDOM: mark => {
const { backcolor } = node.attrs const { backcolor } = mark.attrs
let style = '' let style = ''
if(backcolor) style += `background-color: ${backcolor};` if(backcolor) style += `background-color: ${backcolor};`
return ['span', { style }, 0] return ['span', { style }, 0]
}, },
} }
const fontsize = { const fontsize: MarkSpec = {
attrs: { attrs: {
fontsize: {}, fontsize: {},
}, },
@ -102,31 +102,33 @@ const fontsize = {
parseDOM: [ parseDOM: [
{ {
style: 'font-size', style: 'font-size',
getAttrs: (fontsize: string) => fontsize ? { fontsize } : {} getAttrs: fontsize => fontsize ? { fontsize } : {}
}, },
], ],
toDOM: (node: Node) => { toDOM: mark => {
const { fontsize } = node.attrs const { fontsize } = mark.attrs
let style = '' let style = ''
if(fontsize) style += `font-size: ${fontsize}` if(fontsize) style += `font-size: ${fontsize}`
return ['span', { style }, 0] return ['span', { style }, 0]
}, },
} }
const fontname = { const fontname: MarkSpec = {
attrs: { attrs: {
fontname: '', fontname: {},
}, },
inline: true, inline: true,
group: 'inline', group: 'inline',
parseDOM: [ parseDOM: [
{ {
style: 'font-family', style: 'font-family',
getAttrs: (fontname: string) => ({ fontname: fontname ? fontname.replace(/[\"\']/g, '') : '' }) getAttrs: fontname => {
return { fontname: fontname && typeof fontname === 'string' ? fontname.replace(/[\"\']/g, '') : '' }
}
}, },
], ],
toDOM: (node: Node) => { toDOM: mark => {
const { fontname } = node.attrs const { fontname } = mark.attrs
let style = '' let style = ''
if(fontname) style += `font-family: ${fontname}` if(fontname) style += `font-family: ${fontname}`
return ['span', { style }, 0] return ['span', { style }, 0]

View File

@ -1,55 +1,58 @@
import { nodes } from 'prosemirror-schema-basic' import { nodes } from 'prosemirror-schema-basic'
import { Node } from 'prosemirror-model' import { Node, NodeSpec } from 'prosemirror-model'
import { orderedList, bulletList, listItem } from 'prosemirror-schema-list' import { orderedList, bulletList, listItem } from 'prosemirror-schema-list'
const listNodes = { const _orderedList: NodeSpec = {
ordered_list: { ...orderedList,
...orderedList, content: 'list_item+',
content: 'list_item+', group: 'block',
group: 'block', }
},
bullet_list: {
...bulletList,
content: 'list_item+',
group: 'block',
},
list_item: {
...listItem,
content: 'paragraph block*',
group: 'block',
},
paragraph: { const _bulletList: NodeSpec = {
attrs: { ...bulletList,
align: { content: 'list_item+',
default: '', group: 'block',
}, }
const _listItem: NodeSpec = {
...listItem,
content: 'paragraph block*',
group: 'block',
}
const paragraph: NodeSpec = {
attrs: {
align: {
default: '',
}, },
content: 'inline*', },
group: 'block', content: 'inline*',
parseDOM: [ group: 'block',
{ parseDOM: [
tag: 'p', {
getAttrs: (dom: HTMLElement) => { tag: 'p',
const { textAlign } = dom.style getAttrs: dom => {
let align = dom.getAttribute('align') || textAlign || '' const { textAlign } = (dom as HTMLElement).style
align = /(left|right|center|justify)/.test(align) ? align : '' let align = (dom as HTMLElement).getAttribute('align') || textAlign || ''
align = /(left|right|center|justify)/.test(align) ? align : ''
return { align }
} return { align }
} }
], }
toDOM: (node: Node) => { ],
const { align } = node.attrs toDOM: (node: Node) => {
let style = '' const { align } = node.attrs
if(align && align !== 'left') style += `text-align: ${align};` let style = ''
if(align && align !== 'left') style += `text-align: ${align};`
return ['p', { style }, 0] return ['p', { style }, 0]
},
}, },
} }
export default { export default {
...nodes, ...nodes,
...listNodes, 'ordered_list': _orderedList,
'bullet_list': _bulletList,
'list_item': _listItem,
paragraph,
} }

4
src/utils/selection.ts Normal file
View File

@ -0,0 +1,4 @@
export const removeAllRanges = () => {
const selection = window.getSelection()
selection && selection.removeAllRanges()
}

View File

@ -82,6 +82,7 @@ import { State, MutationTypes } from '@/store'
import { ContextmenuItem } from '@/components/Contextmenu/types' import { ContextmenuItem } from '@/components/Contextmenu/types'
import { PPTElement, Slide } from '@/types/slides' import { PPTElement, Slide } from '@/types/slides'
import { AlignmentLineProps, CreateElementSelectionData } from '@/types/edit' import { AlignmentLineProps, CreateElementSelectionData } from '@/types/edit'
import { removeAllRanges } from '@/utils/selection'
import useViewportSize from './hooks/useViewportSize' import useViewportSize from './hooks/useViewportSize'
import useMouseSelection from './hooks/useMouseSelection' import useMouseSelection from './hooks/useMouseSelection'
@ -161,6 +162,7 @@ export default defineComponent({
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, []) store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [])
if(!ctrlOrShiftKeyActive.value) updateMouseSelection(e) if(!ctrlOrShiftKeyActive.value) updateMouseSelection(e)
if(!editorAreaFocus.value) store.commit(MutationTypes.SET_EDITORAREA_FOCUS, true) if(!editorAreaFocus.value) store.commit(MutationTypes.SET_EDITORAREA_FOCUS, true)
removeAllRanges()
} }
const removeEditorAreaFocus = () => { const removeEditorAreaFocus = () => {

View File

@ -41,7 +41,6 @@ export default () => {
const { redo, undo } = useHistorySnapshot() const { redo, undo } = useHistorySnapshot()
const copy = () => { const copy = () => {
if(disableHotkeys.value) return
if(activeElementIdList.value.length) copyElement() if(activeElementIdList.value.length) copyElement()
else if(thumbnailsFocus.value) copySlide() else if(thumbnailsFocus.value) copySlide()
} }
@ -53,38 +52,36 @@ export default () => {
} }
const selectAll = () => { const selectAll = () => {
if(!editorAreaFocus.value && disableHotkeys.value) return if(!editorAreaFocus.value) return
selectAllElement() selectAllElement()
} }
const lock = () => { const lock = () => {
if(!editorAreaFocus.value && disableHotkeys.value) return if(!editorAreaFocus.value) return
lockElement() lockElement()
} }
const combine = () => { const combine = () => {
if(!editorAreaFocus.value && disableHotkeys.value) return if(!editorAreaFocus.value) return
combineElements() combineElements()
} }
const uncombine = () => { const uncombine = () => {
if(!editorAreaFocus.value && disableHotkeys.value) return if(!editorAreaFocus.value) return
uncombineElements() uncombineElements()
} }
const remove = () => { const remove = () => {
if(disableHotkeys.value) return
if(activeElementIdList.value.length) deleteElement() if(activeElementIdList.value.length) deleteElement()
else if(thumbnailsFocus.value) deleteSlide() else if(thumbnailsFocus.value) deleteSlide()
} }
const move = (key: string) => { const move = (key: string) => {
if(disableHotkeys.value) return
if(activeElementIdList.value.length) moveElement(key) if(activeElementIdList.value.length) moveElement(key)
else if(key === KEYS.UP || key === KEYS.DOWN) updateSlideIndex(key) else if(key === KEYS.UP || key === KEYS.DOWN) updateSlideIndex(key)
} }
const create = () => { const create = () => {
if(!thumbnailsFocus.value || disableHotkeys.value) return if(!thumbnailsFocus.value) return
createSlide() createSlide()
} }
@ -109,58 +106,72 @@ export default () => {
if(!editorAreaFocus.value && !thumbnailsFocus.value) return if(!editorAreaFocus.value && !thumbnailsFocus.value) return
if(ctrlKey && key === KEYS.C) { if(ctrlKey && key === KEYS.C) {
if(disableHotkeys.value) return
e.preventDefault() e.preventDefault()
copy() copy()
} }
if(ctrlKey && key === KEYS.X) { if(ctrlKey && key === KEYS.X) {
if(disableHotkeys.value) return
e.preventDefault() e.preventDefault()
cut() cut()
} }
if(ctrlKey && key === KEYS.Z) { if(ctrlKey && key === KEYS.Z) {
if(disableHotkeys.value) return
e.preventDefault() e.preventDefault()
undo() undo()
} }
if(ctrlKey && key === KEYS.Y) { if(ctrlKey && key === KEYS.Y) {
if(disableHotkeys.value) return
e.preventDefault() e.preventDefault()
redo() redo()
} }
if(ctrlKey && key === KEYS.A) { if(ctrlKey && key === KEYS.A) {
if(disableHotkeys.value) return
e.preventDefault() e.preventDefault()
selectAll() selectAll()
} }
if(ctrlKey && key === KEYS.L) { if(ctrlKey && key === KEYS.L) {
if(disableHotkeys.value) return
e.preventDefault() e.preventDefault()
lock() lock()
} }
if(!shiftKey && ctrlKey && key === KEYS.G) { if(!shiftKey && ctrlKey && key === KEYS.G) {
if(disableHotkeys.value) return
e.preventDefault() e.preventDefault()
combine() combine()
} }
if(shiftKey && ctrlKey && key === KEYS.G) { if(shiftKey && ctrlKey && key === KEYS.G) {
if(disableHotkeys.value) return
e.preventDefault() e.preventDefault()
uncombine() uncombine()
} }
if(key === KEYS.DELETE) { if(key === KEYS.DELETE) {
if(disableHotkeys.value) return
e.preventDefault() e.preventDefault()
remove() remove()
} }
if(key === KEYS.UP) { if(key === KEYS.UP) {
if(disableHotkeys.value) return
e.preventDefault() e.preventDefault()
move(KEYS.UP) move(KEYS.UP)
} }
if(key === KEYS.DOWN) { if(key === KEYS.DOWN) {
if(disableHotkeys.value) return
e.preventDefault() e.preventDefault()
move(KEYS.DOWN) move(KEYS.DOWN)
} }
if(key === KEYS.LEFT) { if(key === KEYS.LEFT) {
if(disableHotkeys.value) return
e.preventDefault() e.preventDefault()
move(KEYS.LEFT) move(KEYS.LEFT)
} }
if(key === KEYS.RIGHT) { if(key === KEYS.RIGHT) {
if(disableHotkeys.value) return
e.preventDefault() e.preventDefault()
move(KEYS.RIGHT) move(KEYS.RIGHT)
} }
if(key === KEYS.ENTER) { if(key === KEYS.ENTER) {
if(disableHotkeys.value) return
e.preventDefault() e.preventDefault()
create() create()
} }

View File

@ -1,5 +1,6 @@
<template> <template>
<div class="editable-element-shape" <div
class="editable-element-shape"
:style="{ :style="{
top: elementInfo.top + 'px', top: elementInfo.top + 'px',
left: elementInfo.left + 'px', left: elementInfo.left + 'px',

View File

@ -1,5 +1,6 @@
<template> <template>
<div class="editable-element-shape" <div
class="editable-element-shape"
:class="{ 'lock': elementInfo.lock }" :class="{ 'lock': elementInfo.lock }"
:style="{ :style="{
top: elementInfo.top + 'px', top: elementInfo.top + 'px',

View File

@ -14,7 +14,8 @@
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '', filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
}" }"
> >
<SvgWrapper overflow="visible" <SvgWrapper
overflow="visible"
:width="elementInfo.width" :width="elementInfo.width"
:height="elementInfo.height" :height="elementInfo.height"
> >

View File

@ -17,7 +17,8 @@
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '', filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
}" }"
> >
<SvgWrapper overflow="visible" <SvgWrapper
overflow="visible"
:width="elementInfo.width" :width="elementInfo.width"
:height="elementInfo.height" :height="elementInfo.height"
> >

View File

@ -8,7 +8,8 @@
transform: `rotate(${elementInfo.rotate}deg)`, transform: `rotate(${elementInfo.rotate}deg)`,
}" }"
> >
<div class="element-content" <div
class="element-content"
:style="{ :style="{
backgroundColor: elementInfo.fill, backgroundColor: elementInfo.fill,
opacity: elementInfo.opacity, opacity: elementInfo.opacity,
@ -20,7 +21,7 @@
:height="elementInfo.height" :height="elementInfo.height"
:outline="elementInfo.outline" :outline="elementInfo.outline"
/> />
<div class="text" v-html="elementInfo.content"></div> <div class="text ProseMirror-static" v-html="elementInfo.content"></div>
</div> </div>
</div> </div>
</template> </template>
@ -68,15 +69,4 @@ export default defineComponent({
position: relative; position: relative;
} }
} }
::v-deep(.text) {
word-break: break-word;
font-family: '微软雅黑';
outline: 0;
::selection {
background-color: rgba(27, 110, 232, 0.3);
color: inherit;
}
}
</style> </style>

View File

@ -1,6 +1,7 @@
<template> <template>
<div <div
class="editable-element-text" class="editable-element-text"
ref="elementRef"
:class="{ 'lock': elementInfo.lock }" :class="{ 'lock': elementInfo.lock }"
:style="{ :style="{
top: elementInfo.top + 'px', top: elementInfo.top + 'px',
@ -10,7 +11,8 @@
}" }"
@mousedown="$event => handleSelectElement($event)" @mousedown="$event => handleSelectElement($event)"
> >
<div class="element-content" <div
class="element-content"
:style="{ :style="{
backgroundColor: elementInfo.fill, backgroundColor: elementInfo.fill,
opacity: elementInfo.opacity, opacity: elementInfo.opacity,
@ -23,9 +25,9 @@
:height="elementInfo.height" :height="elementInfo.height"
:outline="elementInfo.outline" :outline="elementInfo.outline"
/> />
<div class="text" <div
v-html="elementInfo.content" class="text"
:contenteditable="!elementInfo.lock" ref="editorViewRef"
@mousedown="$event => handleSelectElement($event, false)" @mousedown="$event => handleSelectElement($event, false)"
></div> ></div>
</div> </div>
@ -33,10 +35,16 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, PropType } from 'vue' import { computed, defineComponent, onMounted, onUnmounted, PropType, ref, watch } from 'vue'
import debounce from 'lodash/debounce'
import { useStore } from 'vuex'
import { MutationTypes, State } from '@/store'
import { EditorView } from 'prosemirror-view'
import { PPTTextElement } from '@/types/slides' import { PPTTextElement } from '@/types/slides'
import { ContextmenuItem } from '@/components/Contextmenu/types' import { ContextmenuItem } from '@/components/Contextmenu/types'
import { initProsemirrorEditor, createDocument } from '@/prosemirror/'
import useElementShadow from '@/views/components/element/hooks/useElementShadow' import useElementShadow from '@/views/components/element/hooks/useElementShadow'
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
import ElementOutline from '@/views/components/element/ElementOutline.vue' import ElementOutline from '@/views/components/element/ElementOutline.vue'
@ -59,6 +67,72 @@ export default defineComponent({
}, },
}, },
setup(props) { setup(props) {
const store = useStore<State>()
const { addHistorySnapshot } = useHistorySnapshot()
const elementRef = ref<HTMLElement | null>(null)
const debounceUpdateTextElementHeight = debounce(function(realHeight) {
store.commit(MutationTypes.UPDATE_ELEMENT, {
id: props.elementInfo.id,
props: { height: realHeight },
})
}, 500, { trailing: true })
const updateTextElementHeight = () => {
if(!elementRef.value) return
const realHeight = elementRef.value.clientHeight
if(props.elementInfo.height !== realHeight) {
debounceUpdateTextElementHeight(realHeight)
}
}
const resizeObserver = new ResizeObserver(updateTextElementHeight)
onMounted(() => {
if(elementRef.value) resizeObserver.observe(elementRef.value)
})
onUnmounted(() => {
if(elementRef.value) resizeObserver.unobserve(elementRef.value)
})
const editorViewRef = ref<Element | null>(null)
let editorView: EditorView
const handleFocus = () => {
store.commit(MutationTypes.SET_DISABLE_HOTKEYS_STATE, true)
}
const handleBlur = () => {
store.commit(MutationTypes.SET_DISABLE_HOTKEYS_STATE, false)
}
const handleInput = debounce(function() {
store.commit(MutationTypes.UPDATE_ELEMENT, {
id: props.elementInfo.id,
props: { content: editorView.dom.innerHTML },
})
addHistorySnapshot()
}, 500, { trailing: true })
const textContent = computed(() => props.elementInfo.content)
watch(textContent, () => {
if(!editorView) return
if(editorView.hasFocus()) return
editorView.dom.innerHTML = textContent.value
})
onMounted(() => {
editorView = initProsemirrorEditor((editorViewRef.value as Element), textContent.value, {
handleDOMEvents: {
focus: handleFocus,
blur: handleBlur,
keydown: handleInput,
},
})
})
onUnmounted(() => {
editorView && editorView.destroy()
})
const handleSelectElement = (e: MouseEvent, canMove = true) => { const handleSelectElement = (e: MouseEvent, canMove = true) => {
if(props.elementInfo.lock) return if(props.elementInfo.lock) return
e.stopPropagation() e.stopPropagation()
@ -70,6 +144,8 @@ export default defineComponent({
const { shadowStyle } = useElementShadow(shadow) const { shadowStyle } = useElementShadow(shadow)
return { return {
elementRef,
editorViewRef,
handleSelectElement, handleSelectElement,
shadowStyle, shadowStyle,
} }
@ -97,15 +173,4 @@ export default defineComponent({
cursor: text; cursor: text;
} }
} }
::v-deep(.text) {
word-break: break-word;
font-family: '微软雅黑';
outline: 0;
::selection {
background-color: rgba(27, 110, 232, 0.3);
color: inherit;
}
}
</style> </style>