You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

253 lines
6.2 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. <template lang='pug'>
  2. .editor-ckeditor
  3. div(ref='toolbarContainer')
  4. div.contents(ref='editor')
  5. v-system-bar.editor-ckeditor-sysbar(dark, status, color='grey darken-3')
  6. .caption.editor-ckeditor-sysbar-locale {{locale.toUpperCase()}}
  7. .caption.px-3 /{{path}}
  8. template(v-if='$vuetify.breakpoint.mdAndUp')
  9. v-spacer
  10. .caption Visual Editor
  11. v-spacer
  12. .caption {{$t('editor:ckeditor.stats', { chars: stats.characters, words: stats.words })}}
  13. editor-conflict(v-model='isConflict', v-if='isConflict')
  14. page-selector(mode='select', v-model='insertLinkDialog', :open-handler='insertLinkHandler', :path='path', :locale='locale')
  15. </template>
  16. <script>
  17. import _ from 'lodash'
  18. import { get, sync } from 'vuex-pathify'
  19. import DecoupledEditor from '@requarks/ckeditor5'
  20. // import DecoupledEditor from '../../../../wiki-ckeditor5/build/ckeditor'
  21. import EditorConflict from './ckeditor/conflict.vue'
  22. import { html as beautify } from 'js-beautify/js/lib/beautifier.min.js'
  23. /* global siteLangs */
  24. export default {
  25. components: {
  26. EditorConflict
  27. },
  28. props: {
  29. save: {
  30. type: Function,
  31. default: () => {}
  32. }
  33. },
  34. data() {
  35. return {
  36. editor: null,
  37. stats: {
  38. characters: 0,
  39. words: 0
  40. },
  41. content: '',
  42. isConflict: false,
  43. insertLinkDialog: false
  44. }
  45. },
  46. computed: {
  47. isMobile() {
  48. return this.$vuetify.breakpoint.smAndDown
  49. },
  50. locale: get('page/locale'),
  51. path: get('page/path'),
  52. activeModal: sync('editor/activeModal')
  53. },
  54. methods: {
  55. insertLink () {
  56. this.insertLinkDialog = true
  57. },
  58. insertLinkHandler ({ locale, path }) {
  59. this.editor.execute('link', siteLangs.length > 0 ? `/${locale}/${path}` : `/${path}`)
  60. }
  61. },
  62. async mounted () {
  63. this.$store.set('editor/editorKey', 'ckeditor')
  64. this.editor = await DecoupledEditor.create(this.$refs.editor, {
  65. language: this.locale,
  66. placeholder: 'Type the page content here',
  67. disableNativeSpellChecker: false,
  68. // TODO: Mention autocomplete
  69. //
  70. // mention: {
  71. // feeds: [
  72. // {
  73. // marker: '@',
  74. // feed: [ '@Barney', '@Lily', '@Marshall', '@Robin', '@Ted' ],
  75. // minimumCharacters: 1
  76. // }
  77. // ]
  78. // },
  79. wordCount: {
  80. onUpdate: stats => {
  81. this.stats = {
  82. characters: stats.characters,
  83. words: stats.words
  84. }
  85. }
  86. }
  87. })
  88. this.$refs.toolbarContainer.appendChild(this.editor.ui.view.toolbar.element)
  89. if (this.mode !== 'create') {
  90. this.editor.setData(this.$store.get('editor/content'))
  91. }
  92. this.editor.model.document.on('change:data', _.debounce(evt => {
  93. this.$store.set('editor/content', beautify(this.editor.getData(), { indent_size: 2, end_with_newline: true }))
  94. }, 300))
  95. this.$root.$on('editorInsert', opts => {
  96. switch (opts.kind) {
  97. case 'IMAGE':
  98. this.editor.execute('imageInsert', {
  99. source: opts.path
  100. })
  101. break
  102. case 'BINARY':
  103. this.editor.execute('link', opts.path, {
  104. linkIsDownloadable: true
  105. })
  106. break
  107. case 'DIAGRAM':
  108. this.editor.execute('imageInsert', {
  109. source: `data:image/svg+xml;base64,${opts.text}`
  110. })
  111. break
  112. }
  113. })
  114. this.$root.$on('editorLinkToPage', opts => {
  115. this.insertLink()
  116. })
  117. // Handle save conflict
  118. this.$root.$on('saveConflict', () => {
  119. this.isConflict = true
  120. })
  121. this.$root.$on('overwriteEditorContent', () => {
  122. this.editor.setData(this.$store.get('editor/content'))
  123. })
  124. },
  125. beforeDestroy () {
  126. if (this.editor) {
  127. this.editor.destroy()
  128. this.editor = null
  129. }
  130. }
  131. }
  132. </script>
  133. <style lang="scss">
  134. $editor-height: calc(100vh - 64px - 24px);
  135. $editor-height-mobile: calc(100vh - 56px - 16px);
  136. .editor-ckeditor {
  137. background-color: mc('grey', '200');
  138. flex: 1 1 50%;
  139. display: flex;
  140. flex-flow: column nowrap;
  141. height: $editor-height;
  142. max-height: $editor-height;
  143. position: relative;
  144. @at-root .theme--dark & {
  145. background-color: mc('grey', '900');
  146. }
  147. @include until($tablet) {
  148. height: $editor-height-mobile;
  149. max-height: $editor-height-mobile;
  150. }
  151. &-sysbar {
  152. padding-left: 0;
  153. &-locale {
  154. background-color: rgba(255,255,255,.25);
  155. display:inline-flex;
  156. padding: 0 12px;
  157. height: 24px;
  158. width: 63px;
  159. justify-content: center;
  160. align-items: center;
  161. }
  162. }
  163. .contents {
  164. table {
  165. margin: inherit;
  166. }
  167. pre > code {
  168. background-color: unset;
  169. color: unset;
  170. padding: .15em;
  171. }
  172. }
  173. .ck.ck-toolbar {
  174. border: none;
  175. justify-content: center;
  176. background-color: mc('grey', '300');
  177. color: #FFF;
  178. }
  179. .ck.ck-toolbar__items {
  180. justify-content: center;
  181. }
  182. > .ck-editor__editable {
  183. background-color: mc('grey', '100');
  184. overflow-y: auto;
  185. overflow-x: hidden;
  186. padding: 2rem;
  187. box-shadow: 0 0 5px hsla(0, 0, 0, .1);
  188. margin: 1rem auto 0;
  189. width: calc(100vw - 256px - 16vw);
  190. min-height: calc(100vh - 64px - 24px - 1rem - 40px);
  191. border-radius: 5px;
  192. @at-root .theme--dark & {
  193. background-color: #303030;
  194. color: #FFF;
  195. }
  196. @include until($widescreen) {
  197. width: calc(100vw - 2rem);
  198. margin: 1rem 1rem 0 1rem;
  199. min-height: calc(100vh - 64px - 24px - 1rem - 40px);
  200. }
  201. @include until($tablet) {
  202. width: 100%;
  203. margin: 0;
  204. min-height: calc(100vh - 56px - 24px - 76px);
  205. }
  206. &.ck.ck-editor__editable:not(.ck-editor__nested-editable).ck-focused {
  207. border-color: #FFF;
  208. box-shadow: 0 0 10px rgba(mc('blue', '700'), .25);
  209. @at-root .theme--dark & {
  210. border-color: #444;
  211. border-bottom: none;
  212. box-shadow: 0 0 10px rgba(#000, .25);
  213. }
  214. }
  215. &.ck .ck-editor__nested-editable.ck-editor__nested-editable_focused,
  216. &.ck .ck-editor__nested-editable:focus,
  217. .ck-widget.table td.ck-editor__nested-editable.ck-editor__nested-editable_focused,
  218. .ck-widget.table th.ck-editor__nested-editable.ck-editor__nested-editable_focused {
  219. background-color: mc('grey', '100');
  220. @at-root .theme--dark & {
  221. background-color: mc('grey', '900');
  222. }
  223. }
  224. }
  225. }
  226. </style>