Skip to content

Commit 52a1d8d

Browse files
committed
feat(texteditor)[markdwon]: create temp fake parent
1 parent 4086f59 commit 52a1d8d

5 files changed

Lines changed: 192 additions & 12 deletions

File tree

contentcuration/contentcuration/frontend/channelEdit/router.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ import ReviewSelectionsPage from './views/ImportFromChannels/ReviewSelectionsPag
1111
import EditModal from './components/edit/EditModal';
1212
import ChannelDetailsModal from 'shared/views/channel/ChannelDetailsModal';
1313
import ChannelModal from 'shared/views/channel/ChannelModal';
14-
import TipTapEditor from 'shared/views/TipTapEditor/TipTapEditor/TipTapEditor.vue';
14+
import DevHarness from 'shared/views/TipTapEditor/TipTapEditor/DevHarness.vue';
1515
import { RouteNames as ChannelRouteNames } from 'frontend/channelList/constants';
1616

1717
const router = new VueRouter({
1818
routes: [
1919
{
2020
path: '/editor-dev',
2121
name: 'TipTapEditorDev',
22-
component: TipTapEditor,
22+
component: DevHarness,
2323
},
2424
{
2525
name: RouteNames.TREE_ROOT_VIEW,

contentcuration/contentcuration/frontend/editorDev/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Vue from 'vue';
22
import Vuex from 'vuex';
33
import VueRouter from 'vue-router';
4-
import TipTapEditor from '../shared/views/TipTapEditor/TipTapEditor/TipTapEditor.vue';
4+
import DevHarness from '../shared/views/TipTapEditor/TipTapEditor/DevHarness.vue';
55
import startApp from 'shared/app';
66
import storeFactory from 'shared/vuex/baseStore';
77

@@ -17,7 +17,7 @@ startApp({
1717
routes: [
1818
{
1919
path: '/',
20-
component: TipTapEditor,
20+
component: DevHarness,
2121
},
2222
],
2323
}),
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import Vue from 'vue';
22
import Router from 'vue-router';
3-
import TipTapEditor from '../shared/views/TipTapEditor/TipTapEditor/TipTapEditor.vue';
3+
import DevHarness from '../shared/views/TipTapEditor/TipTapEditor/DevHarness.vue';
44

55
Vue.use(Router);
66
export default new Router({
77
mode: 'history',
88
base: '/editor-dev/',
9-
routes: [{ path: '/', component: TipTapEditor }],
9+
routes: [{ path: '/', component: DevHarness }],
1010
});
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<template>
2+
3+
<div class="dev-harness">
4+
<header class="harness-header">
5+
<h1>TipTap Editor - Development Harness</h1>
6+
<p>This page simulates a parent component to test the editor in isolation.</p>
7+
</header>
8+
<hr >
9+
<TipTapEditor
10+
v-model="markdownContent"
11+
:mode="mode"
12+
/>
13+
<hr >
14+
<div class="raw-output-wrapper">
15+
<h2>Live Markdown Output (v-model state)</h2>
16+
<pre>{{ markdownContent }}</pre>
17+
</div>
18+
</div>
19+
20+
</template>
21+
22+
23+
<script>
24+
25+
import { defineComponent, ref } from 'vue';
26+
import TipTapEditor from './TipTapEditor.vue';
27+
28+
const SAMPLE_MARKDOWN = `# Testing basic & special nodes
29+
30+
**bold** *italic* <u>underline</u> ~~strikethrough~~
31+
32+
try inline formulas<span data-latex="x^2"></span> test
33+
34+
- list a
35+
- list b
36+
37+
<small class="small-text">
38+
small text
39+
</small>
40+
41+
1. list one
42+
2. list two
43+
44+
There is a [link here](https://github.com/learningequality/studio/pull/5155/checks)!
45+
46+
\`\`\`javascript
47+
export default html => {
48+
const domParser = new DOMParser();
49+
const doc = domParser.parseFromString(html, 'text/html');
50+
const mdImages = doc.querySelectorAll('span[is="markdown-image-field"]');
51+
52+
for (const mdImageEl of mdImages) {
53+
mdImageEl.replaceWith(mdImageEl.innerHTML.trim());
54+
}
55+
56+
const editOptionButtons = doc.querySelectorAll('.ignore-md');
57+
for (const editOptionsEl of editOptionButtons) {
58+
editOptionsEl.remove();
59+
}
60+
return doc.body.innerHTML;
61+
};
62+
\`\`\`
63+
`;
64+
65+
export default defineComponent({
66+
name: 'DevHarness',
67+
components: {
68+
TipTapEditor,
69+
},
70+
setup() {
71+
// simulates the state of the parent component
72+
const markdownContent = ref(SAMPLE_MARKDOWN);
73+
return { markdownContent };
74+
},
75+
});
76+
77+
</script>
78+
79+
80+
<style scoped>
81+
82+
.dev-harness {
83+
display: flex;
84+
flex-direction: column;
85+
align-items: center;
86+
padding: 20px;
87+
}
88+
89+
.harness-header {
90+
margin: 20px;
91+
text-align: center;
92+
}
93+
94+
.raw-output-wrapper {
95+
width: 1000px;
96+
padding: 1rem;
97+
margin-top: 20px;
98+
color: #f8f8f2;
99+
background-color: #2d2d2d;
100+
border-radius: 8px;
101+
}
102+
103+
.raw-output-wrapper h2 {
104+
margin-top: 0;
105+
color: #ffffff;
106+
}
107+
108+
pre {
109+
word-wrap: break-word;
110+
white-space: pre-wrap;
111+
}
112+
113+
</style>

contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/TipTapEditor.vue

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373

7474
<script>
7575
76-
import { defineComponent, provide, onMounted } from 'vue';
76+
import { defineComponent, provide, watch, nextTick } from 'vue';
7777
import EditorToolbar from './components/EditorToolbar.vue';
7878
import EditorContentWrapper from './components/EditorContentWrapper.vue';
7979
import { useEditor } from './composables/useEditor';
@@ -96,7 +96,7 @@
9696
LinkEditor,
9797
FormulasMenu,
9898
},
99-
setup() {
99+
setup(props, { emit }) {
100100
const { editor, isReady, initializeEditor } = useEditor();
101101
provide('editor', editor);
102102
provide('isReady', isReady);
@@ -107,10 +107,6 @@
107107
const mathHandler = useMathHandling(editor);
108108
provide('mathHandler', mathHandler);
109109
110-
onMounted(() => {
111-
initializeEditor();
112-
});
113-
114110
const {
115111
modalMode,
116112
modalInitialData,
@@ -130,6 +126,70 @@
130126
}
131127
};
132128
129+
const getMarkdownContent = () => {
130+
if (!editor.value || !isReady.value || !editor.value.storage?.markdown) {
131+
return '';
132+
}
133+
return editor.value.storage.markdown.getMarkdown();
134+
};
135+
136+
const setMarkdownContent = (content, emitUpdate = false) => {
137+
if (!editor.value || !isReady.value || !editor.value.storage?.markdown) {
138+
return;
139+
}
140+
editor.value.storage.markdown.setMarkdown(content, emitUpdate);
141+
};
142+
143+
let isUpdatingFromOutside = false; // A flag to prevent infinite update loops
144+
145+
// sync changes from the parent component to the editor
146+
watch(
147+
() => props.value,
148+
newValue => {
149+
if (!editor.value) {
150+
initializeEditor(newValue);
151+
return;
152+
}
153+
154+
if (!isReady.value || !editor.value.storage?.markdown) {
155+
return;
156+
}
157+
158+
const editorContent = getMarkdownContent();
159+
160+
if (editorContent !== newValue) {
161+
isUpdatingFromOutside = true;
162+
setMarkdownContent(newValue, false);
163+
nextTick(() => {
164+
isUpdatingFromOutside = false;
165+
});
166+
}
167+
},
168+
{ immediate: true },
169+
);
170+
171+
// sync changes from the editor to the parent component
172+
watch(
173+
() => editor.value?.state,
174+
() => {
175+
if (
176+
!editor.value ||
177+
!isReady.value ||
178+
isUpdatingFromOutside ||
179+
!editor.value.storage?.markdown
180+
) {
181+
return;
182+
}
183+
184+
const markdown = getMarkdownContent();
185+
186+
if (markdown !== props.value) {
187+
emit('input', markdown);
188+
}
189+
},
190+
{ deep: true },
191+
);
192+
133193
return {
134194
isReady,
135195
modalMode,
@@ -147,6 +207,13 @@
147207
mathHandler,
148208
};
149209
},
210+
props: {
211+
value: {
212+
type: String,
213+
default: '',
214+
},
215+
},
216+
emits: ['input'],
150217
});
151218
152219
</script>

0 commit comments

Comments
 (0)