검색
검색
공개 노트 검색
회원가입로그인

prose mirror 표 도입하기 2

prose mirror 에 표를 드디어 도입했다. 예상대로 험난했다.

schema와 기본 로직은 stacks editor를 참고했다.

https://github.com/StackExchange/Stacks-Editor/blob/22e84dd43b1fb8f9779fe026529b574f1eba0779/src/shared/schema.ts

const tableNodeSpec: NodeSpec = {
    content: "table_head table_body*",
    isolating: true,
    group: "block",
    selectable: false,
    parseDOM: [{ tag: "table" }],
    toDOM() {
        return [
            "div",
            { class: "s-table-container" },
            ["table", { class: "s-table" }, 0],
        ];
    },
};

하나의 table_head 와 0 개 이상의 table_body* 를 구조로 갖는다.

html에서는 table head 와 table body 를 입력하지 않아도 자동으로 주입하지만 common mark spec 에는 두 개가 꼭 존재해야 된다. markdown 을 parsing 할 때 이 같은 구조가 없으면 제대로 구문 해석이 되지 않는다.

Isolating은 요소의 외곽에 에디팅을 할 수 있게 만들어주는 속성이다.

gap cursor와 표가 충돌을 일으켜 갭커서를 해제했다. 아마 순서 문제인것 같다. 둘 다 이벤트를 캡쳐하는데 있어 충돌을 일으키는 듯. 우선 순위를 조절해야 할것 같다.

문제는 사용자가 테이블을 여러 출처에서 붙여넣기를 할 수 있는데 이 때 해당 구조가 없는 경우가 많다는 것이다.

tui editor 를 보니 paste 할 때 테이블을 보완해서 해당 구조를 만드는 로직을 사용하고 있었다. 찾다보니 알게 됐는데 tui editor 처럼 멋진 오픈소스가 한국에 있다니 정말 멋지다.

<ProseMirror      
    transformPastedHTML: changePastedHTML,
    transformPasted: (slice: Slice) =>
        changePastedSlice(slice, this.schema,     isInTableNode(this.view.state.selection.$from)),
...>

Editor를 세팅할 때 html 이 붙여넣기 하는 순간이나 pasted 된 부분(slice) 를 편집할 수 있다. 순서대로 html -> transformPasted 의 순서로 실행이 된다. 이 때 table tag 가 없으면 붙여주고 있다면 table_head 와 table_body 로 구조를 만들어 주면 된다.

tui 의 paste.ts 에서 해당 로직을 살펴볼 수 있다.

md serialize 는 header 부분과 body 부분을 순회하면서 markdown 양식에 맞게 표를 만들어 주면 된다.

| header1 | header 2|
| —- | —- |
| body1 | body 2|

이런 식으로 예쁘게 serializing 해주면 된다.

parser 는 markdown-it 에서 지원이 돼서 다행히 안짜도 됐다.

제일 많이 문제를 겪었던 건 테이블 내에서 멀티 블록 에디팅에 대한 문제였다. markdown 표에서는 기본적으로 inline editing 만 지원이 되기 때문이었다. 여러번 시도하다 결국 인라인 에디팅만 일단 지원하기로 했다.

구현하려고 별 방법을 다 썼었다. 원리는 head 와 body 에 여러개의 paragraph 를 허용하고 serializing 시 가짜
tag를 사용하는 것이다. 이 경우 parsing 할 때 br 태그를 캐치해서 여러개의 paragraph 로 나눠줘야 한다. tui editor 는 이 방법을 구현한 것 같다. schema, parser 의 head / body 에 paragraph 를 지원하고 serializing 에 가짜 br 태그를 만드는 것까지는 성공했다. 하지만 parser 에서 여러개의 paragraph 를 교체해주는 것에서 gg 쳤다…ㅋㅋ 하다보니 이렇게 하면 안될 것 같다는 생각이 강하게 들었다…

컴퓨터는 되는 것이라면 무조건 사용자가 원하는 대로 구현을 해준다. 하지만 안되는 것이라면 안된다. 물론 내가 모르는 것일 수도 있다. 하지만 하다보니 그렇더라…

때론 도망치는 것도 도움이 된다. 결국 나중에 정말 필요하면 다시 구현하기로 하고 인라인 모드로 코딩을 했다.

마크다운 문법은 이해하기는 어렵지 않지만 철학을 받아들이려면 시간이 걸린다. 예전에 엔터키가 newline 인 \n 이 아니라 paragraph 로 전환이 되는 것이 정말 이해가 안됐는데 쓰다보니 이유를 알게 되었다.

daring fireball 블로그에서 보면 그 이유가 나온다.

A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines. (A blank liis any line that looks like a blank line — a line containing nothing but spaces or tabs is considered blank.) Normal paragraphs should not be indented with spaces or tabs.

The implication of the “one or more consecutive lines of text” rule is that Markdown supports “hard-wrapped” text paragraphs. This differs significantly from most other text-to-HTML formatters (including Movable Type’s “Convert Line Breaks” option) which translate every line break character in a paragraph into a <br /> tag.

When you do want to insert a <br /> break tag using Markdown, you end a line with two or more spaces, then type return.

Yes, this takes a tad more effort to create a <br />, but a simplistic “every line break is a <br />” rule wouldn’t work for Markdown. Markdown’s email-style blockquoting and multi-paragraph list items work best — and look better — when you format them with hard breaks.

paragraph 는 하나 또는 연속적인 텍스트를 위한 표현이다. 이게 어쩌면 br 보다 생각을 표현하는데 더 유리할 수도 있다. 그리고 동의하는 점은 포맷이 br 보다 낫다는 것이다. 그렇다 마크다운에서는 newline을 입력하려면 두 번의 스페이스 다음에 엔터를 쳐야 한다.

물론 마크다운의 철학이 완벽한 것은 아니다. 이러한 점들에 대해서는 논의도 많이 이루어지는 것 같다.

html 이 data를 위한 markup 단어라면 mark down 은 읽고 쓰기에 편리한 문법이다.

그냥 나는 일반 텍스트보다 조금 편리하면서 구조가 있는 문장들을 사용하고 싶을 뿐이고 마크다운이 거기에 가장 가깝고 편리하다.

오늘은 여기서 끝!

나는 표입니다.당신은 표입니까?
오늘의 에러는언젠가
자동으로고쳐졌으면
조회수 : 185
공유하기
카카오로 공유하기
페이스북 공유하기
트위터로 공유하기
url 복사하기