@live-change/wysiwyg-frontend 0.2.9 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/front/src/DocumentEditorTest.vue +12 -7
- package/front/src/DocumentPreviewTest.vue +35 -5
- package/front/src/components/ComponentComponent.vue +22 -0
- package/front/src/components/ComponentNode.js +74 -0
- package/front/src/components/ComponentsMenu.vue +107 -0
- package/front/src/components/ContentView.js +2 -2
- package/front/src/components/DocumentEditor.vue +8 -0
- package/front/src/components/Editor.vue +8 -1
- package/front/src/components/EditorMenu.vue +34 -4
- package/front/src/components/EmptyComponent.vue +11 -0
- package/front/src/components/ImageNode.js +0 -1
- package/front/src/components/RemoteAuthority.js +1 -0
- package/front/src/components/components.js +15 -0
- package/front/src/components/contentConfig.js +10 -0
- package/front/src/components/contentConfigExtensions.js +4 -1
- package/front/src/router.js +8 -3
- package/index.js +4 -0
- package/package.json +10 -10
- package/server/page.documentType.js +103 -0
- package/server/services.config.js +2 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div>
|
|
3
|
-
<DocumentEditor targetType="Example" :target="target" :type="'
|
|
2
|
+
<div class="wysiwyg">
|
|
3
|
+
<DocumentEditor targetType="Example" :target="target" :type="'page'" :purpose="'test'"
|
|
4
4
|
:initialContent="emptyContent"
|
|
5
5
|
:config="contentConfig" />
|
|
6
6
|
</div>
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import DocumentEditor from "./components/DocumentEditor.vue"
|
|
12
12
|
import { ref } from 'vue'
|
|
13
|
-
import { basicMarks, messageNodes, richEditorNodes } from "./components/contentConfig.js"
|
|
13
|
+
import { basicMarks, messageNodes, richEditorNodes, pageNodes } from "./components/contentConfig.js"
|
|
14
14
|
import { toRefs } from '@vueuse/core'
|
|
15
15
|
|
|
16
16
|
const props = defineProps({
|
|
@@ -28,12 +28,11 @@
|
|
|
28
28
|
},
|
|
29
29
|
nodes: {
|
|
30
30
|
//...messageNodes,
|
|
31
|
-
|
|
31
|
+
//...richEditorNodes
|
|
32
|
+
...pageNodes
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
const document = 'testDocument'
|
|
36
|
-
|
|
37
36
|
const emptyContent = {
|
|
38
37
|
"type": "doc",
|
|
39
38
|
"content": [].concat(new Array(2).fill(
|
|
@@ -60,6 +59,12 @@
|
|
|
60
59
|
|
|
61
60
|
</script>
|
|
62
61
|
|
|
63
|
-
<style
|
|
62
|
+
<style lang="scss">
|
|
63
|
+
.wysiwyg {
|
|
64
|
+
|
|
65
|
+
.ProseMirror:focus {
|
|
66
|
+
outline: none;
|
|
67
|
+
}
|
|
64
68
|
|
|
69
|
+
}
|
|
65
70
|
</style>
|
|
@@ -1,11 +1,41 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
2
|
+
<ContentView :content="document.content" :config="contentConfig" class="w-full" />
|
|
3
|
+
<pre>{{ JSON.stringify(document.content, null, " ") }}</pre>
|
|
3
4
|
</template>
|
|
4
5
|
|
|
5
|
-
<script>
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
<script setup>
|
|
7
|
+
import ContentView from "./components/ContentView.js"
|
|
8
|
+
import { ref } from 'vue'
|
|
9
|
+
|
|
10
|
+
const props = defineProps({
|
|
11
|
+
target: {
|
|
12
|
+
type: String,
|
|
13
|
+
default: 'one'
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
import { basicMarks, messageNodes, richEditorNodes, pageNodes } from "./components/contentConfig.js"
|
|
18
|
+
|
|
19
|
+
const contentConfig = {
|
|
20
|
+
marks: {
|
|
21
|
+
...basicMarks
|
|
22
|
+
},
|
|
23
|
+
nodes: {
|
|
24
|
+
//...messageNodes,
|
|
25
|
+
//...richEditorNodes,
|
|
26
|
+
...pageNodes
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
import { path, live, useApi } from '@live-change/vue3-ssr'
|
|
31
|
+
const api = useApi()
|
|
32
|
+
const p = path()
|
|
33
|
+
|
|
34
|
+
const document = await live(
|
|
35
|
+
p.prosemirror.document({ targetType: 'Example', target: props.target })
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
9
39
|
</script>
|
|
10
40
|
|
|
11
41
|
<style scoped>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<node-view-wrapper>
|
|
3
|
+
<ComponentEditor>
|
|
4
|
+
<node-view-content class="content" />
|
|
5
|
+
</ComponentEditor>
|
|
6
|
+
</node-view-wrapper>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script setup>
|
|
10
|
+
import { NodeViewContent, nodeViewProps, NodeViewWrapper } from '@tiptap/vue-3'
|
|
11
|
+
import components from "./components.js";
|
|
12
|
+
|
|
13
|
+
const props = defineProps(nodeViewProps)
|
|
14
|
+
|
|
15
|
+
const { is, attrs } = props.node.attrs
|
|
16
|
+
|
|
17
|
+
const ComponentEditor = components[is].editor
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<style scoped>
|
|
21
|
+
|
|
22
|
+
</style>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Node, mergeAttributes } from '@tiptap/core'
|
|
2
|
+
import { VueNodeViewRenderer } from '@tiptap/vue-3'
|
|
3
|
+
import ComponentComponent from "./ComponentComponent.vue"
|
|
4
|
+
|
|
5
|
+
export default Node.create({
|
|
6
|
+
name: 'component',
|
|
7
|
+
group: 'block',
|
|
8
|
+
//atom: true,
|
|
9
|
+
inline: false,
|
|
10
|
+
selectable: true,
|
|
11
|
+
draggable: true,
|
|
12
|
+
content: 'block*',
|
|
13
|
+
marks: '',
|
|
14
|
+
|
|
15
|
+
addAttributes() {
|
|
16
|
+
return {
|
|
17
|
+
is: {
|
|
18
|
+
default: 'card',
|
|
19
|
+
renderHTML: attributes => {
|
|
20
|
+
return {
|
|
21
|
+
'data-is': attributes.is
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
attrs: {
|
|
26
|
+
default: {},
|
|
27
|
+
renderHTML: attributes => {
|
|
28
|
+
const htmlAttrs = {}
|
|
29
|
+
const attrs = attributes.attrs
|
|
30
|
+
for(let key in this.options) {
|
|
31
|
+
htmlAttrs[`data-attr-${key}`] = attrs[key]
|
|
32
|
+
}
|
|
33
|
+
return htmlAttrs
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
/*parseHTML() {
|
|
40
|
+
return [
|
|
41
|
+
{
|
|
42
|
+
tag: this.options.allowBase64
|
|
43
|
+
? 'img[src]'
|
|
44
|
+
: 'img[src]:not([src^="data:"])',
|
|
45
|
+
},
|
|
46
|
+
]
|
|
47
|
+
},*/
|
|
48
|
+
|
|
49
|
+
renderHTML({ HTMLAttributes }) {
|
|
50
|
+
/*const htmlAttrs = {}
|
|
51
|
+
const attrs = this.options.attrs
|
|
52
|
+
for(let key in this.options) {
|
|
53
|
+
htmlAttrs[`data-attr-${key}`] = attrs[key]
|
|
54
|
+
}
|
|
55
|
+
htmlAttrs['data-is'] = this.options.is
|
|
56
|
+
return ['component', mergeAttributes(HTMLAttributes, htmlAttrs)]*/
|
|
57
|
+
return ['component', HTMLAttributes]
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
addNodeView() {
|
|
61
|
+
return VueNodeViewRenderer(ComponentComponent)
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
addCommands() {
|
|
65
|
+
return {
|
|
66
|
+
setImage: options => ({ commands }) => {
|
|
67
|
+
return commands.insertContent({
|
|
68
|
+
type: this.name,
|
|
69
|
+
attrs: options
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
})
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<div class="px-2 pb-2">
|
|
4
|
+
<span class="p-input-icon-left w-full flex">
|
|
5
|
+
<i class="pi pi-search flex-shrink-0" />
|
|
6
|
+
<InputText type="text" v-model="searchQuery" placeholder="Search" class="flex-grow-1" v-focus />
|
|
7
|
+
</span>
|
|
8
|
+
</div>
|
|
9
|
+
<div class="overflow-auto" style="max-height: 75vh">
|
|
10
|
+
<!-- <div v-for="i in [1,2,3,4,5,6,7,8,9,10]">-->
|
|
11
|
+
<div v-for="([ name, component ], index) in searchResults" :key="name"
|
|
12
|
+
class="flex flex-wrap relative cursor-pointer hover:surface-100"
|
|
13
|
+
@click="() => emit('selected', name)" >
|
|
14
|
+
<div class="w-3 ml-2 mr-2 my-1" style="aspect-ratio: 1/1">
|
|
15
|
+
<div class="pointer-events-none w-6 absolute p-1"
|
|
16
|
+
style="aspect-ratio: 1/1; transform: scale(0.5); transform-origin: 0 0;">
|
|
17
|
+
<ContentView :config="config" :content="getTestContent(name, component)" style="aspect-ratio: 1/1" />
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="ml-2">
|
|
21
|
+
<h3>{{ name[0].toUpperCase() + name.slice(1) }}</h3>
|
|
22
|
+
<p>{{ component.description }}</p>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
<!-- </div>-->
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<script setup>
|
|
31
|
+
import InputText from "primevue/inputtext"
|
|
32
|
+
|
|
33
|
+
import ContentView from "./ContentView.js"
|
|
34
|
+
import components from "./components.js"
|
|
35
|
+
|
|
36
|
+
import { ref, computed } from 'vue'
|
|
37
|
+
|
|
38
|
+
const props = defineProps({
|
|
39
|
+
config: {
|
|
40
|
+
type: Object,
|
|
41
|
+
required: true
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const { config } = props
|
|
46
|
+
|
|
47
|
+
const emit = defineEmits(['selected'])
|
|
48
|
+
|
|
49
|
+
const searchQuery = ref('')
|
|
50
|
+
|
|
51
|
+
const searchResults = computed(() => {
|
|
52
|
+
const query = searchQuery.value.toLowerCase()
|
|
53
|
+
if(!query) return Object.entries(components)
|
|
54
|
+
const byName = Object.entries(components).filter(([name, component]) => {
|
|
55
|
+
return name.toLowerCase().includes(query)
|
|
56
|
+
})
|
|
57
|
+
const byDescription = Object.entries(components).filter(([name, component]) => {
|
|
58
|
+
return (component.description || '').toLowerCase().includes(query)
|
|
59
|
+
}).filter(([name, component]) => !byName.find(([name2]) => name == name2))
|
|
60
|
+
return [...byName, ...byDescription]
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
function getTestContent(name, component) {
|
|
64
|
+
return {
|
|
65
|
+
"type": 'component',
|
|
66
|
+
"attrs": {
|
|
67
|
+
"is": name,
|
|
68
|
+
"attrs": {
|
|
69
|
+
...component.previewAttrs
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"content": component.previewContent ? [
|
|
73
|
+
{
|
|
74
|
+
"type": "heading",
|
|
75
|
+
"attrs": {
|
|
76
|
+
"level": 3
|
|
77
|
+
},
|
|
78
|
+
"content": [
|
|
79
|
+
{
|
|
80
|
+
"type": "text",
|
|
81
|
+
"text": name[0].toUpperCase() + name.slice(1)
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"type": "paragraph",
|
|
87
|
+
"content": [
|
|
88
|
+
{
|
|
89
|
+
"type": "text",
|
|
90
|
+
"text": component.description
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
] : [
|
|
95
|
+
{
|
|
96
|
+
"type": "text",
|
|
97
|
+
"text": component.description
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
</script>
|
|
104
|
+
|
|
105
|
+
<style scoped>
|
|
106
|
+
|
|
107
|
+
</style>
|
|
@@ -9,9 +9,9 @@ function renderNode(node, r, marks, nodes) {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
function ContentView(props, context) {
|
|
12
|
-
|
|
12
|
+
let { content, config } = props
|
|
13
13
|
if(content) {
|
|
14
|
-
const doc = JSON.parse(content)
|
|
14
|
+
const doc = typeof content == 'string' ? JSON.parse(content) : content
|
|
15
15
|
const r = nodes => nodes?.map(n => renderNode(n, r, config.marks, config.nodes))
|
|
16
16
|
return renderNode(doc, r, config.marks, config.nodes)
|
|
17
17
|
}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
<script setup>
|
|
16
16
|
|
|
17
17
|
import { useEditor, EditorContent } from '@tiptap/vue-3'
|
|
18
|
+
import { History } from '@tiptap/extension-history'
|
|
18
19
|
import {
|
|
19
20
|
ref, computed, watch, provide, defineEmits, defineProps, getCurrentInstance, onUnmounted, inject, onMounted
|
|
20
21
|
} from 'vue'
|
|
@@ -119,6 +120,10 @@
|
|
|
119
120
|
ProseMirrorCollab.configure({
|
|
120
121
|
version: documentData.version,
|
|
121
122
|
clientID
|
|
123
|
+
}),
|
|
124
|
+
History.configure({
|
|
125
|
+
depth: 100,
|
|
126
|
+
newGroupDelay: 500
|
|
122
127
|
})
|
|
123
128
|
],
|
|
124
129
|
onTransaction: ({ editor, transaction }) => {
|
|
@@ -134,6 +139,8 @@
|
|
|
134
139
|
? serializeSchema(editor.view.state.schema.spec)
|
|
135
140
|
: getSchemaSpecFromConfig(props.config)
|
|
136
141
|
|
|
142
|
+
if(typeof window != 'undefined') window.schemaSpecJson = JSON.stringify(schemaSpec, null, " ")
|
|
143
|
+
|
|
137
144
|
async function save() {
|
|
138
145
|
const state = editor.value.reactiveState.value
|
|
139
146
|
const sendable = sendableSteps(state)
|
|
@@ -155,6 +162,7 @@
|
|
|
155
162
|
transaction.apply(editor.value.view.state)
|
|
156
163
|
}
|
|
157
164
|
}
|
|
165
|
+
|
|
158
166
|
</script>
|
|
159
167
|
|
|
160
168
|
<style scoped>
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
<script setup>
|
|
16
16
|
|
|
17
17
|
import { useEditor, EditorContent } from '@tiptap/vue-3'
|
|
18
|
+
import { History } from '@tiptap/extension-history'
|
|
18
19
|
|
|
19
20
|
import { ref, computed, watch, provide, defineEmits, defineProps } from 'vue'
|
|
20
21
|
import { toRefs } from '@vueuse/core'
|
|
@@ -39,7 +40,13 @@
|
|
|
39
40
|
|
|
40
41
|
const editor = useEditor({
|
|
41
42
|
content: JSON.parse(props.modelValue),
|
|
42
|
-
extensions
|
|
43
|
+
extensions: [
|
|
44
|
+
...extensions,
|
|
45
|
+
History.configure({
|
|
46
|
+
depth: 100,
|
|
47
|
+
newGroupDelay: 500
|
|
48
|
+
})
|
|
49
|
+
],
|
|
43
50
|
onUpdate: ({ editor, transaction }) => {
|
|
44
51
|
console.log("EDITOR UPDATE", editor, transaction)
|
|
45
52
|
const editorJson = JSON.stringify(editor.getJSON())
|
|
@@ -76,8 +76,9 @@
|
|
|
76
76
|
</FileInput>
|
|
77
77
|
<!-- </div>-->
|
|
78
78
|
|
|
79
|
-
<Button v-if="config.nodes.component" label="Insert Component"
|
|
80
|
-
|
|
79
|
+
<Button v-if="config.nodes.component" label="Insert Component"
|
|
80
|
+
class="p-button-sm p-button-primary inline-block mr-1 mb-1"
|
|
81
|
+
@click="openComponentMenu" />
|
|
81
82
|
|
|
82
83
|
<span v-if="saveState"
|
|
83
84
|
class="p-button p-component p-button-sm p-button-outlined p-button-secondary cursor-auto
|
|
@@ -98,14 +99,23 @@
|
|
|
98
99
|
|
|
99
100
|
</div>
|
|
100
101
|
<slot name="after" />
|
|
102
|
+
<OverlayPanel ref="insertComponentOverlay" style="width: 450px" :breakpoints="{'960px': '75vw'}">
|
|
103
|
+
<ComponentsMenu :config="config" @selected="c => handleComponentSelected(c)" />
|
|
104
|
+
</OverlayPanel>
|
|
105
|
+
<!-- <div class="surface-card p-1 shadow-2" style="width: 450px">
|
|
106
|
+
<h3>components:</h3>
|
|
107
|
+
<ComponentsMenu :config="config" />
|
|
108
|
+
</div>-->
|
|
101
109
|
</div>
|
|
102
110
|
</template>
|
|
103
111
|
|
|
104
112
|
<script setup>
|
|
105
113
|
import Button from "primevue/button"
|
|
106
114
|
import ProgressSpinner from "primevue/progressspinner"
|
|
115
|
+
import OverlayPanel from 'primevue/overlaypanel'
|
|
107
116
|
|
|
108
|
-
import
|
|
117
|
+
import ComponentsMenu from "./ComponentsMenu.vue"
|
|
118
|
+
import { inject, getCurrentInstance, ref } from "vue"
|
|
109
119
|
import { toRefs } from "@vueuse/core"
|
|
110
120
|
import { uploadImage } from "@live-change/image-frontend"
|
|
111
121
|
import { FileInput } from "@live-change/upload-frontend"
|
|
@@ -145,7 +155,27 @@
|
|
|
145
155
|
await upload.upload()
|
|
146
156
|
}
|
|
147
157
|
|
|
148
|
-
|
|
158
|
+
const insertComponentOverlay = ref()
|
|
159
|
+
function openComponentMenu(event) {
|
|
160
|
+
insertComponentOverlay.value.show(event)
|
|
161
|
+
}
|
|
162
|
+
function handleComponentSelected(component) {
|
|
163
|
+
insertComponentOverlay.value.hide()
|
|
164
|
+
editor.value.chain().focus().insertContent({
|
|
165
|
+
type: 'component',
|
|
166
|
+
attrs: {
|
|
167
|
+
is: component,
|
|
168
|
+
},
|
|
169
|
+
content: [
|
|
170
|
+
{ type: 'paragraph', content: [
|
|
171
|
+
{ type: 'text', text: 'test' }
|
|
172
|
+
] }
|
|
173
|
+
]
|
|
174
|
+
}).run()
|
|
175
|
+
//editor.value.chain().focus().setNode('component', { is: component }).run()
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if(typeof window != 'undefined') window.editor = editor.value
|
|
149
179
|
</script>
|
|
150
180
|
|
|
151
181
|
<style scoped>
|
|
@@ -117,6 +117,7 @@ class RemoteAuthority{
|
|
|
117
117
|
debug("LOAD DOCUMENT!")
|
|
118
118
|
const identifier = { targetType: this.targetType, target: this.target }
|
|
119
119
|
let documentData = await this.api.get(['prosemirror', 'document', identifier])
|
|
120
|
+
console.log("DOCUMENT DATA", documentData)
|
|
120
121
|
if(!documentData) {
|
|
121
122
|
documentData = {
|
|
122
123
|
...identifier,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { h } from 'vue'
|
|
2
|
+
|
|
3
|
+
const components = {
|
|
4
|
+
card: {
|
|
5
|
+
description: 'PrimeVUE card',
|
|
6
|
+
previewContent: true,
|
|
7
|
+
attrs: [],
|
|
8
|
+
render: ({ content, attrs }, r) => h('div', { 'class': 'surface-card py-1 px-3 shadow-2 w-full' }, r(content)),
|
|
9
|
+
editor: (props, { slots }) => h('div', { 'class': 'surface-card py-1 px-3 shadow-2 w-full' }, slots.default()),
|
|
10
|
+
async: true
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export default components
|
|
@@ -48,3 +48,13 @@ export const richEditorNodes = {
|
|
|
48
48
|
|
|
49
49
|
...messageNodes
|
|
50
50
|
}
|
|
51
|
+
|
|
52
|
+
import components from './components.js'
|
|
53
|
+
|
|
54
|
+
export const pageNodes = {
|
|
55
|
+
...richEditorNodes,
|
|
56
|
+
|
|
57
|
+
component: (params, r) => {
|
|
58
|
+
return components[params.attrs.is].render(params, r)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -19,6 +19,7 @@ import Strike from "@tiptap/extension-strike"
|
|
|
19
19
|
|
|
20
20
|
import ImageNode from "./ImageNode.js"
|
|
21
21
|
|
|
22
|
+
import ComponentNode from "./ComponentNode.js"
|
|
22
23
|
|
|
23
24
|
export const marksExtensions = {
|
|
24
25
|
bold: [Bold],
|
|
@@ -43,7 +44,9 @@ export const nodesExtensions = {
|
|
|
43
44
|
orderedList: [OrderedList],
|
|
44
45
|
listItem: [ListItem],
|
|
45
46
|
|
|
46
|
-
image: [ImageNode]
|
|
47
|
+
image: [ImageNode],
|
|
48
|
+
|
|
49
|
+
component: [ComponentNode]
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
export function getExtensions(contentConfig) {
|
package/front/src/router.js
CHANGED
|
@@ -18,10 +18,15 @@ export function wysiwygRoutes(config = {}) {
|
|
|
18
18
|
}),
|
|
19
19
|
|
|
20
20
|
route({
|
|
21
|
-
name: 'wysiwyg:documentEditor', path: prefix + '
|
|
21
|
+
name: 'wysiwyg:documentEditor', path: prefix + 'doc/:target', meta: { },
|
|
22
22
|
component: () => import("./DocumentEditorTest.vue"),
|
|
23
|
-
props:
|
|
24
|
-
|
|
23
|
+
props: true
|
|
24
|
+
}),
|
|
25
|
+
|
|
26
|
+
route({
|
|
27
|
+
name: 'wysiwyg:documentPreview', path: prefix + 'view/:target', meta: { },
|
|
28
|
+
component: () => import("./DocumentPreviewTest.vue"),
|
|
29
|
+
props: true
|
|
25
30
|
}),
|
|
26
31
|
|
|
27
32
|
...dbAdminRoutes({ prefix: '/_db', route: r => ({ ...r, meta: { ...r.meta, raw: true }}) })
|
package/index.js
CHANGED
|
@@ -11,3 +11,7 @@ import EditorMenu from "./front/src/components/EditorMenu.vue"
|
|
|
11
11
|
import ImageComponent from "./front/src/components/ImageComponent.vue"
|
|
12
12
|
import ImageNode from "./front/src/components/ImageNode.js"
|
|
13
13
|
export { DocumentEditor, Editor, EditorMenu, ImageComponent, ImageNode }
|
|
14
|
+
|
|
15
|
+
import components from "./front/src/components/components.js"
|
|
16
|
+
|
|
17
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@live-change/wysiwyg-frontend",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"memDev": "lcli memDev --enableSessions --initScript ./init.js --dbAccess",
|
|
6
6
|
"localDevInit": "rm tmp.db; lcli localDev --enableSessions --initScript ./init.js",
|
|
@@ -21,16 +21,16 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@fortawesome/fontawesome-free": "^6.2.0",
|
|
24
|
-
"@live-change/cli": "0.7.
|
|
24
|
+
"@live-change/cli": "0.7.5",
|
|
25
25
|
"@live-change/dao": "0.5.8",
|
|
26
26
|
"@live-change/dao-vue3": "0.5.8",
|
|
27
27
|
"@live-change/dao-websocket": "0.5.8",
|
|
28
|
-
"@live-change/framework": "0.7.
|
|
29
|
-
"@live-change/password-authentication-service": "0.3.
|
|
30
|
-
"@live-change/prosemirror-service": "0.3.
|
|
31
|
-
"@live-change/secret-code-service": "0.3.
|
|
32
|
-
"@live-change/secret-link-service": "0.3.
|
|
33
|
-
"@live-change/session-service": "0.3.
|
|
28
|
+
"@live-change/framework": "0.7.5",
|
|
29
|
+
"@live-change/password-authentication-service": "0.3.3",
|
|
30
|
+
"@live-change/prosemirror-service": "0.3.3",
|
|
31
|
+
"@live-change/secret-code-service": "0.3.3",
|
|
32
|
+
"@live-change/secret-link-service": "0.3.3",
|
|
33
|
+
"@live-change/session-service": "0.3.3",
|
|
34
34
|
"@live-change/vue3-components": "0.2.16",
|
|
35
35
|
"@live-change/vue3-ssr": "0.2.16",
|
|
36
36
|
"@tiptap/extension-blockquote": "^2.0.0-beta.29",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"vue3-scroll-border": "0.1.2"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
|
-
"@live-change/codeceptjs-helper": "0.7.
|
|
76
|
+
"@live-change/codeceptjs-helper": "0.7.5",
|
|
77
77
|
"@wdio/selenium-standalone-service": "^7.20.8",
|
|
78
78
|
"codeceptjs": "^3.3.4",
|
|
79
79
|
"generate-password": "1.7.0",
|
|
@@ -85,5 +85,5 @@
|
|
|
85
85
|
"author": "",
|
|
86
86
|
"license": "ISC",
|
|
87
87
|
"description": "",
|
|
88
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "7d4c56cc610d692df963c3916411ceb46663b344"
|
|
89
89
|
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
"marks": {
|
|
3
|
+
"bold": {},
|
|
4
|
+
"italic": {},
|
|
5
|
+
"underline": {},
|
|
6
|
+
"strike": {}
|
|
7
|
+
},
|
|
8
|
+
"nodes": {
|
|
9
|
+
"paragraph": {
|
|
10
|
+
"content": "inline*",
|
|
11
|
+
"group": "block"
|
|
12
|
+
},
|
|
13
|
+
"horizontalRule": {
|
|
14
|
+
"group": "block"
|
|
15
|
+
},
|
|
16
|
+
"heading": {
|
|
17
|
+
"content": "inline*",
|
|
18
|
+
"group": "block",
|
|
19
|
+
"defining": true,
|
|
20
|
+
"attrs": {
|
|
21
|
+
"level": {
|
|
22
|
+
"default": 1
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"blockquote": {
|
|
27
|
+
"content": "block+",
|
|
28
|
+
"group": "block",
|
|
29
|
+
"defining": true
|
|
30
|
+
},
|
|
31
|
+
"codeBlock": {
|
|
32
|
+
"content": "text*",
|
|
33
|
+
"marks": "",
|
|
34
|
+
"group": "block",
|
|
35
|
+
"code": true,
|
|
36
|
+
"defining": true,
|
|
37
|
+
"attrs": {
|
|
38
|
+
"language": {
|
|
39
|
+
"default": null
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"bulletList": {
|
|
44
|
+
"content": "listItem+",
|
|
45
|
+
"group": "block list"
|
|
46
|
+
},
|
|
47
|
+
"orderedList": {
|
|
48
|
+
"content": "listItem+",
|
|
49
|
+
"group": "block list",
|
|
50
|
+
"attrs": {
|
|
51
|
+
"start": {
|
|
52
|
+
"default": 1
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"listItem": {
|
|
57
|
+
"content": "paragraph block*",
|
|
58
|
+
"defining": true
|
|
59
|
+
},
|
|
60
|
+
"image": {
|
|
61
|
+
"content": "",
|
|
62
|
+
"marks": "",
|
|
63
|
+
"group": "block",
|
|
64
|
+
"inline": false,
|
|
65
|
+
"atom": true,
|
|
66
|
+
"selectable": true,
|
|
67
|
+
"draggable": true,
|
|
68
|
+
"attrs": {
|
|
69
|
+
"image": {
|
|
70
|
+
"default": null
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"doc": {
|
|
75
|
+
"content": "block+"
|
|
76
|
+
},
|
|
77
|
+
"text": {
|
|
78
|
+
"group": "inline"
|
|
79
|
+
},
|
|
80
|
+
"hardBreak": {
|
|
81
|
+
"group": "inline",
|
|
82
|
+
"inline": true,
|
|
83
|
+
"selectable": false
|
|
84
|
+
},
|
|
85
|
+
"component": {
|
|
86
|
+
"content": "block*",
|
|
87
|
+
"marks": "",
|
|
88
|
+
"group": "block",
|
|
89
|
+
"inline": false,
|
|
90
|
+
"selectable": true,
|
|
91
|
+
"draggable": true,
|
|
92
|
+
"attrs": {
|
|
93
|
+
"is": {
|
|
94
|
+
"default": "card"
|
|
95
|
+
},
|
|
96
|
+
"attrs": {
|
|
97
|
+
"default": {}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"topNode": "doc"
|
|
103
|
+
}
|
|
@@ -17,7 +17,8 @@ module.exports = {
|
|
|
17
17
|
name: 'prosemirror',
|
|
18
18
|
path: '@live-change/prosemirror-service',
|
|
19
19
|
documentTypes: {
|
|
20
|
-
rich: require('./rich.documentType.js')
|
|
20
|
+
rich: require('./rich.documentType.js'),
|
|
21
|
+
page: require('./page.documentType.js')
|
|
21
22
|
},
|
|
22
23
|
testLatency: 1000
|
|
23
24
|
}
|