index.vue 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. <template>
  2. <div class="editor editor-squished">
  3. <basic-menu :editor="editor">
  4. <template #saveButton>
  5. <button
  6. @click="emitUpdate"
  7. class="button is-success button-save">
  8. Save
  9. </button>
  10. </template>
  11. </basic-menu>
  12. <bubble-menu :editor="editor" />
  13. <editor-content
  14. class="editor__content"
  15. :editor="editor"
  16. />
  17. </div>
  18. </template>
  19. <script>
  20. import { Editor, EditorContent } from "tiptap"
  21. import BubbleMenu from '~/components/editor/BubbleMenu'
  22. import BasicMenu from '~/components/editor/BasicMenu'
  23. import {
  24. Heading,
  25. Bold,
  26. Code,
  27. Italic,
  28. Strike,
  29. Underline,
  30. History,
  31. Blockquote,
  32. HorizontalRule,
  33. OrderedList,
  34. BulletList,
  35. ListItem,
  36. CodeBlockHighlight,
  37. Placeholder
  38. } from 'tiptap-extensions'
  39. import Title from '~/components/editor/components/Title'
  40. import Subtitle from '~/components/editor/components/Subtitle'
  41. import Doc from '~/components/editor/components/Doc'
  42. import javascript from 'highlight.js/lib/languages/javascript'
  43. import css from 'highlight.js/lib/languages/css'
  44. export default {
  45. components: {
  46. EditorContent,
  47. BubbleMenu,
  48. BasicMenu
  49. },
  50. data(){
  51. return {
  52. editor: null
  53. }
  54. },
  55. // This is called only on client (in browser)
  56. mounted(){
  57. this.editor = new Editor({
  58. extensions: [
  59. new Doc(),
  60. new Title(),
  61. new Subtitle(),
  62. new Placeholder({
  63. showOnlyCurrent: false,
  64. emptyNodeText: node => {
  65. if (node.type.name === 'title') {
  66. return 'Inspirational Title'
  67. }
  68. if (node.type.name === 'subtitle') {
  69. return 'Some catchy subtitle'
  70. }
  71. return 'Write your story...'
  72. }
  73. }),
  74. new Heading({ levels: [1, 2, 3]}),
  75. new Bold(),
  76. new Code(),
  77. new Italic(),
  78. new Strike(),
  79. new Underline(),
  80. new History(),
  81. new Blockquote(),
  82. new HorizontalRule(),
  83. new OrderedList(),
  84. new BulletList(),
  85. new ListItem(),
  86. new CodeBlockHighlight({
  87. languages: {
  88. javascript,
  89. css,
  90. }
  91. })
  92. ]
  93. })
  94. },
  95. beforeDestroy(){
  96. // Always destroy your editor instance when it's no longer needed
  97. this.editor && this.editor.destroy()
  98. },
  99. methods: {
  100. emitUpdate() {
  101. const html = this.editor.getHTML()
  102. const title = this.getNodeValueByName('title')
  103. const subtitle = this.getNodeValueByName('subtitle')
  104. this.$emit('editorUpdated', {content: html, title, subtitle})
  105. },
  106. getNodeValueByName(name) {
  107. const docContent = this.editor.state.doc.content
  108. const nodes = docContent.content
  109. const node = nodes.find(n => n.type.name === name)
  110. if (!node) return ''
  111. return node.textContent
  112. }
  113. }
  114. }
  115. </script>
  116. <style lang="scss" scoped>
  117. .button-save {
  118. float: right;
  119. background-color: #23d160;
  120. &:hover {
  121. background-color: #2bc76c;
  122. }
  123. &:disabled {
  124. cursor: not-allowed;
  125. }
  126. }
  127. </style>