InputEditor.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. <template>
  2. <div class='input_editor'>
  3. <tab-view :tabs='["Editor", "Preview"]' :name='name' small-tabs='true'>
  4. <template slot='Editor'>
  5. <div class='input_editor__format_bar'>
  6. <div class='input_editor__format_button' @click='replaceSelectedText("**", "**")'>B</div>
  7. <div class='input_editor__format_button' @click='replaceSelectedText("*", "*")'>I</div>
  8. <div class='input_editor__spacer'></div>
  9. <div class='input_editor__format_button' @click='showLinkModal("thread_editor--link")'><span class='fa fa-link'></span></div>
  10. <div class='input_editor__format_button' @click='formatCode'><span class='fa fa-code'></span></div>
  11. <div class='input_editor__format_button' @click='showModal("thread_editor--picture")'><span class='fa fa-picture-o'></span></div>
  12. </div>
  13. <textarea class='input_editor__input' ref='textarea' :value='editor' @input='setEditor($event.target.value)'></textarea>
  14. </template>
  15. <div slot='Preview' v-html='markdownHTML' class='input_editor__markdownHTML'></div>
  16. </tab-view>
  17. <modal-window name='thread_editor--link'>
  18. <div style='padding: 1rem;'>
  19. <p style='margin-top: 0;'>
  20. Enter the web address in the input box below
  21. </p>
  22. <fancy-input placeholder='Text for link' width='100%' v-model='linkText'></fancy-input>
  23. <fancy-input placeholder='Web address for link' width='100%' v-model='linkURL'></fancy-input>
  24. <button class='button' @click='addLink'>
  25. OK
  26. </button>
  27. <button class='button' @click='hideLinkModal'>
  28. Cancel
  29. </button>
  30. </div>
  31. </modal-window>
  32. <modal-window name='thread_editor--picture'></modal-window>
  33. </div>
  34. </template>
  35. <script>
  36. import ModalWindow from './ModalWindow'
  37. import FancyInput from './FancyInput'
  38. import TabView from './TabView'
  39. import Marked from 'marked'
  40. export default {
  41. name: 'InputEditor',
  42. props: ['name'],
  43. components: {
  44. ModalWindow,
  45. FancyInput,
  46. TabView
  47. },
  48. data () {
  49. return {
  50. linkText: '',
  51. linkURL: ''
  52. }
  53. },
  54. computed: {
  55. editor () {
  56. return this.$store.state.editors[this.name];
  57. },
  58. markdownHTML () {
  59. return Marked(this.editor);
  60. }
  61. },
  62. methods: {
  63. showImageModal () {
  64. this.$store.commit('showModal', 'thread_editor--image');
  65. },
  66. hideImageModal () {
  67. this.$store.commit('hideModal', 'thread_editor--image');
  68. },
  69. showLinkModal () {
  70. this.$store.commit('showModal', 'thread_editor--link');
  71. this.linkText = this.getSelectionData().val;
  72. },
  73. hideLinkModal () {
  74. this.$store.commit('hideModal', 'thread_editor--link');
  75. this.linkText = '';
  76. this.linkURL = '';
  77. },
  78. setEditor (value) {
  79. this.$store.commit({
  80. type: 'setEditor',
  81. name: this.name,
  82. value: value
  83. });
  84. },
  85. getSelectionData () {
  86. var el = this.$refs.textarea,
  87. start = el.selectionStart,
  88. end = el.selectionEnd;
  89. return {
  90. val: el.value.slice(start, end),
  91. start,
  92. end
  93. };
  94. },
  95. replaceSelectedText (before, after) {
  96. var selectionData = this.getSelectionData();
  97. var el = this.$refs.textarea;
  98. this.setEditor(
  99. this.editor.slice(0, selectionData.start) +
  100. before + selectionData.val + after +
  101. this.editor.slice(selectionData.end)
  102. );
  103. el.focus();
  104. setTimeout(function() {
  105. el.selectionStart = selectionData.start + before.length;
  106. el.selectionEnd = selectionData.end + before.length;
  107. }, 1);
  108. },
  109. addLink () {
  110. var linkTextLength = this.linkText.length;
  111. var selectionData = this.getSelectionData();
  112. var el = this.$refs.textarea;
  113. this.setEditor(
  114. this.editor.slice(0, selectionData.start) +
  115. '[' + this.linkText + '](' + this.linkURL + ')' +
  116. this.editor.slice(selectionData.end)
  117. );
  118. el.focus();
  119. setTimeout(function() {
  120. el.selectionStart = selectionData.start + 1;
  121. el.selectionEnd = selectionData.start + 1 + linkTextLength;
  122. }, 1);
  123. this.hideLinkModal();
  124. },
  125. formatCode () {
  126. var selectionData = this.getSelectionData();
  127. if(this.editor[selectionData.start-1] === '\n' || selectionData.start === 0) {
  128. this.replaceSelectedText(' ', '');
  129. } else {
  130. this.replaceSelectedText('`', '`');
  131. }
  132. }
  133. }
  134. }
  135. </script>
  136. <style lang='scss' scoped>
  137. @import '../assets/scss/variables.scss';
  138. .input_editor {
  139. width: 35rem;
  140. border: 0.125rem solid $color__gray--primary;
  141. position: relative;
  142. &:hover {
  143. border-color: $color__gray--darker;
  144. }
  145. &:focus {
  146. border-color: $color__gray--darkest;
  147. }
  148. @at-root #{&}__format_bar {
  149. width: auto;
  150. position: absolute;
  151. height: 2rem;
  152. top: 0.25rem;
  153. right: 0;
  154. background-color: transparent;
  155. display: flex;
  156. align-items: center;
  157. padding: 0 0.125rem;
  158. }
  159. @at-root #{&}__format_button {
  160. height: 1.5rem;
  161. width: 1.5rem;
  162. text-align: center;
  163. line-height: 1.4rem;
  164. cursor: pointer;
  165. @include user-select(none);
  166. @include text($font--role-default, 1rem, 600);
  167. color: $color__darkgray--primary;
  168. border: thin solid $color__gray--primary;
  169. transition: background-color 0.2s;
  170. margin: 0;
  171. &:hover {
  172. background-color: $color__gray--darker;
  173. }
  174. &:active {
  175. background-color: $color__gray--darkest;
  176. }
  177. }
  178. @at-root #{&}__spacer {
  179. width: 0.6rem;
  180. }
  181. @at-root #{&}__input {
  182. width: 100%;
  183. height: 8rem;
  184. border: 0;
  185. padding: 0.5rem;
  186. @include text;
  187. outline: none;
  188. resize: none;
  189. }
  190. @at-root #{&}__markdownHTML {
  191. height: 8.2rem;
  192. overflow: auto;
  193. word-break: break-word;
  194. padding: 0.5rem;
  195. }
  196. }
  197. </style>