Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]: '',
}