|
|
@@ -1,244 +0,0 @@
|
|
|
-<template>
|
|
|
- <div
|
|
|
- class="reset-div item-container"
|
|
|
- @mouseenter="handleMouseEnter"
|
|
|
- @mouseleave="handleMouseLeave"
|
|
|
- >
|
|
|
- <div
|
|
|
- class="item-menu"
|
|
|
- :class="className"
|
|
|
- >{{iconfontMap[type]}}</div>
|
|
|
- <component
|
|
|
- ref="element"
|
|
|
- :is="showInfo.tagName"
|
|
|
- :id="item.frontendId"
|
|
|
- @compositionstart.stop="handleCompositionStart"
|
|
|
- @compositionend.stop="handleCompositionEnd"
|
|
|
- @input.stop="handleInput"
|
|
|
- @keydown.stop="handleKeyDown"
|
|
|
- contenteditable
|
|
|
- v-html="showInfo.content"
|
|
|
- :class="showInfo.className"
|
|
|
- :placeholder="showInfo.placeholder"
|
|
|
- >
|
|
|
- </component>
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script>
|
|
|
-import {
|
|
|
- MODIFY_ITEM,
|
|
|
- ADD_ITEM_AFTER,
|
|
|
- DELETE_ITEM,
|
|
|
- SET_FOCUS_AFTER,
|
|
|
- SET_FOCUS_BEFORE,
|
|
|
- SET_FOCUS, SET_HOVER
|
|
|
-} from '@/store/types/editor-types'
|
|
|
-import defaultPlaceholder from '@/utils/default-placeholder'
|
|
|
-import defaultMarkdownFragment from '@/utils/default-markdown-fragment'
|
|
|
-import { matchType, generateShowInfo } from '@/utils/item-common'
|
|
|
-import iconfontMap from '@/utils/iconfont-map'
|
|
|
-import { mapState } from 'vuex'
|
|
|
-
|
|
|
-export default {
|
|
|
- name: 'editable-item',
|
|
|
- props: ['item'],
|
|
|
- data: function () {
|
|
|
- return {
|
|
|
- isCompositionStarted: false,
|
|
|
- range: null,
|
|
|
- type: 'div',
|
|
|
- iconfontMap: iconfontMap
|
|
|
- }
|
|
|
- },
|
|
|
- mounted () {
|
|
|
- this.type = this.matchType(this.item.content)
|
|
|
- },
|
|
|
- updated () {
|
|
|
- this.type = this.matchType(this.item.content)
|
|
|
- },
|
|
|
- computed: {
|
|
|
- ...mapState({
|
|
|
- hover: state => state.editor.hover
|
|
|
- }),
|
|
|
- showInfo () {
|
|
|
- return generateShowInfo(this.type, this.item)
|
|
|
- },
|
|
|
- className() {
|
|
|
- return {
|
|
|
- 'icon-hover': this.hover === this.item.frontendId,
|
|
|
- 'heading': this.type.match(/h[0-6]/)
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
- methods: {
|
|
|
- matchType,
|
|
|
- handleKeyDown (e) {
|
|
|
- if (!this.isCompositionStarted) {
|
|
|
- switch (e.key) {
|
|
|
- case 'Enter':
|
|
|
- e.preventDefault()
|
|
|
- this.$store.commit(ADD_ITEM_AFTER, {
|
|
|
- frontendId: this.item.frontendId
|
|
|
- })
|
|
|
- this.$store.commit(SET_FOCUS_AFTER, {
|
|
|
- focusId: this.item.frontendId
|
|
|
- })
|
|
|
- break
|
|
|
-
|
|
|
- case 'Backspace':
|
|
|
- if (!this.item.content) {
|
|
|
- e.preventDefault()
|
|
|
- this.$store.commit(SET_FOCUS_BEFORE, {
|
|
|
- focusId: this.item.frontendId
|
|
|
- })
|
|
|
- this.$store.commit(DELETE_ITEM, {
|
|
|
- frontendId: this.item.frontendId
|
|
|
- })
|
|
|
- break
|
|
|
- }
|
|
|
-
|
|
|
- this.range = window.getSelection().getRangeAt(0)
|
|
|
- const offset = this.range.startOffset
|
|
|
-
|
|
|
- if (offset === 0 && this.range.collapsed && this.type !== 'div') {
|
|
|
- this.$store.commit(MODIFY_ITEM, {
|
|
|
- frontendId: this.item.frontendId,
|
|
|
- diff: {
|
|
|
- placeholder: defaultPlaceholder['div'],
|
|
|
- content: this.showInfo.content
|
|
|
- }
|
|
|
- })
|
|
|
-
|
|
|
- this.$store.commit(SET_FOCUS, {
|
|
|
- diff: { focusId: this.item.frontendId, offset }
|
|
|
- })
|
|
|
- }
|
|
|
- break
|
|
|
-
|
|
|
- case 'ArrowUp':
|
|
|
- this.handleRangeChanged(() => {
|
|
|
- this.$store.commit(SET_FOCUS_BEFORE, {
|
|
|
- focusId: this.item.frontendId
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- break
|
|
|
- case 'ArrowDown':
|
|
|
- this.handleRangeChanged(() => {
|
|
|
- this.$store.commit(SET_FOCUS_AFTER, {
|
|
|
- focusId: this.item.frontendId
|
|
|
- })
|
|
|
- })
|
|
|
- break
|
|
|
-
|
|
|
- default:
|
|
|
- if (this.type === 'slideBreak') {
|
|
|
- e.preventDefault()
|
|
|
- }
|
|
|
- break
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
- handleCompositionStart () {
|
|
|
- this.isCompositionStarted = true
|
|
|
- },
|
|
|
- handleCompositionEnd (e) {
|
|
|
- this.isCompositionStarted = false
|
|
|
- this.handleInputValue(e)
|
|
|
- },
|
|
|
- handleInput (e) {
|
|
|
- !this.isCompositionStarted && this.handleInputValue(e)
|
|
|
- },
|
|
|
- handleInputValue (e) {
|
|
|
- const diff = {
|
|
|
- content: defaultMarkdownFragment[this.type] + e.target.textContent
|
|
|
- }
|
|
|
- if (this.type === 'div' && this.matchType(diff.content) !== this.type) {
|
|
|
- diff.placeholder = defaultPlaceholder[this.matchType(diff.content)]
|
|
|
- }
|
|
|
-
|
|
|
- this.$store.commit(MODIFY_ITEM, {
|
|
|
- frontendId: this.item.frontendId,
|
|
|
- diff
|
|
|
- })
|
|
|
-
|
|
|
- if (this.matchType(diff.content) === 'slideBreak') {
|
|
|
- this.$store.commit(ADD_ITEM_AFTER, { frontendId: this.item.frontendId })
|
|
|
-
|
|
|
- this.$store.commit(SET_FOCUS_AFTER, {
|
|
|
- focusId: this.item.frontendId,
|
|
|
- offset: 0
|
|
|
- })
|
|
|
- } else {
|
|
|
- this.range = window.getSelection().getRangeAt(0)
|
|
|
- const offset = this.range.startOffset
|
|
|
-
|
|
|
- this.$store.commit(SET_FOCUS, {
|
|
|
- diff: { focusId: this.item.frontendId, offset }
|
|
|
- })
|
|
|
- }
|
|
|
- },
|
|
|
- handleRangeChanged (cb) {
|
|
|
- let range = window.getSelection().getRangeAt(0)
|
|
|
- const firstOffset = range.startOffset
|
|
|
- console.log(range)
|
|
|
-
|
|
|
- this.$nextTick(() => {
|
|
|
- range = window.getSelection().getRangeAt(0)
|
|
|
- const nextOffset = range.startOffset
|
|
|
- console.log(range)
|
|
|
- if (firstOffset === nextOffset) {
|
|
|
- cb()
|
|
|
- }
|
|
|
- })
|
|
|
- },
|
|
|
- handleMouseEnter () {
|
|
|
- this.$store.commit(SET_HOVER, { frontendId: this.item.frontendId })
|
|
|
- },
|
|
|
- handleMouseLeave () {
|
|
|
- this.$store.commit(SET_HOVER, { frontendId: '' })
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-</script>
|
|
|
-
|
|
|
-<style lang="scss" scoped>
|
|
|
-@import '~@/common/slide/editing-item.scss';
|
|
|
-
|
|
|
-.reset-div {
|
|
|
- padding: unset;
|
|
|
- line-height: unset;
|
|
|
-}
|
|
|
-
|
|
|
-.item-menu {
|
|
|
- opacity: 0;
|
|
|
- cursor: pointer;
|
|
|
- font-family: 'iconfont', sans-serif;
|
|
|
- position: absolute;
|
|
|
- transform: translateX(-32px);
|
|
|
- color: #9aaaba;
|
|
|
- font-size: .9em;
|
|
|
- transition: 0.2s;
|
|
|
- &:hover {
|
|
|
- color: #6a7a8a;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.icon-hover {
|
|
|
- opacity: 1;
|
|
|
-}
|
|
|
-
|
|
|
-.heading {
|
|
|
- transform: translateX(-32px) scaleY(.9);
|
|
|
-}
|
|
|
-
|
|
|
-.item-container {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
-
|
|
|
- & > * {
|
|
|
- flex: 1;
|
|
|
- }
|
|
|
-}
|
|
|
-</style>
|