profile
viewpoint
박정환 junghwan-park NHN Republic of Korea

nhn/tui.editor 12068

🍞📝 Markdown WYSIWYG Editor. GFM Standard + Chart & UML Extensible.

nhn/toast-ui.react-grid 17

This repository is DEPRECATED! GO TO 👉

nhn/karma-webdriver-launcher 3

A plugin for Karma. Launch any browser with WebDriver.

nhn/Squire 2

HTML5 rich text editor. Try the demo integration at

junghwan-park/junghwan-park.github.io 1

NHN Entertainment - Toast Rookie 3rd

junghwan-park/react-boilerplate 1

React.js and Redux boilerplate of FE Dev lab.

shiren/toMark 1

html to markdown(+gfm) convertor

junghwan-park/Android-GoogleDirectionAndPlaceLibrary 0

Library for Google Direction API and Google Place API for Google Maps Android API v2

issue openednhn/tui.editor

How to add a link button to the left of heading.

As GitHub adds a link button to the left of the heading. This is useful.

https://codesandbox.io/s/holy-paper-p384e?file=/src/App.js see it live

for better visual type below MD into the editor.

# 1
# 2
# 3
# 4 
# 5 
# 6
# 7
# 8 
# 9
# 10
# 11 
# 12
# 13
# 14
# 15
# 16
 # 18
 # 19
 # 20

This helps us to share a page section.

created time in 4 hours

issue openednhn/tui.editor

Issues with typings

Describe the bug

I wanted to write a plug-in in TypeScript that would display help text via a pop-up window. I use version 2.5.1 for this. The popup should appear below the button.

In the typings I did not find a way to get to the EventManager via the editor. The following line throws me an error:

const eventManager = toastUiEditor.eventManager as toastui.EventManager;

Next I wanted to give the toolbar button a name, which also led to an error:

toolbar.addItem({ type: 'button', options: {..., name: 'help'}});

When creating the popup, I just wanted to set the content, but it is mandatory:

toastUiEditor.getUI().createPopup({/* el: */ content: helpContainer});

To Reproduce

A part of my function to create the popup:

`

export default function helpPlugin(editor: toastui.Editor | toastui.Viewer): void { if (!editor.isViewer()) { const toastUiEditor = editor as toastui.Editor; const toolbar = toastUiEditor.getUI().getToolbar();

const eventManager = toastUiEditor.eventManager as toastui.EventManager;

toolbar.addItem({
  type: 'button',
  options: {
    el: createTooltipButton(),
    name: 'help',
    event: 'helpContent',
    tooltip: 'Help',
  },
});

const popup = toastUiEditor.getUI().createPopup({
  content: helpContainer,
  title: 'Help',
  modal: false,
  header: true,
  target: toolbar.el,
});

`

created time in 5 hours

push eventnhn/tui.editor

seonim-ryu

commit sha aea27b7b17034d708a7afdb00d1f89b3395892f6

feat: apply code review

view details

