Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/text.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulius Härtl <jus@bitgrid.net>2019-06-07 15:02:18 +0300
committerJulius Härtl <jus@bitgrid.net>2019-06-11 11:40:52 +0300
commit97749c7405d58129f2cd2e93dea5bc95037b1be8 (patch)
treed8d1c7d61b288e60bfa2e1e94f510eb33761568d
parent6087995cb8ed34c72c2b59dc2e4fdf7626025029 (diff)
Frontend code cleanup
Signed-off-by: Julius Härtl <jus@bitgrid.net>
-rw-r--r--css/style.scss362
-rw-r--r--src/EditorFactory.js67
-rw-r--r--src/components/EditorWrapper.vue (renamed from src/components/Editor.vue)117
-rw-r--r--src/components/ReadOnlyEditor.vue40
-rw-r--r--src/files.js2
-rw-r--r--src/public.js2
-rw-r--r--src/services/SyncService.js6
7 files changed, 120 insertions, 476 deletions
diff --git a/css/style.scss b/css/style.scss
index fdfb0c17b..cf2d2aa81 100644
--- a/css/style.scss
+++ b/css/style.scss
@@ -1,38 +1,15 @@
-.ProseMirror.ProseMirror-example-setup-style {
- overflow: scroll;
-}
-
-.ProseMirror.ProseMirror-example-setup-style,
-.ProseMirror-menubar-wrapper {
- width: 100%;
- max-width: 900px;
- margin: auto;
- display: flex;
+#files-public-content {
+ width: 100% !important;
height: 100%;
- flex-direction: column;
-
- * {
- box-sizing: border-box;
- }
}
-.ProseMirror {
- word-wrap: break-word;
- white-space: pre-wrap;
- -webkit-font-variant-ligatures: none;
- font-variant-ligatures: none;
+#viewer-content.modal-mask .modal-wrapper .modal-container {
+ border-radius: 3px !important;
}
-.ProseMirror pre {
- white-space: pre-wrap;
- background-color: var(--color-background-dark);
- border-radius: 5px;
- padding: 5px;
- padding-left: 11px;
-}
-
-.ProseMirror li {
- position: relative;
+.modal-container #editor-container {
+ position: absolute;
+ max-height: calc(100% - 100px);
}
.ProseMirror-hideselection *::selection { background: transparent; }
@@ -44,7 +21,6 @@
}
/* Make sure li selections wrap around markers */
-
li.ProseMirror-selectednode {
outline: none;
}
@@ -57,139 +33,6 @@ li.ProseMirror-selectednode:after {
border: 2px solid #8cf;
pointer-events: none;
}
-.ProseMirror-textblock-dropdown {
- min-width: 3em;
-}
-
-.ProseMirror-menu {
- margin: 0 -4px;
- line-height: 1;
-}
-
-.ProseMirror-tooltip .ProseMirror-menu {
- width: fit-content;
- white-space: pre;
-}
-
-.ProseMirror-menuitem {
- margin-right: 3px;
- padding: 4px;
- display: inline-block;
- vertical-align: top;
-}
-
-.ProseMirror-menuseparator {
- border: 0;
- margin-right: 3px;
-}
-
-.ProseMirror-menu-dropdown, .ProseMirror-menu-dropdown-menu {
- white-space: nowrap;
-}
-
-.ProseMirror-menu-dropdown {
- vertical-align: 1px;
- cursor: pointer;
- position: relative;
- padding: 8px;
- padding-right: 30px;
-}
-
-.ProseMirror-menu-dropdown-wrap {
- padding: 1px 0 1px 4px;
- display: inline-block;
- position: relative;
-}
-
-.ProseMirror-menu-dropdown:after {
- content: "";
- border-left: 4px solid transparent;
- border-right: 4px solid transparent;
- border-top: 4px solid currentColor;
- opacity: .6;
- position: absolute;
- right: 9px;
- top: calc(50% - 2px);
-}
-
-.ProseMirror-menu-dropdown-menu, .ProseMirror-menu-submenu {
- position: absolute;
- background: var(--color-main-background);
- color: var(--color-main-text);
- box-shadow: 0 0 3px var(--color-box-shadow);
- padding: 2px;
-}
-
-.ProseMirror-menu-dropdown-menu {
- z-index: 15;
- min-width: 6em;
-}
-
-.ProseMirror-menu-dropdown-item {
- cursor: pointer;
- padding: 2px 8px 2px 4px;
-}
-
-.ProseMirror-menu-dropdown-item:hover {
- background: var(--color-background-dark);
-}
-
-.ProseMirror-menu-submenu-wrap {
- position: relative;
- margin-right: -4px;
-}
-
-.ProseMirror-menu-submenu-label {
- padding-right: 20px;
-}
-
-.ProseMirror-menu-submenu-label:after {
- content: "";
- border-top: 4px solid transparent;
- border-bottom: 4px solid transparent;
- border-left: 4px solid currentColor;
- opacity: .6;
- position: absolute;
- right: 4px;
- top: calc(50% - 4px);
-}
-
-.ProseMirror-menu-submenu {
- display: none;
- min-width: 4em;
- left: 100%;
- top: -3px;
-}
-
-.ProseMirror-menu-active {
- background: var(--color-background-darker);
- border-radius: 4px;
-}
-
-.ProseMirror-menu-disabled {
- opacity: .3;
-}
-
-.ProseMirror-menu-submenu-wrap:hover .ProseMirror-menu-submenu, .ProseMirror-menu-submenu-wrap-active .ProseMirror-menu-submenu {
- display: block;
-}
-
-
-
-.ProseMirror-menubar {
- border-top-left-radius: inherit;
- border-top-right-radius: inherit;
- position: relative;
- min-height: 1em;
- color: var(--color-text-light);
- padding: 1px 6px;
- top: 0; left: 0; right: 0;
- background: var(--color-main-background-translucent);
- z-index: 10;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- overflow: visible;
-}
.has-conflicts,
#editor-wrapper.icon-loading {
@@ -198,49 +41,6 @@ li.ProseMirror-selectednode:after {
}
}
-.ProseMirror {
- margin-top: 44px;
- height: 100%;
- position: relative;
-}
-
-.ProseMirror-icon, .ProseMirror-menu-dropdown-wrap {
- display: inline-block;
- line-height: .8;
- border-radius: 2px;
- cursor: pointer;
- &:hover, &:focus {
- background-color: var(--color-background-dark);
- }
-}
-
-.ProseMirror-icon, .Prosemirror-menu-dropdown {
- padding: 8px;
- width: 34px;
- text-align: center;
-}
-
-.ProseMirror-menu-dropdown-menu {
- padding: 8px;
- border-radius: 2px;
- cursor: pointer;
- .ProseMirror-menu-dropdown-item {
- padding: 8px;
- }
-}
-
-.ProseMirror-menu-disabled.ProseMirror-icon {
- cursor: default;
-}
-
-.ProseMirror-icon svg {
- fill: currentColor;
- height: 1em;
-}
-
-.ProseMirror-icon span {
- vertical-align: text-top;
-}
.ProseMirror-gapcursor {
display: none;
pointer-events: none;
@@ -263,93 +63,6 @@ li.ProseMirror-selectednode:after {
}
}
-.ProseMirror-focused .ProseMirror-gapcursor {
- display: block;
-}
-/* Add space around the hr to make clicking it easier */
-
-.ProseMirror-example-setup-style hr {
- padding: 2px 10px;
- border: none;
- margin: 1em 0;
- width: 100%;
-}
-
-.ProseMirror-example-setup-style hr:after {
- content: "";
- display: block;
- height: 1px;
- background-color: silver;
- line-height: 2px;
-}
-
-.ProseMirror ul, .ProseMirror ol {
- padding-left: 30px;
-}
-.ProseMirror ul li {
- list-style-type: disc;
-}
-.ProseMirror blockquote {
- padding-left: 1em;
- border-left: 3px solid var(--color-text-lighter);
- margin-left: 0; margin-right: 0;
-}
-
-.ProseMirror-example-setup-style img,
-.ProseMirror img {
- cursor: default;
- max-height: 50vh;
- max-width: 100%;
-}
-
-.ProseMirror-prompt {
- background: var(--color-main-background);
- padding: 20px;
- position: fixed;
- border-radius: 3px;
- z-index: 20000;
- box-shadow: -.5px 2px 5px var(--color-box-shadow);
-}
-
-.ProseMirror-prompt h5 {
- margin: 0;
- font-weight: normal;
- font-size: 100%;
- color: var(--color-main-text);
-}
-
-.ProseMirror-prompt-close {
- position: absolute;
- left: 2px; top: 1px;
- color: var(--color-main-text);
- border: none; background: transparent; padding: 0;
-}
-
-.ProseMirror-prompt-close:after {
- content: "✕";
- font-size: 12px;
-}
-
-.ProseMirror-invalid {
- background: #ffc;
- border: 1px solid #cc7;
- border-radius: 4px;
- padding: 5px 10px;
- position: absolute;
- min-width: 10em;
-}
-
-.ProseMirror-prompt-buttons {
- margin-top: 5px;
- float: right;
-}
-
-.ProseMirror-prompt-submit {
- color: var(--color-primary-text);
- background-color: var(--color-primary);
- border: 0;
-}
-
#editor, .editor {
background: var(--color-main-background);
color: var(--color-main-text);
@@ -359,6 +72,7 @@ li.ProseMirror-selectednode:after {
position: relative;
overflow-y: scroll;
overflow-x: hidden;
+ width: 100%;
}
div[contenteditable=true],
@@ -370,66 +84,6 @@ div[contenteditable=false] {
opacity: 1;
}
-.ProseMirror p:first-child,
-.ProseMirror h1:first-child,
-.ProseMirror h2:first-child,
-.ProseMirror h3:first-child,
-.ProseMirror h4:first-child,
-.ProseMirror h5:first-child,
-.ProseMirror h6:first-child {
- margin-top: 10px;
-}
-
-.ProseMirror {
- padding: 4px 8px 4px 14px;
- line-height: 150%;
- font-size: 14px;
- outline: none;
-}
-
-.ProseMirror a {
- color: var(--color-primary);
- text-decoration: underline;
-}
-
-.ProseMirror p {
- margin-bottom: 1em;
- line-height: 150%;
-}
-.ProseMirror em {
- font-style: italic;
-}
-
-
-.ProseMirror h1 {
- font-size: 24px;
-}
-.ProseMirror h2 {
- font-size: 22px;
-}
-.ProseMirror h3 {
- font-size: 20px;
-}
-.ProseMirror h4 {
- font-size: 18px;
-}
-.ProseMirror h5 {
- font-size: 16px;
-}
-.ProseMirror h6 {
- font-size: 14px;
-}
-.ProseMirror h1,
-.ProseMirror h2,
-.ProseMirror h3,
-.ProseMirror h4,
-.ProseMirror h5,
-.ProseMirror h6 {
- font-weight: 600;
- margin-top: 10px;
- margin-bottom: 20px;
-}
-
.icon-bold {
background-image: url('./../../img/icons/bold.svg');
diff --git a/src/EditorFactory.js b/src/EditorFactory.js
new file mode 100644
index 000000000..2e2e435fc
--- /dev/null
+++ b/src/EditorFactory.js
@@ -0,0 +1,67 @@
+/*
+ * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+import {Editor} from 'tiptap';
+import {
+ HardBreak,
+ Heading,
+ Bold,
+ Code,
+ Link,
+ BulletList,
+ OrderedList,
+ ListItem,
+ Blockquote,
+ CodeBlock,
+ Image,
+ History
+} from 'tiptap-extensions'
+import { Strong, Italic } from './marks'
+import MarkdownIt from 'markdown-it'
+
+const createEditor = ({content, onUpdate, extensions}) => {
+ extensions = extensions ? extensions : []
+ return new Editor({
+ content: content,
+ onUpdate: onUpdate,
+ extensions: [
+ new HardBreak,
+ new Heading,
+ new Code,
+ new Strong,
+ new Italic,
+ new BulletList,
+ new OrderedList,
+ new Blockquote,
+ new CodeBlock,
+ new ListItem,
+ new Link,
+ new Image,
+ new History()
+ ].concat(extensions),
+ })
+}
+
+const markdownit = MarkdownIt('commonmark', {html: false});
+
+
+export default createEditor
+export { markdownit, createEditor}
diff --git a/src/components/Editor.vue b/src/components/EditorWrapper.vue
index c81c96c6b..a9592b3f3 100644
--- a/src/components/Editor.vue
+++ b/src/components/EditorWrapper.vue
@@ -65,7 +65,7 @@
<div class="menububble" :class="{ 'is-active': menu.isActive }" :style="`left: ${menu.left}px; bottom: ${menu.bottom}px;`">
<form class="menububble__form" v-if="linkMenuIsActive" @submit.prevent="setLinkUrl(commands.link, linkUrl)">
- <input class="menububble__input" type="text" v-model="linkUrl" placeholder="https://" ref="linkInput" @keydown.esc="hideLinkMenu"/>
+ <input class="menububble__input" type="text" v-model="linkUrl" placeholder="https://" ref="linkInput" @keydown.esc="hideLinkMenu" />
<button class="menububble__button" @click="setLinkUrl(commands.link, null)" type="button"></button>
</form>
@@ -92,54 +92,32 @@
</button>
</div>
- <div v-if="isPublic && !guestNameConfirmed" class="guest-name-dialog">
- <p>{{ t('text', 'Please enter a name to identify you as a public editor:') }}</p>
- <form @submit.prevent="setGuestName()">
- <input type="text" v-model="guestName" />
- <input type="submit" class="icon-confirm" value="" />
- </form>
- </div>
+ <guest-name-dialog v-if="isPublic && !guestNameConfirmed" :value="guestName" @input="setGuestName($event)"></guest-name-dialog>
</div>
</template>
<script>
import Vue from 'vue'
-import debounce from 'lodash/debounce'
-import { EditorSync, ERROR_TYPE } from './../EditorSync'
-import SyncService from './../services/SyncService'
+import { SyncService, ERROR_TYPE } from './../services/SyncService'
import { endpointUrl } from './../helpers'
+import { createEditor, markdownit } from './../EditorFactory'
-import { getVersion } from 'prosemirror-collab'
import { defaultMarkdownParser, defaultMarkdownSerializer } from 'prosemirror-markdown'
import { Editor, EditorContent, EditorMenuBar, EditorMenuBubble } from 'tiptap'
-import {
- HardBreak,
- Heading,
- Bold,
- Code,
- Link,
- BulletList,
- OrderedList,
- ListItem,
- Blockquote,
- CodeBlock,
- Image,
- History,
- Collaboration,
-} from 'tiptap-extensions'
-import { Strong, Italic } from './../marks'
+import { Collaboration } from 'tiptap-extensions'
import { Keymap } from './../extensions'
-import MarkdownIt from 'markdown-it'
-
+import { getVersion } from 'prosemirror-collab'
import Avatar from 'nextcloud-vue/dist/Components/Avatar'
-import ReadOnlyEditor from './ReadOnlyEditor'
import Tooltip from 'nextcloud-vue/dist/Directives/Tooltip'
import Actions from 'nextcloud-vue/dist/Components/Actions'
import ActionButton from 'nextcloud-vue/dist/Components/ActionButton'
+import ReadOnlyEditor from './ReadOnlyEditor'
+import GuestNameDialog from './GuestNameDialog'
+
const COLLABORATOR_IDLE_TIME = 5
const COLLABORATOR_DISCONNECT_TIME = 20
const EDITOR_PUSH_DEBOUNCE = 200
@@ -153,7 +131,8 @@ export default {
EditorContent,
EditorMenuBar,
EditorMenuBubble,
- ActionButton
+ ActionButton,
+ GuestNameDialog
},
directives: {
Tooltip
@@ -178,15 +157,16 @@ export default {
},
data() {
return {
- editor: null,
tiptap: null,
/** @type SyncService */
syncService: null,
+
document: null,
- currentSession: null,
sessions: [],
+ currentSession: null,
+
filteredSessions: {},
- name: null,
+
dirty: false,
initialLoading: false,
lastSavedString: '',
@@ -253,12 +233,13 @@ export default {
return document.getElementById('isPublic') && document.getElementById('isPublic').value === '1'
}
},
- mounted() {
+ beforeMount() {
const guestName = localStorage.getItem('text-guestName')
if (guestName !== null) {
this.guestName = guestName
}
-
+ },
+ mounted() {
if (this.active && (this.hasDocumentParameters) && !this.isPublic) {
this.initSession()
}
@@ -272,7 +253,8 @@ export default {
}
},
methods: {
- setGuestName() {
+ setGuestName(guestName) {
+ this.guestName = guestName
this.guestNameConfirmed = true
localStorage.setItem('text-guestName', this.guestName)
this.initSession()
@@ -307,34 +289,20 @@ export default {
this.updateSessions.bind(this)(sessions);
this.document = document
this.syncError = null
- this.tiptap.setOptions({editable: true && !this.readOnly})
+ this.tiptap.setOptions({editable: !this.readOnly})
})
.on('loaded', ({document, session, documentSource}) => {
const documentData = {document, session}
const initialDocument = defaultMarkdownParser.parse(documentSource)
- this.markdownit = MarkdownIt('commonmark', {html: false});
- this.tiptap = new Editor({
- content: this.markdownit.render(documentSource),
+ this.tiptap = createEditor({
+ content: markdownit.render(documentSource),
onUpdate: ({state}) => {
console.log("=> FROM doc")
console.log(defaultMarkdownSerializer.serialize(state.doc))
this.syncService.state = state
},
extensions: [
- new HardBreak,
- new Heading,
- new Code,
- new Strong,
- new Italic,
- new BulletList,
- new OrderedList,
- new Blockquote,
- new CodeBlock,
- new ListItem,
- new Link,
- new Image,
- new History(),
new Collaboration({
// the initial version we start with
// version is an integer which is incremented with every change
@@ -523,14 +491,10 @@ export default {
#editor-container #editor-wrapper.has-conflicts {
height: calc(100% - 50px);
- #remote, #editor {
+ #editor, #read-only-editor {
width: 50%;
height: 100%;
}
-
- .ProseMirror-menubar {
- visibility: hidden;
- }
}
#editor-session-list {
@@ -637,39 +601,8 @@ export default {
margin: auto;
}
- .guest-name-dialog {
- padding: 30px;
- text-align: center;
-
- form {
- display: flex;
- width: 100%;
- max-width: 200px;
- margin: auto;
- margin-top: 30px;
-
- input[type=text] {
- flex-grow: 1;
- }
- }
- }
-
</style>
<style lang="scss">
@import './../../css/style';
-
- #files-public-content {
- width: 100% !important;
- height: 100%;
- }
-
- #viewer-content.modal-mask .modal-wrapper .modal-container {
- border-radius: 3px !important;
- }
-
- .modal-container #editor-container {
- position: absolute;
- max-height: calc(100% - 100px);
- }
-
+ @import './../../css/prosemirror';
</style>
diff --git a/src/components/ReadOnlyEditor.vue b/src/components/ReadOnlyEditor.vue
index 47883ace1..6272abdef 100644
--- a/src/components/ReadOnlyEditor.vue
+++ b/src/components/ReadOnlyEditor.vue
@@ -21,17 +21,16 @@
-->
<template>
- <div id="remote" ref="remote" />
+ <EditorContent id="read-only-editor" v-if="editor" :editor="editor"></EditorContent>
</template>
<script>
-import { EditorState } from 'prosemirror-state'
-import { EditorView } from 'prosemirror-view'
-import { exampleSetup } from 'prosemirror-example-setup'
-import { schema, defaultMarkdownParser } from 'prosemirror-markdown'
+import { EditorContent } from 'tiptap'
+import {createEditor, markdownit} from '../EditorFactory'
export default {
name: 'ReadOnlyEditor',
+ components: {EditorContent},
props: {
content: {
type: String,
@@ -40,34 +39,25 @@ export default {
},
data: () => {
return {
- remoteView: null
+ editor: null
}
},
mounted() {
- this.initRemoteView()
+ this.editor = createEditor({
+ content: markdownit.render(this.content)
+ })
+ this.editor.setOptions({editable: false})
},
beforeDestroy() {
- this.remoteView.destroy()
- },
- methods: {
- initRemoteView() {
- if (this.remoteView) {
- return
- }
- this.remoteView = new EditorView(this.$refs.remote, {
- state: EditorState.create({
- doc: defaultMarkdownParser.parse(this.content),
- plugins: [
- ...exampleSetup({ schema })
- ]
- })
- })
- this.remoteView.setProps({ editable: () => false })
- }
+ this.editor.destroy()
}
}
</script>
-<style scoped>
+<style scoped lang="scss">
+
+ #read-only-editor {
+ overflow: scroll;
+ }
</style>
diff --git a/src/files.js b/src/files.js
index b2a70a2d2..829dfefc6 100644
--- a/src/files.js
+++ b/src/files.js
@@ -20,7 +20,7 @@
*
*/
-import Editor from './components/Editor'
+import Editor from './components/EditorWrapper'
import { documentReady } from './helpers'
const newFileMenuPlugin = {
diff --git a/src/public.js b/src/public.js
index dda717a0a..313d84788 100644
--- a/src/public.js
+++ b/src/public.js
@@ -1,5 +1,5 @@
import Vue from 'vue'
-import Editor from './components/Editor'
+import Editor from './components/EditorWrapper'
import { documentReady } from './helpers'
__webpack_nonce__ = btoa(OC.requestToken) // eslint-disable-line
diff --git a/src/services/SyncService.js b/src/services/SyncService.js
index ab77b0ca9..0207555f2 100644
--- a/src/services/SyncService.js
+++ b/src/services/SyncService.js
@@ -23,8 +23,7 @@ import axios from 'nextcloud-axios'
import PollingBackend from './PollingBackend'
import { endpointUrl } from './../helpers'
-import {Step} from 'prosemirror-transform';
-import {getVersion, sendableSteps} from 'prosemirror-collab'
+import { getVersion, sendableSteps } from 'prosemirror-collab'
const defaultOptions = {
shareToken: null,
@@ -222,4 +221,5 @@ class SyncService {
}
}
-export default SyncService;
+export default SyncService
+export { SyncService, ERROR_TYPE }