Skip to content

Commit 513552b

Browse files
committed
refactor(app): make editor nodes have an id
1 parent cc31ce5 commit 513552b

File tree

7 files changed

+151
-40
lines changed

7 files changed

+151
-40
lines changed

packages/app/modules/editor/components/MEditor.vue

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script setup lang="ts">
22
import MonacoEditor from '../../monaco/components/MEditor.vue'
33
import { Parser, Node as MarkdownNode, NodeType } from '@language-kit/markdown'
4+
import NodeEditor from './NodeEditor.vue'
45
56
import uniqueId from 'lodash/uniqueId'
67
@@ -10,56 +11,51 @@ import MSetup from './MSetup.vue'
1011
import MComponent from './MComponent.vue'
1112
import { provideContext } from '../composable/context'
1213
import { provideManager } from '../composable/nodes-manager'
14+
import { NodeWithId } from '../types/node'
1315
14-
const props = defineProps({
15-
modelValue: {
16-
type: String,
17-
default: '',
18-
},
16+
const model = defineModel({
17+
type: String,
18+
default: '',
1919
})
2020
21-
const emit = defineEmits(['update:modelValue'])
22-
2321
const parser = new Parser()
2422
2523
const manager = provideManager()
2624
2725
const text = ref('')
26+
const nodes = ref<NodeWithId[]>([])
2827
29-
function load() {
30-
const prepareText = props.modelValue.replace(/\r\n/g, '\n')
31-
32-
manager.nodes = parser.toNodes(prepareText)
33-
34-
text.value = prepareText
28+
function mountNodes(value: string) {
29+
return parser.toNodes(value).map((n) => new NodeWithId(uniqueId('node:'), n))
3530
}
3631
37-
function updateNode(index: number, node: MarkdownNode) {
38-
manager.nodes.splice(index, 1, node)
39-
40-
text.value = manager.nodes.map((n) => n.toText()).join('')
32+
function load() {
33+
nodes.value = mountNodes(model.value)
4134
42-
emit('update:modelValue', text.value)
35+
text.value = model.value
4336
}
4437
4538
function updateText(value: string) {
4639
text.value = value
4740
48-
manager.nodes = parser.toNodes(value)
41+
nodes.value = mountNodes(value)
4942
50-
emit('update:modelValue', value)
43+
model.value = value
5144
}
5245
53-
function updateTextFromNodes() {
54-
text.value = manager.nodes.map((n) => n.toText()).join('')
46+
function updateNodes(newNodes: NodeWithId[]) {
47+
nodes.value = newNodes
48+
49+
text.value = nodes.value.map((n) => n.toText()).join('')
5550
}
5651
57-
watch(() => props.modelValue, load, {
58-
immediate: true,
59-
})
52+
function onChangeNodes() {
53+
text.value = nodes.value.map((n) => n.toText()).join('')
54+
55+
model.value = text.value
56+
}
6057
61-
manager.on('remove', updateTextFromNodes)
62-
manager.on('add', updateTextFromNodes)
58+
onMounted(load)
6359
6460
// context
6561
@@ -79,7 +75,10 @@ function isSetup(node: MarkdownNode) {
7975
<MonacoEditor v-model="text" language="markdown" @keydown.ctrl.s="updateText(text)" />
8076
</div>
8177

82-
<div class="h-full w-6/12 overflow-auto pb-80 border-l border-b-secondary/25">
78+
<div class="h-full w-6/12">
79+
<NodeEditor v-model="nodes" @change="onChangeNodes" />
80+
</div>
81+
<!-- <div class="h-full w-6/12 overflow-auto pb-80 border-l border-b-secondary/25">
8382
<template v-for="(node, index) in manager.nodes" :key="index">
8483
<MSetup
8584
v-if="isSetup(node)"
@@ -109,6 +108,6 @@ function isSetup(node: MarkdownNode) {
109108
{{ $t('errors.errorRenderingBlock', [node.type]) }}
110109
</div>
111110
</template>
112-
</div>
111+
</div> -->
113112
</div>
114113
</template>

packages/app/modules/editor/components/MParagraph.vue

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ import { useManger } from '../composable/nodes-manager'
77
import { useFocusList } from '../composable/focus-list'
88
import { Token } from '@language-kit/lexer'
99
import { useCursorHelper } from '../composable/cursor'
10+
import { NodeWithId } from '../types/node'
1011
1112
// Props & Emit
1213
1314
const model = defineModel({
14-
type: MarkdownNode,
15+
type: NodeWithId,
1516
required: true,
1617
})
1718
@@ -41,15 +42,15 @@ function update(newHtml: string) {
4142
4243
tokens.splice(lastIndex, 0, breakLine as any)
4344
44-
const node = new MarkdownNode({
45+
const node = new NodeWithId(model.value.id, {
4546
type: NodeType.Paragraph,
4647
tokens,
4748
})
4849
4950
model.value = node
5051
}
5152
52-
watch(model, load)
53+
// watch(model, load)
5354
5455
onMounted(load)
5556
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<script setup lang="ts">
2+
import { NodeWithId } from '@modules/editor/types/node'
3+
4+
import MHeading from './MHeading.vue'
5+
import MParagraph from './MParagraph.vue'
6+
import MSetup from './MSetup.vue'
7+
import MComponent from './MComponent.vue'
8+
import { provideNodeEditor } from '../composable/node-editor'
9+
10+
const nodes = defineModel({
11+
type: Array as PropType<NodeWithId[]>,
12+
default: () => [],
13+
})
14+
15+
const emit = defineEmits(['change'])
16+
17+
const editor = provideNodeEditor()
18+
19+
function shouldHide(node: NodeWithId) {
20+
if (!node.isComponent()) return
21+
22+
return node.name === 'setup'
23+
}
24+
25+
function isSetup(node: NodeWithId) {
26+
if (!node.isComponent()) return
27+
28+
return node.name === 'setup'
29+
}
30+
31+
function updateNode(index: number, node: NodeWithId) {
32+
nodes.value.splice(index, 1, node)
33+
34+
emit('change', nodes.value)
35+
}
36+
</script>
37+
<template>
38+
<div class="h-full overflow-auto pb-80 border-l border-b-secondary/25">
39+
<div
40+
v-for="(node, index) in nodes"
41+
:key="node.id"
42+
:class="shouldHide(node) ? 'hidden' : ''"
43+
class="w-full"
44+
>
45+
<MSetup
46+
v-if="isSetup(node)"
47+
:model-value="node"
48+
@update:model-value="updateNode(index, $event)"
49+
/>
50+
51+
<MComponent
52+
v-else-if="node.type === 'component'"
53+
:model-value="node"
54+
@update:model-value="updateNode(index, $event)"
55+
/>
56+
57+
<MHeading
58+
v-else-if="node.type === 'heading'"
59+
:model-value="node"
60+
@update:model-value="updateNode(index, $event)"
61+
/>
62+
63+
<MParagraph
64+
v-else-if="node.type === 'paragraph'"
65+
:model-value="node"
66+
@update:model-value="updateNode(index, $event)"
67+
/>
68+
69+
<div v-else class="p-5 text-danger bg-danger/25 border-b">
70+
{{ $t('errors.errorRenderingBlock', [node.type]) }}
71+
</div>
72+
</div>
73+
</div>
74+
</template>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const key = Symbol('node-editor')
2+
3+
export function create() {
4+
const setupContext = reactive({})
5+
6+
return {
7+
setupContext,
8+
}
9+
}
10+
11+
export function provideNodeEditor() {
12+
const state = create()
13+
14+
provide(key, state)
15+
16+
return state
17+
}
18+
19+
export function useNodeEditor() {
20+
return inject(key, create())
21+
}

packages/app/modules/editor/composable/nodes-manager.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Node } from '@language-kit/markdown'
1+
import type { NodeWithId } from '../types/node'
22

33
const key = Symbol('editor:nodes')
44

@@ -8,7 +8,7 @@ interface Observer {
88
}
99

1010
function create() {
11-
const nodes = ref<Node[]>([])
11+
const nodes = ref<NodeWithId[]>([])
1212

1313
const observers: Observer[] = []
1414

@@ -19,11 +19,11 @@ function create() {
1919
observers.filter((o) => o.name === name).forEach((o) => o.callback())
2020
}
2121

22-
function updateNodeByIndex(index: number, node: Node) {
22+
function updateNodeByIndex(index: number, node: NodeWithId) {
2323
nodes.value[index] = node
2424
}
2525

26-
function removeNode(node: Node) {
26+
function removeNode(node: NodeWithId) {
2727
const index = nodes.value.indexOf(node)
2828

2929
if (index === -1) return
@@ -33,21 +33,21 @@ function create() {
3333
emit('remove')
3434
}
3535

36-
function addNode(index: number, node: Node) {
36+
function addNode(index: number, node: NodeWithId) {
3737
nodes.value.splice(index, 0, node)
3838

3939
emit('add')
4040
}
4141

42-
function addNodeBefore(node: Node, before: Node) {
42+
function addNodeBefore(node: NodeWithId, before: NodeWithId) {
4343
const index = nodes.value.indexOf(node)
4444

4545
if (index === -1) return
4646

4747
addNode(index - 1, before)
4848
}
4949

50-
function addNodeAfter(node: Node, after: Node) {
50+
function addNodeAfter(node: NodeWithId, after: NodeWithId) {
5151
const index = nodes.value.indexOf(node)
5252

5353
if (index === -1) return
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Node } from '@language-kit/markdown'
2+
3+
export class NodeWithId extends Node {
4+
public id: string
5+
6+
constructor(id: string, node: Pick<Node, 'tokens' | 'type'>) {
7+
super(node)
8+
9+
this.id = id
10+
}
11+
}

packages/app/modules/entry/pages/EMarkdown.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,12 @@ const props = defineProps({
2828
// set content
2929
const store = useStore()
3030
31+
const loading = ref(false)
3132
const content = ref('')
3233
3334
async function setContent() {
35+
loading.value = true
36+
3437
const decoder = new TextDecoder('utf-8')
3538
3639
const contentBuffer = await store.read({
@@ -40,6 +43,8 @@ async function setContent() {
4043
if (!contentBuffer) return
4144
4245
content.value = decoder.decode(contentBuffer)
46+
47+
loading.value = false
4348
}
4449
4550
const save = debounce(async () => {
@@ -104,7 +109,7 @@ const modeLabels: Record<typeof mode.value, string> = {
104109
</v-layout-toolbar>
105110

106111
<v-layout-content>
107-
<m-editor v-model="content" />
112+
<m-editor v-if="!loading" v-model="content" />
108113
</v-layout-content>
109114
</v-layout>
110115
</template>

0 commit comments

Comments
 (0)