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.

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