Skip to content

Commit 2c161b2

Browse files
authored
Merge pull request #8027 from nextcloud/backport/8002/stable30
[stable30] feat: Add table-only editor API
2 parents d5782b1 + 988b890 commit 2c161b2

File tree

5 files changed

+200
-0
lines changed

5 files changed

+200
-0
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<!--
2+
- SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3+
- SPDX-License-Identifier: AGPL-3.0-or-later
4+
-->
5+
<template>
6+
<Wrapper :content-loaded="true" :show-outline-outside="false">
7+
<MainContainer>
8+
<ContentContainer :read-only="readOnly" />
9+
</MainContainer>
10+
</Wrapper>
11+
</template>
12+
13+
<script>
14+
import { Editor } from '@tiptap/core'
15+
import History from '@tiptap/extension-history'
16+
import MainContainer from './MainContainer.vue'
17+
import Wrapper from './Wrapper.vue'
18+
import ContentContainer from './ContentContainer.vue'
19+
import { PlainTable } from '../../extensions/index.js'
20+
import { createMarkdownSerializer } from '../../extensions/Markdown.js'
21+
import { EDITOR, IS_PUBLIC, IS_RICH_EDITOR, IS_RICH_WORKSPACE, EDITOR_UPLOAD } from '../Editor.provider.js'
22+
import markdownit from '../../markdownit/index.js'
23+
24+
export default {
25+
name: 'PlainTableContentEditor',
26+
components: { ContentContainer, MainContainer, Wrapper },
27+
28+
provide() {
29+
const val = {}
30+
Object.defineProperties(val, {
31+
[EDITOR]: { get: () => this.$editor },
32+
[IS_PUBLIC]: { get: () => false },
33+
[IS_RICH_EDITOR]: { get: () => false },
34+
[IS_RICH_WORKSPACE]: { get: () => false },
35+
[EDITOR_UPLOAD]: { get: () => false },
36+
})
37+
return val
38+
},
39+
40+
props: {
41+
content: {
42+
type: String,
43+
default: '',
44+
},
45+
readOnly: {
46+
type: Boolean,
47+
default: false,
48+
},
49+
},
50+
51+
emits: ['update:content', 'ready', 'create:content'],
52+
53+
computed: {
54+
editor() {
55+
return this.$editor
56+
},
57+
},
58+
59+
created() {
60+
const html = markdownit.render(this.content)
61+
const extensions = [PlainTable, History]
62+
63+
this.$editor = new Editor({
64+
content: html,
65+
extensions,
66+
editable: !this.readOnly,
67+
})
68+
69+
this.$editor.on('create', () => {
70+
this.$emit('ready')
71+
this.$parent.$emit('ready')
72+
73+
try {
74+
const markdown = createMarkdownSerializer(this.$editor.schema).serialize(this.$editor.state.doc)
75+
this.emit('create:content', {
76+
json: this.$editor.state.doc,
77+
markdown,
78+
})
79+
} catch (error) {
80+
console.error('Error serializing table:', error)
81+
}
82+
})
83+
84+
this.$editor.on('update', ({ editor }) => {
85+
try {
86+
const markdown = createMarkdownSerializer(editor.schema).serialize(editor.state.doc)
87+
this.emit('update:content', {
88+
json: editor.state.doc,
89+
markdown,
90+
})
91+
} catch (error) {
92+
console.error('Error serializing table:', error)
93+
}
94+
})
95+
},
96+
97+
updated() {
98+
this.$editor.setEditable(!this.readOnly)
99+
},
100+
101+
beforeDestroy() {
102+
this.$editor.destroy()
103+
},
104+
105+
methods: {
106+
/**
107+
* Wrapper to emit events on our own and the parent component
108+
*
109+
* @param {string} event The event name
110+
* @param {any} data The data to pass along
111+
*/
112+
emit(event, data) {
113+
this.$emit(event, data)
114+
this.$parent?.$emit(event, data)
115+
},
116+
},
117+
}
118+
</script>
119+
120+
<style scoped>
121+
122+
</style>

src/editor.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,3 +269,47 @@ window.OCA.Text.createEditor = async function({
269269
.onSearch(onSearch)
270270
.render(el)
271271
}
272+
273+
window.OCA.Text.createTable = async function({
274+
// Element to render the editor to
275+
el,
276+
277+
content = '',
278+
279+
readOnly = false,
280+
autofocus = true,
281+
282+
onCreate = ({ markdown }) => {},
283+
onLoaded = () => {},
284+
onUpdate = ({ markdown }) => {},
285+
}) {
286+
const { default: PlainTableContentEditor } = await import(
287+
'./components/Editor/PlainTableContentEditor.vue'
288+
)
289+
290+
const data = Vue.observable({
291+
readOnly,
292+
content,
293+
})
294+
295+
const vm = new Vue({
296+
data() {
297+
return data
298+
},
299+
render: (h) => {
300+
return h(PlainTableContentEditor, {
301+
props: {
302+
content: data.content,
303+
readOnly: data.readOnly,
304+
showOutlineOutside: false,
305+
},
306+
})
307+
},
308+
})
309+
310+
return new TextEditorEmbed(vm, data)
311+
.onCreate(onCreate)
312+
.onLoaded(onLoaded)
313+
.onUpdate(onUpdate)
314+
.render(el)
315+
}

src/extensions/PlainTable.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { Extension } from '@tiptap/core'
7+
8+
import EditableTable from './../nodes/EditableTable.js'
9+
import PlainTableDocument from './../nodes/PlainTableDocument.js'
10+
import Keymap from './Keymap.js'
11+
import Markdown from './Markdown.js'
12+
/* eslint-disable import/no-named-as-default */
13+
import Text from '@tiptap/extension-text'
14+
15+
export default Extension.create({
16+
name: 'PlainTable',
17+
18+
addExtensions() {
19+
return [Markdown, PlainTableDocument, EditableTable, Keymap, Text]
20+
},
21+
})

src/extensions/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import FocusTrap from './FocusTrap.js'
99
import Keymap from './Keymap.js'
1010
import UserColor from './UserColor.js'
1111
import Markdown from './Markdown.js'
12+
import PlainTable from './PlainTable.js'
1213
import PlainText from './PlainText.js'
1314
import RichText from './RichText.js'
1415
import KeepSyntax from './KeepSyntax.js'
@@ -21,6 +22,7 @@ export {
2122
Keymap,
2223
UserColor,
2324
Markdown,
25+
PlainTable,
2426
PlainText,
2527
RichText,
2628
KeepSyntax,

src/nodes/PlainTableDocument.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { Node } from '@tiptap/core'
7+
8+
export default Node.create({
9+
name: 'doc',
10+
content: 'table',
11+
})

0 commit comments

Comments
 (0)