editor-ckeditor.vue 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  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. </template>
  15. <script>
  16. import _ from 'lodash'
  17. import { get, sync } from 'vuex-pathify'
  18. import DecoupledEditor from '@requarks/ckeditor5'
  19. import EditorConflict from './ckeditor/conflict.vue'
  20. export default {
  21. components: {
  22. EditorConflict
  23. },
  24. props: {
  25. save: {
  26. type: Function,
  27. default: () => {}
  28. }
  29. },
  30. data() {
  31. return {
  32. editor: null,
  33. stats: {
  34. characters: 0,
  35. words: 0
  36. },
  37. content: '',
  38. isConflict: false
  39. }
  40. },
  41. computed: {
  42. isMobile() {
  43. return this.$vuetify.breakpoint.smAndDown
  44. },
  45. locale: get('page/locale'),
  46. path: get('page/path'),
  47. activeModal: sync('editor/activeModal')
  48. },
  49. methods: {
  50. },
  51. async mounted () {
  52. this.$store.set('editor/editorKey', 'ckeditor')
  53. this.editor = await DecoupledEditor.create(this.$refs.editor, {
  54. placeholder: 'Type the page content here',
  55. wordCount: {
  56. onUpdate: stats => {
  57. this.stats = {
  58. characters: stats.characters,
  59. words: stats.words
  60. }
  61. }
  62. }
  63. })
  64. this.$refs.toolbarContainer.appendChild(this.editor.ui.view.toolbar.element)
  65. if (this.mode !== 'create') {
  66. this.editor.setData(this.$store.get('editor/content'))
  67. }
  68. this.editor.model.document.on('change:data', _.debounce(evt => {
  69. this.$store.set('editor/content', this.editor.getData())
  70. }, 300))
  71. this.$root.$on('editorInsert', opts => {
  72. switch (opts.kind) {
  73. case 'IMAGE':
  74. this.editor.execute('imageInsert', {
  75. source: opts.path
  76. })
  77. break
  78. case 'BINARY':
  79. this.editor.execute('link', opts.path, {
  80. linkIsDownloadable: true
  81. })
  82. break
  83. }
  84. })
  85. // Handle save conflict
  86. this.$root.$on('saveConflict', () => {
  87. this.isConflict = true
  88. })
  89. this.$root.$on('overwriteEditorContent', () => {
  90. this.editor.setData(this.$store.get('editor/content'))
  91. })
  92. },
  93. beforeDestroy () {
  94. if (this.editor) {
  95. this.editor.destroy()
  96. this.editor = null
  97. }
  98. }
  99. }
  100. </script>
  101. <style lang="scss">
  102. $editor-height: calc(100vh - 64px - 24px);
  103. $editor-height-mobile: calc(100vh - 56px - 16px);
  104. .editor-ckeditor {
  105. background-color: mc('grey', '200');
  106. flex: 1 1 50%;
  107. display: flex;
  108. flex-flow: column nowrap;
  109. height: $editor-height;
  110. max-height: $editor-height;
  111. position: relative;
  112. @at-root .theme--dark & {
  113. background-color: mc('grey', '900');
  114. }
  115. @include until($tablet) {
  116. height: $editor-height-mobile;
  117. max-height: $editor-height-mobile;
  118. }
  119. &-sysbar {
  120. padding-left: 0;
  121. &-locale {
  122. background-color: rgba(255,255,255,.25);
  123. display:inline-flex;
  124. padding: 0 12px;
  125. height: 24px;
  126. width: 63px;
  127. justify-content: center;
  128. align-items: center;
  129. }
  130. }
  131. .ck.ck-toolbar {
  132. border: none;
  133. justify-content: center;
  134. background-color: mc('grey', '300');
  135. color: #FFF;
  136. }
  137. > .ck-editor__editable {
  138. background-color: mc('grey', '100');
  139. overflow-y: auto;
  140. overflow-x: hidden;
  141. padding: 2rem;
  142. box-shadow: 0 0 5px hsla(0, 0, 0, .1);
  143. margin: 1rem auto 0;
  144. width: calc(100vw - 256px - 16vw);
  145. min-height: calc(100vh - 64px - 24px - 1rem - 40px);
  146. border-radius: 5px;
  147. @at-root .theme--dark & {
  148. background-color: #303030;
  149. color: #FFF;
  150. }
  151. @include until($widescreen) {
  152. width: calc(100vw - 2rem);
  153. margin: 1rem 1rem 0 1rem;
  154. min-height: calc(100vh - 64px - 24px - 1rem - 40px);
  155. }
  156. @include until($tablet) {
  157. width: 100%;
  158. margin: 0;
  159. min-height: calc(100vh - 56px - 24px - 76px);
  160. }
  161. &.ck.ck-editor__editable:not(.ck-editor__nested-editable).ck-focused {
  162. border-color: #FFF;
  163. box-shadow: 0 0 10px rgba(mc('blue', '700'), .25);
  164. @at-root .theme--dark & {
  165. border-color: #444;
  166. border-bottom: none;
  167. box-shadow: 0 0 10px rgba(#000, .25);
  168. }
  169. }
  170. &.ck .ck-editor__nested-editable.ck-editor__nested-editable_focused,
  171. &.ck .ck-editor__nested-editable:focus,
  172. .ck-widget.table td.ck-editor__nested-editable.ck-editor__nested-editable_focused,
  173. .ck-widget.table th.ck-editor__nested-editable.ck-editor__nested-editable_focused {
  174. background-color: mc('grey', '100');
  175. @at-root .theme--dark & {
  176. background-color: mc('grey', '900');
  177. }
  178. }
  179. }
  180. }
  181. </style>