push time in 6 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { Node, Schema, ResolvedPos } from 'prosemirror-model';+import { Selection, TextSelection } from 'prosemirror-state';++import { findNodeBy } from '@/wysiwyg/helper/node';++export interface CellInfo {+  offset: number;+  nodeSize: number;+}++export function createTableRows(+  columnCount: number,+  rowCount: number,+  schema: Schema,+  isTableBody: boolean,+  data?: string[] | null+) {+  const { tableRow, tableHeadCell, tableBodyCell } = schema.nodes;+  const tableRows = [];+  const cellType = isTableBody ? tableBodyCell : tableHeadCell;++  let index = isTableBody ? columnCount : 0;++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    const cells = [];++    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {+      const text = data && data[index];++      if (text) {+        index += 1;+      }++      cells.push(cellType.create(null, text ? schema.text(text) : []));+    }++    tableRows.push(tableRow.create(null, cells));+  }++  return tableRows;+}++export function createCellsToAdd(+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number,+  cellType: any+) {+  const cellCount =+    startRowIndex !== endRowIndex ? columnCount : getCountByRange(startColumnIndex, endColumnIndex);++  const cells = [];++  for (let i = 0, len = cellCount; i < len; i += 1) {+    cells.push(cellType.createAndFill());+  }++  return cells;+}++export function findCell({ nodes }: Schema, pos: ResolvedPos) {+  const { tableHeadCell, tableBodyCell } = nodes;++  return findNodeBy(pos, ({ type }: Node) => type === tableHeadCell || type === tableBodyCell);+}++export function isInCellElement(node: HTMLElement, root: Element) {+  while (node && node !== root) {+    if (node.nodeName === 'TD' || node.nodeName === 'TH') {+      return true;+    }++    node = node.parentNode as HTMLElement;+  }++  return false;+}++export function isToRemoveCells(+  [startRowIndex, stratColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number+) {+  const selectedColumnCount = getCountByRange(stratColumnIndex, endColumnIndex);+  const selectedRows = startRowIndex !== endRowIndex;+  const selectedAllColumns = startRowIndex === endRowIndex && selectedColumnCount === columnCount;++  if (selectedRows || selectedAllColumns) {+    return false;+  }++  return true;

좋네요ㅎ 대신 되도록이면 괄호는 사용하지 않는게 컨벤션이라 && 사용하는거로 수정할게요~

return !selectedRows && !selectedAllColumns;
seonim-ryu

comment created time in 6 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

 export class Table extends Node {       }     };   }++  private addTable(): EditorCommand {+    return (payload = { columns: 1, rows: 1, data: [] }) => (state, dispatch) => {+      const { columns, rows, data } = payload;+      const { schema, selection, tr } = state;+      const { from, to, $from } = selection;+      const collapsed = from === to;++      if (collapsed && !isInTableNode(schema, $from)) {+        const { tableHead, tableBody } = schema.nodes;+        const tableHeadRows = createTableRows(columns, 1, schema, false, data);+        const tableBodyRows = createTableRows(columns, rows, schema, true, data);+        const table = schema.nodes.table.create(null, [+          tableHead.create(null, tableHeadRows),+          tableBody.create(null, tableBodyRows)+        ]);++        dispatch!(tr.replaceSelectionWith(table));++        return true;+      }++      return false;+    };+  }++  private removeTable(): EditorCommand {+    return () => (state, dispatch) => {+      const { selection, schema, tr } = state;+      const { head } = getResolvedSelection(schema, selection);+      const { table } = schema.nodes;+      const foundTable = findNodeBy(head, ({ type }: ProsemirrorNode) => type === table);++      if (foundTable) {+        const { depth } = foundTable;+        const startCellPos = head.before(depth);+        const endCellPos = head.after(depth);+        const cursorPos = createTextSelection(tr.delete(startCellPos, endCellPos), startCellPos);++        dispatch!(tr.setSelection(cursorPos));++        return true;+      }++      return false;+    };+  }++  private addColumn(): EditorCommand {+    return () => (state, dispatch) => {+      const { selection, schema, tr } = state;+      const { tableHeadCell, tableBodyCell } = schema.nodes;+      const { anchor, head } = getResolvedSelection(schema, selection);++      if (anchor && head) {+        const table = getTableByCellPos(head);++        const startCellPos = getCellPosition(anchor);+        const endCellPos = getCellPosition(head);++        const cellIndexes = getCellIndexesByRange(table, endCellPos, endCellPos);+        const cells = getAllCellPositionInfos(head);++        const columnCount = getColumnCount(table);++        cellIndexes.forEach(index => {+          const { offset, nodeSize } = cells[index];++          const startPos = tr.mapping.map(offset + nodeSize);+          const cellType = index < columnCount ? tableHeadCell : tableBodyCell;

현재 위치에 있는 셀에서 노드를 복사해서 사용하는 것으로 변경합니다

 const cell = doc.resolve(offset + 1).node().copy();
seonim-ryu

comment created time in 7 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { Node, Schema, ResolvedPos } from 'prosemirror-model';+import { Selection, TextSelection } from 'prosemirror-state';++import { findNodeBy } from '@/wysiwyg/helper/node';++export interface CellInfo {+  offset: number;+  nodeSize: number;+}++export function createTableRows(+  columnCount: number,+  rowCount: number,+  schema: Schema,+  isTableBody: boolean,+  data?: string[] | null+) {+  const { tableRow, tableHeadCell, tableBodyCell } = schema.nodes;+  const tableRows = [];+  const cellType = isTableBody ? tableBodyCell : tableHeadCell;++  let index = isTableBody ? columnCount : 0;++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    const cells = [];++    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {+      const text = data && data[index];++      if (text) {+        index += 1;+      }++      cells.push(cellType.create(null, text ? schema.text(text) : []));+    }++    tableRows.push(tableRow.create(null, cells));+  }++  return tableRows;+}++export function createCellsToAdd(+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number,+  cellType: any+) {+  const cellCount =+    startRowIndex !== endRowIndex ? columnCount : getCountByRange(startColumnIndex, endColumnIndex);++  const cells = [];++  for (let i = 0, len = cellCount; i < len; i += 1) {+    cells.push(cellType.createAndFill());+  }++  return cells;+}++export function findCell({ nodes }: Schema, pos: ResolvedPos) {+  const { tableHeadCell, tableBodyCell } = nodes;++  return findNodeBy(pos, ({ type }: Node) => type === tableHeadCell || type === tableBodyCell);+}++export function isInCellElement(node: HTMLElement, root: Element) {+  while (node && node !== root) {+    if (node.nodeName === 'TD' || node.nodeName === 'TH') {+      return true;+    }++    node = node.parentNode as HTMLElement;+  }++  return false;+}++export function isToRemoveCells(+  [startRowIndex, stratColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number+) {+  const selectedColumnCount = getCountByRange(stratColumnIndex, endColumnIndex);+  const selectedRows = startRowIndex !== endRowIndex;+  const selectedAllColumns = startRowIndex === endRowIndex && selectedColumnCount === columnCount;++  if (selectedRows || selectedAllColumns) {+    return false;+  }++  return true;+}++export function getResolvedSelection(schema: Schema, selection: Selection) {+  const { $anchor, $head } = selection;+  let anchor = $anchor;+  let head = $head;++  if (selection instanceof TextSelection) {+    const foundCell = findCell(schema, $anchor);++    if (foundCell) {+      anchor = $anchor.node(0).resolve($anchor.before(foundCell.depth));+      head = anchor;+    }+  }++  return { anchor, head };+}++function getCellPositionInfos(headOrBody: Node, startOffset: number) {+  const cellInfos: CellInfo[] = [];++  headOrBody.forEach((row: Node, rowOffset: number) => {+    row.forEach(({ nodeSize }: Node, cellOffset: number) => {+      cellInfos.push({+        offset: startOffset + rowOffset + cellOffset + 2,+        nodeSize+      });+    });+  });++  return cellInfos;+}++export function getAllCellPositionInfos(cellPos: ResolvedPos) {+  const foundTable = findNodeBy(cellPos, ({ type }: Node) => type.name === 'table');++  if (foundTable) {+    const { depth } = foundTable;+    const table = cellPos.node(depth);+    const tablePos = cellPos.start(depth);++    const thead = table.child(0);+    const tbody = table.child(1);++    const theadCellPositions = getCellPositionInfos(thead, tablePos);+    const tbodyCellPositions = getCellPositionInfos(tbody, tablePos + thead.nodeSize);++    return theadCellPositions.concat(tbodyCellPositions);+  }++  return [];+}++export function getTableByCellPos(cellPos: ResolvedPos) {+  return cellPos.node(cellPos.depth - 2);+}++export function getRowCount(table: Node) {+  return table.child(1).childCount;+}++export function getColumnCount(table: Node) {+  return table.child(0).child(0).childCount;+}++export function getCountByRange(startIndex: number, endIndex: number) {+  return Math.max(startIndex, endIndex) - Math.min(startIndex, endIndex) + 1;+}++export function getCellIndexesByRange(+  table: Node,+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[]+) {+  const columnCount = getColumnCount(table);+  const rowCount = getRowCount(table) + 1;++  const columnStart =+    startRowIndex !== endRowIndex ? 0 : Math.min(startColumnIndex, endColumnIndex);+  const columnEnd = Math.max(startColumnIndex, endColumnIndex);++  const indexes = [];++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    for (let columnIndex = columnStart, len = columnEnd; columnIndex <= len; columnIndex += 1) {+      indexes.push(rowIndex * columnCount + columnIndex);+    }+  }++  return indexes;+}++export function getCellPosition(cellPos: ResolvedPos) {

getIndices로 사용하는 케이스는 없고.. getIndexes의 경우에도 컬렉션을 반환해서 복수형이 맞지 않는 것 같네요~ getCellIndexInfo로 정리하겠습니다

seonim-ryu

comment created time in 7 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { Node, Schema, ResolvedPos } from 'prosemirror-model';+import { Selection, TextSelection } from 'prosemirror-state';++import { findNodeBy } from '@/wysiwyg/helper/node';++export interface CellInfo {+  offset: number;+  nodeSize: number;+}++export function createTableRows(+  columnCount: number,+  rowCount: number,+  schema: Schema,+  isTableBody: boolean,+  data?: string[] | null+) {+  const { tableRow, tableHeadCell, tableBodyCell } = schema.nodes;+  const tableRows = [];+  const cellType = isTableBody ? tableBodyCell : tableHeadCell;++  let index = isTableBody ? columnCount : 0;++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    const cells = [];++    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {+      const text = data && data[index];++      if (text) {+        index += 1;+      }++      cells.push(cellType.create(null, text ? schema.text(text) : []));+    }++    tableRows.push(tableRow.create(null, cells));+  }++  return tableRows;+}++export function createCellsToAdd(+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number,+  cellType: any

이건 Node를 넘기는 방식으로 해결합니다. (파라미터 타입 변경: any -> Node)

seonim-ryu

comment created time in 7 hours

issue commentnhn/tui.image-editor

Custom menu : when I add icon it does not draw anything

This issue has been automatically marked as inactive because there hasn’t been much going on it lately. It is going to be closed after 7 days. Thanks!

mersimbakalli

comment created time in 8 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { Node, Schema, ResolvedPos } from 'prosemirror-model';+import { Selection, TextSelection } from 'prosemirror-state';++import { findNodeBy } from '@/wysiwyg/helper/node';++export interface CellInfo {+  offset: number;+  nodeSize: number;+}++export function createTableRows(+  columnCount: number,+  rowCount: number,+  schema: Schema,+  isTableBody: boolean,+  data?: string[] | null+) {+  const { tableRow, tableHeadCell, tableBodyCell } = schema.nodes;+  const tableRows = [];+  const cellType = isTableBody ? tableBodyCell : tableHeadCell;++  let index = isTableBody ? columnCount : 0;++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    const cells = [];++    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {+      const text = data && data[index];++      if (text) {+        index += 1;+      }++      cells.push(cellType.create(null, text ? schema.text(text) : []));+    }++    tableRows.push(tableRow.create(null, cells));+  }++  return tableRows;+}++export function createCellsToAdd(+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number,+  cellType: any

이건 주석을 달아둘게요~

seonim-ryu

comment created time in 9 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { Node, Schema, ResolvedPos } from 'prosemirror-model';+import { Selection, TextSelection } from 'prosemirror-state';++import { findNodeBy } from '@/wysiwyg/helper/node';++export interface CellInfo {+  offset: number;+  nodeSize: number;+}++export function createTableRows(+  columnCount: number,+  rowCount: number,+  schema: Schema,+  isTableBody: boolean,+  data?: string[] | null+) {+  const { tableRow, tableHeadCell, tableBodyCell } = schema.nodes;+  const tableRows = [];+  const cellType = isTableBody ? tableBodyCell : tableHeadCell;++  let index = isTableBody ? columnCount : 0;++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    const cells = [];++    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {+      const text = data && data[index];++      if (text) {+        index += 1;+      }++      cells.push(cellType.create(null, text ? schema.text(text) : []));+    }++    tableRows.push(tableRow.create(null, cells));+  }++  return tableRows;+}++export function createCellsToAdd(+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number,+  cellType: any+) {+  const cellCount =+    startRowIndex !== endRowIndex ? columnCount : getCountByRange(startColumnIndex, endColumnIndex);

첫 번째 컬럼만 여러행 선택 <- 이건 어떤 뜻인지 이해가 잘 안되지만.. 일단 좀 더 설명을 드리자면,

단일 행이 선택되었을 때는[2, 0], [0, 2] 범위에서 전체 컬럼이 선택된건지 판별하기 위해서 getCountByRange(startColumnIndex, endColumnIndex);를 호출하게 되고 single 여러 행이 선택되었을 때는 [0, 2], [2, 2] 범위 안에서 무조건 전체 컬럼이 선택되기 때문에 startRowIndex !== endRowIndex ? columnCount로 전체 컬럼 수를 설정하게 된거예요~ multi

seonim-ryu

comment created time in 9 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { Node, Schema, ResolvedPos } from 'prosemirror-model';+import { Selection, TextSelection } from 'prosemirror-state';++import { findNodeBy } from '@/wysiwyg/helper/node';++export interface CellInfo {+  offset: number;+  nodeSize: number;+}++export function createTableRows(+  columnCount: number,+  rowCount: number,+  schema: Schema,+  isTableBody: boolean,+  data?: string[] | null+) {+  const { tableRow, tableHeadCell, tableBodyCell } = schema.nodes;+  const tableRows = [];+  const cellType = isTableBody ? tableBodyCell : tableHeadCell;++  let index = isTableBody ? columnCount : 0;++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    const cells = [];++    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {+      const text = data && data[index];++      if (text) {+        index += 1;+      }++      cells.push(cellType.create(null, text ? schema.text(text) : []));+    }++    tableRows.push(tableRow.create(null, cells));+  }++  return tableRows;+}++export function createCellsToAdd(+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number,+  cellType: any+) {+  const cellCount =+    startRowIndex !== endRowIndex ? columnCount : getCountByRange(startColumnIndex, endColumnIndex);++  const cells = [];++  for (let i = 0, len = cellCount; i < len; i += 1) {+    cells.push(cellType.createAndFill());+  }++  return cells;+}++export function findCell({ nodes }: Schema, pos: ResolvedPos) {+  const { tableHeadCell, tableBodyCell } = nodes;++  return findNodeBy(pos, ({ type }: Node) => type === tableHeadCell || type === tableBodyCell);+}++export function isInCellElement(node: HTMLElement, root: Element) {+  while (node && node !== root) {+    if (node.nodeName === 'TD' || node.nodeName === 'TH') {+      return true;+    }++    node = node.parentNode as HTMLElement;+  }++  return false;+}++export function isToRemoveCells(+  [startRowIndex, stratColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number+) {+  const selectedColumnCount = getCountByRange(stratColumnIndex, endColumnIndex);+  const selectedRows = startRowIndex !== endRowIndex;+  const selectedAllColumns = startRowIndex === endRowIndex && selectedColumnCount === columnCount;++  if (selectedRows || selectedAllColumns) {+    return false;+  }++  return true;+}++export function getResolvedSelection(schema: Schema, selection: Selection) {+  const { $anchor, $head } = selection;+  let anchor = $anchor;+  let head = $head;++  if (selection instanceof TextSelection) {+    const foundCell = findCell(schema, $anchor);++    if (foundCell) {+      anchor = $anchor.node(0).resolve($anchor.before(foundCell.depth));+      head = anchor;+    }+  }++  return { anchor, head };+}++function getCellPositionInfos(headOrBody: Node, startOffset: number) {+  const cellInfos: CellInfo[] = [];++  headOrBody.forEach((row: Node, rowOffset: number) => {+    row.forEach(({ nodeSize }: Node, cellOffset: number) => {+      cellInfos.push({+        offset: startOffset + rowOffset + cellOffset + 2,+        nodeSize+      });+    });+  });++  return cellInfos;+}++export function getAllCellPositionInfos(cellPos: ResolvedPos) {+  const foundTable = findNodeBy(cellPos, ({ type }: Node) => type.name === 'table');++  if (foundTable) {+    const { depth } = foundTable;+    const table = cellPos.node(depth);+    const tablePos = cellPos.start(depth);++    const thead = table.child(0);+    const tbody = table.child(1);++    const theadCellPositions = getCellPositionInfos(thead, tablePos);+    const tbodyCellPositions = getCellPositionInfos(tbody, tablePos + thead.nodeSize);++    return theadCellPositions.concat(tbodyCellPositions);+  }++  return [];+}++export function getTableByCellPos(cellPos: ResolvedPos) {+  return cellPos.node(cellPos.depth - 2);+}++export function getRowCount(table: Node) {+  return table.child(1).childCount;+}++export function getColumnCount(table: Node) {+  return table.child(0).child(0).childCount;+}++export function getCountByRange(startIndex: number, endIndex: number) {+  return Math.max(startIndex, endIndex) - Math.min(startIndex, endIndex) + 1;+}++export function getCellIndexesByRange(+  table: Node,+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[]+) {+  const columnCount = getColumnCount(table);+  const rowCount = getRowCount(table) + 1;++  const columnStart =+    startRowIndex !== endRowIndex ? 0 : Math.min(startColumnIndex, endColumnIndex);+  const columnEnd = Math.max(startColumnIndex, endColumnIndex);++  const indexes = [];++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    for (let columnIndex = columnStart, len = columnEnd; columnIndex <= len; columnIndex += 1) {+      indexes.push(rowIndex * columnCount + columnIndex);+    }+  }++  return indexes;+}++export function getCellPosition(cellPos: ResolvedPos) {

index의 복수형이면 indices로 하는게 어떨까요?

seonim-ryu

comment created time in 10 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { Node, Schema, ResolvedPos } from 'prosemirror-model';+import { Selection, TextSelection } from 'prosemirror-state';++import { findNodeBy } from '@/wysiwyg/helper/node';++export interface CellInfo {+  offset: number;+  nodeSize: number;+}++export function createTableRows(+  columnCount: number,+  rowCount: number,+  schema: Schema,+  isTableBody: boolean,+  data?: string[] | null+) {+  const { tableRow, tableHeadCell, tableBodyCell } = schema.nodes;+  const tableRows = [];+  const cellType = isTableBody ? tableBodyCell : tableHeadCell;++  let index = isTableBody ? columnCount : 0;++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    const cells = [];++    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {+      const text = data && data[index];++      if (text) {+        index += 1;+      }++      cells.push(cellType.create(null, text ? schema.text(text) : []));+    }++    tableRows.push(tableRow.create(null, cells));+  }++  return tableRows;+}++export function createCellsToAdd(+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number,+  cellType: any+) {+  const cellCount =+    startRowIndex !== endRowIndex ? columnCount : getCountByRange(startColumnIndex, endColumnIndex);++  const cells = [];++  for (let i = 0, len = cellCount; i < len; i += 1) {+    cells.push(cellType.createAndFill());+  }++  return cells;+}++export function findCell({ nodes }: Schema, pos: ResolvedPos) {+  const { tableHeadCell, tableBodyCell } = nodes;++  return findNodeBy(pos, ({ type }: Node) => type === tableHeadCell || type === tableBodyCell);+}++export function isInCellElement(node: HTMLElement, root: Element) {+  while (node && node !== root) {+    if (node.nodeName === 'TD' || node.nodeName === 'TH') {+      return true;+    }++    node = node.parentNode as HTMLElement;+  }++  return false;+}++export function isToRemoveCells(+  [startRowIndex, stratColumnIndex]: number[],

startColumnIndex 오타 발견했습니다

seonim-ryu

comment created time in 10 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { Node, Schema, ResolvedPos } from 'prosemirror-model';+import { Selection, TextSelection } from 'prosemirror-state';++import { findNodeBy } from '@/wysiwyg/helper/node';++export interface CellInfo {+  offset: number;+  nodeSize: number;+}++export function createTableRows(+  columnCount: number,+  rowCount: number,+  schema: Schema,+  isTableBody: boolean,+  data?: string[] | null+) {+  const { tableRow, tableHeadCell, tableBodyCell } = schema.nodes;+  const tableRows = [];+  const cellType = isTableBody ? tableBodyCell : tableHeadCell;++  let index = isTableBody ? columnCount : 0;++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    const cells = [];++    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {+      const text = data && data[index];++      if (text) {+        index += 1;+      }++      cells.push(cellType.create(null, text ? schema.text(text) : []));+    }++    tableRows.push(tableRow.create(null, cells));+  }++  return tableRows;+}++export function createCellsToAdd(+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number,+  cellType: any+) {+  const cellCount =+    startRowIndex !== endRowIndex ? columnCount : getCountByRange(startColumnIndex, endColumnIndex);++  const cells = [];++  for (let i = 0, len = cellCount; i < len; i += 1) {+    cells.push(cellType.createAndFill());+  }++  return cells;+}++export function findCell({ nodes }: Schema, pos: ResolvedPos) {+  const { tableHeadCell, tableBodyCell } = nodes;++  return findNodeBy(pos, ({ type }: Node) => type === tableHeadCell || type === tableBodyCell);+}++export function isInCellElement(node: HTMLElement, root: Element) {+  while (node && node !== root) {+    if (node.nodeName === 'TD' || node.nodeName === 'TH') {+      return true;+    }++    node = node.parentNode as HTMLElement;+  }++  return false;+}++export function isToRemoveCells(+  [startRowIndex, stratColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number+) {+  const selectedColumnCount = getCountByRange(stratColumnIndex, endColumnIndex);+  const selectedRows = startRowIndex !== endRowIndex;+  const selectedAllColumns = startRowIndex === endRowIndex && selectedColumnCount === columnCount;++  if (selectedRows || selectedAllColumns) {+    return false;+  }++  return true;

return !(selectedRows || selectedAllColumns); 는 어떤가요?

seonim-ryu

comment created time in 10 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { Node, Schema, ResolvedPos } from 'prosemirror-model';+import { Selection, TextSelection } from 'prosemirror-state';++import { findNodeBy } from '@/wysiwyg/helper/node';++export interface CellInfo {+  offset: number;+  nodeSize: number;+}++export function createTableRows(+  columnCount: number,+  rowCount: number,+  schema: Schema,+  isTableBody: boolean,+  data?: string[] | null+) {+  const { tableRow, tableHeadCell, tableBodyCell } = schema.nodes;+  const tableRows = [];+  const cellType = isTableBody ? tableBodyCell : tableHeadCell;++  let index = isTableBody ? columnCount : 0;++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    const cells = [];++    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {+      const text = data && data[index];++      if (text) {+        index += 1;+      }++      cells.push(cellType.create(null, text ? schema.text(text) : []));+    }++    tableRows.push(tableRow.create(null, cells));+  }++  return tableRows;+}++export function createCellsToAdd(+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number,+  cellType: any+) {+  const cellCount =+    startRowIndex !== endRowIndex ? columnCount : getCountByRange(startColumnIndex, endColumnIndex);++  const cells = [];++  for (let i = 0, len = cellCount; i < len; i += 1) {+    cells.push(cellType.createAndFill());+  }++  return cells;+}++export function findCell({ nodes }: Schema, pos: ResolvedPos) {+  const { tableHeadCell, tableBodyCell } = nodes;++  return findNodeBy(pos, ({ type }: Node) => type === tableHeadCell || type === tableBodyCell);+}++export function isInCellElement(node: HTMLElement, root: Element) {+  while (node && node !== root) {+    if (node.nodeName === 'TD' || node.nodeName === 'TH') {+      return true;+    }++    node = node.parentNode as HTMLElement;+  }++  return false;+}++export function isToRemoveCells(

오.. judge 좋네요ㅎ

seonim-ryu

comment created time in 9 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { Node, Schema, ResolvedPos } from 'prosemirror-model';+import { Selection, TextSelection } from 'prosemirror-state';++import { findNodeBy } from '@/wysiwyg/helper/node';++export interface CellInfo {+  offset: number;+  nodeSize: number;+}++export function createTableRows(+  columnCount: number,+  rowCount: number,+  schema: Schema,+  isTableBody: boolean,+  data?: string[] | null+) {+  const { tableRow, tableHeadCell, tableBodyCell } = schema.nodes;+  const tableRows = [];+  const cellType = isTableBody ? tableBodyCell : tableHeadCell;++  let index = isTableBody ? columnCount : 0;++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    const cells = [];++    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {+      const text = data && data[index];++      if (text) {+        index += 1;+      }++      cells.push(cellType.create(null, text ? schema.text(text) : []));+    }++    tableRows.push(tableRow.create(null, cells));+  }++  return tableRows;+}++export function createCellsToAdd(+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number,+  cellType: any+) {+  const cellCount =+    startRowIndex !== endRowIndex ? columnCount : getCountByRange(startColumnIndex, endColumnIndex);++  const cells = [];++  for (let i = 0, len = cellCount; i < len; i += 1) {+    cells.push(cellType.createAndFill());+  }++  return cells;+}++export function findCell({ nodes }: Schema, pos: ResolvedPos) {+  const { tableHeadCell, tableBodyCell } = nodes;++  return findNodeBy(pos, ({ type }: Node) => type === tableHeadCell || type === tableBodyCell);+}++export function isInCellElement(node: HTMLElement, root: Element) {+  while (node && node !== root) {+    if (node.nodeName === 'TD' || node.nodeName === 'TH') {+      return true;+    }++    node = node.parentNode as HTMLElement;+  }++  return false;+}++export function isToRemoveCells(+  [startRowIndex, stratColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number+) {+  const selectedColumnCount = getCountByRange(stratColumnIndex, endColumnIndex);+  const selectedRows = startRowIndex !== endRowIndex;+  const selectedAllColumns = startRowIndex === endRowIndex && selectedColumnCount === columnCount;

모든 컬럼이 선택된 경우가.. 아래 두 가지 경우가 아닌가요~? (혹시 모든 인가요?) selectedRows selectedAllColumns

seonim-ryu

comment created time in 9 hours

PR opened nhn/tui.image-editor

Feat/resize move add undo stack

<!-- EDIT TITLE PLEASE --> <!-- It should be one of them <ISSUE TYPE>: Short Description (<CLOSING TYPE> #<ISSUE NUMBERS>) ex) feat: add new feature (close #111) fix: wrong behavior (fix #111) chore: change build tool (ref #111) -->

<!-- SPECIFY A ISSUE TYPE AT HEAD feat: A new feature fix: A bug fix docs: Documentation only changes style: Changes that do not affect the meaning of the code (white-space, formatting etc) refactor: A code change that neither fixes a bug or adds a feature perf: A code change that improves performance test: Adding missing tests chore: Changes to the build process or auxiliary tools and libraries such as documentation generation -->

<!-- ADD CLOSING TYPE AND ISSUE NUMBER AT TAIL (<CLOSING TYPE> #<ISSUE NUMBERS>) close: resolve not a bug(feature, docs, etc) completely fix: resolve a bug completely ref: not fully resolved or related to -->

Please check if the PR fulfills these requirements

  • [ ] It's submitted to right branch according to our branching model
  • [ ] It's right issue type on title
  • [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. fix #xxx[,#xxx], where "xxx" is the issue number)
  • [ ] The commit message follows our guidelines
  • [ ] Tests for the changes have been added (for bug fixes/features)
  • [ ] Docs have been added/updated (for bug fixes/features)
  • [ ] It does not introduce a breaking change or has description for the breaking change

Description


Thank you for your contribution to TOAST UI product. 🎉 😘 ✨

+290 -5

0 comment

8 changed files

pr created time in 9 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

 export class Table extends Node {       }     };   }++  private addTable(): EditorCommand {+    return (payload = { columns: 1, rows: 1, data: [] }) => (state, dispatch) => {+      const { columns, rows, data } = payload;+      const { schema, selection, tr } = state;+      const { from, to, $from } = selection;+      const collapsed = from === to;++      if (collapsed && !isInTableNode(schema, $from)) {+        const { tableHead, tableBody } = schema.nodes;+        const tableHeadRows = createTableRows(columns, 1, schema, false, data);+        const tableBodyRows = createTableRows(columns, rows, schema, true, data);+        const table = schema.nodes.table.create(null, [+          tableHead.create(null, tableHeadRows),+          tableBody.create(null, tableBodyRows)+        ]);++        dispatch!(tr.replaceSelectionWith(table));++        return true;+      }++      return false;+    };+  }++  private removeTable(): EditorCommand {+    return () => (state, dispatch) => {+      const { selection, schema, tr } = state;+      const { head } = getResolvedSelection(schema, selection);+      const { table } = schema.nodes;+      const foundTable = findNodeBy(head, ({ type }: ProsemirrorNode) => type === table);++      if (foundTable) {+        const { depth } = foundTable;+        const startCellPos = head.before(depth);+        const endCellPos = head.after(depth);+        const cursorPos = createTextSelection(tr.delete(startCellPos, endCellPos), startCellPos);++        dispatch!(tr.setSelection(cursorPos));++        return true;+      }++      return false;+    };+  }++  private addColumn(): EditorCommand {+    return () => (state, dispatch) => {+      const { selection, schema, tr } = state;+      const { tableHeadCell, tableBodyCell } = schema.nodes;+      const { anchor, head } = getResolvedSelection(schema, selection);++      if (anchor && head) {+        const table = getTableByCellPos(head);++        const startCellPos = getCellPosition(anchor);+        const endCellPos = getCellPosition(head);++        const cellIndexes = getCellIndexesByRange(table, endCellPos, endCellPos);+        const cells = getAllCellPositionInfos(head);++        const columnCount = getColumnCount(table);++        cellIndexes.forEach(index => {+          const { offset, nodeSize } = cells[index];++          const startPos = tr.mapping.map(offset + nodeSize);+          const cellType = index < columnCount ? tableHeadCell : tableBodyCell;+          const addedCells = createCellsToAdd(startCellPos, endCellPos, columnCount, cellType);++          tr.insert(startPos, addedCells);+        });++        dispatch!(tr);++        return true;+      }++      return false;+    };+  }++  private removeColumn(): EditorCommand {+    return () => (state, dispatch) => {+      const { selection, schema, tr } = state;+      const { anchor, head } = getResolvedSelection(schema, selection);++      if (anchor && head) {+        const table = getTableByCellPos(head);++        const startCellPos = getCellPosition(anchor);+        const endCellPos = getCellPosition(head);+        const columnCount = getColumnCount(table);++        if (!isToRemoveCells(startCellPos, endCellPos, columnCount)) {+          return false;+        }++        const cellIndexes = getCellIndexesByRange(table, startCellPos, endCellPos);+        const cells = getAllCellPositionInfos(head);++        const startPos = tr.mapping.maps.length;++        cellIndexes.forEach(index => {+          const { offset, nodeSize } = cells[index];+          const from = tr.mapping.slice(startPos).map(offset);+          const to = from + nodeSize;++          tr.delete(from, to);+        });++        dispatch!(tr);++        return true;+      }++      return false;+    };+  }++  private addRow(): EditorCommand {+    return () => (state, dispatch) => {+      const { selection, schema, tr } = state;+      const { anchor, head } = getResolvedSelection(schema, selection);++      if (anchor && head) {+        const table = getTableByCellPos(head);++        const [startRowIndex] = getCellPosition(anchor);+        const [endRowIndex] = getCellPosition(head);++        const [from, to] = getPositionsToAddRow(head, startRowIndex, endRowIndex);+        const columnCount = getColumnCount(table);+        const rowCount = getCountByRange(startRowIndex, endRowIndex);+        const rows = createTableRows(columnCount, rowCount, schema, true);++        dispatch!(tr.step(new ReplaceStep(from, to, new Slice(Fragment.from(rows), 0, 0))));++        return true;+      }++      return false;+    };+  }++  private removeRow(): EditorCommand {+    return () => (state, dispatch) => {+      const { selection, schema, tr } = state;+      const { anchor, head } = getResolvedSelection(schema, selection);++      if (anchor && head) {+        const table = getTableByCellPos(head);++        const rowCount = getRowCount(table);+        const [from, to] = getPositionsToRemoveRow(anchor, head, rowCount);++        if (from && to) {+          dispatch!(tr.step(new ReplaceStep(from, to, Slice.empty)));++          return true;+        }+      }++      return false;+    };+  }++  private alignColumn(): EditorCommand {+    return (payload = { align: 'center' }) => (state, dispatch) => {+      const { align } = payload;+      const { selection, schema, tr } = state;+      const { anchor, head } = getResolvedSelection(schema, selection);++      if (anchor && head) {+        const table = getTableByCellPos(head);++        const startCellPos = getCellPosition(anchor);+        const endCellPos = getCellPosition(head);++        const cellIndexes = getCellIndexesByRange(table, startCellPos, endCellPos);+        const cells = getAllCellPositionInfos(head);++        cellIndexes.forEach(index => {+          const { offset } = cells[index];+          const { pos } = head.node(0).resolve(offset);++          tr.setNodeMarkup(pos, null!, { align });

그 방법이 있었네요ㅎ

seonim-ryu

comment created time in 9 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { oneLineTrim } from 'common-tags';++import WysiwygEditor from '@/wysiwyg/wwEditor';+import EventEmitter from '@/event/eventEmitter';+import CommandManager from '@/commands/commandManager';+import CellSelection from '@/wysiwyg/plugins/tableSelection/cellSelection';++describe('wysiwyg table commands', () => {+  let container: HTMLElement, wwe: WysiwygEditor, em: EventEmitter, cmd: CommandManager;++  function setCellSelection(from: number, to: number) {+    const { state, dispatch } = wwe.view;+    const { doc, tr } = state;++    const startCellPos = doc.resolve(from);+    const endCellPos = doc.resolve(to);+    const selection = new CellSelection(startCellPos, endCellPos);++    dispatch!(tr.setSelection(selection));+  }++  beforeEach(() => {+    container = document.createElement('div');+    document.body.appendChild(container);++    em = new EventEmitter();+    wwe = new WysiwygEditor(container, em);+    cmd = new CommandManager(em, {}, wwe.commands);+  });++  afterEach(() => {+    document.body.removeChild(container);+  });++  describe('addTable command', () => {+    it('should create 1 by 1 table', () => {+      cmd.exec('wysiwyg', 'addTable');++      const expected = oneLineTrim`+        <table>+          <thead>+            <tr><th><br></th></tr>+          </thead>+          <tbody>+            <tr><td><br></td></tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);+    });++    it('should create table with column and row count', () => {+      cmd.exec('wysiwyg', 'addTable', { columns: 2, rows: 3 });++      const expected = oneLineTrim`+        <table>+          <thead>+            <tr><th><br></th><th><br></th></tr>+          </thead>+          <tbody>+            <tr><td><br></td><td><br></td></tr>+            <tr><td><br></td><td><br></td></tr>+            <tr><td><br></td><td><br></td></tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);+    });++    it('should create table with data', () => {+      cmd.exec('wysiwyg', 'addTable', { columns: 2, rows: 1, data: ['foo', 'bar', 'baz', 'qux'] });++      const expected = oneLineTrim`+        <table>+          <thead>+            <tr><th>foo</th><th>bar</th></tr>+          </thead>+          <tbody>+            <tr><td>baz</td><td>qux</td></tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);+    });+  });++  describe('removeTable command', () => {+    beforeEach(() => {+      cmd.exec('wysiwyg', 'addTable');+    });++    it('should remove table when cursor is in table head', () => {+      wwe.setSelection(4, 4);++      cmd.exec('wysiwyg', 'removeTable');++      expect(wwe.getHTML()).toBe('<p><br></p>');+    });++    it('should remove table when cursor is in table body', () => {+      wwe.setSelection(11, 11);++      cmd.exec('wysiwyg', 'removeTable');++      expect(wwe.getHTML()).toBe('<p><br></p>');+    });++    it('should remove table when selected cells', () => {+      setCellSelection(4, 11);++      cmd.exec('wysiwyg', 'removeTable');++      expect(wwe.getHTML()).toBe('<p><br></p>');+    });+  });++  describe('addRow command', () => {+    beforeEach(() => {+      cmd.exec('wysiwyg', 'addTable', { columns: 2, rows: 1, data: ['foo', 'bar', 'baz', 'qux'] });+    });++    it('should add row to table body when cursor is in table header', () => {+      wwe.setSelection(5, 5); // select 'foo' cell++      cmd.exec('wysiwyg', 'addRow');++      const expected = oneLineTrim`+        <table>+          <thead>+            <tr>+              <th>foo</th>+              <th>bar</th>+            </tr>+          </thead>+          <tbody>+            <tr>+              <td><br></td>+              <td><br></td>+            </tr>+            <tr>+              <td>baz</td>+              <td>qux</td>+            </tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);+    });++    it('should add row when cursor is in table body', () => {+      wwe.setSelection(19, 19); // select 'baz' cell++      cmd.exec('wysiwyg', 'addRow');++      const expected = oneLineTrim`+        <table>+          <thead>+            <tr>+              <th>foo</th>+              <th>bar</th>+            </tr>+          </thead>+          <tbody>+            <tr>+              <td>baz</td>+              <td>qux</td>+            </tr>+            <tr>+              <td><br></td>+              <td><br></td>+            </tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);+    });++    it('should add row to table body when cells is same line are selected in table header', () => {+      setCellSelection(3, 8); // select from 'foo' to 'bar' cell++      cmd.exec('wysiwyg', 'addRow');++      let expected = oneLineTrim`+        <table>+          <thead>+            <tr>+              <th class="te-cell-selected">foo</th>+              <th class="te-cell-selected">bar</th>+            </tr>+          </thead>+          <tbody>+            <tr>+              <td><br></td>+              <td><br></td>+            </tr>+            <tr>+              <td>baz</td>+              <td>qux</td>+            </tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);++      setCellSelection(8, 3); // select from 'bar' to 'foo' cell++      cmd.exec('wysiwyg', 'addRow');++      expected = oneLineTrim`+        <table>+          <thead>+            <tr>+              <th class="te-cell-selected">foo</th>+              <th class="te-cell-selected">bar</th>+            </tr>+          </thead>+          <tbody>+            <tr>+              <td><br></td>+              <td><br></td>+            </tr>+            <tr>+              <td><br></td>+              <td><br></td>+            </tr>+            <tr>+              <td>baz</td>+              <td>qux</td>+            </tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);+    });++    it('should add row to table body when cells is same line are selected in table body', () => {+      setCellSelection(17, 22); // select from 'baz' to 'qux' cell++      cmd.exec('wysiwyg', 'addRow');++      let expected = oneLineTrim`+        <table>+          <thead>+            <tr>+              <th>foo</th>+              <th>bar</th>+            </tr>+          </thead>+          <tbody>+            <tr>+              <td class="te-cell-selected">baz</td>+              <td class="te-cell-selected">qux</td>+            </tr>+            <tr>+              <td><br></td>+              <td><br></td>+            </tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);++      setCellSelection(22, 17); // select from 'qux' to 'baz' cell++      cmd.exec('wysiwyg', 'addRow');++      expected = oneLineTrim`+        <table>+          <thead>+            <tr>+              <th>foo</th>+              <th>bar</th>+            </tr>+          </thead>+          <tbody>+            <tr>+              <td class="te-cell-selected">baz</td>+              <td class="te-cell-selected">qux</td>+            </tr>+            <tr>+              <td><br></td>+              <td><br></td>+            </tr>+            <tr>+              <td><br></td>+              <td><br></td>+            </tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);+    });++    it('should add rows after head cell by row count in selection', () => {+      setCellSelection(8, 22); // select from 'bar' to 'qux' cell++      cmd.exec('wysiwyg', 'addRow');++      let expected = oneLineTrim`+        <table>+          <thead>+            <tr>+              <th>foo</th>+              <th class="te-cell-selected">bar</th>+            </tr>+          </thead>+          <tbody>+            <tr>+              <td class="te-cell-selected">baz</td>+              <td class="te-cell-selected">qux</td>+            </tr>+            <tr>+              <td><br></td>+              <td><br></td>+            </tr>+            <tr>+              <td><br></td>+              <td><br></td>+            </tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);++      setCellSelection(22, 8); // select from 'qux' to 'bar' cell++      cmd.exec('wysiwyg', 'addRow');++      expected = oneLineTrim`+        <table>+          <thead>+            <tr>+              <th>foo</th>+              <th class="te-cell-selected">bar</th>+            </tr>+          </thead>+          <tbody>+            <tr>+              <td class="te-cell-selected"><br></td>+              <td class="te-cell-selected"><br></td>+            </tr>+            <tr>+              <td class="te-cell-selected"><br></td>+              <td class="te-cell-selected"><br></td>+            </tr>+            <tr>+              <td class="te-cell-selected">baz</td>+              <td class="te-cell-selected">qux</td>+            </tr>+            <tr>+              <td><br></td>+              <td><br></td>+            </tr>+            <tr>+              <td><br></td>+              <td><br></td>+            </tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);+    });+  });++  describe('removeRow command', () => {+    beforeEach(() => {+      cmd.exec('wysiwyg', 'addTable', {+        columns: 2,+        rows: 3,+        data: ['foo', 'bar', 'baz', 'qux', 'quux', 'quuz', 'corge', '']+      });+    });++    it('should not remove row when cursor is in table header', () => {+      wwe.setSelection(5, 5); // select 'foo' cell++      cmd.exec('wysiwyg', 'removeRow');++      const expected = oneLineTrim`+        <table>+          <thead>+            <tr>+              <th>foo</th>+              <th>bar</th>+            </tr>+          </thead>+          <tbody>+            <tr>+              <td>baz</td>+              <td>qux</td>+            </tr>+            <tr>+              <td>quux</td>+              <td>quuz</td>+            </tr>+            <tr>+              <td>corge</td>+              <td><br></td>+            </tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);+    });++    it('should remove row when cursor is in table body', () => {+      wwe.setSelection(19, 19); // select 'baz' cell++      cmd.exec('wysiwyg', 'removeRow');++      const expected = oneLineTrim`+        <table>+          <thead>+            <tr>+              <th>foo</th>+              <th>bar</th>+            </tr>+          </thead>+          <tbody>+            <tr>+              <td>quux</td>+              <td>quuz</td>+            </tr>+            <tr>+              <td>corge</td>+              <td><br></td>+            </tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);+    });++    it('should not remove row when cells of same row are selected in table header', () => {+      setCellSelection(3, 8); // select from 'foo' to 'bar' cell++      cmd.exec('wysiwyg', 'removeRow');++      const expected = oneLineTrim`+        <table>+          <thead>+            <tr>+              <th class="te-cell-selected">foo</th>+              <th class="te-cell-selected">bar</th>+            </tr>+          </thead>+          <tbody>+            <tr>+              <td>baz</td>+              <td>qux</td>+            </tr>+            <tr>+              <td>quux</td>+              <td>quuz</td>+            </tr>+            <tr>+              <td>corge</td>+              <td><br></td>+            </tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);++      setCellSelection(3, 8); // select from 'bar' to 'foo' cell++      cmd.exec('wysiwyg', 'removeRow');++      expect(wwe.getHTML()).toBe(expected);+    });++    it('should remove row when cells of same row are selected in table body', () => {+      setCellSelection(29, 35); // select from 'quux' to 'quuz' cell++      cmd.exec('wysiwyg', 'removeRow');++      let expected = oneLineTrim`+        <table>+          <thead>+            <tr>+              <th>foo</th>+              <th>bar</th>+            </tr>+          </thead>+          <tbody>+            <tr>+              <td>baz</td>+              <td>qux</td>+            </tr>+            <tr>+              <td>corge</td>+              <td><br></td>+            </tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);++      setCellSelection(36, 29); // select from last to 'corge' cell++      cmd.exec('wysiwyg', 'removeRow');++      expected = oneLineTrim`+        <table>+          <thead>+            <tr>+              <th>foo</th>+              <th>bar</th>+            </tr>+          </thead>+          <tbody>+            <tr>+              <td>baz</td>+              <td>qux</td>+            </tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);+    });++    it('should remove rows when cells of multiple rows are selected in table body', () => {+      setCellSelection(29, 43); // select from 'quux' to 'corge' cell++      cmd.exec('wysiwyg', 'removeRow');++      const expected = oneLineTrim`+        <table>+          <thead>+            <tr>+              <th>foo</th>+              <th>bar</th>+            </tr>+          </thead>+          <tbody>+            <tr>+              <td>baz</td>+              <td>qux</td>+            </tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);+    });++    it('should not remove row when there is one row in table body', () => {+      wwe.setSelection(22, 22);++      cmd.exec('wysiwyg', 'removeRow');+      cmd.exec('wysiwyg', 'removeRow');++      const expected = oneLineTrim`+        <table>+          <thead>+            <tr>+              <th>foo</th>+              <th>bar</th>+            </tr>+          </thead>+          <tbody>+            <tr>+              <td>corge</td>+              <td><br></td>+            </tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);+    });++    it('should remove rows except last row when all cells are selected in table body', () => {+      setCellSelection(8, 43); // select from 'bar' to 'corge' cell++      cmd.exec('wysiwyg', 'removeRow');++      const expected = oneLineTrim`+        <table>+          <thead>+            <tr>+              <th>foo</th>+              <th class="te-cell-selected">bar</th>+            </tr>+          </thead>+          <tbody>+            <tr>+              <td class="te-cell-selected">corge</td>+              <td><br></td>+            </tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);+    });++    it('should not remove rows when cells are selected from table body to table head', () => {

워드에서는 다 삭제가 되네요~ 요건 내일 얘기해봐요

seonim-ryu

comment created time in 10 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { EditorState, Plugin, SelectionRange } from 'prosemirror-state';+import { EditorView, Decoration, DecorationSet } from 'prosemirror-view';++import CellSelection from './cellSelection';+import TableSelection from './tableSelectionView';++const SELECTED_CELL_CLASS_NAME = 'te-cell-selected';++function drawCellSelection({ selection, doc }: EditorState) {+  if (selection instanceof CellSelection) {+    const cells: Decoration[] = [];+    const { ranges } = selection;++    ranges.forEach(({ $from, $to }: SelectionRange) => {+      cells.push(Decoration.node($from.pos, $to.pos, { class: SELECTED_CELL_CLASS_NAME }));+    });++    return DecorationSet.create(doc, cells);+  }++  return null;+}++export function tableSelectionPlugin() {+  return new Plugin({+    props: {+      decorations: drawCellSelection+    },+    view(editorView: EditorView) {+      return new TableSelection(editorView);

아 인스턴스는 한번 생성될텐데 다른 부분 수정할때마다 저 인스턴스 update 메서드가 호출될 것 같아서요.

seonim-ryu

comment created time in 10 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

 export class Table extends Node {       }     };   }++  private addTable(): EditorCommand {+    return (payload = { columns: 1, rows: 1, data: [] }) => (state, dispatch) => {+      const { columns, rows, data } = payload;+      const { schema, selection, tr } = state;+      const { from, to, $from } = selection;+      const collapsed = from === to;++      if (collapsed && !isInTableNode(schema, $from)) {+        const { tableHead, tableBody } = schema.nodes;+        const tableHeadRows = createTableRows(columns, 1, schema, false, data);+        const tableBodyRows = createTableRows(columns, rows, schema, true, data);+        const table = schema.nodes.table.create(null, [+          tableHead.create(null, tableHeadRows),+          tableBody.create(null, tableBodyRows)+        ]);++        dispatch!(tr.replaceSelectionWith(table));++        return true;+      }++      return false;+    };+  }++  private removeTable(): EditorCommand {+    return () => (state, dispatch) => {+      const { selection, schema, tr } = state;+      const { head } = getResolvedSelection(schema, selection);+      const { table } = schema.nodes;+      const foundTable = findNodeBy(head, ({ type }: ProsemirrorNode) => type === table);++      if (foundTable) {+        const { depth } = foundTable;+        const startCellPos = head.before(depth);+        const endCellPos = head.after(depth);+        const cursorPos = createTextSelection(tr.delete(startCellPos, endCellPos), startCellPos);++        dispatch!(tr.setSelection(cursorPos));++        return true;+      }++      return false;+    };+  }++  private addColumn(): EditorCommand {+    return () => (state, dispatch) => {+      const { selection, schema, tr } = state;+      const { tableHeadCell, tableBodyCell } = schema.nodes;+      const { anchor, head } = getResolvedSelection(schema, selection);++      if (anchor && head) {+        const table = getTableByCellPos(head);++        const startCellPos = getCellPosition(anchor);+        const endCellPos = getCellPosition(head);++        const cellIndexes = getCellIndexesByRange(table, endCellPos, endCellPos);+        const cells = getAllCellPositionInfos(head);++        const columnCount = getColumnCount(table);++        cellIndexes.forEach(index => {+          const { offset, nodeSize } = cells[index];++          const startPos = tr.mapping.map(offset + nodeSize);+          const cellType = index < columnCount ? tableHeadCell : tableBodyCell;+          const addedCells = createCellsToAdd(startCellPos, endCellPos, columnCount, cellType);++          tr.insert(startPos, addedCells);+        });++        dispatch!(tr);++        return true;+      }++      return false;+    };+  }++  private removeColumn(): EditorCommand {+    return () => (state, dispatch) => {+      const { selection, schema, tr } = state;+      const { anchor, head } = getResolvedSelection(schema, selection);++      if (anchor && head) {+        const table = getTableByCellPos(head);++        const startCellPos = getCellPosition(anchor);+        const endCellPos = getCellPosition(head);+        const columnCount = getColumnCount(table);++        if (!isToRemoveCells(startCellPos, endCellPos, columnCount)) {+          return false;+        }++        const cellIndexes = getCellIndexesByRange(table, startCellPos, endCellPos);+        const cells = getAllCellPositionInfos(head);++        const startPos = tr.mapping.maps.length;++        cellIndexes.forEach(index => {+          const { offset, nodeSize } = cells[index];+          const from = tr.mapping.slice(startPos).map(offset);+          const to = from + nodeSize;++          tr.delete(from, to);+        });++        dispatch!(tr);++        return true;+      }++      return false;+    };+  }++  private addRow(): EditorCommand {+    return () => (state, dispatch) => {+      const { selection, schema, tr } = state;+      const { anchor, head } = getResolvedSelection(schema, selection);++      if (anchor && head) {+        const table = getTableByCellPos(head);++        const [startRowIndex] = getCellPosition(anchor);+        const [endRowIndex] = getCellPosition(head);++        const [from, to] = getPositionsToAddRow(head, startRowIndex, endRowIndex);+        const columnCount = getColumnCount(table);+        const rowCount = getCountByRange(startRowIndex, endRowIndex);+        const rows = createTableRows(columnCount, rowCount, schema, true);++        dispatch!(tr.step(new ReplaceStep(from, to, new Slice(Fragment.from(rows), 0, 0))));++        return true;+      }++      return false;+    };+  }++  private removeRow(): EditorCommand {+    return () => (state, dispatch) => {+      const { selection, schema, tr } = state;+      const { anchor, head } = getResolvedSelection(schema, selection);++      if (anchor && head) {+        const table = getTableByCellPos(head);++        const rowCount = getRowCount(table);+        const [from, to] = getPositionsToRemoveRow(anchor, head, rowCount);++        if (from && to) {+          dispatch!(tr.step(new ReplaceStep(from, to, Slice.empty)));++          return true;+        }+      }++      return false;+    };+  }++  private alignColumn(): EditorCommand {+    return (payload = { align: 'center' }) => (state, dispatch) => {+      const { align } = payload;+      const { selection, schema, tr } = state;+      const { anchor, head } = getResolvedSelection(schema, selection);++      if (anchor && head) {+        const table = getTableByCellPos(head);++        const startCellPos = getCellPosition(anchor);+        const endCellPos = getCellPosition(head);++        const cellIndexes = getCellIndexesByRange(table, startCellPos, endCellPos);+        const cells = getAllCellPositionInfos(head);++        cellIndexes.forEach(index => {+          const { offset } = cells[index];+          const { pos } = head.node(0).resolve(offset);++          tr.setNodeMarkup(pos, null!, { align });

ambinent 타입에 하나 추가하면 되지 않을까요?

seonim-ryu

comment created time in 10 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { oneLineTrim } from 'common-tags';++import WysiwygEditor from '@/wysiwyg/wwEditor';+import EventEmitter from '@/event/eventEmitter';+import CommandManager from '@/commands/commandManager';+import CellSelection from '@/wysiwyg/plugins/tableSelection/cellSelection';++describe('wysiwyg table commands', () => {+  let container: HTMLElement, wwe: WysiwygEditor, em: EventEmitter, cmd: CommandManager;++  function setCellSelection(from: number, to: number) {+    const { state, dispatch } = wwe.view;+    const { doc, tr } = state;++    const startCellPos = doc.resolve(from);+    const endCellPos = doc.resolve(to);+    const selection = new CellSelection(startCellPos, endCellPos);++    dispatch!(tr.setSelection(selection));+  }++  beforeEach(() => {+    container = document.createElement('div');+    document.body.appendChild(container);++    em = new EventEmitter();+    wwe = new WysiwygEditor(container, em);+    cmd = new CommandManager(em, {}, wwe.commands);+  });++  afterEach(() => {+    document.body.removeChild(container);+  });++  describe('addTable command', () => {+    it('should create 1 by 1 table', () => {+      cmd.exec('wysiwyg', 'addTable');++      const expected = oneLineTrim`+        <table>+          <thead>+            <tr><th><br></th></tr>+          </thead>+          <tbody>+            <tr><td><br></td></tr>+          </tbody>+        </table>+      `;++      expect(wwe.getHTML()).toBe(expected);+    });++    it('should create table with column and row count', () => {+      cmd.exec('wysiwyg', 'addTable', { columns: 2, rows: 3 });++      const expected = oneLineTrim`+        <table>+          <thead>+            <tr><th><br></th><th><br></th></tr>

요건 수정이 안되었군요ㅎ 수정하겠습니다~

seonim-ryu

comment created time in 10 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

 export class Table extends Node {       }     };   }++  private addTable(): EditorCommand {+    return (payload = { columns: 1, rows: 1, data: [] }) => (state, dispatch) => {+      const { columns, rows, data } = payload;+      const { schema, selection, tr } = state;+      const { from, to, $from } = selection;+      const collapsed = from === to;++      if (collapsed && !isInTableNode(schema, $from)) {+        const { tableHead, tableBody } = schema.nodes;+        const tableHeadRows = createTableRows(columns, 1, schema, false, data);+        const tableBodyRows = createTableRows(columns, rows, schema, true, data);+        const table = schema.nodes.table.create(null, [+          tableHead.create(null, tableHeadRows),+          tableBody.create(null, tableBodyRows)+        ]);++        dispatch!(tr.replaceSelectionWith(table));++        return true;+      }++      return false;+    };+  }++  private removeTable(): EditorCommand {+    return () => (state, dispatch) => {+      const { selection, schema, tr } = state;+      const { head } = getResolvedSelection(schema, selection);+      const { table } = schema.nodes;+      const foundTable = findNodeBy(head, ({ type }: ProsemirrorNode) => type === table);++      if (foundTable) {+        const { depth } = foundTable;+        const startCellPos = head.before(depth);+        const endCellPos = head.after(depth);+        const cursorPos = createTextSelection(tr.delete(startCellPos, endCellPos), startCellPos);++        dispatch!(tr.setSelection(cursorPos));++        return true;+      }++      return false;+    };+  }++  private addColumn(): EditorCommand {+    return () => (state, dispatch) => {+      const { selection, schema, tr } = state;+      const { tableHeadCell, tableBodyCell } = schema.nodes;+      const { anchor, head } = getResolvedSelection(schema, selection);++      if (anchor && head) {+        const table = getTableByCellPos(head);++        const startCellPos = getCellPosition(anchor);+        const endCellPos = getCellPosition(head);++        const cellIndexes = getCellIndexesByRange(table, endCellPos, endCellPos);+        const cells = getAllCellPositionInfos(head);++        const columnCount = getColumnCount(table);++        cellIndexes.forEach(index => {+          const { offset, nodeSize } = cells[index];++          const startPos = tr.mapping.map(offset + nodeSize);+          const cellType = index < columnCount ? tableHeadCell : tableBodyCell;

index < columnCount로 구분하는게 아니라 앞에서 정보를 주면 좋을 것 같다는 이야기였습니다!

seonim-ryu

comment created time in 10 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { Node, Schema, ResolvedPos } from 'prosemirror-model';+import { Selection, TextSelection } from 'prosemirror-state';++import { findNodeBy } from '@/wysiwyg/helper/node';++export interface CellInfo {+  offset: number;+  nodeSize: number;+}++export function createTableRows(+  columnCount: number,+  rowCount: number,+  schema: Schema,+  isTableBody: boolean,+  data?: string[] | null+) {+  const { tableRow, tableHeadCell, tableBodyCell } = schema.nodes;+  const tableRows = [];+  const cellType = isTableBody ? tableBodyCell : tableHeadCell;++  let index = isTableBody ? columnCount : 0;++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    const cells = [];++    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {+      const text = data && data[index];++      if (text) {+        index += 1;+      }++      cells.push(cellType.create(null, text ? schema.text(text) : []));+    }++    tableRows.push(tableRow.create(null, cells));+  }++  return tableRows;+}++export function createCellsToAdd(+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number,+  cellType: any+) {+  const cellCount =+    startRowIndex !== endRowIndex ? columnCount : getCountByRange(startColumnIndex, endColumnIndex);++  const cells = [];++  for (let i = 0, len = cellCount; i < len; i += 1) {+    cells.push(cellType.createAndFill());+  }++  return cells;+}++export function findCell({ nodes }: Schema, pos: ResolvedPos) {+  const { tableHeadCell, tableBodyCell } = nodes;++  return findNodeBy(pos, ({ type }: Node) => type === tableHeadCell || type === tableBodyCell);+}++export function isInCellElement(node: HTMLElement, root: Element) {+  while (node && node !== root) {+    if (node.nodeName === 'TD' || node.nodeName === 'TH') {+      return true;+    }++    node = node.parentNode as HTMLElement;+  }++  return false;+}++export function isToRemoveCells(+  [startRowIndex, stratColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number+) {+  const selectedColumnCount = getCountByRange(stratColumnIndex, endColumnIndex);+  const selectedRows = startRowIndex !== endRowIndex;+  const selectedAllColumns = startRowIndex === endRowIndex && selectedColumnCount === columnCount;++  if (selectedRows || selectedAllColumns) {+    return false;+  }++  return true;+}++export function getResolvedSelection(schema: Schema, selection: Selection) {+  const { $anchor, $head } = selection;+  let anchor = $anchor;+  let head = $head;++  if (selection instanceof TextSelection) {+    const foundCell = findCell(schema, $anchor);++    if (foundCell) {+      anchor = $anchor.node(0).resolve($anchor.before(foundCell.depth));+      head = anchor;+    }+  }++  return { anchor, head };+}++function getCellPositionInfos(headOrBody: Node, startOffset: number) {+  const cellInfos: CellInfo[] = [];++  headOrBody.forEach((row: Node, rowOffset: number) => {+    row.forEach(({ nodeSize }: Node, cellOffset: number) => {+      cellInfos.push({+        offset: startOffset + rowOffset + cellOffset + 2,

주석을 다는게 더 나을 것 같아요ㅎㅎ

seonim-ryu

comment created time in 10 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { Node, Schema, ResolvedPos } from 'prosemirror-model';+import { Selection, TextSelection } from 'prosemirror-state';++import { findNodeBy } from '@/wysiwyg/helper/node';++export interface CellInfo {+  offset: number;+  nodeSize: number;+}++export function createTableRows(+  columnCount: number,+  rowCount: number,+  schema: Schema,+  isTableBody: boolean,+  data?: string[] | null+) {+  const { tableRow, tableHeadCell, tableBodyCell } = schema.nodes;+  const tableRows = [];+  const cellType = isTableBody ? tableBodyCell : tableHeadCell;++  let index = isTableBody ? columnCount : 0;++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    const cells = [];++    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {+      const text = data && data[index];++      if (text) {+        index += 1;+      }++      cells.push(cellType.create(null, text ? schema.text(text) : []));+    }++    tableRows.push(tableRow.create(null, cells));+  }++  return tableRows;+}++export function createCellsToAdd(+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number,+  cellType: any+) {+  const cellCount =+    startRowIndex !== endRowIndex ? columnCount : getCountByRange(startColumnIndex, endColumnIndex);++  const cells = [];++  for (let i = 0, len = cellCount; i < len; i += 1) {+    cells.push(cellType.createAndFill());+  }++  return cells;+}++export function findCell({ nodes }: Schema, pos: ResolvedPos) {+  const { tableHeadCell, tableBodyCell } = nodes;++  return findNodeBy(pos, ({ type }: Node) => type === tableHeadCell || type === tableBodyCell);+}++export function isInCellElement(node: HTMLElement, root: Element) {+  while (node && node !== root) {+    if (node.nodeName === 'TD' || node.nodeName === 'TH') {+      return true;+    }++    node = node.parentNode as HTMLElement;+  }++  return false;+}++export function isToRemoveCells(+  [startRowIndex, stratColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number+) {+  const selectedColumnCount = getCountByRange(stratColumnIndex, endColumnIndex);+  const selectedRows = startRowIndex !== endRowIndex;+  const selectedAllColumns = startRowIndex === endRowIndex && selectedColumnCount === columnCount;

근데 모든 컬럼이 선택되었을 때만 막아야하지 않을까요?

seonim-ryu

comment created time in 10 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { Node, Schema, ResolvedPos } from 'prosemirror-model';+import { Selection, TextSelection } from 'prosemirror-state';++import { findNodeBy } from '@/wysiwyg/helper/node';++export interface CellInfo {+  offset: number;+  nodeSize: number;+}++export function createTableRows(+  columnCount: number,+  rowCount: number,+  schema: Schema,+  isTableBody: boolean,+  data?: string[] | null+) {+  const { tableRow, tableHeadCell, tableBodyCell } = schema.nodes;+  const tableRows = [];+  const cellType = isTableBody ? tableBodyCell : tableHeadCell;++  let index = isTableBody ? columnCount : 0;++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    const cells = [];++    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {+      const text = data && data[index];++      if (text) {+        index += 1;+      }++      cells.push(cellType.create(null, text ? schema.text(text) : []));+    }++    tableRows.push(tableRow.create(null, cells));+  }++  return tableRows;+}++export function createCellsToAdd(+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number,+  cellType: any+) {+  const cellCount =+    startRowIndex !== endRowIndex ? columnCount : getCountByRange(startColumnIndex, endColumnIndex);++  const cells = [];++  for (let i = 0, len = cellCount; i < len; i += 1) {+    cells.push(cellType.createAndFill());+  }++  return cells;+}++export function findCell({ nodes }: Schema, pos: ResolvedPos) {+  const { tableHeadCell, tableBodyCell } = nodes;++  return findNodeBy(pos, ({ type }: Node) => type === tableHeadCell || type === tableBodyCell);+}++export function isInCellElement(node: HTMLElement, root: Element) {+  while (node && node !== root) {+    if (node.nodeName === 'TD' || node.nodeName === 'TH') {+      return true;+    }++    node = node.parentNode as HTMLElement;+  }++  return false;+}++export function isToRemoveCells(+  [startRowIndex, stratColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number+) {+  const selectedColumnCount = getCountByRange(stratColumnIndex, endColumnIndex);+  const selectedRows = startRowIndex !== endRowIndex;+  const selectedAllColumns = startRowIndex === endRowIndex && selectedColumnCount === columnCount;

아 넵 이해했습니다!

seonim-ryu

comment created time in 10 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { Node, Schema, ResolvedPos } from 'prosemirror-model';+import { Selection, TextSelection } from 'prosemirror-state';++import { findNodeBy } from '@/wysiwyg/helper/node';++export interface CellInfo {+  offset: number;+  nodeSize: number;+}++export function createTableRows(+  columnCount: number,+  rowCount: number,+  schema: Schema,+  isTableBody: boolean,+  data?: string[] | null+) {+  const { tableRow, tableHeadCell, tableBodyCell } = schema.nodes;+  const tableRows = [];+  const cellType = isTableBody ? tableBodyCell : tableHeadCell;++  let index = isTableBody ? columnCount : 0;++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    const cells = [];++    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {+      const text = data && data[index];++      if (text) {+        index += 1;+      }++      cells.push(cellType.create(null, text ? schema.text(text) : []));+    }++    tableRows.push(tableRow.create(null, cells));+  }++  return tableRows;+}++export function createCellsToAdd(+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number,+  cellType: any+) {+  const cellCount =+    startRowIndex !== endRowIndex ? columnCount : getCountByRange(startColumnIndex, endColumnIndex);++  const cells = [];++  for (let i = 0, len = cellCount; i < len; i += 1) {+    cells.push(cellType.createAndFill());+  }++  return cells;+}++export function findCell({ nodes }: Schema, pos: ResolvedPos) {+  const { tableHeadCell, tableBodyCell } = nodes;++  return findNodeBy(pos, ({ type }: Node) => type === tableHeadCell || type === tableBodyCell);+}++export function isInCellElement(node: HTMLElement, root: Element) {+  while (node && node !== root) {+    if (node.nodeName === 'TD' || node.nodeName === 'TH') {+      return true;+    }++    node = node.parentNode as HTMLElement;+  }++  return false;+}++export function isToRemoveCells(

judgeToRemoveCells는 어떤가요?

seonim-ryu

comment created time in 10 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { Node, Schema, ResolvedPos } from 'prosemirror-model';+import { Selection, TextSelection } from 'prosemirror-state';++import { findNodeBy } from '@/wysiwyg/helper/node';++export interface CellInfo {+  offset: number;+  nodeSize: number;+}++export function createTableRows(+  columnCount: number,+  rowCount: number,+  schema: Schema,+  isTableBody: boolean,+  data?: string[] | null+) {+  const { tableRow, tableHeadCell, tableBodyCell } = schema.nodes;+  const tableRows = [];+  const cellType = isTableBody ? tableBodyCell : tableHeadCell;++  let index = isTableBody ? columnCount : 0;++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    const cells = [];++    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {+      const text = data && data[index];++      if (text) {+        index += 1;+      }++      cells.push(cellType.create(null, text ? schema.text(text) : []));+    }++    tableRows.push(tableRow.create(null, cells));+  }++  return tableRows;+}++export function createCellsToAdd(+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number,+  cellType: any+) {+  const cellCount =+    startRowIndex !== endRowIndex ? columnCount : getCountByRange(startColumnIndex, endColumnIndex);++  const cells = [];++  for (let i = 0, len = cellCount; i < len; i += 1) {+    cells.push(cellType.createAndFill());+  }++  return cells;+}++export function findCell({ nodes }: Schema, pos: ResolvedPos) {+  const { tableHeadCell, tableBodyCell } = nodes;++  return findNodeBy(pos, ({ type }: Node) => type === tableHeadCell || type === tableBodyCell);+}

넵~!

seonim-ryu

comment created time in 10 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { Node, Schema, ResolvedPos } from 'prosemirror-model';+import { Selection, TextSelection } from 'prosemirror-state';++import { findNodeBy } from '@/wysiwyg/helper/node';++export interface CellInfo {+  offset: number;+  nodeSize: number;+}++export function createTableRows(+  columnCount: number,+  rowCount: number,+  schema: Schema,+  isTableBody: boolean,+  data?: string[] | null+) {+  const { tableRow, tableHeadCell, tableBodyCell } = schema.nodes;+  const tableRows = [];+  const cellType = isTableBody ? tableBodyCell : tableHeadCell;++  let index = isTableBody ? columnCount : 0;++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    const cells = [];++    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {+      const text = data && data[index];++      if (text) {+        index += 1;+      }++      cells.push(cellType.create(null, text ? schema.text(text) : []));+    }++    tableRows.push(tableRow.create(null, cells));+  }++  return tableRows;+}++export function createCellsToAdd(+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number,+  cellType: any

아 그러네요 프로즈미러 쪽에서도 아예 타입이 any군요..

seonim-ryu

comment created time in 10 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { Node, Schema, ResolvedPos } from 'prosemirror-model';+import { Selection, TextSelection } from 'prosemirror-state';++import { findNodeBy } from '@/wysiwyg/helper/node';++export interface CellInfo {+  offset: number;+  nodeSize: number;+}++export function createTableRows(+  columnCount: number,+  rowCount: number,+  schema: Schema,+  isTableBody: boolean,+  data?: string[] | null+) {+  const { tableRow, tableHeadCell, tableBodyCell } = schema.nodes;+  const tableRows = [];+  const cellType = isTableBody ? tableBodyCell : tableHeadCell;++  let index = isTableBody ? columnCount : 0;++  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {+    const cells = [];++    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {+      const text = data && data[index];++      if (text) {+        index += 1;+      }++      cells.push(cellType.create(null, text ? schema.text(text) : []));+    }++    tableRows.push(tableRow.create(null, cells));+  }++  return tableRows;+}++export function createCellsToAdd(+  [startRowIndex, startColumnIndex]: number[],+  [endRowIndex, endColumnIndex]: number[],+  columnCount: number,+  cellType: any+) {+  const cellCount =+    startRowIndex !== endRowIndex ? columnCount : getCountByRange(startColumnIndex, endColumnIndex);

여러 행이 선택 되어도 행들의 보통 컬럼 범위 만큼 선택되지 않나요? 만약 첫 번째 컬럼만 여러행 선택하면 하나가 아니라 컬럼 수만큼 추가될텐데 사용자가 원하는 동작은 아닐 것 같아요

seonim-ryu

comment created time in 10 hours

Pull request review commentnhn/tui.editor

feat: add wysiwyg table commands

+import { EditorState, Plugin, SelectionRange } from 'prosemirror-state';+import { EditorView, Decoration, DecorationSet } from 'prosemirror-view';++import CellSelection from './cellSelection';+import TableSelection from './tableSelectionView';++const SELECTED_CELL_CLASS_NAME = 'te-cell-selected';++function drawCellSelection({ selection, doc }: EditorState) {+  if (selection instanceof CellSelection) {+    const cells: Decoration[] = [];+    const { ranges } = selection;++    ranges.forEach(({ $from, $to }: SelectionRange) => {+      cells.push(Decoration.node($from.pos, $to.pos, { class: SELECTED_CELL_CLASS_NAME }));+    });++    return DecorationSet.create(doc, cells);+  }++  return null;+}++export function tableSelectionPlugin() {+  return new Plugin({+    props: {+      decorations: drawCellSelection+    },+    view(editorView: EditorView) {+      return new TableSelection(editorView);

플러그인 생성할 때 뷰가 한번만 생성되는거 아닌가요~? 제가 테스트 했을 땐 수정될 때 TableSelection 인스턴스를 매번 생성하지는 않는 것 같아요. 혹시 제가 잘못 이해하고 있는건가요..?

seonim-ryu

comment created time in 10 hours

more