Skip to content

Commit

Permalink
Refactor HeadingBlock using tiptap
Browse files Browse the repository at this point in the history
  • Loading branch information
holazz committed Aug 3, 2022
1 parent 0b68c73 commit 89e80a4
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 22 deletions.
12 changes: 9 additions & 3 deletions src/components/Block.vue
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,18 @@ function getInnerContent () {
}
function getTextContent () {
if (props.block.type === BlockType.Text || props.block.type === BlockType.Quote || props.block.type === BlockType.H1) {
return content.value.$el.querySelector('.ProseMirror').editor.getText()
}
const innerContent = getInnerContent()
if (innerContent) return innerContent.parentElement ? innerContent.parentElement.textContent : innerContent.textContent
else return ''
}
function getHtmlContent () {
if (props.block.type === BlockType.Text || props.block.type === BlockType.Quote || props.block.type === BlockType.H1) {
return content.value.$el.querySelector('.ProseMirror').editor.getHTML()
}
const innerContent = getInnerContent()
if (innerContent) return innerContent.parentElement.innerHTML
else return ''
Expand Down Expand Up @@ -299,9 +305,9 @@ function getCaretCoordinates () {
function getCaretPos () {
const selection = window.getSelection()
if (selection) {
let selectedNode = selection.anchorNode
if (props.block.type === BlockType.Text || props.block.type === BlockType.Quote) {
let offsetNode, offset = 0, tag = null
let selectedNode = selection.anchorNode
if (['STRONG', 'EM'].includes(selectedNode?.parentElement?.tagName as string)) {
selectedNode = selectedNode?.parentElement as Node
tag = (selectedNode as HTMLElement).tagName.toLowerCase()
Expand All @@ -323,7 +329,7 @@ function getCaretPos () {
}
return { pos: offset + selection.anchorOffset + (selectedNode?.parentElement?.tagName === 'P' ? 3 : 0), tag }
} else {
return { pos: selection.anchorOffset }
return { pos: selection.anchorOffset + (selectedNode?.parentElement?.tagName === 'H1' ? 4 : 0) }
}
} else {
return { pos: 0 }
Expand Down Expand Up @@ -365,7 +371,7 @@ function getCaretPosWithoutTags () {
function setCaretPos (caretPos:number) {
const innerContent = getInnerContent()
if (innerContent) {
if (props.block.type === BlockType.Text || props.block.type === BlockType.Quote) {
if (props.block.type === BlockType.Text || props.block.type === BlockType.Quote || props.block.type === BlockType.H1) {
let offsetNode, offset = 0
const numNodes = (content.value as any).$el.firstChild.firstChild.childNodes.length
for (const [i, node] of (content.value as any).$el.firstChild.firstChild.childNodes.entries()) {
Expand Down
60 changes: 46 additions & 14 deletions src/components/Lotion.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
</template>

<script setup lang="ts">
import { ref, onBeforeUpdate, PropType } from 'vue'
import { ref, onBeforeUpdate, PropType, nextTick } from 'vue'
import { VueDraggableNext as draggable } from 'vue-draggable-next'
import { Block, BlockType } from '@/utils/types'
import { Block, BlockType, blockTagMap } from '@/utils/types'
import BlockComponent from './Block.vue'
const props = defineProps({
Expand Down Expand Up @@ -97,33 +97,65 @@ function merge (blockIdx: number) {
if (props.page.blocks[blockIdx-1].type === BlockType.Text || props.page.blocks[blockIdx-1].type === BlockType.Quote) {
const prevBlockContentLength = blockElements.value[blockIdx-1].getTextContent().length
props.page.blocks[blockIdx-1].details.value = ('<p>' + (props.page.blocks[blockIdx-1] as any).details.value.replace('<p>', '').replace('</p>', '') + blockElements.value[blockIdx].getHtmlContent().replaceAll(/\<br.*?\>/g, '').replace('<p>', '').replace('</p>', '') + '</p>').replace('</strong><strong>', '').replace('</em><em>', '')
setTimeout(() => {
props.page.blocks[blockIdx-1].details.value = replaceTag(
(removeTag(props.page.blocks[blockIdx-1].details.value) + removeTag(blockElements.value[blockIdx].getHtmlContent())).replace('</strong><strong>', '').replace('</em><em>', ''),
blockTagMap[props.page.blocks[blockIdx-1].type],
)
nextTick(() => {
blockElements.value[blockIdx-1].setCaretPos(prevBlockContentLength)
props.page.blocks.splice(blockIdx, 1)
})
} else if ([BlockType.H1, BlockType.H2, BlockType.H3].includes(props.page.blocks[blockIdx-1].type)) {
} else if (props.page.blocks[blockIdx-1].type === BlockType.H1) {
const prevBlockContentLength = blockElements.value[blockIdx-1].getTextContent().length
props.page.blocks[blockIdx-1].details.value = replaceTag(
removeTag(props.page.blocks[blockIdx-1].details.value) + blockElements.value[blockIdx].getTextContent(),
blockTagMap[props.page.blocks[blockIdx-1].type],
)
nextTick(() => {
blockElements.value[blockIdx-1].setCaretPos(prevBlockContentLength)
props.page.blocks.splice(blockIdx, 1)
})
} else if ([BlockType.H2, BlockType.H3].includes(props.page.blocks[blockIdx-1].type)) {
const prevBlockContentLength = (props.page.blocks[blockIdx-1] as any).details.value.length
props.page.blocks[blockIdx-1].details.value += blockElements.value[blockIdx].getTextContent()
setTimeout(() => {
nextTick(() => {
blockElements.value[blockIdx-1].setCaretPos(prevBlockContentLength)
props.page.blocks.splice(blockIdx, 1)
})
} else {
props.page.blocks.splice(blockIdx-1, 1)
setTimeout(() => blockElements.value[blockIdx-1].moveToStart())
nextTick(() => blockElements.value[blockIdx-1].moveToStart())
}
}
function split (blockIdx: number) {
const caretPos = blockElements.value[blockIdx].getCaretPos()
insertBlock(blockIdx)
props.page.blocks[blockIdx+1].details.value = (caretPos.tag ? `<p><${caretPos.tag}>` : '<p>') + props.page.blocks[blockIdx].details.value?.slice(caretPos.pos)
if (props.page.blocks[blockIdx].type === BlockType.Text || props.page.blocks[blockIdx].type === BlockType.Quote) {
props.page.blocks[blockIdx].details.value = props.page.blocks[blockIdx].details.value?.slice(0, caretPos.pos) + (caretPos.tag ? `</${caretPos.tag}></p>` : '</p>')
} else {
props.page.blocks[blockIdx].details.value = props.page.blocks[blockIdx].details.value?.slice(0, caretPos.pos) + (caretPos.tag ? `</${caretPos.tag}></p>` : '')
}
setTimeout(() => blockElements.value[blockIdx+1].moveToStart())
const [prevContent, nextContent] = splitContentByIndex(props.page.blocks[blockIdx].details.value, caretPos.pos)
props.page.blocks[blockIdx+1].details.value = replaceTag(caretPos.tag ? `<${caretPos.tag}>${nextContent}` : nextContent, 'p')
props.page.blocks[blockIdx].details.value = replaceTag(caretPos.tag ? `${prevContent}</${caretPos.tag}>` : prevContent, blockTagMap[props.page.blocks[blockIdx].type])
nextTick(() => blockElements.value[blockIdx+1].moveToStart())
}
function splitContentByIndex(content: string | undefined, index: number) {
if (!content) return ['', '']
return [content.slice(0, index), content.slice(index)];
}
function removeTag(content: string | undefined) {
if (!content) return ''
return content.replace(/^<(p|h[123])>/, '').replace(/<\/(p|h[123])>$/, '')
}
function replaceTag(content: string, tag: string) {
const text = removeTag(content)
if (!tag) return text
return `<${tag}>${text}</${tag}>`
}
</script>
2 changes: 1 addition & 1 deletion src/components/Main.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const page = ref({
blocks:[{
type: BlockType.H1,
details: {
value: 'Get Started'
value: '<h1>Get Started</h1>'
},
}, {
type: BlockType.Divider,
Expand Down
4 changes: 3 additions & 1 deletion src/components/Markdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ const props = defineProps({
const markdownBlocks = computed(() => {
return props.page.blocks.map(block => {
if (block.type === BlockType.Text || block.type === BlockType.Quote) {
if (block.type === BlockType.Text || block.type === BlockType.Quote || block.type === BlockType.H1) {
return {
type: block.type,
details: {
value: (block.details.value as string)
.replaceAll('<p>', '')
.replaceAll('</p>', '')
.replaceAll('<h1>', '')
.replaceAll('</h1>', '')
.replaceAll('<strong>', '**')
.replaceAll('</strong>', '**')
.replaceAll('<em>', '*')
Expand Down
24 changes: 22 additions & 2 deletions src/components/blocks/TextBlock.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<template>
<Editor v-model="props.block.details.value"
class="py-1.5" />
class="py-1.5 "
:class="textConfig[props.block.type]?.class"
/>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { Block } from '@/utils/types'
import { Block, BlockType } from '@/utils/types'
import Editor from '../elements/Editor.vue'
const props = defineProps({
Expand All @@ -14,4 +16,22 @@ const props = defineProps({
required: true,
}
})
const textConfig = {
[BlockType.Text]: {
class: '',
},
[BlockType.H1]: {
class: 'text-4xl font-semibold',
},
[BlockType.H2]: {
class: 'text-3xl font-medium',
},
[BlockType.H3]: {
class: 'text-2xl font-medium',
},
// Irrelevant BlockTypes
[BlockType.Divider]: null,
[BlockType.Quote]: null,
}
</script>
4 changes: 4 additions & 0 deletions src/components/elements/Editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { watch } from 'vue'
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import Heading from '@tiptap/extension-heading'
import Bold from '@tiptap/extension-bold'
import Italic from '@tiptap/extension-italic'
import History from '@tiptap/extension-history'
Expand All @@ -27,6 +28,9 @@ const editor = useEditor({
Document,
Paragraph,
Text,
Heading.configure({
levels: [1],
}),
Bold,
Italic,
History,
Expand Down
11 changes: 10 additions & 1 deletion src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,18 @@ export interface Details {

export const BlockComponents = {
[BlockType.Text]: TextBlock,
[BlockType.H1]: HeadingBlock,
[BlockType.H1]: TextBlock,
[BlockType.H2]: HeadingBlock,
[BlockType.H3]: HeadingBlock,
[BlockType.Divider]: DividerBlock,
[BlockType.Quote]: QuoteBlock,
}

export const blockTagMap = {
[BlockType.Text]: 'p',
[BlockType.H1]: 'h1',
[BlockType.H2]: '',
[BlockType.H3]: '',
[BlockType.Quote]: 'p',
[BlockType.Divider]: '',
}

0 comments on commit 89e80a4

Please sign in to comment.