Skip to content

Commit

Permalink
feat(katzencore): Removed Any DOM Parser and Wrote own implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
maxlkatze committed Jul 25, 2024
1 parent 14cb590 commit 4a2b78c
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 25 deletions.
8 changes: 3 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,14 @@
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
},
"dependencies": {
"@nuxt/image": "1.7.0",
"@nuxtjs/tailwindcss": "6.12.1",
"@pinia/nuxt": "^0.5.2-beta.0",
"@nuxtjs/tailwindcss": "^6.12.1",
"@pinia/nuxt": "^0.5.1",
"@tiptap/extension-highlight": "^2.5.2",
"@tiptap/extension-placeholder": "^2.5.1",
"@tiptap/pm": "^2.5.1",
"@tiptap/starter-kit": "^2.5.1",
"@tiptap/vue-3": "^2.5.1",
"node-html-parser": "^6.1.13",
"pinia": "^2.1.8-beta.0"
"pinia": "^2.1.7"
},
"overrides": {
"vue": "latest",
Expand Down
2 changes: 0 additions & 2 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ export default defineNuxtModule<ModuleOptions>({
},
},
})

await installModule('@nuxt/image')
await installModule('@pinia/nuxt', {
storesDirs: [
'./runtime/stores/**',
Expand Down
146 changes: 128 additions & 18 deletions src/runtime/components/ui/KatzeRichText.vue
Original file line number Diff line number Diff line change
@@ -1,42 +1,152 @@
<script setup lang="ts">
import { parse, type HTMLElement } from 'node-html-parser'
import { onMounted, ref, watch } from '#imports'
const props = defineProps({
content: String,
})
const htmlElements = ref<HTMLElement[]>()
interface Node {
type: string
}
interface ElementNode extends Node {
tag: string
nodes: Node[]
}
interface TextNode extends Node {
text: string
}
interface RecursiveElementNode extends ElementNode {
parent?: RecursiveElementNode
}
const parseIntoNodes = (html: string): Node[] => {
// loop through characters
const characters = html.split('')
let tagOpen = false
let tagEnd = false
let currentElementTag = ''
let currentTextContent = ''
let currentElement: RecursiveElementNode | undefined = undefined
const elementTree = []
for (let i = 0; i < characters.length; i++) {
const char = characters[i]
if (tagOpen) {
if (char === '/') {
tagEnd = true
}
else if (char === '>') {
tagOpen = false
if (tagEnd) {
// AN ELEMENT HAS ENDED
if (currentElement) {
currentElement = currentElement.parent
}
}
else {
// AN ELEMENT HAS STARTED
const parent: RecursiveElementNode | undefined = currentElement
currentElement = {
tag: currentElementTag,
nodes: [],
parent,
type: 'element',
}
if (parent) {
parent.nodes.push(currentElement)
}
else {
elementTree.push(currentElement)
}
if (currentElementTag === 'br') {
currentElement = parent
tagOpen = false
}
}
}
else {
currentElementTag += char
}
continue
}
if (char === '<') {
if (currentTextContent) {
// A TEXT NODE HAS ENDED
const textNode: TextNode = {
text: currentTextContent,
type: 'text',
}
currentTextContent = ''
if (currentElement) {
currentElement.nodes.push(textNode)
}
else {
elementTree.push(textNode)
}
}
currentElementTag = ''
tagOpen = true
tagEnd = false
continue
}
currentTextContent += char
}
return elementTree
}
const nodesToHtml = (nodes: Node[]): string => {
let html = ''
for (const node of nodes) {
if (node.type === 'element') {
const element = node as ElementNode
html += `<${element.tag}>${nodesToHtml(element.nodes)}</${element.tag}>`
}
else if (node.type === 'text') {
const text = node as TextNode
html += text.text
}
}
return html
}
const parsedNodes = ref<Node[]>(parseIntoNodes(props.content || ''))
onMounted(() => {
updateHtmlElements(props.content || '')
parsedNodes.value = parseIntoNodes(props.content || '')
})
watch(() => props.content, (content) => {
updateHtmlElements(content || '')
parsedNodes.value = parseIntoNodes(content || '')
})
const updateHtmlElements = (content: string) => {
if (!content) return
const parsedContent = parse(content)
htmlElements.value = parsedContent.childNodes as HTMLElement[]
}
updateHtmlElements(props.content || '')
const getElementTag = (element: HTMLElement) => {
return element.rawTagName
const getElementTag = (element: Node) => {
if (element.type === 'element') {
return (element as ElementNode).tag
}
return 'span'
}
const getElementContent = (element: HTMLElement) => {
return element.innerHTML
const getElementContent = (element: Node) => {
return element.type === 'element' ? nodesToHtml((element as ElementNode).nodes) : ''
}
</script>

<template>
<component
:is="getElementTag(element)"
v-for="(element, index) in htmlElements"
:key="index+element.rawTagName"
v-for="(element, index) in parsedNodes"
:key="index+element.type"
v-hypertext="getElementContent(element)"
/>
</template>

0 comments on commit 4a2b78c

Please sign in to comment.