Programming/Vue.js

[Vue.js, tiptap] Editor에서 tab을 들여쓰기로 사용하기

stein 2021. 11. 11. 14:41

필자는 Vue에서 tiptap Editor를 사용하는데, 한가지 불편한 점이 있다.

바로 코드를 작성하다 무의식적으로 tab을 입력하는데, 그러면 focus가 이동해버리는 현상이다. IDE들 처럼 들여쓰기(indentation)이 된다면 얼마나 좋을까...

 

해결해봅시다.


1. focus 막고 tab을 띄워쓰기로 교체하기

먼저 html에서 어떻게 tab으로 focus 이동을 금지시키는지 찾아보았는데 두 가지 방법을 찾았다.

  1. 이동하고 싶지 않은 곳에 tabindex=-1 속성 추가

  2. keydown 이벤트를 찾아서 e.preventDefault() 걸기

 

첫 번째 방법은 우리가 원하는 것이 아니므로 패스(모든 elements에 걸수는 없으니).

꽤나 low한 방법이지만 2번으로 해결 가능해 보인다.

https://stackoverflow.com/questions/6637341/use-tab-to-indent-in-textarea

 

Use tab to indent in textarea

I have a simple HTML textarea on my site. Right now, if you click Tab in it, it goes to the next field. I would like to make the tab button indent a few spaces instead. How can I do this?

stackoverflow.com

stackoverflow 답변 중에 마음에 드는 방식을 선택하자. 좋다 다음 문제로 가자.

 

2. tiptap editor content를 코드로 수정하기

1 번에서 살펴본 방식은 keydown이 일어났을 때, content에 직접 접근해서 indentation을 추가해주는 방식이다. 일반적인 textarea나 input을 사용했다면 문제가 없겠지만, tiptap editor를 쓰고 있기 때문에 tiptap 기능을 사용해서 수정해야한다.

https://tiptap.dev/api/commands/set-content

 

setContent – Tiptap Editor

 

tiptap.dev

editor.commands.setContent('<p>change!</p>', true)

editor.options.content = '~~~'를 사용하면 안된다. editor가 객체이니 setter로 수정하는게 자연스럽기도 하고(캡슐화), 반응성때문에라도 method(event)를 통해 update하는게 맞다

...

는 걸 알면서도, 필자는 삽질을 열심히 했다. 이 글을 통해 다른 분들은 그러지 않기를 바라고, 기능이 있을 것 같으면 docs를 한 번 더 꼼꼼히 읽어보자.

 

3. tiptap의 cursor position 알아내기

삽질에 지대한 도움을 준 글.

https://stackoverflow.com/questions/2897155/get-cursor-position-in-characters-within-a-text-input-field/48150864#48150864

 

Get cursor position (in characters) within a text Input field

How can I get the caret position from within an input field? I have found a few bits and pieces via Google, but nothing bullet proof. Basically something like a jQuery plugin would be ideal, so I...

stackoverflow.com

 

위 방식대로 하면 e.target.selectionStart가 undefined로 나올것이다. 분명히 event의 target도 존재하는데 무슨일일까...

바보

순수 html이니 (당연히..) cursor 자체가 존재하지 않는다.

자.. 그러면 분명히 tiptap이 기능을 만들어 두었을테니 열심히 docs를 뒤져본다.

...

 

안나와서 포기하고 stackoverflow에서 찾아냈다.

https://github.com/ueberdosis/tiptap/issues/367

 

How do I get the cursor position? · Issue #367 · ueberdosis/tiptap

Sorry for the poor English. Thanks for using tiptap. Ask about the position of the cursor on the tip tap editor. (I'm sorry if it was the question of "prosemirror") the purpose I want...

github.com

new Editor({
  onTransaction: ({ state }) => {
    console.log(state.selection.anchor)
  },
})

위 코드가 답변이었는데, 필자의 코드에서는 정상작동하지 않았다. 위 코드가 v1의 tiptap이라 그럴수도 있겠다.

 

그래서 console.log로 찍어보면서 anchor 속성을 찾아냈다

editor.on('transaction', ({ transaction }) => {
        console.log(transaction.curSelection.$anchor.pos)
      })

이제 editor를 클릭할 때마다(정확히는 state가 바뀔 때마다) cursor pos가 출력된다!

 

4. content에 들여쓰기를 추가하기

이 부분은 단순 정보가 아니라 로직을 새롭게 구성해야해서, 코드를 공유하기 보다 참고하면 좋은 정보들을 작성해두겠다. 그리고 필자도 완벽한 로직을 구현하지 못하였다. (버그를 다 잡지 못함)
1. html에 tab을 구현하려면 nbsp;를 사용해서 띄워보여야한다.

2. tiptap에 보여주는 화면(텍스트)을 기준으로 cursorPosition이 계산되는데, 실제로 우리가 get/setContet를 하는 건 html이다.

3. 1, 2를 감안해서 태그<->텍스트 변환 함수가 필요하고, 그에 따라 적절한 위치에 nbsp;를 집어넣어야한다.

 

2번 때문에 코드가 상당히 더러워졌고, 버그를 잡는데 꽤 시간이 걸렸다. 우선은 엄청 중요한 기능이 아니어서 이정도에서 마무리 하기로한다.

 

결론

그냥 html editor를 쓰지말고 inpu이나 textarea로 구현해보는걸 추천한다.

 

마지막 코드를 시원하게 공개하진 못했지만, 그래도 다른 구현에라도 이 게시글이 도움이 되길 바란다.